This is an automated email from the ASF dual-hosted git repository.
hulk pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new 7a08dfdc Add support of EXPIRETIME and PEXPIRETIME (#1965)
7a08dfdc is described below
commit 7a08dfdccea05266992d95e1b22c616e17bdbb35
Author: kay011 <[email protected]>
AuthorDate: Tue Dec 26 21:07:05 2023 +0800
Add support of EXPIRETIME and PEXPIRETIME (#1965)
---
src/commands/cmd_key.cc | 36 +++++++++++++++
src/stats/disk_stats.cc | 14 +++---
src/storage/redis_db.cc | 22 ++++++---
src/storage/redis_db.h | 5 ++-
src/types/redis_bloom_chain.cc | 2 +-
src/types/redis_hash.cc | 2 +-
src/types/redis_json.cc | 4 +-
src/types/redis_list.cc | 2 +-
src/types/redis_set.cc | 2 +-
src/types/redis_sortedint.cc | 2 +-
src/types/redis_stream.cc | 2 +-
src/types/redis_zset.cc | 2 +-
tests/cppunit/metadata_test.cc | 79 ++++++++++++++++++++++++++++++++-
tests/gocase/unit/expire/expire_test.go | 52 ++++++++++++++++++++++
14 files changed, 201 insertions(+), 25 deletions(-)
diff --git a/src/commands/cmd_key.cc b/src/commands/cmd_key.cc
index 5cea1d70..35bfeefd 100644
--- a/src/commands/cmd_key.cc
+++ b/src/commands/cmd_key.cc
@@ -233,6 +233,40 @@ class CommandPExpireAt : public Commander {
uint64_t timestamp_ = 0;
};
+class CommandExpireTime : public Commander {
+ public:
+ Status Execute(Server *srv, Connection *conn, std::string *output) override {
+ redis::Database redis(srv->storage, conn->GetNamespace());
+ uint64_t timestamp = 0;
+ auto s = redis.GetExpireTime(args_[1], ×tamp);
+ if (s.ok()) {
+ *output = timestamp > 0 ? redis::Integer(timestamp / 1000) :
redis::Integer(-1);
+ } else if (s.IsNotFound()) {
+ *output = redis::Integer(-2);
+ } else {
+ return {Status::RedisExecErr, s.ToString()};
+ }
+ return Status::OK();
+ }
+};
+
+class CommandPExpireTime : public Commander {
+ public:
+ Status Execute(Server *srv, Connection *conn, std::string *output) override {
+ redis::Database redis(srv->storage, conn->GetNamespace());
+ uint64_t timestamp = 0;
+ auto s = redis.GetExpireTime(args_[1], ×tamp);
+ if (s.ok()) {
+ *output = timestamp > 0 ? redis::Integer(timestamp) : redis::Integer(-1);
+ } else if (s.IsNotFound()) {
+ *output = redis::Integer(-2);
+ } else {
+ return {Status::RedisExecErr, s.ToString()};
+ }
+ return Status::OK();
+ }
+};
+
class CommandPersist : public Commander {
public:
Status Execute(Server *srv, Connection *conn, std::string *output) override {
@@ -284,6 +318,8 @@ REDIS_REGISTER_COMMANDS(MakeCmdAttr<CommandTTL>("ttl", 2,
"read-only", 1, 1, 1),
MakeCmdAttr<CommandPExpire>("pexpire", 3, "write", 1,
1, 1),
MakeCmdAttr<CommandExpireAt>("expireat", 3, "write",
1, 1, 1),
MakeCmdAttr<CommandPExpireAt>("pexpireat", 3, "write",
1, 1, 1),
+ MakeCmdAttr<CommandExpireTime>("expiretime", 2,
"read-only", 1, 1, 1),
+ MakeCmdAttr<CommandPExpireTime>("pexpiretime", 2,
"read-only", 1, 1, 1),
MakeCmdAttr<CommandDel>("del", -2, "write", 1, -1, 1),
MakeCmdAttr<CommandDel>("unlink", -2, "write", 1, -1,
1), )
diff --git a/src/stats/disk_stats.cc b/src/stats/disk_stats.cc
index a5e946eb..7c3c9998 100644
--- a/src/stats/disk_stats.cc
+++ b/src/stats/disk_stats.cc
@@ -77,21 +77,21 @@ rocksdb::Status Disk::GetStringSize(const Slice &ns_key,
uint64_t *key_size) {
rocksdb::Status Disk::GetHashSize(const Slice &ns_key, uint64_t *key_size) {
HashMetadata metadata(false);
- rocksdb::Status s = Database::GetMetadata(kRedisHash, ns_key, &metadata);
+ rocksdb::Status s = Database::GetMetadata({kRedisHash}, ns_key, &metadata);
if (!s.ok()) return s.IsNotFound() ? rocksdb::Status::OK() : s;
return GetApproximateSizes(metadata, ns_key,
storage_->GetCFHandle(engine::kSubkeyColumnFamilyName), key_size);
}
rocksdb::Status Disk::GetSetSize(const Slice &ns_key, uint64_t *key_size) {
SetMetadata metadata(false);
- rocksdb::Status s = Database::GetMetadata(kRedisSet, ns_key, &metadata);
+ rocksdb::Status s = Database::GetMetadata({kRedisSet}, ns_key, &metadata);
if (!s.ok()) return s.IsNotFound() ? rocksdb::Status::OK() : s;
return GetApproximateSizes(metadata, ns_key,
storage_->GetCFHandle(engine::kSubkeyColumnFamilyName), key_size);
}
rocksdb::Status Disk::GetListSize(const Slice &ns_key, uint64_t *key_size) {
ListMetadata metadata(false);
- rocksdb::Status s = Database::GetMetadata(kRedisList, ns_key, &metadata);
+ rocksdb::Status s = Database::GetMetadata({kRedisList}, ns_key, &metadata);
if (!s.ok()) return s.IsNotFound() ? rocksdb::Status::OK() : s;
std::string buf;
PutFixed64(&buf, metadata.head);
@@ -100,7 +100,7 @@ rocksdb::Status Disk::GetListSize(const Slice &ns_key,
uint64_t *key_size) {
rocksdb::Status Disk::GetZsetSize(const Slice &ns_key, uint64_t *key_size) {
ZSetMetadata metadata(false);
- rocksdb::Status s = Database::GetMetadata(kRedisZSet, ns_key, &metadata);
+ rocksdb::Status s = Database::GetMetadata({kRedisZSet}, ns_key, &metadata);
if (!s.ok()) return s.IsNotFound() ? rocksdb::Status::OK() : s;
std::string score_bytes;
PutDouble(&score_bytes, kMinScore);
@@ -112,7 +112,7 @@ rocksdb::Status Disk::GetZsetSize(const Slice &ns_key,
uint64_t *key_size) {
rocksdb::Status Disk::GetBitmapSize(const Slice &ns_key, uint64_t *key_size) {
BitmapMetadata metadata(false);
- rocksdb::Status s = Database::GetMetadata(kRedisBitmap, ns_key, &metadata);
+ rocksdb::Status s = Database::GetMetadata({kRedisBitmap}, ns_key, &metadata);
if (!s.ok()) return s.IsNotFound() ? rocksdb::Status::OK() : s;
return GetApproximateSizes(metadata, ns_key,
storage_->GetCFHandle(engine::kSubkeyColumnFamilyName), key_size,
std::to_string(0), std::to_string(0));
@@ -120,7 +120,7 @@ rocksdb::Status Disk::GetBitmapSize(const Slice &ns_key,
uint64_t *key_size) {
rocksdb::Status Disk::GetSortedintSize(const Slice &ns_key, uint64_t
*key_size) {
SortedintMetadata metadata(false);
- rocksdb::Status s = Database::GetMetadata(kRedisSortedint, ns_key,
&metadata);
+ rocksdb::Status s = Database::GetMetadata({kRedisSortedint}, ns_key,
&metadata);
if (!s.ok()) return s.IsNotFound() ? rocksdb::Status::OK() : s;
std::string start_buf;
PutFixed64(&start_buf, 0);
@@ -130,7 +130,7 @@ rocksdb::Status Disk::GetSortedintSize(const Slice &ns_key,
uint64_t *key_size)
rocksdb::Status Disk::GetStreamSize(const Slice &ns_key, uint64_t *key_size) {
StreamMetadata metadata(false);
- rocksdb::Status s = Database::GetMetadata(kRedisStream, ns_key, &metadata);
+ rocksdb::Status s = Database::GetMetadata({kRedisStream}, ns_key, &metadata);
if (!s.ok()) return s.IsNotFound() ? rocksdb::Status::OK() : s;
return GetApproximateSizes(metadata, ns_key,
storage_->GetCFHandle(engine::kStreamColumnFamilyName), key_size);
}
diff --git a/src/storage/redis_db.cc b/src/storage/redis_db.cc
index 7708b491..a3734730 100644
--- a/src/storage/redis_db.cc
+++ b/src/storage/redis_db.cc
@@ -67,18 +67,18 @@ rocksdb::Status Database::ParseMetadata(RedisTypes types,
Slice *bytes, Metadata
return s;
}
-rocksdb::Status Database::GetMetadata(RedisType type, const Slice &ns_key,
Metadata *metadata) {
+rocksdb::Status Database::GetMetadata(RedisTypes types, const Slice &ns_key,
Metadata *metadata) {
std::string raw_value;
Slice rest;
- return GetMetadata(type, ns_key, &raw_value, metadata, &rest);
+ return GetMetadata(types, ns_key, &raw_value, metadata, &rest);
}
-rocksdb::Status Database::GetMetadata(RedisType type, const Slice &ns_key,
std::string *raw_value, Metadata *metadata,
+rocksdb::Status Database::GetMetadata(RedisTypes types, const Slice &ns_key,
std::string *raw_value, Metadata *metadata,
Slice *rest) {
auto s = GetRawMetadata(ns_key, raw_value);
*rest = *raw_value;
if (!s.ok()) return s;
- return ParseMetadata({type}, rest, metadata);
+ return ParseMetadata(types, rest, metadata);
}
rocksdb::Status Database::GetRawMetadata(const Slice &ns_key, std::string
*bytes) {
@@ -225,6 +225,16 @@ rocksdb::Status Database::TTL(const Slice &user_key,
int64_t *ttl) {
return rocksdb::Status::OK();
}
+rocksdb::Status Database::GetExpireTime(const Slice &user_key, uint64_t
*timestamp) {
+ std::string ns_key = AppendNamespacePrefix(user_key);
+ Metadata metadata(kRedisNone, false);
+ auto s = GetMetadata(RedisTypes::All(), ns_key, &metadata);
+ if (!s.ok()) return s;
+ *timestamp = metadata.expire;
+
+ return rocksdb::Status::OK();
+}
+
rocksdb::Status Database::GetKeyNumStats(const std::string &prefix,
KeyNumStats *stats) {
return Keys(prefix, nullptr, stats);
}
@@ -488,7 +498,7 @@ rocksdb::Status Database::Dump(const Slice &user_key,
std::vector<std::string> *
if (metadata.Type() == kRedisList) {
ListMetadata list_metadata(false);
- s = GetMetadata(kRedisList, ns_key, &list_metadata);
+ s = GetMetadata({kRedisList}, ns_key, &list_metadata);
if (!s.ok()) return s.IsNotFound() ? rocksdb::Status::OK() : s;
infos->emplace_back("head");
infos->emplace_back(std::to_string(list_metadata.head));
@@ -644,7 +654,7 @@ rocksdb::Status SubKeyScanner::Scan(RedisType type, const
Slice &user_key, const
uint64_t cnt = 0;
std::string ns_key = AppendNamespacePrefix(user_key);
Metadata metadata(type, false);
- rocksdb::Status s = GetMetadata(type, ns_key, &metadata);
+ rocksdb::Status s = GetMetadata({type}, ns_key, &metadata);
if (!s.ok()) return s;
LatestSnapShot ss(storage_);
diff --git a/src/storage/redis_db.h b/src/storage/redis_db.h
index 181723a4..7258923c 100644
--- a/src/storage/redis_db.h
+++ b/src/storage/redis_db.h
@@ -35,8 +35,8 @@ class Database {
explicit Database(engine::Storage *storage, std::string ns = "");
[[nodiscard]] static rocksdb::Status ParseMetadata(RedisTypes types, Slice
*bytes, Metadata *metadata);
- [[nodiscard]] rocksdb::Status GetMetadata(RedisType type, const Slice
&ns_key, Metadata *metadata);
- [[nodiscard]] rocksdb::Status GetMetadata(RedisType type, const Slice
&ns_key, std::string *raw_value,
+ [[nodiscard]] rocksdb::Status GetMetadata(RedisTypes types, const Slice
&ns_key, Metadata *metadata);
+ [[nodiscard]] rocksdb::Status GetMetadata(RedisTypes types, const Slice
&ns_key, std::string *raw_value,
Metadata *metadata, Slice *rest);
[[nodiscard]] rocksdb::Status GetRawMetadata(const Slice &ns_key,
std::string *bytes);
[[nodiscard]] rocksdb::Status Expire(const Slice &user_key, uint64_t
timestamp);
@@ -44,6 +44,7 @@ class Database {
[[nodiscard]] rocksdb::Status MDel(const std::vector<Slice> &keys, uint64_t
*deleted_cnt);
[[nodiscard]] rocksdb::Status Exists(const std::vector<Slice> &keys, int
*ret);
[[nodiscard]] rocksdb::Status TTL(const Slice &user_key, int64_t *ttl);
+ [[nodiscard]] rocksdb::Status GetExpireTime(const Slice &user_key, uint64_t
*timestamp);
[[nodiscard]] rocksdb::Status Type(const Slice &user_key, RedisType *type);
[[nodiscard]] rocksdb::Status Dump(const Slice &user_key,
std::vector<std::string> *infos);
[[nodiscard]] rocksdb::Status FlushDB();
diff --git a/src/types/redis_bloom_chain.cc b/src/types/redis_bloom_chain.cc
index 4c4e32d0..90a43f05 100644
--- a/src/types/redis_bloom_chain.cc
+++ b/src/types/redis_bloom_chain.cc
@@ -25,7 +25,7 @@
namespace redis {
rocksdb::Status BloomChain::getBloomChainMetadata(const Slice &ns_key,
BloomChainMetadata *metadata) {
- return Database::GetMetadata(kRedisBloomFilter, ns_key, metadata);
+ return Database::GetMetadata({kRedisBloomFilter}, ns_key, metadata);
}
std::string BloomChain::getBFKey(const Slice &ns_key, const BloomChainMetadata
&metadata, uint16_t filters_index) {
diff --git a/src/types/redis_hash.cc b/src/types/redis_hash.cc
index c2e348cf..0ee9c16e 100644
--- a/src/types/redis_hash.cc
+++ b/src/types/redis_hash.cc
@@ -34,7 +34,7 @@
namespace redis {
rocksdb::Status Hash::GetMetadata(const Slice &ns_key, HashMetadata *metadata)
{
- return Database::GetMetadata(kRedisHash, ns_key, metadata);
+ return Database::GetMetadata({kRedisHash}, ns_key, metadata);
}
rocksdb::Status Hash::Size(const Slice &user_key, uint64_t *size) {
diff --git a/src/types/redis_json.cc b/src/types/redis_json.cc
index b7c0d4c4..fb3add5f 100644
--- a/src/types/redis_json.cc
+++ b/src/types/redis_json.cc
@@ -74,7 +74,7 @@ rocksdb::Status Json::read(const Slice &ns_key, JsonMetadata
*metadata, JsonValu
std::string bytes;
Slice rest;
- auto s = GetMetadata(kRedisJson, ns_key, &bytes, metadata, &rest);
+ auto s = GetMetadata({kRedisJson}, ns_key, &bytes, metadata, &rest);
if (!s.ok()) return s;
return parse(*metadata, rest, value);
@@ -105,7 +105,7 @@ rocksdb::Status Json::Info(const std::string &user_key,
JsonStorageFormat *stora
Slice rest;
JsonMetadata metadata;
- auto s = GetMetadata(kRedisJson, ns_key, &bytes, &metadata, &rest);
+ auto s = GetMetadata({kRedisJson}, ns_key, &bytes, &metadata, &rest);
if (!s.ok()) return s;
*storage_format = metadata.format;
diff --git a/src/types/redis_list.cc b/src/types/redis_list.cc
index b862a330..ed67007e 100644
--- a/src/types/redis_list.cc
+++ b/src/types/redis_list.cc
@@ -28,7 +28,7 @@
namespace redis {
rocksdb::Status List::GetMetadata(const Slice &ns_key, ListMetadata *metadata)
{
- return Database::GetMetadata(kRedisList, ns_key, metadata);
+ return Database::GetMetadata({kRedisList}, ns_key, metadata);
}
rocksdb::Status List::Size(const Slice &user_key, uint64_t *size) {
diff --git a/src/types/redis_set.cc b/src/types/redis_set.cc
index ddfd6c0b..32c06ef9 100644
--- a/src/types/redis_set.cc
+++ b/src/types/redis_set.cc
@@ -29,7 +29,7 @@
namespace redis {
rocksdb::Status Set::GetMetadata(const Slice &ns_key, SetMetadata *metadata) {
- return Database::GetMetadata(kRedisSet, ns_key, metadata);
+ return Database::GetMetadata({kRedisSet}, ns_key, metadata);
}
// Make sure members are uniq before use Overwrite
diff --git a/src/types/redis_sortedint.cc b/src/types/redis_sortedint.cc
index 76ad31fc..6670eb74 100644
--- a/src/types/redis_sortedint.cc
+++ b/src/types/redis_sortedint.cc
@@ -29,7 +29,7 @@
namespace redis {
rocksdb::Status Sortedint::GetMetadata(const Slice &ns_key, SortedintMetadata
*metadata) {
- return Database::GetMetadata(kRedisSortedint, ns_key, metadata);
+ return Database::GetMetadata({kRedisSortedint}, ns_key, metadata);
}
rocksdb::Status Sortedint::Add(const Slice &user_key, const
std::vector<uint64_t> &ids, uint64_t *added_cnt) {
diff --git a/src/types/redis_stream.cc b/src/types/redis_stream.cc
index bc050ad0..48fe7928 100644
--- a/src/types/redis_stream.cc
+++ b/src/types/redis_stream.cc
@@ -45,7 +45,7 @@ const char *errXGroupSubcommandRequiresKeyExist =
Note that for CREATE you may want to use the MKSTREAM option to create an
empty stream automatically.";
rocksdb::Status Stream::GetMetadata(const Slice &stream_name, StreamMetadata
*metadata) {
- return Database::GetMetadata(kRedisStream, stream_name, metadata);
+ return Database::GetMetadata({kRedisStream}, stream_name, metadata);
}
rocksdb::Status Stream::GetLastGeneratedID(const Slice &stream_name,
StreamEntryID *id) {
diff --git a/src/types/redis_zset.cc b/src/types/redis_zset.cc
index c99d3c8a..92bf5be7 100644
--- a/src/types/redis_zset.cc
+++ b/src/types/redis_zset.cc
@@ -32,7 +32,7 @@
namespace redis {
rocksdb::Status ZSet::GetMetadata(const Slice &ns_key, ZSetMetadata *metadata)
{
- return Database::GetMetadata(kRedisZSet, ns_key, metadata);
+ return Database::GetMetadata({kRedisZSet}, ns_key, metadata);
}
rocksdb::Status ZSet::Add(const Slice &user_key, ZAddFlags flags, MemberScores
*mscores, uint64_t *added_cnt) {
diff --git a/tests/cppunit/metadata_test.cc b/tests/cppunit/metadata_test.cc
index a454973b..e17735c3 100644
--- a/tests/cppunit/metadata_test.cc
+++ b/tests/cppunit/metadata_test.cc
@@ -20,7 +20,9 @@
#include <gtest/gtest.h>
+#include <chrono>
#include <memory>
+#include <thread>
#include "storage/redis_metadata.h"
#include "test_base.h"
@@ -90,7 +92,7 @@ TEST_F(RedisTypeTest, GetMetadata) {
EXPECT_TRUE(s.ok() && fvs.size() == ret);
HashMetadata metadata;
std::string ns_key = redis_->AppendNamespacePrefix(key_);
- s = redis_->GetMetadata(kRedisHash, ns_key, &metadata);
+ s = redis_->GetMetadata({kRedisHash}, ns_key, &metadata);
EXPECT_EQ(fvs.size(), metadata.size);
s = redis_->Del(key_);
EXPECT_TRUE(s.ok());
@@ -114,6 +116,81 @@ TEST_F(RedisTypeTest, Expire) {
s = redis_->Del(key_);
}
+TEST_F(RedisTypeTest, ExpireTime) {
+ uint64_t ret = 0;
+ std::vector<FieldValue> fvs;
+ for (size_t i = 0; i < fields_.size(); i++) {
+ fvs.emplace_back(fields_[i].ToString(), values_[i].ToString());
+ }
+ rocksdb::Status s = hash_->MSet(key_, fvs, false, &ret);
+ EXPECT_TRUE(s.ok() && fvs.size() == ret);
+ int64_t now = 0;
+ rocksdb::Env::Default()->GetCurrentTime(&now);
+ uint64_t ms_offset = 2314;
+ uint64_t expire_timestamp_ms = now * 1000 + ms_offset;
+ s = redis_->Expire(key_, expire_timestamp_ms);
+ EXPECT_TRUE(s.ok());
+ uint64_t timestamp = 0;
+ s = redis_->GetExpireTime(key_, ×tamp);
+ EXPECT_TRUE(s.ok() && timestamp != 0);
+ if (METADATA_ENCODING_VERSION != 0) {
+ EXPECT_EQ(timestamp, expire_timestamp_ms);
+ } else {
+ EXPECT_EQ(timestamp, Metadata::ExpireMsToS(expire_timestamp_ms) * 1000);
+ }
+ s = redis_->Del(key_);
+}
+
+TEST_F(RedisTypeTest, ExpireTimeKeyNoExpireTime) {
+ uint64_t ret = 0;
+ std::vector<FieldValue> fvs;
+ for (size_t i = 0; i < fields_.size(); i++) {
+ fvs.emplace_back(fields_[i].ToString(), values_[i].ToString());
+ }
+ rocksdb::Status s = hash_->MSet(key_, fvs, false, &ret);
+ EXPECT_TRUE(s.ok() && fvs.size() == ret);
+ uint64_t timestamp = 0;
+ s = redis_->GetExpireTime(key_, ×tamp);
+ EXPECT_TRUE(s.ok() && timestamp == 0);
+ s = redis_->Del(key_);
+}
+
+TEST_F(RedisTypeTest, ExpireTimeKeyExpired) {
+ uint64_t ret = 0;
+ std::vector<FieldValue> fvs;
+ for (size_t i = 0; i < fields_.size(); i++) {
+ fvs.emplace_back(fields_[i].ToString(), values_[i].ToString());
+ }
+ rocksdb::Status s = hash_->MSet(key_, fvs, false, &ret);
+ EXPECT_TRUE(s.ok() && fvs.size() == ret);
+ int64_t now = 0;
+ rocksdb::Env::Default()->GetCurrentTime(&now);
+ uint64_t ms_offset = 1120;
+ uint64_t expire_timestamp_ms = now * 1000 + ms_offset;
+ s = redis_->Expire(key_, expire_timestamp_ms);
+ EXPECT_TRUE(s.ok());
+ std::this_thread::sleep_for(std::chrono::milliseconds(2000));
+ uint64_t timestamp = 0;
+ s = redis_->GetExpireTime(key_, ×tamp);
+ EXPECT_TRUE(s.IsNotFound() && timestamp == 0);
+ s = redis_->Del(key_);
+}
+
+TEST_F(RedisTypeTest, ExpireTimeKeyNotExisted) {
+ uint64_t ret = 0;
+ std::vector<FieldValue> fvs;
+ for (size_t i = 0; i < fields_.size(); i++) {
+ fvs.emplace_back(fields_[i].ToString(), values_[i].ToString());
+ }
+ rocksdb::Status s = hash_->MSet(key_, fvs, false, &ret);
+ EXPECT_TRUE(s.ok() && fvs.size() == ret);
+ uint64_t timestamp = 0;
+ s = redis_->GetExpireTime(key_ + "test", ×tamp);
+ EXPECT_TRUE(s.IsNotFound() && timestamp == 0);
+
+ s = redis_->Del(key_);
+}
+
TEST(Metadata, MetadataDecodingBackwardCompatibleSimpleKey) {
auto expire_at = (util::GetTimeStamp() + 10) * 1000;
Metadata md_old(kRedisString, true, false);
diff --git a/tests/gocase/unit/expire/expire_test.go
b/tests/gocase/unit/expire/expire_test.go
index 9986afaf..1a787f4e 100644
--- a/tests/gocase/unit/expire/expire_test.go
+++ b/tests/gocase/unit/expire/expire_test.go
@@ -71,6 +71,58 @@ func TestExpire(t *testing.T) {
util.BetweenValues(t, rdb.TTL(ctx, "x").Val(), 13*time.Second,
16*time.Second)
})
+ t.Run("EXPIRETIME - Check for EXPRIRETIME 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.ExpireTime(ctx, "x").Err())
+ require.Equal(t, int64(-1), rdb.ExpireTime(ctx,
"x").Val().Nanoseconds())
+
+ expireTime := time.Unix(time.Now().Unix()+1, 0)
+ require.NoError(t, rdb.ExpireAt(ctx, "x",
time.Unix(time.Now().Unix()+1, 0)).Err())
+ require.NoError(t, rdb.ExpireTime(ctx, "x").Err())
+ require.Equal(t, expireTime.UnixMilli(), rdb.ExpireTime(ctx,
"x").Val().Milliseconds())
+
+ require.NoError(t, rdb.ExpireTime(ctx, "x_key_no_exist").Err())
+ require.Equal(t, int64(-2), rdb.ExpireTime(ctx,
"x_key_no_exist").Val().Nanoseconds())
+
+ time.Sleep(1001 * time.Millisecond)
+ require.NoError(t, rdb.ExpireTime(ctx, "x").Err())
+ require.Equal(t, int64(-2), rdb.ExpireTime(ctx,
"x").Val().Nanoseconds())
+ })
+
+ t.Run("PEXPIRETIME - Check for PEXPRIRETIME 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.PExpireTime(ctx, "x").Err())
+ require.Equal(t, int64(-1), rdb.PExpireTime(ctx,
"x").Val().Nanoseconds())
+
+ expireTime := time.Unix(time.Now().Unix()+1, 0)
+ require.NoError(t, rdb.PExpireAt(ctx, "x", expireTime).Err())
+ require.NoError(t, rdb.PExpireTime(ctx, "x").Err())
+ require.Equal(t, expireTime.UnixMilli(), rdb.PExpireTime(ctx,
"x").Val().Milliseconds())
+
+ expireTime = time.UnixMilli(time.Now().Unix()*1000 + 1456)
+ require.NoError(t, rdb.PExpireAt(ctx, "x", expireTime).Err())
+ require.NoError(t, rdb.PExpireTime(ctx, "x").Err())
+ require.GreaterOrEqual(t, expireTime.UnixMilli(),
rdb.PExpireTime(ctx, "x").Val().Milliseconds())
+ require.LessOrEqual(t, (expireTime.UnixMilli()+499)/1000*1000,
rdb.PExpireTime(ctx, "x").Val().Milliseconds())
+
+ expireTime = time.UnixMilli(time.Now().Unix()*1000 + 1789)
+ require.NoError(t, rdb.PExpireAt(ctx, "x", expireTime).Err())
+ require.NoError(t, rdb.PExpireTime(ctx, "x").Err())
+ require.LessOrEqual(t, expireTime.UnixMilli(),
rdb.PExpireTime(ctx, "x").Val().Milliseconds())
+ require.GreaterOrEqual(t,
(expireTime.UnixMilli()+499)/1000*1000, rdb.PExpireTime(ctx,
"x").Val().Milliseconds())
+
+ require.NoError(t, rdb.PExpireTime(ctx, "x_key_no_exist").Err())
+ require.Equal(t, int64(-2), rdb.PExpireTime(ctx,
"x_key_no_exist").Val().Nanoseconds())
+
+ time.Sleep(2000 * time.Millisecond)
+ require.NoError(t, rdb.PExpireTime(ctx, "x").Err())
+ require.Equal(t, int64(-2), rdb.PExpireTime(ctx,
"x").Val().Nanoseconds())
+ })
+
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)