mirror of
https://github.com/redis/go-redis.git
synced 2025-11-08 13:02:18 +03:00
feat(cmd): Add support for MSetEX command (#3580)
Co-authored-by: Nedyalko Dyakov <1547186+ndyakov@users.noreply.github.com>
This commit is contained in:
131
commands_test.go
131
commands_test.go
@@ -1935,6 +1935,137 @@ var _ = Describe("Commands", func() {
|
|||||||
Expect(mSetNX.Val()).To(Equal(true))
|
Expect(mSetNX.Val()).To(Equal(true))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("should MSetEX", func() {
|
||||||
|
SkipBeforeRedisVersion(8.3, "MSetEX is available since redis 8.4")
|
||||||
|
args := redis.MSetEXArgs{
|
||||||
|
Expiration: &redis.ExpirationOption{
|
||||||
|
Mode: redis.EX,
|
||||||
|
Value: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mSetEX := client.MSetEX(ctx, args, "key1", "hello1", "key2", "hello2")
|
||||||
|
Expect(mSetEX.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(mSetEX.Val()).To(Equal(int64(1)))
|
||||||
|
|
||||||
|
// Verify keys were set
|
||||||
|
val1 := client.Get(ctx, "key1")
|
||||||
|
Expect(val1.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(val1.Val()).To(Equal("hello1"))
|
||||||
|
|
||||||
|
val2 := client.Get(ctx, "key2")
|
||||||
|
Expect(val2.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(val2.Val()).To(Equal("hello2"))
|
||||||
|
|
||||||
|
// Verify TTL was set
|
||||||
|
ttl1 := client.TTL(ctx, "key1")
|
||||||
|
Expect(ttl1.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(ttl1.Val()).To(BeNumerically(">", 0))
|
||||||
|
Expect(ttl1.Val()).To(BeNumerically("<=", 1*time.Second))
|
||||||
|
|
||||||
|
ttl2 := client.TTL(ctx, "key2")
|
||||||
|
Expect(ttl2.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(ttl2.Val()).To(BeNumerically(">", 0))
|
||||||
|
Expect(ttl2.Val()).To(BeNumerically("<=", 1*time.Second))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should MSetEX with NX mode", func() {
|
||||||
|
SkipBeforeRedisVersion(8.3, "MSetEX is available since redis 8.4")
|
||||||
|
|
||||||
|
client.Set(ctx, "key1", "existing", 0)
|
||||||
|
|
||||||
|
// Try to set with NX mode - should fail because key1 exists
|
||||||
|
args := redis.MSetEXArgs{
|
||||||
|
Condition: redis.NX,
|
||||||
|
Expiration: &redis.ExpirationOption{
|
||||||
|
Mode: redis.EX,
|
||||||
|
Value: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mSetEX := client.MSetEX(ctx, args, "key1", "new1", "key2", "new2")
|
||||||
|
Expect(mSetEX.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(mSetEX.Val()).To(Equal(int64(0)))
|
||||||
|
|
||||||
|
val1 := client.Get(ctx, "key1")
|
||||||
|
Expect(val1.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(val1.Val()).To(Equal("existing"))
|
||||||
|
|
||||||
|
val2 := client.Get(ctx, "key2")
|
||||||
|
Expect(val2.Err()).To(Equal(redis.Nil))
|
||||||
|
|
||||||
|
client.Del(ctx, "key1")
|
||||||
|
|
||||||
|
// Now try with NX mode when keys don't exist - should succeed
|
||||||
|
mSetEX = client.MSetEX(ctx, args, "key1", "new1", "key2", "new2")
|
||||||
|
Expect(mSetEX.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(mSetEX.Val()).To(Equal(int64(1)))
|
||||||
|
|
||||||
|
val1 = client.Get(ctx, "key1")
|
||||||
|
Expect(val1.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(val1.Val()).To(Equal("new1"))
|
||||||
|
|
||||||
|
val2 = client.Get(ctx, "key2")
|
||||||
|
Expect(val2.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(val2.Val()).To(Equal("new2"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should MSetEX with XX mode", func() {
|
||||||
|
SkipBeforeRedisVersion(8.3, "MSetEX is available since redis 8.4")
|
||||||
|
|
||||||
|
args := redis.MSetEXArgs{
|
||||||
|
Condition: redis.XX,
|
||||||
|
Expiration: &redis.ExpirationOption{
|
||||||
|
Mode: redis.EX,
|
||||||
|
Value: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mSetEX := client.MSetEX(ctx, args, "key1", "new1", "key2", "new2")
|
||||||
|
Expect(mSetEX.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(mSetEX.Val()).To(Equal(int64(0)))
|
||||||
|
|
||||||
|
client.Set(ctx, "key1", "existing1", 0)
|
||||||
|
client.Set(ctx, "key2", "existing2", 0)
|
||||||
|
|
||||||
|
mSetEX = client.MSetEX(ctx, args, "key1", "new1", "key2", "new2")
|
||||||
|
Expect(mSetEX.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(mSetEX.Val()).To(Equal(int64(1)))
|
||||||
|
|
||||||
|
val1 := client.Get(ctx, "key1")
|
||||||
|
Expect(val1.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(val1.Val()).To(Equal("new1"))
|
||||||
|
|
||||||
|
val2 := client.Get(ctx, "key2")
|
||||||
|
Expect(val2.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(val2.Val()).To(Equal("new2"))
|
||||||
|
|
||||||
|
ttl1 := client.TTL(ctx, "key1")
|
||||||
|
Expect(ttl1.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(ttl1.Val()).To(BeNumerically(">", 0))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should MSetEX with map", func() {
|
||||||
|
SkipBeforeRedisVersion(8.3, "MSetEX is available since redis 8.4")
|
||||||
|
args := redis.MSetEXArgs{
|
||||||
|
Expiration: &redis.ExpirationOption{
|
||||||
|
Mode: redis.EX,
|
||||||
|
Value: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mSetEX := client.MSetEX(ctx, args, map[string]interface{}{
|
||||||
|
"key1": "value1",
|
||||||
|
"key2": "value2",
|
||||||
|
})
|
||||||
|
Expect(mSetEX.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(mSetEX.Val()).To(Equal(int64(1)))
|
||||||
|
|
||||||
|
val1 := client.Get(ctx, "key1")
|
||||||
|
Expect(val1.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(val1.Val()).To(Equal("value1"))
|
||||||
|
|
||||||
|
val2 := client.Get(ctx, "key2")
|
||||||
|
Expect(val2.Err()).NotTo(HaveOccurred())
|
||||||
|
Expect(val2.Val()).To(Equal("value2"))
|
||||||
|
})
|
||||||
|
|
||||||
It("should SetWithArgs with TTL", func() {
|
It("should SetWithArgs with TTL", func() {
|
||||||
args := redis.SetArgs{
|
args := redis.SetArgs{
|
||||||
TTL: 500 * time.Millisecond,
|
TTL: 500 * time.Millisecond,
|
||||||
|
|||||||
@@ -22,4 +22,3 @@ retract (
|
|||||||
v9.7.2 // This version was accidentally released. Please use version 9.7.3 instead.
|
v9.7.2 // This version was accidentally released. Please use version 9.7.3 instead.
|
||||||
v9.5.3 // This version was accidentally released. Please use version 9.6.0 instead.
|
v9.5.3 // This version was accidentally released. Please use version 9.6.0 instead.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -19,4 +19,3 @@ retract (
|
|||||||
v9.7.2 // This version was accidentally released. Please use version 9.7.3 instead.
|
v9.7.2 // This version was accidentally released. Please use version 9.7.3 instead.
|
||||||
v9.5.3 // This version was accidentally released. Please use version 9.6.0 instead.
|
v9.5.3 // This version was accidentally released. Please use version 9.6.0 instead.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -27,4 +27,3 @@ retract (
|
|||||||
v9.7.2 // This version was accidentally released. Please use version 9.7.3 instead.
|
v9.7.2 // This version was accidentally released. Please use version 9.7.3 instead.
|
||||||
v9.5.3 // This version was accidentally released. Please use version 9.6.0 instead.
|
v9.5.3 // This version was accidentally released. Please use version 9.6.0 instead.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -26,4 +26,3 @@ retract (
|
|||||||
v9.7.2 // This version was accidentally released. Please use version 9.7.3 instead.
|
v9.7.2 // This version was accidentally released. Please use version 9.7.3 instead.
|
||||||
v9.5.3 // This version was accidentally released. Please use version 9.6.0 instead.
|
v9.5.3 // This version was accidentally released. Please use version 9.6.0 instead.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ type StringCmdable interface {
|
|||||||
MGet(ctx context.Context, keys ...string) *SliceCmd
|
MGet(ctx context.Context, keys ...string) *SliceCmd
|
||||||
MSet(ctx context.Context, values ...interface{}) *StatusCmd
|
MSet(ctx context.Context, values ...interface{}) *StatusCmd
|
||||||
MSetNX(ctx context.Context, values ...interface{}) *BoolCmd
|
MSetNX(ctx context.Context, values ...interface{}) *BoolCmd
|
||||||
|
MSetEX(ctx context.Context, args MSetEXArgs, values ...interface{}) *IntCmd
|
||||||
Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd
|
Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd
|
||||||
SetArgs(ctx context.Context, key string, value interface{}, a SetArgs) *StatusCmd
|
SetArgs(ctx context.Context, key string, value interface{}, a SetArgs) *StatusCmd
|
||||||
SetEx(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd
|
SetEx(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd
|
||||||
@@ -112,6 +113,35 @@ func (c cmdable) IncrByFloat(ctx context.Context, key string, value float64) *Fl
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SetCondition string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NX only set the keys and their expiration if none exist
|
||||||
|
NX SetCondition = "NX"
|
||||||
|
// XX only set the keys and their expiration if all already exist
|
||||||
|
XX SetCondition = "XX"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExpirationMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// EX sets expiration in seconds
|
||||||
|
EX ExpirationMode = "EX"
|
||||||
|
// PX sets expiration in milliseconds
|
||||||
|
PX ExpirationMode = "PX"
|
||||||
|
// EXAT sets expiration as Unix timestamp in seconds
|
||||||
|
EXAT ExpirationMode = "EXAT"
|
||||||
|
// PXAT sets expiration as Unix timestamp in milliseconds
|
||||||
|
PXAT ExpirationMode = "PXAT"
|
||||||
|
// KEEPTTL keeps the existing TTL
|
||||||
|
KEEPTTL ExpirationMode = "KEEPTTL"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExpirationOption struct {
|
||||||
|
Mode ExpirationMode
|
||||||
|
Value int64
|
||||||
|
}
|
||||||
|
|
||||||
func (c cmdable) LCS(ctx context.Context, q *LCSQuery) *LCSCmd {
|
func (c cmdable) LCS(ctx context.Context, q *LCSQuery) *LCSCmd {
|
||||||
cmd := NewLCSCmd(ctx, q)
|
cmd := NewLCSCmd(ctx, q)
|
||||||
_ = c(ctx, cmd)
|
_ = c(ctx, cmd)
|
||||||
@@ -157,6 +187,49 @@ func (c cmdable) MSetNX(ctx context.Context, values ...interface{}) *BoolCmd {
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MSetEXArgs struct {
|
||||||
|
Condition SetCondition
|
||||||
|
Expiration *ExpirationOption
|
||||||
|
}
|
||||||
|
|
||||||
|
// MSetEX sets the given keys to their respective values.
|
||||||
|
// This command is an extension of the MSETNX that adds expiration and XX options.
|
||||||
|
// Available since Redis 8.4
|
||||||
|
// Important: When this method is used with Cluster clients, all keys
|
||||||
|
// must be in the same hash slot, otherwise CROSSSLOT error will be returned.
|
||||||
|
// For more information, see https://redis.io/commands/msetex
|
||||||
|
func (c cmdable) MSetEX(ctx context.Context, args MSetEXArgs, values ...interface{}) *IntCmd {
|
||||||
|
expandedArgs := appendArgs([]interface{}{}, values)
|
||||||
|
numkeys := len(expandedArgs) / 2
|
||||||
|
|
||||||
|
cmdArgs := make([]interface{}, 0, 2+len(expandedArgs)+3)
|
||||||
|
cmdArgs = append(cmdArgs, "msetex", numkeys)
|
||||||
|
cmdArgs = append(cmdArgs, expandedArgs...)
|
||||||
|
|
||||||
|
if args.Condition != "" {
|
||||||
|
cmdArgs = append(cmdArgs, string(args.Condition))
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.Expiration != nil {
|
||||||
|
switch args.Expiration.Mode {
|
||||||
|
case EX:
|
||||||
|
cmdArgs = append(cmdArgs, "ex", args.Expiration.Value)
|
||||||
|
case PX:
|
||||||
|
cmdArgs = append(cmdArgs, "px", args.Expiration.Value)
|
||||||
|
case EXAT:
|
||||||
|
cmdArgs = append(cmdArgs, "exat", args.Expiration.Value)
|
||||||
|
case PXAT:
|
||||||
|
cmdArgs = append(cmdArgs, "pxat", args.Expiration.Value)
|
||||||
|
case KEEPTTL:
|
||||||
|
cmdArgs = append(cmdArgs, "keepttl")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := NewIntCmd(ctx, cmdArgs...)
|
||||||
|
_ = c(ctx, cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
// Set Redis `SET key value [expiration]` command.
|
// Set Redis `SET key value [expiration]` command.
|
||||||
// Use expiration for `SETEx`-like behavior.
|
// Use expiration for `SETEx`-like behavior.
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user