feat(raft-sim-conformance): runtime fault-injection switch (G1 term-stamp, G3 truncate) (task 7.1)
This commit is contained in:
parent
7087dc19d9
commit
62523892d9
4 changed files with 156 additions and 1 deletions
|
|
@ -455,6 +455,22 @@ void raft_snapshot(const raft_state_t *s, raft_snapshot_t *snap);
|
|||
* failure traces. One raft_sim_action_t is recorded per step. */
|
||||
#define RAFT_SIM_MAX_STEPS 1024
|
||||
|
||||
/* ---- deliberately-broken-build fault switch (raft-sim-conformance task 7.1) ----
|
||||
* Runtime-togglable fault-injection flags, confined to this XCE_TESTING build and
|
||||
* DEFAULT OFF (cluster.fault_inject is zero-initialized by the init routines, so
|
||||
* neither legacy nor conformance runs are perturbed, and the default CI conformance
|
||||
* sweep is unaffected — Req 7.3). The corruption lives ONLY in the harness ADAPTER
|
||||
* callbacks (raft_sim.c); raft.c is never touched. Setting a flag reproduces a
|
||||
* bug-851-class invariant divergence so the negative test (task 7.2) can assert that
|
||||
* a broken adapter config FAILS an invariant while the correct config PASSES.
|
||||
* - RAFT_SIM_BREAK_TERM_STAMP: on_persist_entry writes term 0 instead of the
|
||||
* supplied term (emulates G1: entries carry term 0 so on_get_entry_term cannot
|
||||
* distinguish same-index/different-term entries).
|
||||
* - RAFT_SIM_BREAK_TRUNCATE: on_truncate_from is a no-op (emulates G3: a divergent
|
||||
* suffix is never deleted). */
|
||||
#define RAFT_SIM_BREAK_TERM_STAMP 0x1u
|
||||
#define RAFT_SIM_BREAK_TRUNCATE 0x2u
|
||||
|
||||
/* Buffered RPC entry: one pending vote-request to route.
|
||||
* last_log_term/last_log_index capture the CANDIDATE's advertised last-log tuple
|
||||
* at SEND time (mirroring raft_sim_ae_t.leader_term) so the (lastLogTerm,
|
||||
|
|
@ -633,6 +649,11 @@ typedef struct {
|
|||
/* Replayable per-step action trace for failure dumps. */
|
||||
raft_sim_action_t trace[RAFT_SIM_MAX_STEPS];
|
||||
int trace_len;
|
||||
/* Deliberately-broken-build fault switch (task 7.1). Bitmask of
|
||||
* RAFT_SIM_BREAK_* flags consulted by the harness adapter callbacks via
|
||||
* node->ctx. Zero (DEFAULT OFF) on init — legacy and conformance runs are
|
||||
* unaffected unless a test explicitly sets it. */
|
||||
unsigned fault_inject;
|
||||
} raft_sim_cluster_t;
|
||||
|
||||
/*
|
||||
|
|
@ -1094,6 +1115,16 @@ int raft_sim_scenario_figure8(raft_sim_cluster_t *c, int n, uint64_t seed);
|
|||
*/
|
||||
int raft_sim_scenario_stale_rejoin(raft_sim_cluster_t *c, int n, uint64_t seed);
|
||||
|
||||
/*
|
||||
* raft_sim_set_fault_inject: set the cluster's deliberately-broken-build fault mask
|
||||
* (raft-sim-conformance task 7.1). `flags` is a bitmask of RAFT_SIM_BREAK_* values;
|
||||
* pass 0 to clear (restore correct adapter behavior). The flags are consulted only by
|
||||
* the harness ADAPTER callbacks (raft_sim.c); raft.c is never modified. DEFAULT OFF —
|
||||
* a cluster that never calls this behaves exactly as before. Provided so the negative
|
||||
* test (task 7.2) can toggle the fault within a single binary.
|
||||
*/
|
||||
void raft_sim_set_fault_inject(raft_sim_cluster_t *c, unsigned flags);
|
||||
|
||||
#endif /* XCE_TESTING */
|
||||
|
||||
#endif /* RAFT_H */
|
||||
|
|
|
|||
|
|
@ -106,6 +106,15 @@ static int sim_persist_entry(void *ctx, uint64_t index, uint64_t term,
|
|||
if (index < 1 || index > RAFT_SIM_MAX_LOG)
|
||||
return -1; /* out of fixed sim bounds */
|
||||
|
||||
/* Deliberately-broken-build fault switch (task 7.1): when RAFT_SIM_BREAK_TERM_STAMP
|
||||
* is set on the cluster, stamp the entry with term 0 instead of `term`. This
|
||||
* emulates bug-851 G1 (entries carry term 0 so on_get_entry_term can't distinguish
|
||||
* same-index/different-term entries). Purely additive — DEFAULT OFF, so legacy and
|
||||
* conformance runs that never set the flag are byte-identical to before. */
|
||||
raft_sim_cluster_t *c = (raft_sim_cluster_t *)node->ctx;
|
||||
if (c && (c->fault_inject & RAFT_SIM_BREAK_TERM_STAMP))
|
||||
term = 0;
|
||||
|
||||
raft_sim_entry_t *slot = &node->log[index - 1];
|
||||
slot->term = term;
|
||||
|
||||
|
|
@ -147,6 +156,15 @@ static int sim_truncate_from(void *ctx, uint64_t index)
|
|||
if (index < 1)
|
||||
return -1;
|
||||
|
||||
/* Deliberately-broken-build fault switch (task 7.1): when RAFT_SIM_BREAK_TRUNCATE
|
||||
* is set on the cluster, make truncation a NO-OP so the divergent suffix is never
|
||||
* deleted (emulates bug-851 G3). Returns 0 (success) without touching the log, so
|
||||
* the library believes the truncate succeeded while the stale suffix lingers.
|
||||
* Purely additive — DEFAULT OFF; runs that never set the flag are unaffected. */
|
||||
raft_sim_cluster_t *c = (raft_sim_cluster_t *)node->ctx;
|
||||
if (c && (c->fault_inject & RAFT_SIM_BREAK_TRUNCATE))
|
||||
return 0;
|
||||
|
||||
for (uint64_t i = index; i <= (uint64_t)node->log_count; i++) {
|
||||
if (i <= RAFT_SIM_MAX_LOG) {
|
||||
raft_sim_entry_t *slot = &node->log[i - 1];
|
||||
|
|
@ -2343,4 +2361,16 @@ int raft_sim_scenario_stale_rejoin(raft_sim_cluster_t *c, int n, uint64_t seed)
|
|||
return sim_scen_check(c);
|
||||
}
|
||||
|
||||
/*
|
||||
* raft_sim_set_fault_inject: set the cluster's deliberately-broken-build fault mask
|
||||
* (task 7.1). Consulted only by the harness adapter callbacks (sim_persist_entry /
|
||||
* sim_truncate_from); raft.c is never touched. DEFAULT OFF — only a test that calls
|
||||
* this enables the corruption, and passing 0 clears it again within the same binary.
|
||||
*/
|
||||
void raft_sim_set_fault_inject(raft_sim_cluster_t *c, unsigned flags)
|
||||
{
|
||||
if (c)
|
||||
c->fault_inject = flags;
|
||||
}
|
||||
|
||||
#endif /* XCE_TESTING */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue