✨ server: add debt repayment notification#722
✨ server: add debt repayment notification#722aguxez wants to merge 1 commit intofeature/webhook-queuefrom
Conversation
🦋 Changeset detectedLatest commit: 1350612 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
WalkthroughThis change implements a debt repayment notification system for maturity checks using BullMQ queues. It introduces scheduled jobs that periodically check account debt status via market or previewer contract implementations, send push notifications to users with debt (with Redis-based deduplication), log errors, and reschedule subsequent checks. Changes
Sequence DiagramsequenceDiagram
participant Server as Server<br/>(Startup)
participant Queue as BullMQ<br/>Queue
participant Worker as BullMQ<br/>Worker
participant Contracts as Market/<br/>Previewer<br/>Contracts
participant Redis as Redis
participant Sentry as Sentry
participant PushSvc as Push<br/>Notification
Server->>Queue: scheduleMaturityChecks()
Queue->>Queue: Add CHECK_DEBTS jobs<br/>(24h, 1h windows)
activate Worker
Worker->>Contracts: Fetch account debt<br/>(per implementation)
Contracts-->>Worker: Debt status aggregated<br/>per user
Worker->>Redis: Check dedup gate<br/>hasNotified?
alt Debt Exists & Not Notified
Redis-->>Worker: Gate check passes
Worker->>PushSvc: Send notification
PushSvc-->>Worker: Notification sent
Worker->>Redis: Set dedup key
else Duplicate or No Debt
Redis-->>Worker: Gate blocked or<br/>no debt
end
Worker->>Sentry: Log metrics &<br/>errors (on failure)
alt Window is "1h"
Worker->>Queue: scheduleMaturityChecks()<br/>(reschedule)
end
deactivate Worker
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @aguxez, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a critical new feature to proactively notify users about their upcoming debt maturities. It establishes a robust server-side queueing system using BullMQ to periodically check user debt positions on the blockchain. Depending on configuration, it leverages either direct market contract interactions or a previewer contract to identify at-risk users. Timely push notifications are then dispatched via OneSignal, with Redis ensuring that users receive alerts only once per window, aiming to help users manage their debts and avoid potential liquidations. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a debt repayment notification system. However, a high-severity Denial of Service vulnerability was identified due to unbounded memory consumption when fetching users, along with a medium-severity logic error causing the notification system to stop rescheduling itself. Additionally, the review suggests improvements for scalability by optimizing database queries, enhancing type safety, and fixing a potential bug in the maturity calculation logic.
|
✅ All tests passed. |
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Fix all issues with AI agents
In `@server/queues/markets.ts`:
- Line 3: The array export DEBT_NOTIFICATION_MARKETS is missing a trailing
comma; update the declaration for DEBT_NOTIFICATION_MARKETS (the constant using
marketUSDCAddress and marketWETHAddress) to include a trailing comma after
marketWETHAddress inside the array literal so it follows the project's
trailing-comma style.
In `@server/queues/maturityQueue.ts`:
- Around line 66-74: The current cast on results to access .errors is fragile;
define a shared return type (e.g., DebtCheckResult with contractCalls, users
array, and optional errors?: Map<string, unknown>) and update both
checkDebtsPreviewerImplementation and checkDebtsMarketImplementation to return
that type so callers (like the code in maturityQueue.ts) can safely read
results.errors without casting; update the function signatures and return shapes
accordingly and remove the (results as ...) cast in the block handling
implementation === "previewer".
- Around line 15-17: The env var parsing for DEBT_NOTIFICATION_IMPLEMENTATION
currently casts any string to ImplementationType which hides invalid values;
update the initialization of implementation to read
process.env.DEBT_NOTIFICATION_IMPLEMENTATION as a raw string, validate it
against the allowed values ("market" | "previewer") (using the
ImplementationType union or a small set/array), and if it's invalid either throw
a clear error or log and fall back to a safe default; ensure you reference the
ImplementationType type and the implementation constant so the check occurs
before any code branches like implementation === "market" run.
- Around line 207-208: The call to publicClient.readContract is using a
redundant cast ``market as `0x${string}```; remove the unnecessary cast and pass
market directly (it already comes from DEBT_NOTIFICATION_MARKETS typed as
readonly [Address, Address]), and if TypeScript complains, adjust the
DEBT_NOTIFICATION_MARKETS/market declaration to be the correct Address type
rather than casting at the call site; locate the usage in maturityQueue.ts
inside the publicClient.readContract invocation and replace the casted argument
with market.
In `@server/test/queues/maturityQueue.test.ts`:
- Around line 318-341: The test "uses previewer implementation when configured"
is placed under the "market implementation" describe but validates the previewer
flow; move this it-block to the existing "previewer implementation" describe (or
to the top-level worker describe) so tests are grouped correctly—relocate the
entire it(...) that imports "../../queues/maturityQueue", uses
previewerProcessor, previewerAddress/previewerAbi, mockUsers and
mocks.readContract expectations into the "previewer implementation" describe
block and remove the original copy from the "market implementation" block.
- Line 350: Replace the hard-coded MATURITY_INTERVAL constant in
maturityQueue.test.ts with an import from the source module to avoid
duplication: remove the local declaration of MATURITY_INTERVAL and import the
exported MATURITY_INTERVAL from server/utils/fixedLibrary.ts (the symbol name is
MATURITY_INTERVAL) so tests stay in sync with the implementation; update any
references in the test file to use the imported MATURITY_INTERVAL.
- Around line 257-264: The mock for mocks.readContract is returning the same
encoded payload for every function call and should discriminate by the requested
function; update the mockImplementation for mocks.readContract to inspect the
first arg's functionName (like the existing pattern around the other mock) and
return the appropriate response for "accounts" vs "fixedBorrowPositions" (e.g.,
return [encoded, 0n, 0n] for the accounts call and the other tuple for
fixedBorrowPositions), preserving the callCount fallback behavior if needed so
the test mirrors the real call sequence and doesn't mask incorrect call
ordering.
In `@server/test/utils/fixedLibrary.test.ts`:
- Around line 62-66: The test "handles offset zero (base maturity)" is redundant
with the existing "returns true when base maturity matches"; remove the
redundant it(...) block or change it to cover a different scenario (e.g., test a
non-zero offset or the case where the maturity bit is unset) so it exercises a
unique code path in hasMaturity; locate the test by the it title "handles offset
zero (base maturity)" and update or delete it accordingly, ensuring the
remaining tests still assert hasMaturity(encoded, baseMaturity) behavior for
distinct cases.
In `@server/utils/fixedLibrary.ts`:
- Around line 1-2: The MATURITY_INTERVAL constant is duplicated; export
MATURITY_INTERVAL from fixedLibrary.ts so other modules reuse it as the single
source of truth: add an export for the existing MATURITY_INTERVAL symbol in
fixedLibrary.ts, remove the duplicate local definition in the other module (the
one that currently defines its own MATURITY_INTERVAL), and update that module to
import MATURITY_INTERVAL from fixedLibrary.ts; keep MASK_BASE_MATURITY
unchanged.
| publicClient.readContract({ | ||
| address: market as `0x${string}`, |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Redundant type cast.
market comes from DEBT_NOTIFICATION_MARKETS which is typed as readonly [Address, Address]. The cast to `0x${string}` is unnecessary.
Proposed fix
positionPromises.push(
publicClient.readContract({
- address: market as `0x${string}`,
+ address: market,
abi: marketAbi,
functionName: "fixedBorrowPositions",
args: [BigInt(maturity), account as `0x${string}`],
}),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| publicClient.readContract({ | |
| address: market as `0x${string}`, | |
| positionPromises.push( | |
| publicClient.readContract({ | |
| address: market, | |
| abi: marketAbi, | |
| functionName: "fixedBorrowPositions", | |
| args: [BigInt(maturity), account as `0x${string}`], | |
| }), |
🤖 Prompt for AI Agents
In `@server/queues/maturityQueue.ts` around lines 207 - 208, The call to
publicClient.readContract is using a redundant cast ``market as `0x${string}```;
remove the unnecessary cast and pass market directly (it already comes from
DEBT_NOTIFICATION_MARKETS typed as readonly [Address, Address]), and if
TypeScript complains, adjust the DEBT_NOTIFICATION_MARKETS/market declaration to
be the correct Address type rather than casting at the call site; locate the
usage in maturityQueue.ts inside the publicClient.readContract invocation and
replace the casted argument with market.
| const module_ = await import("../../queues/maturityQueue"); | ||
| const processor = module_.processor; | ||
|
|
||
| const MATURITY_INTERVAL = 2_419_200; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Import MATURITY_INTERVAL from the source module.
Same duplication issue as in fixedLibrary.test.ts. Import from server/utils/fixedLibrary.ts to stay in sync.
♻️ Proposed fix
+import { MATURITY_INTERVAL } from "../../utils/fixedLibrary";
+
// ... later in the test
- const MATURITY_INTERVAL = 2_419_200;
const jobMaturity = MATURITY_INTERVAL * 10;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const MATURITY_INTERVAL = 2_419_200; | |
| import { MATURITY_INTERVAL } from "../../utils/fixedLibrary"; | |
| // ... rest of imports and test setup ... | |
| const jobMaturity = MATURITY_INTERVAL * 10; |
🤖 Prompt for AI Agents
In `@server/test/queues/maturityQueue.test.ts` at line 350, Replace the hard-coded
MATURITY_INTERVAL constant in maturityQueue.test.ts with an import from the
source module to avoid duplication: remove the local declaration of
MATURITY_INTERVAL and import the exported MATURITY_INTERVAL from
server/utils/fixedLibrary.ts (the symbol name is MATURITY_INTERVAL) so tests
stay in sync with the implementation; update any references in the test file to
use the imported MATURITY_INTERVAL.
| it("handles offset zero (base maturity)", () => { | ||
| const baseMaturity = 1_000_000; | ||
| const encoded = BigInt(baseMaturity) | (1n << 32n); | ||
| expect(hasMaturity(encoded, baseMaturity)).toBe(true); | ||
| }); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Redundant test case.
This test is functionally identical to "returns true when base maturity matches" (Lines 18-22). Both verify offset zero with bit 0 set. Consider removing or differentiating.
🤖 Prompt for AI Agents
In `@server/test/utils/fixedLibrary.test.ts` around lines 62 - 66, The test
"handles offset zero (base maturity)" is redundant with the existing "returns
true when base maturity matches"; remove the redundant it(...) block or change
it to cover a different scenario (e.g., test a non-zero offset or the case where
the maturity bit is unset) so it exercises a unique code path in hasMaturity;
locate the test by the it title "handles offset zero (base maturity)" and update
or delete it accordingly, ensuring the remaining tests still assert
hasMaturity(encoded, baseMaturity) behavior for distinct cases.
server/utils/fixedLibrary.ts
Outdated
| const MATURITY_INTERVAL = 2_419_200; | ||
| const MASK_BASE_MATURITY = (1n << 32n) - 1n; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider exporting MATURITY_INTERVAL to avoid duplication.
This constant is also defined in server/queues/maturityQueue.ts (line 19). Exporting it from here would maintain a single source of truth.
Proposed fix
-const MATURITY_INTERVAL = 2_419_200;
+export const MATURITY_INTERVAL = 2_419_200;
const MASK_BASE_MATURITY = (1n << 32n) - 1n;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const MATURITY_INTERVAL = 2_419_200; | |
| const MASK_BASE_MATURITY = (1n << 32n) - 1n; | |
| export const MATURITY_INTERVAL = 2_419_200; | |
| const MASK_BASE_MATURITY = (1n << 32n) - 1n; |
🤖 Prompt for AI Agents
In `@server/utils/fixedLibrary.ts` around lines 1 - 2, The MATURITY_INTERVAL
constant is duplicated; export MATURITY_INTERVAL from fixedLibrary.ts so other
modules reuse it as the single source of truth: add an export for the existing
MATURITY_INTERVAL symbol in fixedLibrary.ts, remove the duplicate local
definition in the other module (the one that currently defines its own
MATURITY_INTERVAL), and update that module to import MATURITY_INTERVAL from
fixedLibrary.ts; keep MASK_BASE_MATURITY unchanged.
6550d21 to
45be58c
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@server/queues/maturityQueue.ts`:
- Around line 308-324: The delay expressions for the two scheduled adds can be
negative if now is past the target time; update the delay calculation used in
getMaturityQueue().add (for MaturityJob.CHECK_DEBTS jobs with jobId
`check-debts-${nextMaturity}-24h` and `check-debts-${nextMaturity}-1h`) to clamp
to zero (e.g., compute the rawDelay as (nextMaturity - 24*3600 - now) * 1000 and
(nextMaturity - 3600 - now) * 1000 and then use Math.max(0, rawDelay) or
equivalent) so BullMQ never receives a negative delay.
- Line 31: Remove the duplicate MATURITY_INTERVAL constant from
server/queues/maturityQueue.ts and instead import the exported MATURITY_INTERVAL
from server/utils/fixedLibrary.ts (ensure fixedLibrary.ts exports
MATURITY_INTERVAL if not already). Update the top of maturityQueue.ts to import
{ MATURITY_INTERVAL } and delete the local const declaration so all code in
functions like the maturity processing logic uses the single shared constant.
In `@server/test/utils/fixedLibrary.test.ts`:
- Line 5: Replace the hard-coded MATURITY_INTERVAL constant in the test with an
import from the module that defines it: export MATURITY_INTERVAL from the source
module (fixedLibrary.ts) if not already exported, then in the test file remove
the local declaration and import { MATURITY_INTERVAL } from that module so the
test uses the canonical value from fixedLibrary rather than a duplicated
literal.
| await getMaturityQueue().add( | ||
| MaturityJob.CHECK_DEBTS, | ||
| { maturity: nextMaturity, window: "24h" }, | ||
| { | ||
| jobId: `check-debts-${nextMaturity}-24h`, | ||
| delay: (nextMaturity - 24 * 3600 - now) * 1000, | ||
| }, | ||
| ); | ||
|
|
||
| await getMaturityQueue().add( | ||
| MaturityJob.CHECK_DEBTS, | ||
| { maturity: nextMaturity, window: "1h" }, | ||
| { | ||
| jobId: `check-debts-${nextMaturity}-1h`, | ||
| delay: (nextMaturity - 3600 - now) * 1000, | ||
| }, | ||
| ); |
There was a problem hiding this comment.
Delay calculation may produce negative values.
If now is already past nextMaturity - 24 * 3600 (or nextMaturity - 3600), the delay becomes negative. BullMQ treats negative delays as immediate execution, which may cause unintended job execution timing.
Consider adding a guard:
Proposed fix
await getMaturityQueue().add(
MaturityJob.CHECK_DEBTS,
{ maturity: nextMaturity, window: "24h" },
{
jobId: `check-debts-${nextMaturity}-24h`,
- delay: (nextMaturity - 24 * 3600 - now) * 1000,
+ delay: Math.max(0, (nextMaturity - 24 * 3600 - now) * 1000),
},
);
await getMaturityQueue().add(
MaturityJob.CHECK_DEBTS,
{ maturity: nextMaturity, window: "1h" },
{
jobId: `check-debts-${nextMaturity}-1h`,
- delay: (nextMaturity - 3600 - now) * 1000,
+ delay: Math.max(0, (nextMaturity - 3600 - now) * 1000),
},
);🤖 Prompt for AI Agents
In `@server/queues/maturityQueue.ts` around lines 308 - 324, The delay expressions
for the two scheduled adds can be negative if now is past the target time;
update the delay calculation used in getMaturityQueue().add (for
MaturityJob.CHECK_DEBTS jobs with jobId `check-debts-${nextMaturity}-24h` and
`check-debts-${nextMaturity}-1h`) to clamp to zero (e.g., compute the rawDelay
as (nextMaturity - 24*3600 - now) * 1000 and (nextMaturity - 3600 - now) * 1000
and then use Math.max(0, rawDelay) or equivalent) so BullMQ never receives a
negative delay.
|
|
||
| import hasMaturity from "../../utils/fixedLibrary"; | ||
|
|
||
| const MATURITY_INTERVAL = 2_419_200; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Import MATURITY_INTERVAL from source module.
Once MATURITY_INTERVAL is exported from server/utils/fixedLibrary.ts, import it here to maintain a single source of truth and avoid drift.
Proposed fix
-import hasMaturity from "../../utils/fixedLibrary";
+import hasMaturity, { MATURITY_INTERVAL } from "../../utils/fixedLibrary";
-const MATURITY_INTERVAL = 2_419_200;🤖 Prompt for AI Agents
In `@server/test/utils/fixedLibrary.test.ts` at line 5, Replace the hard-coded
MATURITY_INTERVAL constant in the test with an import from the module that
defines it: export MATURITY_INTERVAL from the source module (fixedLibrary.ts) if
not already exported, then in the test file remove the local declaration and
import { MATURITY_INTERVAL } from that module so the test uses the canonical
value from fixedLibrary rather than a duplicated literal.
45be58c to
06f8efd
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@server/queues/maturityQueue.ts`:
- Around line 310-312: The close() function currently awaits closing
maturityWorker and _maturityQueue but leaves their references intact, causing
initializeWorker() to no-op on subsequent calls; after awaiting the closures in
close(), reset the module-level references (maturityWorker and _maturityQueue)
to undefined (or null) so initializeWorker() can create new instances on
re-initialization.
In `@server/utils/redis.ts`:
- Line 5: The shared Redis client exported currently with "new
Redis(process.env.REDIS_URL, { maxRetriesPerRequest: null })" must not be used
on request-path handlers because null causes indefinite retries; create two
clients instead: keep a background client (e.g., backgroundRedis) with
maxRetriesPerRequest: null for queues/workers, and add a separate request-safe
client (e.g., redisClient or requestRedis) with a bounded retry strategy (set
maxRetriesPerRequest to a finite number) and a connectTimeout to be used by
request handlers referenced in authentication.ts, registration.ts and
hooks/block.ts; update exports so request handlers import the bounded client
while queue code uses the background client.
| export async function close() { | ||
| await Promise.all([maturityWorker?.close() ?? Promise.resolve(), _maturityQueue?.close() ?? Promise.resolve()]); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Reset queue/worker refs after close.
Without resetting these references, subsequent initializeWorker() calls in the same process will no-op (tests, hot reloads).
♻️ Proposed fix
export async function close() {
await Promise.all([maturityWorker?.close() ?? Promise.resolve(), _maturityQueue?.close() ?? Promise.resolve()]);
+ maturityWorker = undefined;
+ _maturityQueue = undefined;
}🤖 Prompt for AI Agents
In `@server/queues/maturityQueue.ts` around lines 310 - 312, The close() function
currently awaits closing maturityWorker and _maturityQueue but leaves their
references intact, causing initializeWorker() to no-op on subsequent calls;
after awaiting the closures in close(), reset the module-level references
(maturityWorker and _maturityQueue) to undefined (or null) so initializeWorker()
can create new instances on re-initialization.
| if (!process.env.REDIS_URL) throw new Error("missing redis url"); | ||
|
|
||
| export default new Redis(process.env.REDIS_URL); | ||
| export default new Redis(process.env.REDIS_URL, { maxRetriesPerRequest: null }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all imports of the shared Redis client
rg "from.*utils/redis|import.*redis.*from.*utils" --type=ts --type=tsx -nRepository: exactly/exa
Length of output: 82
🏁 Script executed:
#!/bin/bash
# Also check for direct imports in the server directory
cd server && find . -name "*.ts" -o -name "*.tsx" | head -20Repository: exactly/exa
Length of output: 424
🏁 Script executed:
#!/bin/bash
# Get an overview of the Redis client export
cat server/utils/redis.tsRepository: exactly/exa
Length of output: 236
🏁 Script executed:
#!/bin/bash
# Corrected search for imports of the Redis client
rg "from.*utils/redis|import.*redis.*from" server --type ts -n -A 2Repository: exactly/exa
Length of output: 983
🏁 Script executed:
#!/bin/bash
# Search for common Redis method calls to understand usage patterns
rg "redis\.(get|set|push|pop|lpush|rpush|blpop|brpop|subscribe|publish)" server --type ts -n -B 2 -A 1 | head -80Repository: exactly/exa
Length of output: 1842
🏁 Script executed:
#!/bin/bash
# Check the context of hooks/block.ts to understand if it's called in request path
sed -n '50,70p' server/hooks/block.tsRepository: exactly/exa
Length of output: 917
🏁 Script executed:
#!/bin/bash
# Check for any timeout configurations on the Redis client
rg "connectTimeout|commandTimeout|lazyConnect|retryStrategy" server --type tsRepository: exactly/exa
Length of output: 37
🏁 Script executed:
#!/bin/bash
# Look at how block.ts is used/imported
rg "from.*hooks/block|hooks/block" server --type ts -nRepository: exactly/exa
Length of output: 299
🏁 Script executed:
#!/bin/bash
# Check how redis is used in block.ts to confirm it's in request handler
rg "redis\." server/hooks/block.ts -n -B 1 -A 1 | head -40Repository: exactly/exa
Length of output: 1311
Split Redis client: use indefinite retries only for background queues.
The shared Redis client is used in request-path handlers (server/api/auth/authentication.ts, server/api/auth/registration.ts, server/hooks/block.ts) where maxRetriesPerRequest: null causes indefinite retries during outages. Create a separate client with reasonable retry limits for request handlers, or add connectTimeout and configure a bounded retry strategy for the shared instance.
🤖 Prompt for AI Agents
In `@server/utils/redis.ts` at line 5, The shared Redis client exported currently
with "new Redis(process.env.REDIS_URL, { maxRetriesPerRequest: null })" must not
be used on request-path handlers because null causes indefinite retries; create
two clients instead: keep a background client (e.g., backgroundRedis) with
maxRetriesPerRequest: null for queues/workers, and add a separate request-safe
client (e.g., redisClient or requestRedis) with a bounded retry strategy (set
maxRetriesPerRequest to a finite number) and a connectTimeout to be used by
request handlers referenced in authentication.ts, registration.ts and
hooks/block.ts; update exports so request handlers import the bounded client
while queue code uses the background client.
This is part 2 of 2 in a stack made with GitButler:
Summary by CodeRabbit