@@ -299,12 +299,8 @@ type RateEntry = {key: Text, windowStart: Timestamp, count: Int}
299299*seqCounter : Timestamp
300300-- Bumped by send/heartbeat to wake all blocked /poll holders.
301301*pollWakeup : Timestamp
302−-- Wall time of the last completed prune sweep (gates /ready).
303−*lastPrunedAt : Timestamp
304302-- Wall time of the last self-presence refresh broadcast to peers.
305303*lastPresenceRefresh : Timestamp
306−-- Wall time of process start; gates /ready (must be ≤ lastPrunedAt).
307−*startedAt : Timestamp
308304
309305-- ============================================
310306-- Routes
@@ -786,7 +782,6 @@ forwardQueuedToServer = \pubkey server -> atomic (do
786782-- locally-expired keys, retry pending forwards, and (occasionally) refresh
787783-- local presence so peers don't see us drop off.
788784pruneAndRetry = \t -> do
789− *lastPrunedAt = t
790785 expiredLocal <- pruneExpired t
791786 fork (broadcastOfflineGossip expiredLocal)
792787 when federationEnabled (do
@@ -1199,13 +1194,11 @@ handleFederationBatchForward = \forwards _ -> do
11991194-- Readiness handlers
12001195-- ============================================
12011196
1202−-- GET /ready: signals readiness once the first background prune has run
1203−-- (load-balancer / orchestrator probe).
1204−handleReady : IO {r *lastPrunedAt, r *startedAt} (Result HttpError {ready: Bool})
1205−handleReady = do
1206− sa <- *startedAt
1207− lastPrune <- *lastPrunedAt
1208− yield (Ok {value: {ready: lastPrune > 0 && lastPrune >= sa}})
1197+-- GET /ready: liveness probe (load-balancer / orchestrator). The process runs
1198+-- a synchronous prune sweep before opening the listener, so any response at
1199+-- all means housekeeping has already happened.
1200+handleReady : IO {} (Result HttpError {ready: Bool})
1201+handleReady = yield (Ok {value: {ready: True}})
12091202
12101203-- ============================================
12111204-- Dispatch
@@ -1235,12 +1228,14 @@ api = serve Api where
12351228-- holders unblock and return an empty heartbeat response, keeping connections
12361229-- fresh.
12371230
1238−-- Endless tick: every `backgroundPruneInterval` run `pruneAndRetry`.
1239−backgroundPrune : IO {rw *challenges, rw *forwardFailures, r *forwards, rw *lastPresenceRefresh, rw *lastPrunedAt, rw *messages, rw *presence, rw *sessions, clock} a
1231+-- Endless tick: prune immediately on first call, then every
1232+-- `backgroundPruneInterval`. The startup prune clears stale rows persisted
1233+-- by the previous process before the next interval elapses.
1234+backgroundPrune : IO {rw *challenges, rw *forwardFailures, r *forwards, rw *lastPresenceRefresh, rw *messages, rw *presence, rw *sessions, clock} a
12401235backgroundPrune = do
1241− sleep backgroundPruneInterval
12421236 t <- now
12431237 pruneAndRetry t
1238+ sleep backgroundPruneInterval
12441239 backgroundPrune
12451240
12461241-- Endless tick that bumps `pollWakeup` every `pollTimeoutMs` so any blocked
@@ -1255,14 +1250,10 @@ pollHeartbeat = do
12551250-- ============================================
12561251-- Entry Point
12571252-- ============================================
1258−-- Stamp `startedAt`, spawn the two background loops, and start the listener.
1259−
1260−-- Entry point: stamp startup metadata, fork the background loops, and start
1261−-- the HTTP listener.
1253+-- Fork the background loops (the prune loop runs an immediate sweep on its
1254+-- first tick) and start the HTTP listener.
12621255main : IO _ {}
12631256main = do
1264− t <- now
1265− atomic (do *startedAt = t)
12661257 logInfo ("Skrepka relay server starting on " ++ bindHost ++ ":" ++ show listenPort ++ " ...")
12671258 logInfo (" federation: " ++ (if federationEnabled then "enabled" else "disabled"))
12681259 fork backgroundPrune