feat(config-upgrade-snapshot): restore host admin-state frame safely (create-if-missing)
Adds restore_host_state(cluster_t*, const char*, uint32_t admin_status) to worker.c: OR's admin bits into an existing worker's status on hit; on miss, creates a minimal XCE_WORKER_UNAVAIL placeholder via makeWorker (mirroring the lost_and_found sentinel host precedent), with a valid non-NULL hostattrs_t so the host's later real join routes through rejoin_worker (not init_worker) and preserves the restored admin status. Idempotent under retry (find first, create only if missing). Standalone -- not yet wired into load_snapshot (task 4). Requirements: 1.10, 1.11, 5.8
This commit is contained in:
parent
387ea35c9f
commit
5b1600a8b2
6 changed files with 467 additions and 1 deletions
|
|
@ -436,6 +436,105 @@ int restore_worker(void* cls, xce_worker_t host)
|
|||
return idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* restore_host_state — restore a host admin-state frame (config-upgrade-
|
||||
* snapshot Requirements 1.10, 1.11, 5.8).
|
||||
*
|
||||
* Unlike restore_queue_state() (queues are config-provisioned, so an
|
||||
* update-only lookup is always safe), a worker_t is join-provisioned: it
|
||||
* may not exist yet on a member that hasn't locally replayed this host's
|
||||
* original join record (e.g. one that received the upgrade snapshot via
|
||||
* the out-of-band install path). So this restore path is create-if-missing:
|
||||
*
|
||||
* - Hit (host already registered -- a real joined worker, or an earlier
|
||||
* placeholder left by a prior restore attempt): OR the restored admin
|
||||
* bits into the existing status only. Nothing else is touched, so a
|
||||
* real worker's live attrs/ep/resource values survive untouched.
|
||||
* - Miss: create a minimal placeholder via makeWorker(), mirroring the
|
||||
* synthetic lost_and_found sentinel host precedent exactly
|
||||
* (config_repl.c:1114-1130) -- the same "makeWorker() without a join"
|
||||
* mechanism, not a new one. The placeholder is marked
|
||||
* XCE_WORKER_UNAVAIL (unjoined, dead-until-connected) and carries the
|
||||
* restored admin bits. Its hostattrs_t is non-NULL with valid sentinel
|
||||
* strings (copy_worker, wobj.c:53, requires non-NULL attrs; downstream
|
||||
* apply_join_state()/add_hostattrs_resvals() treat hosttype/hostmodel
|
||||
* as always-valid strings) so the host's later real join -- which
|
||||
* join.c routes to rejoin_worker(), unmodified, because key_find() now
|
||||
* finds this placeholder -- doesn't crash on rejoin_worker's direct
|
||||
* attrs->hosttype dereference (worker.c:376-379).
|
||||
*
|
||||
* Idempotent under retry (5.8): find first via our own key_find, create
|
||||
* only if truly missing. A second call for the same host (e.g. a retried
|
||||
* restore after an earlier attempt failed partway through the snapshot)
|
||||
* finds either the real worker or the already-created placeholder and just
|
||||
* OR's the bits in again -- never a duplicate-create conflict.
|
||||
*
|
||||
* admin_status is masked to XCE_WORKER_ADMIN_MASK defensively (mirrors the
|
||||
* collect side's own discipline, collect_host_frames only ever encoding
|
||||
* admin-masked bits) so a caller passing an unmasked status word can never
|
||||
* leak a non-admin bit (e.g. XCE_WORKER_BUSY) onto the restored host.
|
||||
*/
|
||||
int restore_host_state(cluster_t* c, const char* name, uint32_t admin_status)
|
||||
{
|
||||
worker_t* w = NULL;
|
||||
int rc = 0;
|
||||
|
||||
if (name == NULL || name[0] == '\0')
|
||||
return -EINVAL;
|
||||
if (c == NULL || c->workermgr == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
admin_status &= XCE_WORKER_ADMIN_MASK;
|
||||
|
||||
/* No reference() pin across the workermgr-unlock/w-lock gap (unlike
|
||||
* join.c's live-join path): this is called from checkpoint restore
|
||||
* (ha_handle_snapshot_chunk -> persist_load_snapshot), which DOES run
|
||||
* on the live vtask pool, not a quiesced phase, so a real first-time
|
||||
* join for the same host CAN race this call. That race is still safe:
|
||||
* a concurrent init_worker/makeWorker on a miss here would itself
|
||||
* find-or-create via the same key_find/makeWorker machinery under
|
||||
* workermgr's own lock, and either ordering converges to a worker
|
||||
* with the admin bits OR'd in and UNAVAIL cleared by the real join's
|
||||
* own apply_join_state() call -- the same "outer key_find, then
|
||||
* unconditionally call into makeWorker" shape already used elsewhere
|
||||
* in this codebase (worker_from_capnp/restore_worker,
|
||||
* create_lost_and_found). Mirrors restore_queue_state's (queue.c:208)
|
||||
* lock idiom, not a claim that this path is concurrency-free. */
|
||||
lock(c->workermgr, RTTI_LOCK_READ);
|
||||
rc = key_find(c->workermgr, (void*)name, (void**)&w);
|
||||
unlock(c->workermgr);
|
||||
|
||||
if (rc < 0 && rc != -ENOENT)
|
||||
return rc;
|
||||
|
||||
if (w != NULL) {
|
||||
/* Hit: real joined worker or an earlier placeholder -- only OR the
|
||||
* admin bits in, never touch anything else. */
|
||||
lock(w, RTTI_LOCK_WRITE);
|
||||
w->host.status |= admin_status;
|
||||
unlock(w);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Miss: create a minimal, valid placeholder -- mirrors the
|
||||
* lost_and_found sentinel host precedent (config_repl.c:1114-1130). */
|
||||
{
|
||||
hostattrs_t placeholder_attrs = { .hosttype = "unknown",
|
||||
.hostmodel = "unknown" };
|
||||
xce_worker_t placeholder_host = { .name = (char*)name,
|
||||
.attrs = &placeholder_attrs,
|
||||
.status = XCE_WORKER_UNAVAIL
|
||||
| admin_status };
|
||||
|
||||
w = makeWorker(c->workermgr, &placeholder_host);
|
||||
if (w == NULL)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct worker_config_publish_args {
|
||||
cluster_t* c;
|
||||
endpoint_t* ep;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue