mirror of
https://github.com/redis/go-redis.git
synced 2026-10-28 08:24:54 +08:00
fix(cmd): keep SetArgs ExpireAt milliseconds (#4053)
SetArgs always sent ExpireAt as EXAT with Unix seconds, so any millisecond part was silently dropped. Send PXAT with Unix milliseconds when ExpireAt has millisecond precision, and keep EXAT otherwise.
This commit is contained in:
1 parent
d8ee0a5e13
commit
a1a0e268ba
2 files changed
+48
-2
No files matched your search
+7
-2
@@ -516,7 +516,8 @@ type SetArgs struct {
|
||||
MatchDigest uint64
|
||||
|
||||
// Zero `TTL` or `Expiration` means that the key has no expiration time.
|
||||
TTL time.Duration
|
||||
TTL time.Duration
|
||||
// ExpireAt is sent as EXAT, or as PXAT when it has millisecond precision.
|
||||
ExpireAt time.Time
|
||||
|
||||
// When Get is true, the command returns the old value stored at key, or nil when key did not exist.
|
||||
@@ -538,7 +539,11 @@ func (c cmdable) SetArgs(ctx context.Context, key string, value interface{}, a S
|
||||
}
|
||||
|
||||
if !a.ExpireAt.IsZero() {
|
||||
args = append(args, "exat", a.ExpireAt.Unix())
|
||||
if a.ExpireAt.Nanosecond() >= int(time.Millisecond) {
|
||||
args = append(args, "pxat", a.ExpireAt.UnixMilli())
|
||||
} else {
|
||||
args = append(args, "exat", a.ExpireAt.Unix())
|
||||
}
|
||||
}
|
||||
if a.TTL > 0 {
|
||||
if usePrecise(a.TTL) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// captureCmdable returns a cmdable that records the Cmder passed to it without
|
||||
@@ -176,3 +177,43 @@ func TestIncrEXFloat_Args(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetArgs_ExpireAt(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expireAt time.Time
|
||||
want []interface{}
|
||||
}{
|
||||
{
|
||||
name: "whole_seconds",
|
||||
expireAt: time.Unix(1700000000, 0),
|
||||
want: []interface{}{"set", "key", "value", "exat", int64(1700000000)},
|
||||
},
|
||||
{
|
||||
name: "sub_millisecond_is_truncated",
|
||||
expireAt: time.Unix(1700000000, 500_000),
|
||||
want: []interface{}{"set", "key", "value", "exat", int64(1700000000)},
|
||||
},
|
||||
{
|
||||
name: "milliseconds",
|
||||
expireAt: time.UnixMilli(1700000000123),
|
||||
want: []interface{}{"set", "key", "value", "pxat", int64(1700000000123)},
|
||||
},
|
||||
{
|
||||
name: "beyond_nanosecond_range",
|
||||
expireAt: time.Date(2270, time.January, 2, 3, 4, 5, 123000000, time.UTC),
|
||||
want: []interface{}{"set", "key", "value", "pxat", int64(9467204645123)},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var cmd Cmder
|
||||
c := captureCmdable(&cmd)
|
||||
c.SetArgs(context.Background(), "key", "value", SetArgs{ExpireAt: tt.expireAt})
|
||||
if got := cmd.Args(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("args = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user