From 7b4f30c599c0f52b4a5465263b64557a7c3123d0 Mon Sep 17 00:00:00 2001 From: ofekshenawa Date: Mon, 15 Jan 2024 22:45:49 +0200 Subject: [PATCH] Add client tracking and client trackinginfo commands --- commands.go | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ commands_test.go | 6 ++++ 2 files changed, 97 insertions(+) diff --git a/commands.go b/commands.go index 546ebafb2..fb08fe750 100644 --- a/commands.go +++ b/commands.go @@ -236,6 +236,12 @@ type StatefulCmdable interface { SwapDB(ctx context.Context, index1, index2 int) *StatusCmd ClientSetName(ctx context.Context, name string) *BoolCmd ClientSetInfo(ctx context.Context, info LibraryInfo) *StatusCmd + ClientTracking(ctx context.Context, on bool, options *ClientTrackingOptions) *StatusCmd + ClientTrackingOn(ctx context.Context) *StatusCmd + ClientTrackingOff(ctx context.Context) *StatusCmd + ClientTrackingOnWithArgs(ctx context.Context, options *ClientTrackingOptions) *StatusCmd + ClientTrackingOffWithArgs(ctx context.Context, options *ClientTrackingOptions) *StatusCmd + ClientTrackingInfo(ctx context.Context) *MapStringInterfaceCmd Hello(ctx context.Context, ver int, username, password, clientName string) *MapStringInterfaceCmd } @@ -319,6 +325,91 @@ func (c statefulCmdable) ClientSetInfo(ctx context.Context, info LibraryInfo) *S return cmd } +type ClientTrackingOptions struct { + ClientID int + Prefixes []interface{} + BCast bool + OptIn bool + OptOut bool + NoLoop bool +} + +// Enables the tracking feature of the Redis server, that is used +// for server assisted client side caching. +// “on“ indicate for tracking on or tracking off. The dafualt is on. +// “clientid“ send invalidation messages to the connection with +// the specified ID. +// “bcast“ enable tracking in broadcasting mode. In this mode +// invalidation messages are reported for all the prefixes +// specified, regardless of the keys requested by the connection. +// “optin“ when broadcasting is NOT active, normally don't track +// keys in read only commands, unless they are called immediately +// after a CLIENT CACHING yes command. +// “optout“ when broadcasting is NOT active, normally track keys in +// read only commands, unless they are called immediately after a +// CLIENT CACHING no command. +// “noloop“ don't send notifications about keys modified by this +// connection itself. +// “prefixes“ for broadcasting, register a given key prefix, so that +// notifications will be provided only for keys starting with this string. +// For more innformation - https://redis.io/commands/client-tracking +func (c statefulCmdable) ClientTracking(ctx context.Context, on bool, options *ClientTrackingOptions) *StatusCmd { + args := []interface{}{"CLIENT", "TRACKING"} + if on { + args = append(args, "ON") + } else { + args = append(args, "OFF") + } + if options != nil { + if options.Prefixes != nil && !options.BCast { + panic("prefixes can only be used with BCast") + } + if options.ClientID != 0 { + args = append(args, "REDIRECT", options.ClientID) + } + for _, prefix := range options.Prefixes { + args = append(args, "PREFIX", prefix) + } + if options.BCast { + args = append(args, "BCAST") + } + if options.OptIn { + args = append(args, "OPTIN") + } + if options.OptOut { + args = append(args, "OPTOUT") + } + if options.NoLoop { + args = append(args, "NOLOOP") + } + } + cmd := NewStatusCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c statefulCmdable) ClientTrackingOn(ctx context.Context) *StatusCmd { + return c.ClientTracking(ctx, true, nil) +} + +func (c statefulCmdable) ClientTrackingOff(ctx context.Context) *StatusCmd { + return c.ClientTracking(ctx, false, nil) +} + +func (c statefulCmdable) ClientTrackingOnWithArgs(ctx context.Context, options *ClientTrackingOptions) *StatusCmd { + return c.ClientTracking(ctx, true, options) +} + +func (c statefulCmdable) ClientTrackingOffWithArgs(ctx context.Context, options *ClientTrackingOptions) *StatusCmd { + return c.ClientTracking(ctx, false, options) +} + +func (c statefulCmdable) ClientTrackingInfo(ctx context.Context) *MapStringInterfaceCmd { + cmd := NewMapStringInterfaceCmd(ctx, "CLIENT", "TRACKINGINFO") + _ = c(ctx, cmd) + return cmd +} + // Validate checks if only one field in the struct is non-nil. func (info LibraryInfo) Validate() error { if info.LibName != nil && info.LibVer != nil { diff --git a/commands_test.go b/commands_test.go index 3d2ebf514..45000c0b2 100644 --- a/commands_test.go +++ b/commands_test.go @@ -243,6 +243,12 @@ var _ = Describe("Commands", func() { Expect(get.Val()).To(Equal("theclientname")) }) + It("should ClientTrackingInfo", func() { + clientTrackingInfo, err := client.Conn().ClientTrackingInfo(ctx).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(clientTrackingInfo["prefixes"]).To(Equal([]interface{}{})) + }) + It("should ClientSetInfo", func() { pipe := client.Pipeline()