feat(ha-wal-replication): HA→replicator standby registration bridge (tasks 2.1-2.3)
Register standbys with replicator on connect (election promotion + late-joiner), deregister on disconnect (vote err handler + standby err handler). repl_add_standby now idempotent (duplicate ep returns existing slot). Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
18e351ee1b
commit
81dbc4d7b4
4 changed files with 542 additions and 18 deletions
|
|
@ -12,6 +12,15 @@
|
|||
#include "repl.h"
|
||||
#include "mylock.h"
|
||||
#include "utils.h"
|
||||
#include "request.h" /* xce_wal_batch_req_raw */
|
||||
#include "typemap.h" /* wal_catchup_entry_t */
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
/* Test-injectable send hook: replaces xce_wal_batch_req_raw in unit tests. */
|
||||
int (*repl_broadcast_send_hook)(void *ep,
|
||||
wal_catchup_entry_t **entries,
|
||||
int n_entries) = NULL;
|
||||
#endif
|
||||
|
||||
/* ============================================================
|
||||
* Lifecycle
|
||||
|
|
@ -69,6 +78,15 @@ int repl_add_standby(replicator_t *r, repl_ep_t ep, const char *host, uint32_t p
|
|||
|
||||
mylock_lock(&r->lock);
|
||||
|
||||
/* Idempotency guard: if ep is already registered, return its slot index.
|
||||
* Req 1.3: duplicate repl_add_standby calls are treated as no-ops. */
|
||||
for (int i = 0; i < MAX_STANDBYS; i++) {
|
||||
if (r->standbys[i].ep == ep) {
|
||||
mylock_unlock(&r->lock);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (r->nstandbys >= MAX_STANDBYS) {
|
||||
mylock_unlock(&r->lock);
|
||||
return -ENOSPC;
|
||||
|
|
@ -187,15 +205,11 @@ void repl_on_ack(replicator_t *r, repl_ep_t ep, uint64_t acked_lsn)
|
|||
* Iterates active standbys; skips empty slots and standbys in catch-up.
|
||||
* Evicts standbys whose pending_ack_count exceeds MAX_PENDING_ACKS by marking
|
||||
* them in_catch_up=1 (inlined to avoid re-entrant lock acquisition).
|
||||
* TCP send wiring deferred to task 7.6.
|
||||
* ============================================================ */
|
||||
|
||||
int repl_broadcast(replicator_t *r, const wal_entry_hdr_t *hdr,
|
||||
const void *payload, uint32_t payload_len)
|
||||
{
|
||||
(void)payload;
|
||||
(void)payload_len;
|
||||
|
||||
/* WAL_CHECKPOINT entries must never be replicated */
|
||||
assert(hdr != NULL);
|
||||
assert(hdr->type != WAL_CHECKPOINT);
|
||||
|
|
@ -225,7 +239,33 @@ int repl_broadcast(replicator_t *r, const wal_entry_hdr_t *hdr,
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Non-evicted standby: track the pending send (TCP wiring in task 7.6) */
|
||||
/* Build a single-entry batch and enqueue it for this standby. */
|
||||
wal_catchup_entry_t entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.lsn = hdr->lsn;
|
||||
entry.entry_type = (uint8_t)hdr->type;
|
||||
entry.raw_payload = (uint8_t *)(uintptr_t)payload;
|
||||
entry.raw_payload_len = payload_len;
|
||||
|
||||
wal_catchup_entry_t *ep_arr[1] = { &entry };
|
||||
|
||||
int rc;
|
||||
#ifdef XCE_TESTING
|
||||
if (repl_broadcast_send_hook)
|
||||
rc = repl_broadcast_send_hook(s->ep, ep_arr, 1);
|
||||
else
|
||||
rc = xce_wal_batch_req_raw(s->ep, ep_arr, 1);
|
||||
#else
|
||||
rc = xce_wal_batch_req_raw(s->ep, ep_arr, 1);
|
||||
#endif
|
||||
if (rc != 0) {
|
||||
WARN("repl_broadcast: send to standby %s:%u failed (rc=%d) — continuing",
|
||||
s->host, s->port, rc);
|
||||
continue;
|
||||
}
|
||||
|
||||
TRACE("repl_broadcast: sent lsn=%lu type=%u to %s:%u",
|
||||
(unsigned long)hdr->lsn, hdr->type, s->host, s->port);
|
||||
s->pending_ack_count++;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue