Repository navigation
fix: robustness — DB outages, crashing events, shutdown and orphaned tickets - #32
Conversation
|
Thanks a lot for this, and for splitting the review into three focused PRs. That made it easy to go through. I checked every point against the current main and all of them still apply there: the missing Tests pass on your branch (179/179). One small nit: The branch currently conflicts with main, mostly in |
- PostgreSQL pool: handle the idle-client 'error' event (it crashed the process on a Postgres restart) and set a 10 s connectionTimeoutMillis. - Event handlers are awaited inside a try/catch, so a failing event such as a DB error in messageCreate no longer becomes an unhandled rejection. - index.js: log unhandled rejections instead of exiting, exit cleanly on an uncaught exception, and on SIGTERM/SIGINT let an in-flight ticket close finish (up to 7 s) before destroying the client and closing the DB pool. - Listen for the client 'error' event. Claude-Session: https://claude.ai/code/session_01Kp1SLbxxAbXw3f42C1qThH
A ticket stayed "open" in the DB when its channel was deleted with transcripts disabled, deleted by hand in Discord, or deleted while the bot was offline. Those rows counted against maxTicketOpened forever (locking the user out) and were skipped silently by auto-close and the staff reminder. - captureFinalTranscript closes the row even when createTranscript is off. - New channelDelete event closes the ticket of a deleted channel. - Auto-close and staff-reminder loops close a ticket when Discord answers Unknown Channel (10003); transient fetch errors leave it alone. - On startup, open tickets are reconciled against existing channels. - New locale key messages.orphanedTicketReason (all languages). Claude-Session: https://claude.ai/code/session_01Kp1SLbxxAbXw3f42C1qThH
… one closeTicket() overwrote the transcript column with NULL whenever no new transcript was passed. A ticket closed (transcript stored), reopened and then orphaned — channel deleted by hand or while the bot was offline — lost its original transcript, including in the dashboard. The column is now only replaced when a new transcript is given. Claude-Session: https://claude.ai/code/session_01Kp1SLbxxAbXw3f42C1qThH
- Database URLs are logged with the password masked: malformed-URL errors and the migrate script printed them verbatim. Masking runs up to the last "@", so an unencoded "@" in the password does not leak its tail. - uncaughtException sets the exit code and exits after 200 ms, so the log line can drain from a piped stdout (dashboard supervisor). Claude-Session: https://claude.ai/code/session_01Kp1SLbxxAbXw3f42C1qThH
Review feedback: the JSDoc block belonged to parseDatabaseUrl but ended up attached to redactUrl. Claude-Session: https://claude.ai/code/session_01HZPZxD3edzJNFFsiWsHYY1
bae5c28 to
51d734d
Compare
|
Thanks for the review! Done:
Tested live today on our test server (PostgreSQL 16, bot without Administrator). Postgres restarted once on its own, and we also killed the bot's database connections 11 times in 60 seconds while tickets were being opened. The bot logged each reconnect, never restarted, and both tickets were created. A ticket channel deleted by hand was closed in the database straight away. Marking it ready. The live test also turned up two bugs that exist on main as well. I'll send them as two small separate PRs so they don't hold these three up:
|
Description
Robustness fixes found while reviewing the bot before deploying it for our community (Uncharted Roleplay). Each commit is one change:
pgpool had noerrorlistener, so an idle client losing its connection emitted an unhandlederrorevent. The pool now logs it (it reconnects on the next query) and has a 10 sconnectionTimeoutMillis.execute()(e.g. a DB error inmessageCreate) became an unhandled rejection, which ends the process on current Node versions. Event handlers are now awaited in a try/catch,index.jslogs unhandled rejections, exits cleanly on an uncaught exception, and the clienterrorevent has a listener.createTranscript: false, deleting the channel by hand, or deleting it while the bot was offline left the rowopen. It counted againstmaxTicketOpenedforever (the user could never open a ticket again) and auto-close / the staff reminder skipped it silently. Now:captureFinalTranscriptcloses the row even without a transcript, a newchannelDeleteevent closes it, the background loops close it when Discord answers Unknown Channel (10003, transient errors are ignored), and open tickets are reconciled once at startup.closeTicket()no longer wipes an earlier transcript when called without a new one (COALESCE), e.g. for a ticket that was closed, reopened and then orphaned.DATABASE_URLerrors and in thedb:migrateoutput.New locale key
messages.orphanedTicketReason, translated in all languages.Related Issue
None opened; these came out of a code review. Happy to open issues if you prefer that workflow.
Type of Change
How Was This Tested?
npm test: all existing tests pass, plus newnode:testsuites (tests/orphaned-tickets.test.js,tests/url-redaction.test.js) that run against an in-memory SQLite database.Checklist
tb_custom-id prefix,client.logger, etc.)client.t(...)and all three locale files (main.json,en.json,de.json) are in sync (all eight locale files, in fact)initDatabase()(no schema change; only thecloseTicketUPDATE changed)MSK_API_KEY(premium features degrade gracefully)npm start) and I verified my change on a test server (see above)README.md/README_GER.md,config.example.jsonc,CLAUDE.md) where behavior, config keys, commands or the schema changed (no config keys or commands changed)CHANGELOG.mdentry under[Unreleased]feat:,fix:,docs:, …)Note on the related PRs
This is one of three PRs from the same review (robustness, permissions, transcripts). They are independent and each applies to
mainon its own, but they touch some of the same lines (ticketActions.jsexports,ready.js,CHANGELOG.md). Following your order: this one first, then #34, then #33; I will rebase the other two after each merge.https://claude.ai/code/session_01Kp1SLbxxAbXw3f42C1qThH