Skip to content

Commit 0fc5ee2

Browse files
author
Yury Kirsanov
committed
cachedb_perf: sync the module to its latest state
Brings the standalone module branch up to the current code, on top of the PR base, module directory only: - memory_backing=auto|core|own-hg|own + arena_hugepage_cap_mb/arena_profile (own slot allocator with full reclaim: retire, re-cut, give-back, private-hoard flush; a dedicated "cachedb_perf reclaim" process) - a module-owned pull transport (pull_transport=udp|tcp + pull_bind/ pull_port), so pulls need neither the clusterer bin links nor the controller plane; HELLO peer discovery over the bin capability - MurmurHash3 key hashing (replaces core_hash, which collides heavily on sequential keys) - regenerated README/docs; keeps STUDY.md, PR-NOTES.md and the study figures that only exist on this branch
1 parent 1694fad commit 0fc5ee2

9 files changed

Lines changed: 3725 additions & 458 deletions

File tree

modules/cachedb_perf/README

Lines changed: 520 additions & 85 deletions
Large diffs are not rendered by default.

modules/cachedb_perf/cachedb_perf.c

Lines changed: 799 additions & 171 deletions
Large diffs are not rendered by default.

modules/cachedb_perf/doc/cachedb_perf_admin.xml

Lines changed: 379 additions & 18 deletions
Large diffs are not rendered by default.

modules/cachedb_perf/pcache_arena.c

Lines changed: 1037 additions & 163 deletions
Large diffs are not rendered by default.

modules/cachedb_perf/pcache_arena.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,43 @@
4545
* and takes cross-process frees (expiry / maintenance worker).
4646
*/
4747

48+
#include "../../mi/item.h"
49+
4850
#define PCACHE_CELL_MAX 65536 /* largest cell; bigger allocs fail (v1) */
4951
#define PCACHE_NCLASSES 21
5052

53+
/* Memory backing, decided in pcache_arena_init() (mod_init, pre-fork):
54+
* PCACHE_BACKING_OWN - this file's chunked allocator (chunks from
55+
* shm_malloc or the dedicated reservation)
56+
* PCACHE_BACKING_CORE - the core shm allocator is HG_MALLOC: every cell
57+
* is an HG slab cell in the shm arena
58+
* PCACHE_BACKING_OWN_HG - arena_hugepage_mb set on an HG_MALLOC build: the
59+
* arena is an HG arena of its own, fully managed
60+
* by HG (classes, GC, growth/shrink, maintenance)
61+
* Policy: the "memory_backing" modparam (auto|core|own-hg|own). */
62+
enum pcache_backing { PCACHE_BACKING_OWN = 0, PCACHE_BACKING_CORE,
63+
PCACHE_BACKING_OWN_HG };
64+
extern char *pcache_backing_policy; /* modparam memory_backing */
65+
extern int pcache_arena_hugepage_cap_mb; /* modparam, 0 = fixed */
66+
extern char *pcache_arena_profile; /* modparam arena_profile */
67+
extern int pcache_reclaim_keep; /* drained chunks kept per class */
68+
extern int pcache_reclaim_quiet_s; /* quiet window before give-back */
69+
extern int pcache_reclaim_cooloff_s; /* no give-back after a carve */
70+
extern int pcache_reclaim_giveback; /* 0 = retire/re-cut only */
71+
int pcache_arena_backing(void);
72+
const char *pcache_arena_backing_str(void);
73+
void pcache_arena_backing_notice(void);
74+
5175
int pcache_arena_init(void);
5276
void pcache_arena_destroy(void);
5377

5478
/* reset inherited allocator state after fork: donates any pre-fork bump
5579
* chunk / private cells to the global pool. Two processes must never
5680
* share a bump pointer. */
5781
void pcache_arena_child_init(void);
82+
void pcache_arena_flush_private(void); /* a done process sends cells home */
83+
void pcache_arena_reclaim_tick(void); /* the reclaim process, 1/s */
84+
int pcache_arena_mi(mi_item_t *aobj); /* reclaim view for perf_stats */
5885

5986
/* a cell of at least @size bytes (including the class byte), or NULL if
6087
* size > PCACHE_CELL_MAX or shm is exhausted */

modules/cachedb_perf/pcache_htable.c

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,40 @@
5353
#include "pcache_arena.h"
5454
#include "pcache_htable.h"
5555

56+
/*
57+
* The key hash. Not the core's, which ADDS per-word mixes, so keys whose
58+
* 4-byte words sum alike collide on the full 32 bits - for sequential or
59+
* numeric keys (phone numbers, "user123456") it produced 65k distinct
60+
* values for 500,000 keys and put 44% of the entries into the overflow
61+
* table, whose single lock then serialised every miss. MurmurHash3
62+
* x86_32 (public domain, Austin Appleby): 0.2% overflow on the same keys,
63+
* the uniform expectation. Local to this node - never on the wire, never
64+
* in the DB - so it can change without a protocol bump.
65+
*/
66+
unsigned int pcache_key_hash(const str *key)
67+
{
68+
const unsigned char *p = (const unsigned char *)key->s;
69+
unsigned int len = (unsigned int)key->len, n = len >> 2, i, k;
70+
unsigned int h = 0x9747b28cU; /* the seed */
71+
72+
for (i = 0; i < n; i++, p += 4) {
73+
memcpy(&k, p, 4);
74+
k *= 0xcc9e2d51U; k = (k << 15) | (k >> 17); k *= 0x1b873593U;
75+
h ^= k; h = (h << 13) | (h >> 19); h = h * 5 + 0xe6546b64U;
76+
}
77+
k = 0;
78+
switch (len & 3) {
79+
case 3: k ^= (unsigned int)p[2] << 16; /* fall through */
80+
case 2: k ^= (unsigned int)p[1] << 8; /* fall through */
81+
case 1: k ^= p[0];
82+
k *= 0xcc9e2d51U; k = (k << 15) | (k >> 17); k *= 0x1b873593U;
83+
h ^= k;
84+
}
85+
h ^= len;
86+
h ^= h >> 16; h *= 0x85ebca6bU; h ^= h >> 13; h *= 0xc2b2ae35U; h ^= h >> 16;
87+
return h;
88+
}
89+
5690
/* The selftest deliberately drives two rejection paths (a non-numeric add
5791
* and an oversize store). Both log at L_ERR by design, which in a PASSING
5892
* selftest reads as a real fault. This flag downgrades exactly those two
@@ -305,7 +339,8 @@ static int ovf_fetch(pcache_htable_t *ht, const str *key, unsigned int hash,
305339
*/
306340
static int _pcache_ht_fetch_buf(pcache_htable_t *ht, const str *key,
307341
char *dst, unsigned int dstlen, unsigned int *vlen_out,
308-
unsigned int now, unsigned int *exp_out, long long *ll_out)
342+
unsigned int now, unsigned int *exp_out, long long *ll_out,
343+
unsigned char *fl_out)
309344
{
310345
pcache_bucket_t *b;
311346
uint64_t route;
@@ -329,7 +364,7 @@ static int _pcache_ht_fetch_buf(pcache_htable_t *ht, const str *key,
329364
if (!ht || !key || (!dst && dstlen))
330365
return -1;
331366

332-
hash = core_hash(key, NULL, 0);
367+
hash = pcache_key_hash(key);
333368
tag = tag_of(hash);
334369

335370
again:
@@ -381,6 +416,8 @@ static int _pcache_ht_fetch_buf(pcache_htable_t *ht, const str *key,
381416
HT_ST(ht, hits);
382417
if (exp_out)
383418
*exp_out = exp; /* absolute ticks, 0 = never */
419+
if (fl_out)
420+
*fl_out = fl; /* record flags, e.g. F_PASSIVE */
384421
*vlen_out = vlen;
385422

386423
/* the value did not fit: the length above tells the caller what it
@@ -411,7 +448,7 @@ static int _pcache_ht_fetch_buf(pcache_htable_t *ht, const str *key,
411448
* be reached from here - it is handled defensively all the same.
412449
*/
413450
static int _pcache_ht_fetch(pcache_htable_t *ht, const str *key, str *val,
414-
unsigned int now, unsigned int *exp_out)
451+
unsigned int now, unsigned int *exp_out, unsigned char *fl_out)
415452
{
416453
unsigned int vlen = 0;
417454
long long ll = 0;
@@ -423,7 +460,7 @@ static int _pcache_ht_fetch(pcache_htable_t *ht, const str *key, str *val,
423460
return -1;
424461

425462
rc = _pcache_ht_fetch_buf(ht, key, scratch, PCACHE_CELL_MAX, &vlen,
426-
now, exp_out, &ll);
463+
now, exp_out, &ll, fl_out);
427464
if (rc < 0)
428465
return rc == PCACHE_E_TOOSMALL ? -1 : rc;
429466

@@ -449,15 +486,17 @@ static int _pcache_ht_fetch(pcache_htable_t *ht, const str *key, str *val,
449486

450487
int pcache_ht_fetch(pcache_htable_t *ht, const str *key, str *val)
451488
{
452-
return _pcache_ht_fetch(ht, key, val, get_ticks(), NULL);
489+
return _pcache_ht_fetch(ht, key, val, get_ticks(), NULL, NULL);
453490
}
454491

455492
/* like pcache_ht_fetch, but also returns the record's absolute expiry
456493
* (0 = never) - the MI perf_get needs the TTL alongside the value */
457494
int pcache_ht_fetch_ex(pcache_htable_t *ht, const str *key, str *val,
458-
unsigned int *expires)
495+
unsigned int *expires, unsigned char *rflags)
459496
{
460-
return _pcache_ht_fetch(ht, key, val, get_ticks(), expires);
497+
if (rflags)
498+
*rflags = 0;
499+
return _pcache_ht_fetch(ht, key, val, get_ticks(), expires, rflags);
461500
}
462501

463502
/* existence probe - see the contract in pcache_htable.h. Shares the whole
@@ -478,7 +517,7 @@ int pcache_ht_probe(pcache_htable_t *ht, const str *key, unsigned int *vlen,
478517
*is_counter = 0;
479518

480519
rc = _pcache_ht_fetch_buf(ht, key, NULL, 0, &len, get_ticks(),
481-
&exp, NULL);
520+
&exp, NULL, NULL);
482521
if (rc < 0)
483522
return rc; /* -2 = absent or expired */
484523
if (vlen)
@@ -511,7 +550,7 @@ int pcache_ht_fetch_buf(pcache_htable_t *ht, const str *key, char *buf,
511550
}
512551

513552
rc = _pcache_ht_fetch_buf(ht, key, buf, buflen, vlen, get_ticks(),
514-
NULL, &ll);
553+
NULL, &ll, NULL);
515554

516555
if (rc == PCACHE_E_TOOSMALL) {
517556
/* *vlen must never exceed the caller's buffer: {buf,*vlen} has to
@@ -564,6 +603,17 @@ static struct povf *ovf_find(pcache_htable_t *ht, const str *key,
564603

565604
int pcache_ht_store(pcache_htable_t *ht, const str *key, const str *val,
566605
unsigned int expires)
606+
{
607+
return pcache_ht_store_ex(ht, key, val, expires, 0);
608+
}
609+
610+
/* @rflags is stamped on the stored record: 0 for a local consumer write
611+
* (the authoritative kind), PCACHE_F_PASSIVE for a value that arrived
612+
* through a cluster pull. The identical-bytes TTL-bump path keeps the
613+
* record's existing flags - re-pulling bytes the owner wrote must not
614+
* demote the owner's copy. */
615+
int pcache_ht_store_ex(pcache_htable_t *ht, const str *key, const str *val,
616+
unsigned int expires, unsigned char rflags)
567617
{
568618
pcache_bucket_t *b;
569619
pcache_rec_t *nr, *old = NULL;
@@ -580,14 +630,14 @@ int pcache_ht_store(pcache_htable_t *ht, const str *key, const str *val,
580630
return -1;
581631
}
582632

583-
hash = core_hash(key, NULL, 0);
633+
hash = pcache_key_hash(key);
584634
tag = tag_of(hash);
585635

586636
/* build the full replacement record before any lock (3.5b rule 3) */
587637
nr = pcache_cell_alloc(PCACHE_REC_SIZE(key->len, val->len));
588638
if (!nr)
589639
return -2; /* arena full - write dropped */
590-
nr->rflags = 0;
640+
nr->rflags = rflags;
591641
nr->klen = (unsigned short)key->len;
592642
nr->vlen = (unsigned int)val->len;
593643
nr->expires = expires;
@@ -645,7 +695,7 @@ int pcache_ht_store(pcache_htable_t *ht, const str *key, const str *val,
645695
* from a previous life. Without it, storing an 8-byte
646696
* string over a native counter left PCACHE_F_INT set and
647697
* the read path re-interpreted the ASCII as an int64 */
648-
old->rflags = 0;
698+
old->rflags = rflags;
649699
old->vlen = (unsigned int)val->len;
650700
memcpy(old->data + key->len, val->s, val->len);
651701
old->expires = expires;
@@ -744,7 +794,7 @@ int pcache_ht_add(pcache_htable_t *ht, const str *key, long long delta,
744794
if (key->len > 0xFFFF)
745795
return -1;
746796

747-
hash = core_hash(key, NULL, 0);
797+
hash = pcache_key_hash(key);
748798
tag = tag_of(hash);
749799

750800
/* the counter record is pre-built outside any lock (3.5b); it either
@@ -910,7 +960,7 @@ int pcache_ht_touch(pcache_htable_t *ht, const str *key, unsigned int expires)
910960
unsigned char tag;
911961
int i, rc = 0;
912962

913-
hash = core_hash(key, NULL, 0);
963+
hash = pcache_key_hash(key);
914964
tag = tag_of(hash);
915965

916966
again:
@@ -965,7 +1015,7 @@ int pcache_ht_remove(pcache_htable_t *ht, const str *key)
9651015
unsigned char tag;
9661016
int i;
9671017

968-
hash = core_hash(key, NULL, 0);
1018+
hash = pcache_key_hash(key);
9691019
tag = tag_of(hash);
9701020

9711021
again:
@@ -1657,7 +1707,7 @@ static int st_walk_cb(const str *key, const str *val, unsigned int exp,
16571707
static pcache_rec_t *st_slot_of(pcache_htable_t *ht, const str *key)
16581708
{
16591709
uint64_t route;
1660-
unsigned int hash = core_hash((str *)key, NULL, 0);
1710+
unsigned int hash = pcache_key_hash((const str *)key);
16611711
pcache_bucket_t *b = bucket_at(ht, route_idx(ht, hash, &route));
16621712
int i = find_slot(b, key, hash, tag_of(hash));
16631713

@@ -1702,7 +1752,7 @@ int pcache_htable_selftest(void)
17021752
pkg_free(out.s);
17031753

17041754
/* versionless TTL bump: byte-identical value, version must hold */
1705-
b = bucket_at(ht, route_idx(ht, core_hash(&k, NULL, 0), &route));
1755+
b = bucket_at(ht, route_idx(ht, pcache_key_hash(&k), &route));
17061756
ver0 = b->version;
17071757
HCHK(pcache_ht_store(ht, &k, &v, get_ticks() + 100) == 0,
17081758
"bump store failed\n");
@@ -1733,9 +1783,9 @@ int pcache_htable_selftest(void)
17331783
* wrapper here) */
17341784
v.s = "temp"; v.len = 4;
17351785
HCHK(pcache_ht_store(ht, &k, &v, 500) == 0, "expired store failed\n");
1736-
HCHK(_pcache_ht_fetch(ht, &k, &out, 1000, NULL) == -2,
1786+
HCHK(_pcache_ht_fetch(ht, &k, &out, 1000, NULL, NULL) == -2,
17371787
"expired key still hits\n");
1738-
rc = _pcache_ht_fetch(ht, &k, &out, 400, NULL);
1788+
rc = _pcache_ht_fetch(ht, &k, &out, 400, NULL, NULL);
17391789
HCHK(rc == 0, "live key missed\n");
17401790
pkg_free(out.s);
17411791
pcache_ht_remove(ht, &k);

modules/cachedb_perf/pcache_htable.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,23 @@ typedef struct pcache_rec {
6060
} pcache_rec_t;
6161

6262
#define PCACHE_REC_HDR 16
63+
64+
/* the key hash (MurmurHash3 x86_32), local to this node */
65+
unsigned int pcache_key_hash(const str *key);
6366
#define PCACHE_REC_SIZE(_kl, _vl) (PCACHE_REC_HDR + (_kl) + (_vl))
6467

6568
/* rflags: native int64 counter (CP-04) - the value payload is 8 raw
6669
* bytes, arithmetic is fixed-width under the bucket lock, and every
6770
* user-facing read (fetch, walker) formats it as a decimal string */
6871
#define PCACHE_F_INT 0x01
72+
/* The record arrived through a cluster pull, not through a local consumer
73+
* write - a passive copy. Provenance doubles as authority: the serve side
74+
* can answer "held, not authoritative" for these instead of shipping the
75+
* value, so in a converged cluster only the writer answers with bytes.
76+
* A later local write over the key clears it (the writer IS the authority);
77+
* a pull landing on identical bytes keeps whatever the record had, which
78+
* keeps an owner's copy authoritative. */
79+
#define PCACHE_F_PASSIVE 0x02
6980

7081
/* strict bounded decimal parse; no overflow guard - counter territory */
7182
static inline int pcache_str2ll(const char *p, int len, long long *out)
@@ -204,6 +215,12 @@ pcache_htable_t *pcache_htable_new(unsigned int size_log2);
204215
int pcache_ht_store(pcache_htable_t *ht, const str *key, const str *val,
205216
unsigned int expires);
206217

218+
/* as pcache_ht_store, stamping @rflags on the record (PCACHE_F_PASSIVE for
219+
* a value that arrived through a cluster pull). pcache_ht_store() is this
220+
* with rflags 0 - a local consumer write, the authoritative kind. */
221+
int pcache_ht_store_ex(pcache_htable_t *ht, const str *key, const str *val,
222+
unsigned int expires, unsigned char rflags);
223+
207224
/* get_buf(): @buf was too small. *vlen stays 0 and *needed carries the
208225
* size the value would have needed - never a length the caller could
209226
* mistake for "bytes written into buf". */
@@ -256,9 +273,10 @@ int pcache_ht_fetch_buf(pcache_htable_t *ht, const str *key, char *buf,
256273
int pcache_ht_fetch(pcache_htable_t *ht, const str *key, str *val);
257274

258275
/* as pcache_ht_fetch, plus *@expires = the record's absolute expiry (0 =
259-
* never) on a hit - the MI perf_get reports the TTL with the value */
276+
* never) on a hit - the MI perf_get reports the TTL with the value - and
277+
* *@rflags = the record's flags (either out pointer may be NULL) */
260278
int pcache_ht_fetch_ex(pcache_htable_t *ht, const str *key, str *val,
261-
unsigned int *expires);
279+
unsigned int *expires, unsigned char *rflags);
262280

263281
/* 1 = removed; 0 = was absent; -1 = error */
264282
int pcache_ht_remove(pcache_htable_t *ht, const str *key);

0 commit comments

Comments
 (0)