You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kvrocks.apache.org by ti...@apache.org on 2022/10/07 14:29:35 UTC
[incubator-kvrocks] branch unstable updated: Move TCL test unit/expire to Go case (#955)
This is an automated email from the ASF dual-hosted git repository.
tison pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/incubator-kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new 6e52105 Move TCL test unit/expire to Go case (#955)
6e52105 is described below
commit 6e521051ba81aa168312ed695a7bbaf06645a158
Author: HaveAnOrangeCat <ma...@gmail.com>
AuthorDate: Fri Oct 7 22:29:29 2022 +0800
Move TCL test unit/expire to Go case (#955)
Co-authored-by: MaoChongxin <ch...@shopee.com>
Co-authored-by: tison <wa...@gmail.com>
---
tests/gocase/unit/expire/expire_test.go | 218 ++++++++++++++++++++++++++++++++
tests/gocase/util/assertions.go | 20 ++-
tests/tcl/tests/test_helper.tcl | 1 -
tests/tcl/tests/unit/expire.tcl | 206 ------------------------------
4 files changed, 236 insertions(+), 209 deletions(-)
diff --git a/tests/gocase/unit/expire/expire_test.go b/tests/gocase/unit/expire/expire_test.go
new file mode 100644
index 0000000..158a7cb
--- /dev/null
+++ b/tests/gocase/unit/expire/expire_test.go
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package expire
+
+import (
+ "context"
+ "sort"
+ "testing"
+ "time"
+
+ "github.com/apache/incubator-kvrocks/tests/gocase/util"
+ "github.com/stretchr/testify/require"
+)
+
+func TestExpire(t *testing.T) {
+ svr := util.StartServer(t, map[string]string{})
+ defer svr.Close()
+
+ ctx := context.Background()
+ rdb := svr.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ t.Run("EXPIRE - set timeouts multiple times", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "x", "foobar", 0).Err())
+ require.True(t, rdb.Expire(ctx, "x", 5*time.Second).Val())
+ util.BetweenValues(t, rdb.TTL(ctx, "x").Val(), 4*time.Second, 5*time.Second)
+ require.True(t, rdb.Expire(ctx, "x", 10*time.Second).Val())
+ require.Equal(t, 10*time.Second, rdb.TTL(ctx, "x").Val())
+ require.NoError(t, rdb.Expire(ctx, "x", 2*time.Second).Err())
+ })
+
+ t.Run("EXPIRE - It should be still possible to read 'x'", func(t *testing.T) {
+ require.Equal(t, "foobar", rdb.Get(ctx, "x").Val())
+ })
+
+ t.Run("EXPIRE - After 3.1 seconds the key should no longer be here", func(t *testing.T) {
+ time.Sleep(3100 * time.Millisecond)
+ require.Equal(t, "", rdb.Get(ctx, "x").Val())
+ require.EqualValues(t, 0, rdb.Exists(ctx, "x").Val())
+ })
+
+ t.Run("EXPIRE - write on expire should work", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "x").Err())
+ require.NoError(t, rdb.LPush(ctx, "x", "foo").Err())
+ require.NoError(t, rdb.Expire(ctx, "x", 1000*time.Second).Err())
+ require.NoError(t, rdb.LPush(ctx, "x", "bar").Err())
+ require.Equal(t, []string{"bar", "foo"}, rdb.LRange(ctx, "x", 0, -1).Val())
+ })
+
+ t.Run("EXPIREAT - Check for EXPIRE alike behavior", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "x").Err())
+ require.NoError(t, rdb.Set(ctx, "x", "foo", 0).Err())
+ require.NoError(t, rdb.ExpireAt(ctx, "x", time.Now().Add(15*time.Second)).Err())
+ util.BetweenValues(t, rdb.TTL(ctx, "x").Val(), 13*time.Second, 16*time.Second)
+ })
+
+ t.Run("SETEX - Set + Expire combo operation. Check for TTL", func(t *testing.T) {
+ require.NoError(t, rdb.SetEx(ctx, "x", "test", 12*time.Second).Err())
+ util.BetweenValues(t, rdb.TTL(ctx, "x").Val(), 10*time.Second, 12*time.Second)
+ })
+
+ t.Run("SETEX - Check value", func(t *testing.T) {
+ require.Equal(t, "test", rdb.Get(ctx, "x").Val())
+ })
+
+ t.Run("SETEX - Overwrite old key", func(t *testing.T) {
+ require.NoError(t, rdb.SetEx(ctx, "y", "foo", 1*time.Second).Err())
+ require.Equal(t, "foo", rdb.Get(ctx, "y").Val())
+ })
+
+ t.Run("SETEX - Wait for the key to expire", func(t *testing.T) {
+ time.Sleep(2100 * time.Millisecond)
+ require.Equal(t, "", rdb.Get(ctx, "y").Val())
+ })
+
+ t.Run("SETEX - Wrong time parameter", func(t *testing.T) {
+ util.ErrorRegexp(t, rdb.SetEx(ctx, "z", "foo", -10).Err(), ".*invalid expire*.")
+ })
+
+ t.Run("PERSIST can undo an EXPIRE", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "x", "foo", 0).Err())
+ require.NoError(t, rdb.Expire(ctx, "x", 12*time.Second).Err())
+ util.BetweenValues(t, rdb.TTL(ctx, "x").Val(), 10*time.Second, 12*time.Second)
+ require.True(t, rdb.Persist(ctx, "x").Val())
+ require.EqualValues(t, -1, rdb.TTL(ctx, "x").Val())
+ require.Equal(t, "foo", rdb.Get(ctx, "x").Val())
+ })
+
+ t.Run("PERSIST returns 0 against non existing or non volatile keys", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "x", "foo", 0).Err())
+ require.False(t, rdb.Persist(ctx, "foo").Val())
+ require.False(t, rdb.Persist(ctx, "nokeyatall").Val())
+ })
+
+ t.Run("EXPIRE precision is now the millisecond", func(t *testing.T) {
+ // This test is very likely to do a false positive if the server is under pressure,
+ // so if it does not work give it a few more chances.
+ a, b := "", ""
+ util.RetryEventually(t, func() bool {
+ require.NoError(t, rdb.Del(ctx, "x").Err())
+ require.NoError(t, rdb.SetEx(ctx, "x", "somevalue", 1*time.Second).Err())
+ time.Sleep(900 * time.Millisecond)
+ a = rdb.Get(ctx, "x").Val()
+ time.Sleep(1100 * time.Millisecond)
+ b = rdb.Get(ctx, "x").Val()
+ return a == "somevalue" && b == ""
+ }, 3)
+ require.Equal(t, "somevalue", a)
+ require.Equal(t, "", b)
+ })
+
+ t.Run("PEXPIRE/PSETEX/PEXPIREAT can set sub-second expires", func(t *testing.T) {
+ // This test is very likely to do a false positive if the server is under pressure,
+ // so if it does not work give it a few more chances.
+ a, b, c, d, e, f := "", "", "", "", "", ""
+ util.RetryEventually(t, func() bool {
+ require.NoError(t, rdb.Del(ctx, "x", "y", "z").Err())
+ require.NoError(t, rdb.Set(ctx, "x", "somevalue", 100*time.Millisecond).Err())
+ time.Sleep(80 * time.Millisecond)
+ a = rdb.Get(ctx, "x").Val()
+ time.Sleep(2100 * time.Millisecond)
+ b = rdb.Get(ctx, "x").Val()
+
+ require.NoError(t, rdb.Set(ctx, "x", "somevalue", 0).Err())
+ require.NoError(t, rdb.PExpire(ctx, "x", 100*time.Millisecond).Err())
+ time.Sleep(80 * time.Millisecond)
+ c = rdb.Get(ctx, "x").Val()
+ time.Sleep(2100 * time.Millisecond)
+ d = rdb.Get(ctx, "x").Val()
+
+ require.NoError(t, rdb.Set(ctx, "x", "somevalue", 0).Err())
+ require.NoError(t, rdb.PExpireAt(ctx, "x", time.UnixMilli(time.Now().Unix()*1000+100)).Err())
+ time.Sleep(80 * time.Millisecond)
+ e = rdb.Get(ctx, "x").Val()
+ time.Sleep(2100 * time.Millisecond)
+ f = rdb.Get(ctx, "x").Val()
+
+ return a == "somevalue" && b == "" && c == "somevalue" && d == "" && e == "somevalue" && f == ""
+ }, 3)
+ require.Equal(t, "somevalue", a)
+ require.Equal(t, "", b)
+ })
+
+ t.Run("TTL returns time to live in seconds", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "x").Err())
+ require.NoError(t, rdb.SetEx(ctx, "x", "somevalue", 10*time.Second).Err())
+ util.BetweenValues(t, rdb.TTL(ctx, "x").Val(), 8*time.Second, 10*time.Second)
+ })
+
+ t.Run("PTTL returns time to live in milliseconds", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "x").Err())
+ require.NoError(t, rdb.SetEx(ctx, "x", "somevalue", 1*time.Second).Err())
+ util.BetweenValues(t, rdb.PTTL(ctx, "x").Val(), 900*time.Millisecond, 1000*time.Millisecond)
+ })
+
+ t.Run("TTL / PTTL return -1 if key has no expire", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "x").Err())
+ require.NoError(t, rdb.Set(ctx, "x", "hello", 0).Err())
+ require.EqualValues(t, -1, rdb.TTL(ctx, "x").Val())
+ require.EqualValues(t, -1, rdb.PTTL(ctx, "x").Val())
+ })
+
+ t.Run("TTL / PTTL return -2 if key does not exit", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "x").Err())
+ require.EqualValues(t, -2, rdb.TTL(ctx, "x").Val())
+ require.EqualValues(t, -2, rdb.PTTL(ctx, "x").Val())
+ })
+
+ t.Run("Redis should actively expire keys incrementally", func(t *testing.T) {
+ require.NoError(t, rdb.FlushDB(ctx).Err())
+ require.NoError(t, rdb.Do(ctx, "PSETEX", "key1", 500, "a").Err())
+ require.NoError(t, rdb.Do(ctx, "PSETEX", "key2", 500, "a").Err())
+ require.NoError(t, rdb.Do(ctx, "PSETEX", "key3", 500, "a").Err())
+ require.NoError(t, rdb.Do(ctx, "DBSIZE", "scan").Err())
+ time.Sleep(100 * time.Millisecond)
+ require.EqualValues(t, 3, rdb.DBSize(ctx).Val())
+ time.Sleep(2000 * time.Millisecond)
+ require.NoError(t, rdb.Do(ctx, "DBSIZE", "scan").Err())
+ time.Sleep(100 * time.Millisecond)
+ require.EqualValues(t, 0, rdb.DBSize(ctx).Val())
+ })
+
+ t.Run("5 keys in, 5 keys out", func(t *testing.T) {
+ require.NoError(t, rdb.FlushDB(ctx).Err())
+ require.NoError(t, rdb.Set(ctx, "a", "c", 0).Err())
+ require.NoError(t, rdb.Expire(ctx, "a", 5*time.Second).Err())
+ require.NoError(t, rdb.Set(ctx, "t", "c", 0).Err())
+ require.NoError(t, rdb.Set(ctx, "e", "c", 0).Err())
+ require.NoError(t, rdb.Set(ctx, "s", "c", 0).Err())
+ require.NoError(t, rdb.Set(ctx, "foo", "b", 0).Err())
+ res := rdb.Keys(ctx, "*").Val()
+ sort.Strings(res)
+ require.Equal(t, []string{"a", "e", "foo", "s", "t"}, res)
+ })
+
+ t.Run("EXPIRE with empty string as TTL should report an error", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "foo", "bar", 0).Err())
+ util.ErrorRegexp(t, rdb.Do(ctx, "expire", "foo", "").Err(), ".*not an integer*.")
+ })
+
+}
diff --git a/tests/gocase/util/assertions.go b/tests/gocase/util/assertions.go
index 41bc0fe..32dccad 100644
--- a/tests/gocase/util/assertions.go
+++ b/tests/gocase/util/assertions.go
@@ -23,9 +23,25 @@ import (
"testing"
"github.com/stretchr/testify/require"
+ "golang.org/x/exp/constraints"
)
func ErrorRegexp(t testing.TB, err error, rx interface{}, msgAndArgs ...interface{}) {
- require.Error(t, err, msgAndArgs)
- require.Regexp(t, rx, err.Error(), msgAndArgs)
+ require.Error(t, err, msgAndArgs...)
+ require.Regexp(t, rx, err.Error(), msgAndArgs...)
+}
+
+func BetweenValues[T constraints.Ordered](t testing.TB, d, start, end T, msgAndArgs ...interface{}) {
+ require.GreaterOrEqual(t, d, start, msgAndArgs...)
+ require.LessOrEqual(t, d, end, msgAndArgs...)
+}
+
+func RetryEventually(t testing.TB, condition func() bool, maxAttempts int, msgAndArgs ...interface{}) {
+ require.Greater(t, maxAttempts, 0, msgAndArgs...)
+ for i := 0; i < maxAttempts; i++ {
+ if condition() {
+ return
+ }
+ }
+ require.Fail(t, "Condition never satisfied", msgAndArgs...)
}
diff --git a/tests/tcl/tests/test_helper.tcl b/tests/tcl/tests/test_helper.tcl
index a003a7b..dfb7801 100644
--- a/tests/tcl/tests/test_helper.tcl
+++ b/tests/tcl/tests/test_helper.tcl
@@ -37,7 +37,6 @@ set ::all_tests {
unit/type/list
unit/type/zset
unit/type/stream
- unit/expire
unit/pubsub
unit/geo
integration/slotmigrate
diff --git a/tests/tcl/tests/unit/expire.tcl b/tests/tcl/tests/unit/expire.tcl
deleted file mode 100644
index c788a0e..0000000
--- a/tests/tcl/tests/unit/expire.tcl
+++ /dev/null
@@ -1,206 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-# Copyright (c) 2006-2020, Salvatore Sanfilippo
-# See bundled license file licenses/LICENSE.redis for details.
-
-# This file is copied and modified from the Redis project,
-# which started out as: https://github.com/redis/redis/blob/dbcc0a8/tests/unit/expire.tcl
-
-start_server {tags {"expire"}} {
- test {EXPIRE - set timeouts multiple times} {
- r set x foobar
- set v1 [r expire x 5]
- set v2 [r ttl x]
- set v3 [r expire x 10]
- set v4 [r ttl x]
- r expire x 2
- list $v1 $v2 $v3 $v4
- } {1 [45] 1 10}
-
- test {EXPIRE - It should be still possible to read 'x'} {
- r get x
- } {foobar}
-
- tags {"slow"} {
- test {EXPIRE - After 3.1 seconds the key should no longer be here} {
- after 3100
- list [r get x] [r exists x]
- } {{} 0}
- }
-
- test {EXPIRE - write on expire should work} {
- r del x
- r lpush x foo
- r expire x 1000
- r lpush x bar
- r lrange x 0 -1
- } {bar foo}
-
- test {EXPIREAT - Check for EXPIRE alike behavior} {
- r del x
- r set x foo
- r expireat x [expr [clock seconds]+15]
- r ttl x
- } {1[3456]}
-
- test {SETEX - Set + Expire combo operation. Check for TTL} {
- r setex x 12 test
- r ttl x
- } {1[012]}
-
- test {SETEX - Check value} {
- r get x
- } {test}
-
- test {SETEX - Overwrite old key} {
- r setex y 1 foo
- r get y
- } {foo}
-
- tags {"slow"} {
- test {SETEX - Wait for the key to expire} {
- after 2100
- r get y
- } {}
- }
-
- test {SETEX - Wrong time parameter} {
- catch {r setex z -10 foo} e
- set _ $e
- } {*invalid expire*}
-
- test {PERSIST can undo an EXPIRE} {
- r set x foo
- r expire x 12
- list [r ttl x] [r persist x] [r ttl x] [r get x]
- } {1[012] 1 -1 foo}
-
- test {PERSIST returns 0 against non existing or non volatile keys} {
- r set x foo
- list [r persist foo] [r persist nokeyatall]
- } {0 0}
-
- test {EXPIRE pricision is now the millisecond} {
- # This test is very likely to do a false positive if the
- # server is under pressure, so if it does not work give it a few more
- # chances.
- for {set j 0} {$j < 3} {incr j} {
- r del x
- r setex x 1 somevalue
- after 900
- set a [r get x]
- after 1100
- set b [r get x]
- if {$a eq {somevalue} && $b eq {}} break
- }
- list $a $b
- } {somevalue {}}
-
- test {PEXPIRE/PSETEX/PEXPIREAT can set sub-second expires} {
- # This test is very likely to do a false positive if the
- # server is under pressure, so if it does not work give it a few more
- # chances.
- for {set j 0} {$j < 3} {incr j} {
- r del x y z
- r psetex x 100 somevalue
- after 80
- set a [r get x]
- after 2100
- set b [r get x]
-
- r set x somevalue
- r pexpire x 100
- after 80
- set c [r get x]
- after 2100
- set d [r get x]
-
- r set x somevalue
- r pexpireat x [expr ([clock seconds]*1000)+100]
- after 80
- set e [r get x]
- after 2100
- set f [r get x]
-
- if {$a eq {somevalue} && $b eq {} &&
- $c eq {somevalue} && $d eq {} &&
- $e eq {somevalue} && $f eq {}} break
- }
- list $a $b
- } {somevalue {}}
-
- test {TTL returns tiem to live in seconds} {
- r del x
- r setex x 10 somevalue
- set ttl [r ttl x]
- assert {$ttl > 8 && $ttl <= 10}
- }
-
- test {PTTL returns time to live in milliseconds} {
- r del x
- r setex x 1 somevalue
- set ttl [r pttl x]
- assert {$ttl > 900 && $ttl <= 1000}
- }
-
- test {TTL / PTTL return -1 if key has no expire} {
- r del x
- r set x hello
- list [r ttl x] [r pttl x]
- } {-1 -1}
-
- test {TTL / PTTL return -2 if key does not exit} {
- r del x
- list [r ttl x] [r pttl x]
- } {-2 -2}
-
- test {Redis should actively expire keys incrementally} {
- r flushdb
- r psetex key1 500 a
- r psetex key2 500 a
- r psetex key3 500 a
- r dbsize scan
- after 100
- set size1 [r dbsize]
- # Redis expires random keys ten times every second so we are
- # fairly sure that all the three keys should be evicted after
- # one second.
- after 2000
- r dbsize scan
- after 100
- set size2 [r dbsize]
- list $size1 $size2
- } {3 0}
-
- test {5 keys in, 5 keys out} {
- r flushdb
- r set a c
- r expire a 5
- r set t c
- r set e c
- r set s c
- r set foo b
- lsort [r keys *]
- } {a e foo s t}
-
- test {EXPIRE with empty string as TTL should report an error} {
- r set foo bar
- catch {r expire foo ""} e
- set e
- } {*not an integer*}
-}