Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support EXAT/PEXAT option in the set command #901

Merged
merged 7 commits into from
Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 38 additions & 8 deletions src/redis_cmd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -477,17 +477,34 @@ class CommandSet : public Commander {
} else if (opt == "xx" && !nx_) {
xx_ = true;
} else if (opt == "ex" && !ttl_ && !last_arg) {
try {
std::string s = args_[++i];
std::string::size_type sz;
ttl_ = std::stoi(s, &sz);
if (sz != s.size()) {
return Status(Status::RedisParseErr, errValueNotInterger);
}
} catch (std::exception &e) {
std::string s = args_[++i];
auto parse_status = ParseInt<int>(s);
if (!parse_status.IsOK()) {
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
return Status(Status::RedisParseErr, errValueNotInterger);
}
ttl_ = parse_status.GetValue();
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
if (ttl_ <= 0) return Status(Status::RedisParseErr, errInvalidExpireTime);
} else if (opt == "exat" && !ttl_ && !expire_ && !last_arg) {
std::string s = args_[++i];
auto parse_status = ParseInt<int64_t>(s);
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
if (!parse_status.IsOK()) {
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
return Status(Status::RedisParseErr, errValueNotInterger);
}
expire_ = parse_status.GetValue();
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
if (expire_ <= 0) return Status(Status::RedisParseErr, errInvalidExpireTime);
} else if (opt == "pxat" && !ttl_ && !expire_ && !last_arg) {
std::string s = args_[++i];
auto parse_status = ParseInt<uint64_t>(s);
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
if (!parse_status.IsOK()) {
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
return Status(Status::RedisParseErr, errValueNotInterger);
}
uint64_t expire_ms = parse_status.GetValue();
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
if (expire_ms <= 0) return Status(Status::RedisParseErr, errInvalidExpireTime);
if (expire_ms < 1000) {
expire_ = 1;
} else {
expire_ = static_cast<int64_t>(expire_ms/1000);
}
} else if (opt == "px" && !ttl_ && !last_arg) {
int64_t ttl_ms = 0;
std::string s = args_[++i];
Expand All @@ -512,6 +529,18 @@ class CommandSet : public Commander {
int ret;
Redis::String string_db(svr->storage_, conn->GetNamespace());
rocksdb::Status s;

if (!ttl_ && expire_) {
int64_t now;
rocksdb::Env::Default()->GetCurrentTime(&now);
ttl_ = expire_ - now;
if (ttl_ <= 0) {
string_db.Del(args_[1]);
*output = Redis::SimpleString("OK");
return Status::OK();
}
}

if (nx_) {
s = string_db.SetNX(args_[1], args_[2], ttl_, &ret);
} else if (xx_) {
Expand All @@ -534,6 +563,7 @@ class CommandSet : public Commander {
bool xx_ = false;
bool nx_ = false;
int ttl_ = 0;
int64_t expire_ = 0;
};

class CommandSetEX : public Commander {
Expand Down
11 changes: 6 additions & 5 deletions src/slot_migrate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,10 @@ Status SlotMigrate::MigrateOneKey(const rocksdb::Slice &key, const rocksdb::Slic
return Status(Status::cOK, "empty");
}

if (metadata.Expired()) {
return Status(Status::cOK, "expired");
}

// Construct command according to type of the key
switch (metadata.Type()) {
case kRedisString: {
Expand Down Expand Up @@ -587,11 +591,8 @@ bool SlotMigrate::MigrateSimpleKey(const rocksdb::Slice &key, const Metadata &me
const std::string &bytes, std::string *restore_cmds) {
std::vector<std::string> command = {"set", key.ToString(), bytes.substr(5, bytes.size() - 5)};
if (metadata.expire > 0) {
int64_t now;
rocksdb::Env::Default()->GetCurrentTime(&now);
int32_t ttl = metadata.expire - uint32_t(now);
command.emplace_back("EX");
command.emplace_back(std::to_string(ttl));
command.emplace_back("EXAT");
command.emplace_back(std::to_string(metadata.expire));
}
*restore_cmds += Redis::MultiBulkString(command, false);
current_pipeline_size_++;
Expand Down
8 changes: 8 additions & 0 deletions tests/tcl/tests/integration/slotmigrate.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ start_server {tags {"Src migration server"} overrides {cluster-enabled yes}} {
# Set keys
set slot1_tag [lindex $::CRC16_SLOT_TABLE 1]
set slot1_key_string string_{$slot1_tag}
set slot1_key_string2 string2_{$slot1_tag}
set slot1_key_list list_{$slot1_tag}
set slot1_key_hash hash_{$slot1_tag}
set slot1_key_set set_{$slot1_tag}
Expand All @@ -168,6 +169,11 @@ start_server {tags {"Src migration server"} overrides {cluster-enabled yes}} {
$r0 set $slot1_key_string $slot1_key_string
$r0 expire $slot1_key_string 10000

# Expired string key
$r0 set $slot1_key_string2 $slot1_key_string2 ex 1
after 3000
assert_equal [$r0 get $slot1_key_string2] {}

# Type: list
$r0 rpush $slot1_key_list 0 1 2 3 4 5
$r0 lpush $slot1_key_list 9 3 7 3 5 4
Expand Down Expand Up @@ -242,6 +248,8 @@ start_server {tags {"Src migration server"} overrides {cluster-enabled yes}} {
set expire_time [$r1 ttl $slot1_key_string]
assert {$expire_time > 1000 && $expire_time <= 10000}

assert_equal [$r1 get $slot1_key_string2] {}

# Check list and expired time
assert {[$r1 lrange $slot1_key_list 0 -1] eq $lvalue}
set expire_time [$r1 ttl $slot1_key_list]
Expand Down
63 changes: 51 additions & 12 deletions tests/tcl/tests/unit/type/string.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -556,17 +556,41 @@ start_server {tags {"string"}} {
assert {$ttl <= 10 && $ttl > 5}
}

# test "Extended SET EXAT option" {
# r del foo
# r set foo bar exat [expr [clock seconds] + 10]
# assert_range [r ttl foo] 5 10
# }
test {Extended SET EXAT option} {
r del foo
r set foo bar exat [expr [clock seconds] + 10]
assert_range [r ttl foo] 5 10
}

# test "Extended SET PXAT option" {
# r del foo
# r set foo bar pxat [expr [clock milliseconds] + 10000]
# assert_range [r ttl foo] 5 10
# }
test {Extended SET EXAT option with expired timestamp} {
r del foo
assert_equal [r set foo bar exat 1] OK
assert_equal [r get foo] {}

r set foo bar
assert_equal [r get foo] bar

assert_equal [r set foo bar exat [expr [clock seconds] - 5]] OK
assert_equal [r get foo] {}
}

test {Extended SET PXAT option} {
r del foo
r set foo bar pxat [expr [clock milliseconds] + 10000]
assert_range [r ttl foo] 5 10
}

test {Extended SET PXAT option with expired timestamp} {
r del foo
assert_equal [r set foo bar pxat 1] OK
assert_equal [r get foo] {}

r set foo bar
assert_equal [r get foo] bar

assert_equal [r set foo bar pxat [expr [clock milliseconds] - 5000]] OK
assert_equal [r get foo] {}
}

test {Extended SET with incorrect use of multi options should result in syntax err} {
catch {r set foo bar ex 10 px 10000} err1
Expand All @@ -576,8 +600,23 @@ start_server {tags {"string"}} {

test {Extended SET with incorrect expire value} {
catch {r set foo bar ex 1234xyz} e
set e
} {*not an integer*}
assert_match {*not an integer*} $e

catch {r set foo bar ex 0} e
assert_match {*invalid expire time*} $e

catch {r set foo bar exat 1234xyz} e
assert_match {*not an integer*} $e

catch {r set foo bar exat 0} e
assert_match {*invalid expire time*} $e

catch {r set foo bar pxat 1234xyz} e
assert_match {*not an integer*} $e

catch {r set foo bar pxat 0} e
assert_match {*invalid expire time*} $e
}

test {Extended SET using multiple options at once} {
r set foo val
Expand Down