Skip to content

Commit 6636a1a

Browse files
committed
refactor(webapp): split mollifier drainer factory into create + start
initializeMollifierDrainer() no longer calls drainer.start() — it returns a configured-but-stopped drainer. worker.server.ts init() now invokes drainer.start() AFTER the SIGTERM/SIGINT handlers are registered, gated on the same __mollifierShutdownRegistered__ guard so dev hot-reloads can't double-start. Closes the residual race window between drainer.start() (previously fired inside the singleton factory) and process.once("SIGTERM", stopDrainer) in worker.server.ts. With construction and starting separated, a signal landing during boot can never find the polling loop running without a graceful-stop path.
1 parent 4016d02 commit 6636a1a

1 file changed

Lines changed: 7 additions & 1 deletion

File tree

apps/webapp/app/v3/mollifier/mollifierDrainer.server.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,16 @@ function initializeMollifierDrainer(): MollifierDrainer<BufferedTriggerPayload>
8383
isRetryable: () => false,
8484
});
8585

86-
drainer.start();
8786
return drainer;
8887
}
8988

89+
// Returns a configured-but-stopped drainer. Callers MUST register their
90+
// SIGTERM / SIGINT shutdown handlers before invoking `drainer.start()` —
91+
// see `apps/webapp/app/services/worker.server.ts`. Starting inside the
92+
// singleton factory would put the polling loop ahead of handler
93+
// registration, leaving a narrow window where a SIGTERM landing between
94+
// `start()` and `process.once("SIGTERM", ...)` would skip the graceful
95+
// stop. The split is intentional.
9096
export function getMollifierDrainer(): MollifierDrainer<BufferedTriggerPayload> | null {
9197
if (env.MOLLIFIER_ENABLED !== "1") return null;
9298
return singleton("mollifierDrainer", initializeMollifierDrainer);

0 commit comments

Comments
 (0)