This is an automated email from the ASF dual-hosted git repository.

twice 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 4025a6d23 feat(config): add RocksDB periodic compaction controls 
(#3170)
4025a6d23 is described below

commit 4025a6d23698b6bd5383511f259d10988fe7dc0e
Author: sryan yuan <[email protected]>
AuthorDate: Tue Sep 16 17:31:34 2025 +0800

    feat(config): add RocksDB periodic compaction controls (#3170)
    
    Introduces three new configuration fields for RocksDB periodic
    compaction management:
    
    1. `rocksdb.periodic_compaction_seconds` - Sets compaction frequency
    2. `rocksdb.ttl` - Enables TTL-based compaction
    3. `rocksdb.daily_offpeak_time_utc` - Configures off-peak compaction
    window
    
    ---------
    
    Co-authored-by: yxj25245 <[email protected]>
    Co-authored-by: Twice <[email protected]>
    Co-authored-by: mwish <[email protected]>
    Co-authored-by: Twice <[email protected]>
---
 kvrocks.conf                            | 51 ++++++++++++++++++++++++++++
 src/config/config.cc                    |  7 ++++
 src/config/config.h                     |  6 ++++
 src/storage/storage.cc                  |  3 ++
 tests/gocase/unit/config/config_test.go | 59 +++++++++++++++++++++++++++++++++
 5 files changed, 126 insertions(+)

diff --git a/kvrocks.conf b/kvrocks.conf
index a4754793b..fb34204ba 100644
--- a/kvrocks.conf
+++ b/kvrocks.conf
@@ -1124,5 +1124,56 @@ rocksdb.max_compaction_bytes 0
 # Default: 0
 rocksdb.sst_file_delete_rate_bytes_per_sec 0
 
+# Enable RocksDB periodic compaction to force full compaction of SST files 
older than the specified time (in seconds).
+# If a compaction filter is registered, it will be applied during these 
compactions. 
+# Set to 0 to disable this feature.
+#
+# Default: 18446744073709551614 (0xFFFFFFFFFFFFFFFE, UINT64_MAX - 1), a 
special value indicating RocksDB-controlled behavior.
+# Currently, RocksDB interprets this default as 30 days (2592000 seconds).
+#
+# Typical use cases:
+# - Enforcing data cleanup via compaction filters (e.g., TTL expiration)
+# - Automatically refreshing data encoding/compression formats without manual 
intervention
+#
+# Reference: 
https://github.com/facebook/rocksdb/wiki/Leveled-Compaction#periodic-compaction
+#
+# rocksdb.periodic_compaction_seconds 2592000
+
+# Enable RocksDB Time-to-Live (TTL) to automatically schedule compaction for 
SST files containing expired data.
+# - Files containing data older than the TTL (in seconds) will be prioritized 
for background compaction.
+# - Requires a registered compaction filter (e.g., TTL filter) to identify and 
remove expired entries.
+# - Set to 0 to disable TTL-based compaction.
+#
+# Default: 18446744073709551614 (0xFFFFFFFFFFFFFFFE, UINT64_MAX - 1), 
delegating control to RocksDB.
+# Current RocksDB behavior interprets this default as 30 days (2592000 
seconds).
+#
+# Use cases:
+# - Automatic expiration of ephemeral data (e.g., session tokens, temporary 
logs)
+# - Lifecycle management for time-series datasets
+#
+# Reference: https://github.com/facebook/rocksdb/wiki/Leveled-Compaction#ttl
+#
+# rocksdb.ttl 2592000
+
+# Schedule RocksDB periodic compactions during daily off-peak windows to 
reduce operational impact.
+# 
+# Requirements:
+# - Periodic compaction must be enabled (`periodic-compaction-seconds > 0`)
+# - Time format: "HH:MM-HH:MM" in UTC (e.g., "02:00-04:30" for a 2.5-hour 
window)
+# - Empty string disables off-peak scheduling
+#
+# Behavior:
+# - RocksDB proactively triggers periodic compactions during the specified 
off-peak window
+# - Compactions are optimized to complete before the next peak period begins
+#
+# Default: "" (disabled)
+#
+# Typical use cases:
+# - Minimize compaction I/O during business hours for latency-sensitive 
workloads
+# - Align resource-heavy operations with maintenance windows
+#
+# Reference: 
https://github.com/facebook/rocksdb/wiki/Daily-Off%E2%80%90peak-Time-Option
+rocksdb.daily_offpeak_time_utc ""
+
 ################################ NAMESPACE 
#####################################
 # namespace.test change.me
diff --git a/src/config/config.cc b/src/config/config.cc
index 746932dbe..c3b3400f7 100644
--- a/src/config/config.cc
+++ b/src/config/config.cc
@@ -307,6 +307,10 @@ Config::Config() {
       {"rocksdb.max_compaction_bytes", false, new 
Int64Field(&rocks_db.max_compaction_bytes, 0, 0, INT64_MAX)},
       {"rocksdb.sst_file_delete_rate_bytes_per_sec", false,
        new Int64Field(&rocks_db.sst_file_delete_rate_bytes_per_sec, 0, 0, 
INT64_MAX)},
+      {"rocksdb.periodic_compaction_seconds", false,
+       new UInt64Field(&rocks_db.periodic_compaction_seconds, 
kDefaultRocksdbPeriodicCompactionSeconds, 0, UINT64_MAX)},
+      {"rocksdb.ttl", false, new UInt64Field(&rocks_db.ttl, 
kDefaultRocksdbTTL, 0, UINT64_MAX)},
+      {"rocksdb.daily_offpeak_time_utc", false, new 
StringField(&rocks_db.daily_offpeak_time_utc, "")},
 
       /* rocksdb write options */
       {"rocksdb.write_options.sync", true, new 
YesNoField(&rocks_db.write_options.sync, false)},
@@ -732,6 +736,9 @@ void Config::initFieldCallback() {
              
srv->storage->SetSstFileDeleteRateBytesPerSecond(rocks_db.sst_file_delete_rate_bytes_per_sec);
              return Status::OK();
            }},
+          {"rocksdb.periodic_compaction_seconds", set_cf_option_cb},
+          {"rocksdb.ttl", set_cf_option_cb},
+          {"rocksdb.daily_offpeak_time_utc", set_db_option_cb},
           {"rocksdb.level0_slowdown_writes_trigger",
            [this, &set_cf_option_cb](Server *srv, const std::string &k,
                                      [[maybe_unused]] const std::string &v) -> 
Status {
diff --git a/src/config/config.h b/src/config/config.h
index 30527eca6..c56a99f0b 100644
--- a/src/config/config.h
+++ b/src/config/config.h
@@ -57,6 +57,9 @@ constexpr const uint32_t kDefaultPort = 6666;
 constexpr const char *kDefaultNamespace = "__namespace";
 constexpr int KVROCKS_MAX_LSM_LEVEL = 7;
 
+constexpr const uint64_t kDefaultRocksdbTTL = UINT64_MAX - 1;
+constexpr const uint64_t kDefaultRocksdbPeriodicCompactionSeconds = UINT64_MAX 
- 1;
+
 const std::vector<ConfigEnum<spdlog::level::level_enum>> log_levels{
     {"debug", spdlog::level::debug}, {"info", spdlog::level::info},      
{"warning", spdlog::level::warn},
     {"error", spdlog::level::err},   {"fatal", spdlog::level::critical},
@@ -243,6 +246,9 @@ struct Config {
     bool partition_filters;
     int64_t max_compaction_bytes;
     int64_t sst_file_delete_rate_bytes_per_sec = 0;
+    uint64_t periodic_compaction_seconds = 
kDefaultRocksdbPeriodicCompactionSeconds;
+    uint64_t ttl = kDefaultRocksdbTTL;
+    std::string daily_offpeak_time_utc;
 
     struct WriteOptions {
       bool sync;
diff --git a/src/storage/storage.cc b/src/storage/storage.cc
index 1138f2ace..3fb83de52 100644
--- a/src/storage/storage.cc
+++ b/src/storage/storage.cc
@@ -219,6 +219,9 @@ rocksdb::Options Storage::InitRocksDBOptions() {
   options.level_compaction_dynamic_level_bytes = 
config_->rocks_db.level_compaction_dynamic_level_bytes;
   options.max_background_jobs = config_->rocks_db.max_background_jobs;
   options.max_compaction_bytes = 
static_cast<uint64_t>(config_->rocks_db.max_compaction_bytes);
+  options.periodic_compaction_seconds = 
config_->rocks_db.periodic_compaction_seconds;
+  options.ttl = config_->rocks_db.ttl;
+  options.daily_offpeak_time_utc = config_->rocks_db.daily_offpeak_time_utc;
 
   // avoid blocking io on iteration
   // see https://github.com/facebook/rocksdb/wiki/IO#avoid-blocking-io
diff --git a/tests/gocase/unit/config/config_test.go 
b/tests/gocase/unit/config/config_test.go
index 3287318ea..be3276c3c 100644
--- a/tests/gocase/unit/config/config_test.go
+++ b/tests/gocase/unit/config/config_test.go
@@ -22,6 +22,7 @@ package config
 import (
        "context"
        "log"
+       "math"
        "os"
        "path/filepath"
        "sort"
@@ -395,3 +396,61 @@ func TestConfigSstFileDeleteRateBytesPerSec(t *testing.T) {
                require.EqualValues(t, "1073741824", result[parameter])
        })
 }
+
+func TestConfigPeriodicCompactionSecondsAndTTL(t *testing.T) {
+       t.Parallel()
+       srv := util.StartServer(t, map[string]string{})
+       defer srv.Close()
+
+       cli := srv.NewClient()
+       defer func() { require.NoError(t, cli.Close()) }()
+
+       testTemplate := func(t *testing.T, field string) func(*testing.T) {
+               return func(*testing.T) {
+                       ctx := context.Background()
+                       parameter := field
+                       result, err := cli.ConfigGet(ctx, parameter).Result()
+                       require.NoError(t, err)
+                       require.EqualValues(t, 
strconv.FormatUint(math.MaxUint64-1, 10), result[parameter])
+
+                       util.ErrorRegexp(t, cli.ConfigSet(ctx, parameter, 
"-1").Err(), ".*not started as an integer")
+
+                       require.NoError(t, cli.ConfigSet(ctx, parameter, 
strconv.FormatUint(math.MaxUint64, 10)).Err())
+                       result, err = cli.ConfigGet(ctx, parameter).Result()
+                       require.NoError(t, err)
+                       require.EqualValues(t, 
strconv.FormatUint(math.MaxUint64, 10), result[parameter])
+               }
+       }
+
+       t.Run("Get and Set rocksdb.periodic_compaction_seconds", 
testTemplate(t, "rocksdb.periodic_compaction_seconds"))
+       t.Run("Get and Set rocksdb.ttl", testTemplate(t, "rocksdb.ttl"))
+}
+
+func TestConfigDailyOffpeakTimeUTC(t *testing.T) {
+       t.Parallel()
+       srv := util.StartServer(t, map[string]string{})
+       defer srv.Close()
+
+       cli := srv.NewClient()
+       defer func() { require.NoError(t, cli.Close()) }()
+
+       t.Run("Get and Set rocksdb.daily_offpeak_time_utc", func(t *testing.T) {
+               ctx := context.Background()
+               parameter := "rocksdb.daily_offpeak_time_utc"
+               result, err := cli.ConfigGet(ctx, parameter).Result()
+               require.NoError(t, err)
+               require.EqualValues(t, "", result[parameter])
+
+               util.ErrorRegexp(t, cli.ConfigSet(ctx, parameter, 
"VALUE").Err(), ".*Invalid argument.*")
+
+               require.NoError(t, cli.ConfigSet(ctx, parameter, 
"00:00-00:30").Err())
+               result, err = cli.ConfigGet(ctx, parameter).Result()
+               require.NoError(t, err)
+               require.EqualValues(t, "00:00-00:30", result[parameter])
+
+               require.NoError(t, cli.ConfigSet(ctx, parameter, "").Err())
+               result, err = cli.ConfigGet(ctx, parameter).Result()
+               require.NoError(t, err)
+               require.EqualValues(t, "", result[parameter])
+       })
+}

Reply via email to