Skip to content

Commit 491c7bd

Browse files
configurable DB seed for consistency
Signed-off-by: Sarthak Aggarwal <[email protected]>
1 parent d21d529 commit 491c7bd

File tree

12 files changed

+155
-2
lines changed

12 files changed

+155
-2
lines changed

src/config.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3177,6 +3177,19 @@ static int applyClientMaxMemoryUsage(const char **err) {
31773177
return 1;
31783178
}
31793179

3180+
#define DB_HASH_SEED_MAX_LEN 256
3181+
static int isValidDbHashSeed(char *val, const char **err) {
3182+
if (val[0] == '\0') {
3183+
*err = "db-hash-seed can't be empty";
3184+
return 0;
3185+
}
3186+
if (strlen(val) > DB_HASH_SEED_MAX_LEN) {
3187+
*err = "db-hash-seed must be less than or equal to " STRINGIFY(DB_HASH_SEED_MAX_LEN) " characters";
3188+
return 0;
3189+
}
3190+
return 1;
3191+
}
3192+
31803193
standardConfig static_configs[] = {
31813194
/* Bool configs */
31823195
createBoolConfig("rdbchecksum", NULL, IMMUTABLE_CONFIG, server.rdb_checksum, 1, NULL, NULL),
@@ -3254,6 +3267,7 @@ standardConfig static_configs[] = {
32543267
createStringConfig("bgsave-cpulist", "bgsave_cpulist", IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bgsave_cpulist, NULL, NULL, NULL),
32553268
createStringConfig("ignore-warnings", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.ignore_warnings, "", NULL, NULL),
32563269
createStringConfig("proc-title-template", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.proc_title_template, CONFIG_DEFAULT_PROC_TITLE_TEMPLATE, isValidProcTitleTemplate, updateProcTitleTemplate),
3270+
createStringConfig("db-hash-seed", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.db_hash_seed, NULL, isValidDbHashSeed, NULL),
32573271
createStringConfig("bind-source-addr", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bind_source_addr, NULL, NULL, NULL),
32583272
createStringConfig("logfile", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.logfile, "", NULL, NULL),
32593273
#ifdef LOG_REQ_RES

src/hashtable.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k)
7272
/* --- Global variables --- */
7373

7474
static uint8_t hash_function_seed[16];
75+
static uint8_t db_hash_function_seed[16];
7576
static hashtableResizePolicy resize_policy = HASHTABLE_RESIZE_ALLOW;
7677

7778
/* --- Fill factor --- */
@@ -112,6 +113,14 @@ uint64_t hashtableGenCaseHashFunction(const char *buf, size_t len) {
112113
return siphash_nocase((const uint8_t *)buf, len, hash_function_seed);
113114
}
114115

116+
void dbHashtableSetHashFunctionSeed(uint8_t *seed) {
117+
memcpy(db_hash_function_seed, seed, sizeof(db_hash_function_seed));
118+
}
119+
120+
uint64_t dbHashtableGenHashFunction(const void *key, size_t len) {
121+
return siphash(key, len, db_hash_function_seed);
122+
}
123+
115124
/* --- Global resize policy API --- */
116125

117126
/* The global resize policy is one of

src/hashtable.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ void hashtableSetHashFunctionSeed(const uint8_t *seed);
110110
uint8_t *hashtableGetHashFunctionSeed(void);
111111
uint64_t hashtableGenHashFunction(const char *buf, size_t len);
112112
uint64_t hashtableGenCaseHashFunction(const char *buf, size_t len);
113+
void dbHashtableSetHashFunctionSeed(uint8_t *seed);
114+
uint64_t dbHashtableGenHashFunction(const void *key, size_t len);
113115

114116
/* Global resize policy */
115117
void hashtableSetResizePolicy(hashtableResizePolicy policy);

src/server.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ hashtableType zsetHashtableType = {
573573
};
574574

575575
uint64_t hashtableSdsHash(const void *key) {
576-
return hashtableGenHashFunction((const char *)key, sdslen((char *)key));
576+
return dbHashtableGenHashFunction((const char *)key, sdslen((char *)key));
577577
}
578578

579579
const void *hashtableObjectGetKey(const void *entry) {
@@ -7136,6 +7136,10 @@ __attribute__((weak)) int main(int argc, char **argv) {
71367136
getRandomBytes(hashseed, sizeof(hashseed));
71377137
dictSetHashFunctionSeed(hashseed);
71387138
hashtableSetHashFunctionSeed(hashseed);
7139+
if (server.db_hash_seed != NULL) {
7140+
getHashSeedFromValue(hashseed, sizeof(hashseed), server.db_hash_seed);
7141+
}
7142+
dbHashtableSetHashFunctionSeed(hashseed);
71397143

71407144
char *exec_name = strrchr(argv[0], '/');
71417145
if (exec_name == NULL) exec_name = argv[0];

src/server.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2213,6 +2213,7 @@ struct valkeyServer {
22132213
* dropping packets of a specific type */
22142214
unsigned long cluster_blacklist_ttl; /* Duration in seconds that a node is denied re-entry into
22152215
* the cluster after it is forgotten with CLUSTER FORGET. */
2216+
char *db_hash_seed; /* Configurable DB hash seed */
22162217
int cluster_slot_stats_enabled; /* Cluster slot usage statistics tracking enabled. */
22172218
mstime_t cluster_mf_timeout; /* Milliseconds to do a manual failover. */
22182219
unsigned long cluster_slot_migration_log_max_len; /* Maximum count of migrations to display in the

src/unit/test_files.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ int test_ld2string(int argc, char **argv, int flags);
202202
int test_fixedpoint_d2string(int argc, char **argv, int flags);
203203
int test_version2num(int argc, char **argv, int flags);
204204
int test_reclaimFilePageCache(int argc, char **argv, int flags);
205+
int test_getHashSeedFromValue(int argc, char **argv, int flags);
205206
int test_writePointerWithPadding(int argc, char **argv, int flags);
206207
int test_valkey_strtod(int argc, char **argv, int flags);
207208
int test_vector(int argc, char **argv, int flags);
@@ -272,7 +273,7 @@ unitTest __test_rax_c[] = {{"test_raxRandomWalk", test_raxRandomWalk}, {"test_ra
272273
unitTest __test_sds_c[] = {{"test_sds", test_sds}, {"test_typesAndAllocSize", test_typesAndAllocSize}, {"test_sdsHeaderSizes", test_sdsHeaderSizes}, {"test_sdssplitargs", test_sdssplitargs}, {NULL, NULL}};
273274
unitTest __test_sha1_c[] = {{"test_sha1", test_sha1}, {NULL, NULL}};
274275
unitTest __test_sha256_c[] = {{"test_sha256_abc", test_sha256_abc}, {"test_sha256_large", test_sha256_large}, {"test_sha256_million_a", test_sha256_million_a}, {NULL, NULL}};
275-
unitTest __test_util_c[] = {{"test_string2ll", test_string2ll}, {"test_string2l", test_string2l}, {"test_ll2string", test_ll2string}, {"test_ld2string", test_ld2string}, {"test_fixedpoint_d2string", test_fixedpoint_d2string}, {"test_version2num", test_version2num}, {"test_reclaimFilePageCache", test_reclaimFilePageCache}, {"test_writePointerWithPadding", test_writePointerWithPadding}, {NULL, NULL}};
276+
unitTest __test_util_c[] = {{"test_string2ll", test_string2ll}, {"test_string2l", test_string2l}, {"test_ll2string", test_ll2string}, {"test_ld2string", test_ld2string}, {"test_fixedpoint_d2string", test_fixedpoint_d2string}, {"test_version2num", test_version2num}, {"test_reclaimFilePageCache", test_reclaimFilePageCache}, {"test_getHashSeedFromValue", test_getHashSeedFromValue}, {"test_writePointerWithPadding", test_writePointerWithPadding}, {NULL, NULL}};
276277
unitTest __test_valkey_strtod_c[] = {{"test_valkey_strtod", test_valkey_strtod}, {NULL, NULL}};
277278
unitTest __test_vector_c[] = {{"test_vector", test_vector}, {NULL, NULL}};
278279
unitTest __test_vset_c[] = {{"test_vset_add_and_iterate", test_vset_add_and_iterate}, {"test_vset_large_batch_same_expiry", test_vset_large_batch_same_expiry}, {"test_vset_large_batch_update_entry_same_expiry", test_vset_large_batch_update_entry_same_expiry}, {"test_vset_large_batch_update_entry_multiple_expiries", test_vset_large_batch_update_entry_multiple_expiries}, {"test_vset_iterate_multiple_expiries", test_vset_iterate_multiple_expiries}, {"test_vset_add_and_remove_all", test_vset_add_and_remove_all}, {"test_vset_remove_expire_shrink", test_vset_remove_expire_shrink}, {"test_vset_defrag", test_vset_defrag}, {"test_vset_fuzzer", test_vset_fuzzer}, {NULL, NULL}};

src/unit/test_util.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,32 @@ int test_reclaimFilePageCache(int argc, char **argv, int flags) {
331331
return 0;
332332
}
333333

334+
int test_getHashSeedFromValue(int argc, char **argv, int flags) {
335+
UNUSED(argc);
336+
UNUSED(argv);
337+
UNUSED(flags);
338+
339+
unsigned char seed[4];
340+
getHashSeedFromValue(seed, sizeof(seed), "abcdefgh");
341+
TEST_ASSERT(seed[0] == ('a' ^ 'e'));
342+
TEST_ASSERT(seed[1] == ('b' ^ 'f'));
343+
TEST_ASSERT(seed[2] == ('c' ^ 'g'));
344+
TEST_ASSERT(seed[3] == ('d' ^ 'h'));
345+
346+
unsigned char seed2[8];
347+
unsigned char expected2[8] = {'a', 'b', 'c', 'a', 'b', 'c', 'a', 'b'};
348+
getHashSeedFromValue(seed2, sizeof(seed2), "abc");
349+
TEST_ASSERT(memcmp(seed2, expected2, sizeof(seed2)) == 0);
350+
351+
unsigned char seed3[4];
352+
getHashSeedFromValue(seed3, sizeof(seed3), "");
353+
for (size_t i = 0; i < sizeof(seed3); i++) {
354+
TEST_ASSERT(seed3[i] == 0);
355+
}
356+
357+
return 0;
358+
}
359+
334360
int test_writePointerWithPadding(int argc, char **argv, int flags) {
335361
UNUSED(argc);
336362
UNUSED(argv);

src/util.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,16 @@ int ld2string(char *buf, size_t len, long double value, ld2string_mode mode) {
10371037
return 0;
10381038
}
10391039

1040+
void getHashSeedFromValue(unsigned char *seed_array, size_t len, const char *value) {
1041+
size_t input_len = strlen(value);
1042+
memset(seed_array, 0, len);
1043+
if (input_len == 0) return;
1044+
size_t max_len = len > input_len ? len : input_len;
1045+
for (size_t i = 0; i < max_len; i++) {
1046+
seed_array[i % len] = value[i % input_len] ^ seed_array[i % len];
1047+
}
1048+
}
1049+
10401050
/* Parses a version string on the form "major.minor.patch" and returns an
10411051
* integer on the form 0xMMmmpp. Returns -1 on parse error. */
10421052
int version2num(const char *version) {

src/util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ int trimDoubleString(char *buf, size_t len);
9191
int d2string(char *buf, size_t len, double value);
9292
int fixedpoint_d2string(char *dst, size_t dstlen, double dvalue, int fractional_digits);
9393
int ld2string(char *buf, size_t len, long double value, ld2string_mode mode);
94+
void getHashSeedFromValue(unsigned char *seed_array, size_t len, const char *value);
9495
int double2ll(double d, long long *out);
9596
int version2num(const char *version);
9697
int yesnotoi(char *s);
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
start_server {tags {"scan-consistency-on-failover external:skip"}} {
2+
proc full_scan_keys {c prefix} {
3+
set cur 0
4+
set out {}
5+
while {1} {
6+
# count 1 to make the ordering fully observable
7+
set res [$c scan $cur count 1]
8+
set cur [lindex $res 0]
9+
set keys [lindex $res 1]
10+
foreach k $keys {
11+
if {[string match "${prefix}*" $k]} {
12+
lappend out $k
13+
}
14+
}
15+
if {$cur eq "0"} break
16+
}
17+
return $out
18+
}
19+
20+
set fixed_seed "00112233445566778899aabbccddeeff"
21+
set shared_overrides [list appendonly no save "" db-hash-seed $fixed_seed]
22+
23+
start_server [list overrides $shared_overrides] {
24+
set primary_host [srv 0 host]
25+
set primary_port [srv 0 port]
26+
27+
start_server [list overrides $shared_overrides] {
28+
set replica_host [srv 0 host]
29+
set replica_port [srv 0 port]
30+
31+
set primary [srv -1 client]
32+
set replica [srv 0 client]
33+
34+
$primary flushall
35+
$replica replicaof $primary_host $primary_port
36+
wait_for_sync $replica
37+
38+
set n 100
39+
for {set i 0} {$i < $n} {incr i} {
40+
$primary set "k:$i" x
41+
}
42+
43+
wait_for_condition 200 50 {
44+
[$replica dbsize] == [$primary dbsize]
45+
} else {
46+
fail "replica did not catch up dbsize (primary=[$primary dbsize], replica=[$replica dbsize])"
47+
}
48+
49+
wait_for_condition 200 50 {
50+
![string match {Hash table 1 stats*} [$primary debug htstats 9]] &&
51+
![string match {Hash table 1 stats*} [$replica debug htstats 9]]
52+
} else {
53+
fail "hash tables still rehashing on primary/replica"
54+
}
55+
56+
set pseed [$primary config get db-hash-seed]
57+
set rseed [$replica config get db-hash-seed]
58+
assert_equal $pseed $rseed
59+
60+
set pkeys [full_scan_keys $primary "k:"]
61+
set rkeys [full_scan_keys $replica "k:"]
62+
63+
assert_equal [$primary dbsize] [llength $pkeys]
64+
assert_equal [$replica dbsize] [llength $rkeys]
65+
assert_equal [llength $pkeys] [llength $rkeys]
66+
assert_equal $pkeys $rkeys
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)