#254 Graceful Shutdown Logic Is Duplicated Across All Three Services … - #284
Open
madisonsc52-del wants to merge 1 commit into
Conversation
Contributor
|
kindly fix conflicts |
Contributor
|
kindky resolve conflict |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR eliminates the duplicated, inconsistent graceful-shutdown logic that was hand-rolled in all four backend services and centralizes it into a single, tested helper.
What changed
shared/validation/shutdown.tsexporting:gracefulShutdown(signal, options)— closes every managed resource in the canonical order server (Fastify) → BullMQ workers → BullMQ queues → Redis → Prisma, usingPromise.allSettledwithin each phase so a single failing resource can't block the others. Enforces a configurable force-exit timeout (default 30 000 ms) and callsprocess.exit(0)on full success orprocess.exit(1)on any failure/timeout.registerGracefulShutdown(options)— wiresSIGTERM/SIGINThandlers with a re-entrancy guard (duplicate signals are ignored), matching the settlement engine's previous behaviour.registerGracefulShutdown({ fastify, prisma })registerGracefulShutdown({ fastify, redis })(removed the now-redundantonCloseRedis-quit hook so Redis is handled centrally)registerGracefulShutdown({ fastify, prisma, redis, bullmq: { worker: [worker, webhookWorker], queues: [settlementQueue, settlementDLQ, webhookQueue] } })registerGracefulShutdown({ fastify, prisma, bullmq: { worker: webhookWorker, queues: [webhookQueue] } })shared/validation/shutdown.test.ts(7 tests) and registered it in the@bettapay/validationtest script.Rationale & trade-offs
SIGINT; the gateway never closed Redis/BullMQ). Centralizing removes the 4× duplication so bug-fixes now land in one place.close/$disconnect/quit) instead of importingfastify/bullmq/ioredis/@prisma/clienttypes, keeping@bettapay/validationdependency-free.worker/queuesaccept either a single value or an array, so services with multiple workers/queues (settlement-engine) and single ones (indexer) use the same API.});inservices/indexer/src/index.tsthat made the whole module unparseable — necessary for the indexer's refactor to compile.Fixes / Closes: Architecture & Refactoring issue — "Graceful shutdown logic manually implemented in every service" (extracted into
@bettapay/validation/shutdown.ts). Insert the tracker issue number once known.Type of Change
Findings
1. Fragmented Shutdown Implementations
api-gateway: Closed only Fastify and Prisma — missing timeout handling and Redis/BullMQ cleanup.
fx-engine: Closed only Fastify; Redis cleanup was hidden in a server hook (
onClose) and lacked a force-exit timeout.settlement-engine: Most complete implementation — included proper shutdown order and a 30s force-exit timeout.
indexer:
SIGTERM(noSIGINT)Result: Four inconsistent patterns, causing maintenance overhead—every change required updates in multiple places.
2. Codebase Blocker Identified
});at ~line 111) inservices/indexer/src/index.tsmade the module unparseable, blocking refactor progress.3. Pre-existing Build/Test Failures (Unrelated)
shared/webhook-delivery/index.test.ts: Missingsuperin constructor (blocks pnpm build).shared/validation/schemas.test.ts: Zod validation failures (env/version mismatch).api-gateway: 13 TypeScript errors increateAuditLogger/rootDir.These issues were confirmed pre-existing and not caused by the shutdown refactor.
Fix Features
1. Centralized Shutdown System
Introduced shared helper:
shared/validation/shutdown.tsExposes:
gracefulShutdown(signal, options)— core shutdown logicregisterGracefulShutdown(options)— auto-wiresSIGTERM+SIGINTwith guardOptions contract:
2. Enforced Correct Shutdown Order
Standardized across all services:
Eliminates race conditions and resource leaks.
3. Fault-Tolerant Shutdown Execution
Uses
Promise.allSettledper phaseEnsures:
4. Built-in Safety Timeout
process.exit(1)if shutdown hangs5. Correct Exit Semantics
process.exit(0)→ All resources closed successfullyprocess.exit(1)→ Any failure or timeout6. Cross-Service Consistency Improvements
All services now:
Specific fixes:
7. Dependency-Free Design
Uses structural typing (
close,$disconnect,quit)No hard dependency on:
Keeps
@bettapay/validationlightweight and reusable.8. Fully Tested Implementation
Test file:
shutdown.test.ts(7 passing tests)Covers:
9. Reduced Code Complexity
Result: Cleaner, maintainable, single source of truth for graceful shutdown logic.
Verification & Test Plan
Automated Tests
Results
shutdown.test.ts→ 7/7 pass (canonical order, array & single worker/queue forms, all-resources-attempted-on-failure → exit 1, 30s timeout → exit 1, customtimeoutMs, duplicate-signal guard).@bettapay/validationsuite → 62 pass / 2 fail. The 2 failures are pre-existingschemas.test.tsZod/CreatePaymentBodytests (confirmed failing on the original committed code, unrelated to this change).fx-engine,settlement-engine,indexer→ build & type-check exit 0.api-gatewaybuild shows no shutdown-related errors (its 13 pre-existingcreateAuditLogger/rootDirtype errors exist on the original code and are untouched by this PR).Manual Verification
pnpm --filter settlement-engine dev) and sendSIGTERM(kill -TERM <pid>); the log shows the server closing, then workers/queues/Redis/Prisma releasing, and the process exits0.SIGTERMimmediately — it is ignored (re-entrancy guard). To observe the timeout path, block a resource'sclose()(e.g. disconnect Redis abruptly) and confirm the process force-exits1after 30s.Sub-System Checklist
1. Smart Contracts (
BettaPay-Contract)cargo build --release --target wasm32-unknown-unknown)cargo test)auth.require_auth())2. Backend Services (
BettaPay-Backend)pnpm build) —validation+fx-engine/settlement-engine/indexerbuild cleanly.env.example— no new env vars introduced3. Frontend Dashboard (
BettaPay-Frontend)pnpm build).env.localChecklist
allSettledrationale documented inshutdown.ts)shared/validation/index.tsCLOSE #254