feat: P2-4 wire repl_broadcast into persist_wal_async/sync_append; last_replicated_crc

- Move repl.h from controller/ to include/ (shared header) to break
  persist.h ↔ repl.h circular include
- Extract wal_entry_hdr_t + wal_entry_type into include/wal_types.h so
  repl.h can include only what it needs without pulling in all of persist.h
- Add replicator_t repl field to persist_t (embedded by value, P2-4)
- Wire repl_init/repl_fini into persist_init/persist_fini lifecycle
- Wire repl_broadcast into persist_wal_async after each WAL write;
  last_replicated_crc updated here (not in persist_write_checkpoint_marker)
- Wire repl_wait_ack into persist_wal_sync_append; returns 0 on local
  WAL success; sync_rc_out carries -ENODEV/-ETIMEDOUT for degraded mode
- Add 4 TDD tests in test_persist_repl.c: structural repl field check,
  async no-standbys succeeds, sync degraded mode rc, checkpoint CRC invariant
This commit is contained in:
Rongsong Shen 2026-05-07 17:54:48 +00:00
parent d326ee1b2e
commit 3563d7387b
7 changed files with 228 additions and 41 deletions

60
src/include/repl.h Normal file
View file

@ -0,0 +1,60 @@
#pragma once
/*
* repl.h — P2-1: Replicator structures and public API.
* Fan-out TCP replication via existing iomgr/SSL endpoint pipeline.
* ACKs processed in io_worker read callback (NOT vtaskmgr) to prevent deadlock.
*/
#include <pthread.h>
#include <stdatomic.h>
#include <stdint.h>
#include <time.h>
#include "config.h"
#include "mylock.h"
#include "wal_types.h" /* wal_entry_hdr_t — P2-4: use wal_types.h to break circular include */
/* Endpoint is an opaque pointer — full type in netio.h (avoid heavy include) */
typedef void* repl_ep_t;
/* Per-standby connection slot */
typedef struct {
repl_ep_t ep; /* NULL = slot empty */
_Atomic uint64_t acked_lsn; /* Last LSN ACKed by this standby */
_Atomic int in_catch_up; /* Non-zero while catching up */
uint64_t join_applied_lsn; /* LSN standby reported on reconnect */
char host[XCE_MAXNAME_LEN];
uint32_t port;
uint64_t last_disconnected_lsn;
time_t last_disconnect_time;
} standby_conn_t;
/* Replicator — embedded in persist_t or cluster_t (P2-4).
* Named tag replicator_s matches the forward declaration in persist.h. */
typedef struct replicator_s {
standby_conn_t standbys[MAX_STANDBYS];
int nstandbys; /* Active standby count */
mylock_t lock; /* Protects standbys[], nstandbys */
pthread_cond_t ack_cond; /* Signaled when any ACK arrives */
pthread_mutex_t ack_lock; /* Guards ack_cond */
} replicator_t;
/* Lifecycle */
int repl_init(replicator_t *r);
void repl_fini(replicator_t *r);
/* Standby management */
int repl_add_standby(replicator_t *r, repl_ep_t ep, const char *host, uint32_t port);
void repl_remove_standby(replicator_t *r, repl_ep_t ep);
void repl_expire_disconnect_slots(replicator_t *r, time_t now_s, uint32_t snap_interval_s);
/* Replication send (stub in P2-1; wired in P2-4) */
int repl_broadcast(replicator_t *r, const wal_entry_hdr_t *hdr,
const void *payload, uint32_t payload_len);
/* ACK handling — call from io_worker read callback ONLY */
void repl_on_ack(replicator_t *r, repl_ep_t ep, uint64_t acked_lsn);
/* Sync replication (stub in P2-1; wired in P2-3) */
int repl_wait_ack(replicator_t *r, uint64_t target_lsn, uint32_t timeout_ms);
/* WAL trim guard */
uint64_t repl_trim_lsn(replicator_t *r, uint64_t checkpoint_lsn, uint32_t snap_interval_s);