mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 18:45:53 +00:00
f3dd544ce2
persistGuestIdentity spawned a detached goroutine per changed guest to write guest_metadata.json, with a comment noting it avoided blocking the monitor. Nothing tracked those goroutines, so neither Monitor.Stop nor MultiTenantMonitor.Stop could wait for them and a queued write could land after shutdown. In hosted mode that means a write into a tenant directory that offboarding is already removing, and a stray guest_metadata.json.tmp left behind when the atomic write is interrupted. The store now owns the goroutine. SetAsync tracks the write on a WaitGroup and WaitForPendingWrites drains it under a bounded timeout matching tenantMonitorShutdownTimeout, so a wedged store cannot hold up tenant teardown. Monitor.Stop drains before closing the metrics store. This is what made TestHostedTenantAgentInstallTokenCannotReportToOtherTenant flaky: t.TempDir cleanup raced a queued write into orgs/client-b and failed with "directory not empty". The test itself is unchanged, because it was never a test bug. A goroutine dump at cleanup time showed the writers still live, created by persistGuestIdentity, blocked on the store mutex. Verified causally rather than by observation alone: the target test fails 0/4 with the drain removed and passes 8/8 with it, against 2/3 failures on the unmodified baseline. The regression tests fail if SetAsync stops tracking its goroutine. Note for a future pass, deliberately not changed here: each changed guest still triggers a full-file save, so one poll cycle over N changed guests does N marshals and N atomic writes that serialize on the store mutex anyway. Fixing that means coalescing at the call site and is a behavioural change beyond this defect.