Skip to content

Commit 1cf77d3

Browse files
committed
Merge remote-tracking branch 'fork/feat-implement-TDIGEST.TRIMMED_MEAN-command' into feat-implement-TDIGEST.TRIMMED_MEAN-command
2 parents 7d02463 + f9ca36d commit 1cf77d3

13 files changed

Lines changed: 762 additions & 34 deletions

File tree

.github/workflows/kvrocks.yaml

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
outputs:
4040
docs_only: ${{ steps.result.outputs.docs_only }}
4141
steps:
42-
- uses: actions/checkout@v4
42+
- uses: actions/checkout@v6
4343
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36
4444
id: changes
4545
with:
@@ -56,7 +56,7 @@ jobs:
5656
env:
5757
FORCE_COLOR: 1
5858
steps:
59-
- uses: actions/checkout@v4
59+
- uses: actions/checkout@v6
6060
- name: Check typos
6161
uses: crate-ci/typos@v1.43.1
6262
with:
@@ -71,8 +71,8 @@ jobs:
7171
if: ${{ needs.precondition.outputs.docs_only != 'true' }}
7272
runs-on: ubuntu-24.04
7373
steps:
74-
- uses: actions/checkout@v4
75-
- uses: actions/setup-go@v5
74+
- uses: actions/checkout@v6
75+
- uses: actions/setup-go@v6
7676
with:
7777
go-version-file: 'tests/gocase/go.mod'
7878
cache: false
@@ -96,7 +96,7 @@ jobs:
9696
git diff -p > clang-format.patch
9797
cat clang-format.patch
9898
- name: Upload format patch
99-
uses: actions/upload-artifact@v4
99+
uses: actions/upload-artifact@v7
100100
if: always() && steps.check-format.outcome != 'success'
101101
with:
102102
path: clang-format.patch
@@ -291,14 +291,14 @@ jobs:
291291
292292
- name: Cache redis
293293
id: cache-redis
294-
uses: actions/cache@v4
294+
uses: actions/cache@v5
295295
with:
296296
path: |
297297
~/local/bin/redis-cli
298298
key: ${{ runner.os }}-${{ runner.arch }}-redis-cli
299299
- name: Cache redis server
300300
id: cache-redis-server
301-
uses: actions/cache@v4
301+
uses: actions/cache@v5
302302
with:
303303
path: |
304304
~/local/bin/redis-server
@@ -312,14 +312,14 @@ jobs:
312312
pushd redis-6.2.14 && BUILD_TLS=yes make -j$NPROC redis-cli && mv src/redis-cli $HOME/local/bin/ && popd
313313
pushd redis-6.2.14 && BUILD_TLS=yes make -j$NPROC redis-server && mv src/redis-server $HOME/local/bin/ && popd
314314
315-
- uses: actions/checkout@v4
315+
- uses: actions/checkout@v6
316316
with:
317317
fetch-depth: 0
318-
- uses: actions/setup-python@v5
318+
- uses: actions/setup-python@v6
319319
if: ${{ !matrix.arm_linux }}
320320
with:
321321
python-version: 3.x
322-
- uses: actions/setup-go@v5
322+
- uses: actions/setup-go@v6
323323
with:
324324
go-version-file: 'tests/gocase/go.mod'
325325
cache: false
@@ -425,7 +425,7 @@ jobs:
425425
exit 1
426426
fi
427427
428-
- uses: actions/upload-artifact@v4
428+
- uses: actions/upload-artifact@v7
429429
if: ${{ failure() && startsWith(matrix.os, 'ubuntu') }}
430430
with:
431431
name: kvrocks-coredumps-${{ matrix.name }}
@@ -447,7 +447,7 @@ jobs:
447447
448448
- name: Upload SonarCloud data
449449
if: ${{ matrix.sonarcloud }}
450-
uses: actions/upload-artifact@v4
450+
uses: actions/upload-artifact@v7
451451
with:
452452
name: sonarcloud-data
453453
path: |
@@ -463,7 +463,7 @@ jobs:
463463
matrix:
464464
os: [ubuntu-24.04, ubuntu-24.04-arm]
465465
steps:
466-
- uses: actions/checkout@v4
466+
- uses: actions/checkout@v6
467467
- name: Get core numbers
468468
run: echo "NPROC=$(nproc)" >> $GITHUB_ENV
469469
- uses: docker/build-push-action@v6
@@ -591,15 +591,15 @@ jobs:
591591
592592
- name: Cache redis
593593
id: cache-redis
594-
uses: actions/cache@v4
594+
uses: actions/cache@v5
595595
with:
596596
path: |
597597
~/local/bin/redis-cli
598598
key: ${{ matrix.image }}-redis-cli
599599

600600
- name: Cache redis server
601601
id: cache-redis-server
602-
uses: actions/cache@v4
602+
uses: actions/cache@v5
603603
with:
604604
path: |
605605
~/local/bin/redis-server
@@ -615,8 +615,8 @@ jobs:
615615
pushd redis-6.2.14 && USE_JEMALLOC=no make -j$NPROC redis-cli && mv src/redis-cli $HOME/local/bin/ && popd
616616
pushd redis-6.2.14 && USE_JEMALLOC=no make -j$NPROC redis-server && mv src/redis-server $HOME/local/bin/ && popd
617617
618-
- uses: actions/checkout@v4
619-
- uses: actions/setup-go@v5
618+
- uses: actions/checkout@v6
619+
- uses: actions/setup-go@v6
620620
if: ${{ !startsWith(matrix.image, 'opensuse') }}
621621
with:
622622
go-version-file: 'tests/gocase/go.mod'

.github/workflows/nightly.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
platform=${{ matrix.platform }}
4343
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
4444
45-
- uses: actions/checkout@v4
45+
- uses: actions/checkout@v6
4646
with:
4747
fetch-depth: 0
4848

@@ -60,7 +60,7 @@ jobs:
6060
password: ${{ secrets.DOCKER_PASSWORD }}
6161

6262
- name: Set up QEMU
63-
uses: docker/setup-qemu-action@v3
63+
uses: docker/setup-qemu-action@v4
6464

6565
- name: Set up Docker Buildx
6666
uses: docker/setup-buildx-action@v3
@@ -82,7 +82,7 @@ jobs:
8282
touch "${{ runner.temp }}/digests/${digest#sha256:}"
8383
8484
- name: Upload digest
85-
uses: actions/upload-artifact@v4
85+
uses: actions/upload-artifact@v7
8686
if: ${{ github.event_name != 'pull_request' }}
8787
with:
8888
name: digests-${{ env.PLATFORM_PAIR }}
@@ -98,14 +98,14 @@ jobs:
9898
- build
9999
steps:
100100
- name: Download digests
101-
uses: actions/download-artifact@v4
101+
uses: actions/download-artifact@v8
102102
with:
103103
path: ${{ runner.temp }}/digests
104104
pattern: digests-*
105105
merge-multiple: true
106106

107107
- name: Login to Docker Hub
108-
uses: docker/login-action@v3
108+
uses: docker/login-action@v4
109109
with:
110110
username: ${{ secrets.DOCKER_USERNAME }}
111111
password: ${{ secrets.DOCKER_PASSWORD }}

.github/workflows/pr-lint.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
name: Validate PR title
3333
runs-on: ubuntu-latest
3434
steps:
35-
- uses: amannn/action-semantic-pull-request@v5
35+
- uses: amannn/action-semantic-pull-request@v6
3636
env:
3737
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3838
with:

.github/workflows/sonar.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ jobs:
2828
runs-on: ubuntu-22.04
2929
if: github.event.workflow_run.conclusion == 'success' && github.repository_owner == 'apache'
3030
steps:
31-
- uses: actions/checkout@v4
31+
- uses: actions/checkout@v6
3232
with:
3333
repository: ${{ github.event.workflow_run.head_repository.full_name }}
3434
ref: ${{ github.event.workflow_run.head_sha }}
3535
fetch-depth: 0
3636
- name: Install Build Wrapper
37-
uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v6.0.0
37+
uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v7.0.0
3838
- name: 'Download code coverage'
39-
uses: actions/github-script@v7
39+
uses: actions/github-script@v8
4040
with:
4141
script: |
4242
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
@@ -62,7 +62,7 @@ jobs:
6262
mkdir -p build/CMakeFiles/CMakeTmp
6363
ls -a sonarcloud-data build
6464
65-
- uses: actions/setup-python@v5
65+
- uses: actions/setup-python@v6
6666
with:
6767
python-version: 3.x
6868

@@ -73,7 +73,7 @@ jobs:
7373
echo "The PR number is ${PR_NUMBER:-<none>}"
7474
7575
- name: SonarQube Scan
76-
uses: SonarSource/sonarqube-scan-action@v6.0.0
76+
uses: SonarSource/sonarqube-scan-action@v7.0.0
7777
env:
7878
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
7979
SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }}

src/commands/cmd_server.cc

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "commander.h"
2727
#include "commands/scan_base.h"
2828
#include "common/io_util.h"
29+
#include "common/logging.h"
2930
#include "common/rdb_stream.h"
3031
#include "common/string_util.h"
3132
#include "common/time_util.h"
@@ -520,6 +521,34 @@ class CommandClient : public Commander {
520521
return Status::OK();
521522
}
522523

524+
if (subcommand_ == "unpause") {
525+
if (args.size() != 2) {
526+
return {Status::RedisParseErr, errInvalidSyntax};
527+
}
528+
return Status::OK();
529+
}
530+
531+
if (subcommand_ == "pause") {
532+
if (args.size() < 3 || args.size() > 4) {
533+
return {Status::RedisParseErr, errInvalidSyntax};
534+
}
535+
auto parse_result = ParseInt<uint64_t>(args[2], 10);
536+
if (!parse_result) {
537+
return {Status::RedisParseErr, errValueNotInteger};
538+
}
539+
pause_timeout_ms_ = *parse_result;
540+
pause_mode_ = PauseMode::kAll;
541+
if (args.size() == 4) {
542+
std::string mode = util::ToLower(args[3]);
543+
if (mode == "write") {
544+
pause_mode_ = PauseMode::kWrite;
545+
} else if (mode != "all") {
546+
return {Status::RedisParseErr, errInvalidSyntax};
547+
}
548+
}
549+
return Status::OK();
550+
}
551+
523552
if ((subcommand_ == "kill")) {
524553
if (args.size() == 2) {
525554
return {Status::RedisParseErr, errInvalidSyntax};
@@ -572,7 +601,9 @@ class CommandClient : public Commander {
572601
}
573602
return Status::OK();
574603
}
575-
return {Status::RedisInvalidCmd, "Syntax error, try CLIENT LIST|INFO|KILL ip:port|GETNAME|SETNAME|REPLY"};
604+
return {Status::RedisInvalidCmd,
605+
"Syntax error, try CLIENT LIST|INFO|KILL ip:port|GETNAME|SETNAME|REPLY|"
606+
"PAUSE|UNPAUSE"};
576607
}
577608

578609
Status Execute([[maybe_unused]] engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
@@ -611,9 +642,28 @@ class CommandClient : public Commander {
611642
*output = redis::RESP_OK;
612643
}
613644
return Status::OK();
645+
} else if (subcommand_ == "pause") {
646+
if (!conn->IsAdmin()) {
647+
return {Status::RedisExecErr, errAdminPermissionRequired};
648+
}
649+
uint64_t now_ms = util::GetTimeStampMS();
650+
srv->PauseConns(now_ms + pause_timeout_ms_, pause_mode_);
651+
WARN("CLIENT PAUSE executed, timeout={}ms, mode={}, addr: {}", pause_timeout_ms_,
652+
pause_mode_ == PauseMode::kWrite ? "write" : "all", conn->GetAddr());
653+
*output = redis::RESP_OK;
654+
return Status::OK();
655+
} else if (subcommand_ == "unpause") {
656+
if (!conn->IsAdmin()) {
657+
return {Status::RedisExecErr, errAdminPermissionRequired};
658+
}
659+
srv->UnpauseConns();
660+
*output = redis::RESP_OK;
661+
return Status::OK();
614662
}
615663

616-
return {Status::RedisInvalidCmd, "Syntax error, try CLIENT LIST|INFO|KILL ip:port|GETNAME|SETNAME|REPLY"};
664+
return {Status::RedisInvalidCmd,
665+
"Syntax error, try CLIENT LIST|INFO|KILL ip:port|GETNAME|SETNAME|REPLY|"
666+
"PAUSE|UNPAUSE"};
617667
}
618668

619669
private:
@@ -625,6 +675,8 @@ class CommandClient : public Commander {
625675
int64_t kill_type_ = 0;
626676
uint64_t id_ = 0;
627677
bool new_format_ = true;
678+
uint64_t pause_timeout_ms_ = 0;
679+
PauseMode pause_mode_ = PauseMode::kAll;
628680
};
629681

630682
class CommandMonitor : public Commander {

src/server/redis_connection.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ std::string Connection::GetFlags() const {
209209
if (IsFlagEnabled(kMonitor)) flags.append("M");
210210
if (IsFlagEnabled(kAsking)) flags.append("A");
211211
if (!subscribe_channels_.empty() || !subscribe_patterns_.empty()) flags.append("P");
212+
if (is_paused_) flags.append("z");
212213
if (flags.empty()) flags = "N";
213214
return flags;
214215
}
@@ -226,6 +227,21 @@ bool Connection::CanMigrate() const {
226227
&& subscribe_channels_.empty() && subscribe_patterns_.empty(); // not subscribing any channel
227228
}
228229

230+
void Connection::Pause() {
231+
if (is_paused_) return;
232+
is_paused_ = true;
233+
bufferevent_disable(bev_, EV_READ);
234+
}
235+
236+
void Connection::Unpause() {
237+
if (!is_paused_) return;
238+
is_paused_ = false;
239+
bufferevent_enable(bev_, EV_READ);
240+
// Trigger OnRead so commands buffered in req_ during the pause are processed.
241+
// Without this, no new data arrives on the socket and OnRead would never fire.
242+
bufferevent_trigger(bev_, EV_READ, BEV_TRIG_IGNORE_WATERMARKS);
243+
}
244+
229245
void Connection::SubscribeChannel(const std::string &channel) {
230246
for (const auto &chan : subscribe_channels_) {
231247
if (channel == chan) return;
@@ -451,6 +467,14 @@ void Connection::ExecuteCommands(std::deque<CommandTokens> *to_process_cmds) {
451467
}
452468

453469
auto cmd_flags = attributes->GenerateFlags(cmd_tokens, *config);
470+
471+
// Push the command back and stop processing; it will be re-executed after unpause.
472+
if (srv_->PauseConnIfNeeded(this, cmd_name, cmd_flags)) {
473+
multi_error_exit.Disable(); // Don't mark transaction as failed - we're deferring, not erroring
474+
to_process_cmds->push_front(std::move(cmd_tokens));
475+
return;
476+
}
477+
454478
if (GetNamespace().empty()) {
455479
if (!password.empty()) {
456480
if (!(cmd_flags & kCmdAuth)) {

src/server/redis_connection.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ class Connection : public EvbufCallbackBase<Connection> {
176176
bool IsImporting() const { return importing_; }
177177
bool CanMigrate() const;
178178

179+
// CLIENT PAUSE async suspend/resume
180+
void Pause();
181+
void Unpause();
182+
bool IsPaused() const { return is_paused_; }
183+
179184
// Multi exec
180185
void SetInExec() { in_exec_ = true; }
181186
bool IsInExec() const { return in_exec_; }
@@ -230,6 +235,8 @@ class Connection : public EvbufCallbackBase<Connection> {
230235

231236
ReplyMode reply_mode_ = ReplyMode::ON;
232237
std::vector<std::string> queued_replies_;
238+
239+
bool is_paused_ = false;
233240
};
234241

235242
} // namespace redis

0 commit comments

Comments
 (0)