feat(ha-entity-snapshots): entity-bearing checkpoint write path for jobs (task 2.2)
write_snapshot now Cap'n-Proto-encodes every retained job under nested table+per-entity RTTI_LOCK_READ (table lock alone does not exclude a concurrent per-entity writer -- design.md's central correctness finding), appends length-prefixed job frames after a new format_version marker, and fixes a previously-unchecked fdatasync return. Any failure anywhere in the path (encode, write, sync) discards the whole checkpoint -- no partial file at the final path, proven by a real mid-encode-loop fault-injection test (forces one specific job's encode to fail among N, asserts nothing survives). Also fixed two pre-existing bugs in task 1.1's test fixture: terminal-state seeded jobs need a non-zero finish_time or job_should_retain's retention window silently excludes them from any checkpoint; the fixture needs xstring_table_init or every seeded job's submit/start_info silently decodes as NULL. Round 1 of this task also touched load_snapshot/persist.h to make the round-trip tests pass end-to-end, but review caught a live hazard: it advanced current_lsn/checkpoint_lsn BEFORE the entity-decode loop ran, so a decode failure still left the position advanced -- reachable today through the unmodified ha_handle_snapshot_chunk production path, not hypothetical. Remediated by fully reverting load_snapshot/persist.h to byte-identical pre-task-2.2 state and rewriting the round-trip tests to parse write_snapshot's raw on-disk bytes directly (read_Job/decode_Job) instead of round-tripping through persist_load_snapshot -- proves the write side without depending on load_snapshot, which stays task 5.2's scope entirely, not started here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JDYFbrgyDHbvNYcxApKhrX
This commit is contained in:
parent
0f344da89a
commit
5929af1078
4 changed files with 453 additions and 54 deletions
|
|
@ -273,6 +273,15 @@ struct persist_s {
|
|||
* production. */
|
||||
pthread_t test_last_fdatasync_thread;
|
||||
atomic_int test_last_fdatasync_thread_set;
|
||||
/* Fault-injection seam (ha-entity-snapshots task 2.2): force the Nth
|
||||
* (0-based, in retained-job walk order) job's checkpoint encode to fail
|
||||
* inside write_snapshot, proving a genuine mid-loop encode failure
|
||||
* discards the WHOLE checkpoint rather than skipping just one job.
|
||||
* Stored as index+1 so the zero-initialized default (0) means
|
||||
* "disabled" — mirrors test_force_fsync_fail's zero-init-safe
|
||||
* convention (no explicit persist_init reset needed). Compiled out of
|
||||
* production. */
|
||||
atomic_int test_force_job_encode_fail_at;
|
||||
#endif
|
||||
pthread_t fsync_thread;
|
||||
pthread_mutex_t fsync_mutex; /* guards the fsync timer wait */
|
||||
|
|
@ -584,6 +593,11 @@ pthread_t persist_test_last_fdatasync_thread(persist_t* p);
|
|||
* compaction hazard where a committed marker's segment is unlinked. Returns 0
|
||||
* on success, negative errno on I/O failure. Compiled out of production. */
|
||||
int persist_test_force_new_segment(persist_t* p);
|
||||
|
||||
/* Arm/disarm the job-encode fault-injection seam (see the struct field
|
||||
* comment above). Pass a negative index to disable. Lock-free atomic
|
||||
* write; compiled out of production. */
|
||||
void persist_test_set_force_job_encode_fail_at(persist_t* p, int index);
|
||||
#endif /* XCE_TESTING */
|
||||
|
||||
/* Checkpoint */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue