myxce/src/include/kvmgr.h
Rongsong Shen dcf9f39c22 fix(job-array-single-wal-record): close bug-1443's bulk-insert partial-visibility gap
restore_job_array()'s bulk-insert pass could fail partway through
(key_add() on realloc growth exhaustion), leaving an already-inserted
prefix permanently visible in c->jobmgr but excluded from
out_new_jobs -- so those elements existed and were individually
addressable but never reached HOOK_DETAIL_JOB_NEW, never scheduled or
pushed, not on this attempt or any later successful retry. A literal
violation of Requirements 2.3 and 9.4, tracked as bug-1443.

Adds kvmgr_reserve()/xzarray_reserve(), new purely-additive primitives
in shared kvmgr.c/zarray.c (following the existing kvmgr_capture_keys()
template -- bare extern function, no RTTI vtable slot, caller-holds-
write-lock contract), that pre-grow c->jobmgr's backing store before
the bulk-insert pass begins, closing the dominant N-scaling failure
trigger structurally.

Found a genuine off-by-one in the existing, unmodified
xzarray_ensure_items(): its strict less-than sufficiency check means
reserving exactly array_size and then performing array_size real
stores still triggers one more growth cycle on the last store.
Reserving array_size + 1 is the exact minimal correct compensation,
independently re-derived and confirmed by review with concrete
arithmetic.

For the now-much-narrower residual case (kvtab_add()'s own small
per-entry allocation, or a content collision -- both non-N-scaling,
honestly documented as still theoretically possible): restore_job_array()
now populates out_new_jobs with whatever prefix was genuinely inserted
before a failure, and apply_committed_entry() fires HOOK_DETAIL_JOB_NEW
for that non-empty partial prefix even when the overall return is
negative, while still propagating the negative rc so the LSN correctly
halts and the Live Apply Resource Fence still engages for the
remainder. A genuinely-inserted element is never left un-pushed again.

Task 7.1 of job-array-single-wal-record (Requirements 2.3, 9.4, 9.6).
2026-07-16 15:08:02 +00:00

70 lines
2.3 KiB
C

#if !defined(__KVMGR_H__)
#define __KVMGR_H__ 1
#include "htab.h"
#include "mylock.h"
#include "xce/types.h"
#include "xlist.h"
#include "zarray.h"
#include "persist.h"
typedef enum {
KVMGR_KEY_INT = 0,
KVMGR_KEY_STRING
} kvmgr_key_type_t;
typedef struct {
kvmgr_key_type_t key_type;
uint32_t nslots;
uint32_t delta;
destroy_fn_t datakiller;
} kvmgr_params_t;
extern typeinfo_t xkvmgr_type;
/*
* kvmgr_capture_keys — capture key→index mappings for checkpoint
* serialization (Part 3.8).
*
* Caller MUST hold the kvmgr write lock for the full duration of
* this call. The function allocates *keys_out and populates it with
* *n_keys_out entries. Each entry's key[] field is an owned deep
* copy that remains valid after the write lock is released.
*
* Caller must free(*keys_out) when done.
*
* Returns 0 on success.
* Returns -ENOMEM if allocation fails.
*/
int kvmgr_capture_keys(var kvmgr,
slot_key_t** keys_out,
size_t* n_keys_out);
/*
* kvmgr_reserve — job-array-single-wal-record task 7.1 (bug-1443).
*
* Pre-grows kvmgr's backing value store (the internal xzarray_type
* instance) for `n` additional entries, via the new xzarray_reserve()
* primitive (zarray.h/zarray.c) -- zero changes to any existing function in
* either file. Closes the DOMINANT, N-scaling failure mode for a bulk
* key_add() loop (e.g. restore_job_array()'s bulk-insert pass, job.c): the
* large realloc() reachable through key_add -> xkvmgr_add -> vstore ->
* xzarray_ensure_items when the value store must grow.
*
* This narrows, but does NOT eliminate, that failure surface: key_add()'s
* own kvtab_add() call (kv.c) still has its own separate, tiny per-entry
* hash-chain-node allocation, and a content-based -EEXIST collision remains
* theoretically possible -- both are far rarer and non-N-scaling compared
* to the growth cycle this call structurally avoids.
*
* Caller MUST hold the kvmgr write lock for the full duration of this call
* -- identical contract to kvmgr_capture_keys() above.
*
* Returns 1 on success (capacity already sufficient, or successfully
* grown). Returns -EINVAL for a NULL kvmgr or negative n. Returns -ENOMEM
* if the underlying growth allocation fails.
*/
int kvmgr_reserve(var kvmgr, int n);
#endif