fix: preflight max-age config, durable webhook metrics, degraded boot, ledger cursor CAS (#341-#344) - #437
Merged
ritaifeoluwa merged 1 commit intoSep 26, 2026
Conversation
…d boot, ledger cursor CAS - cors.js/config.js: preflight responses advertise Access-Control-Max-Age from CORS_MAX_AGE_SECONDS (default 24h) so the cache window is explicit and tunable instead of a magic literal (SmartDropLabs#344) - webhookDispatcher.js: delivery counters are written through to Redis (one aggregate hash plus one hash per webhook) and reloaded once at startup via hydrateMetrics(), so success rates survive a restart (SmartDropLabs#343) - index.js: startServer() runs in degraded mode when Redis is down at boot — warn once, hydrate metrics best-effort, then warm the cache — rather than letting either step stop the process (SmartDropLabs#342) - eventStore.js/eventPoller.js: advance indexer:last_ledger through a compare-and-set Lua script that also refuses to move the cursor backwards; the poller keeps the events it already saved and reports a skip when another writer wins the race (SmartDropLabs#341) Collateral repairs to code that was broken on main and blocked these fixes: config.js module.exports structure (syntax error), the duplicate /health websocket key, the priceWebSocket getHealth() export lost in a merge, and priceOracle's single-flight wrapper promise (SmartDropLabs#417).
|
@Sweet-Kid Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
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.
What changed
Access-Control-Max-Ageon OPTIONS responsesis now sourced from
CORS_MAX_AGE_SECONDS(default 86400) instead of a hardcodedliteral, so operators can tune the preflight cache window without a code change.
one aggregate hash plus one hash per webhook, written through with HINCRBY on every
completed delivery (fire-and-forget, never able to fail the delivery).
hydrateMetrics()reloads them at startup, so success/failure rates survive restarts.
startServer()now runs degraded when Redis isunreachable: one explicit warn, metrics hydration treated as best-effort, then cache
warm. Neither step can stop the process from listening; both recover once ioredis
reconnects.
indexer:last_ledgerrace:** cursor advance is now a compare-and-set Luascript (
advanceLastLedger) that also refuses to move the cursor backwards. The pollersaves events first (idempotent), then attempts the CAS; on losing the race it keeps its
saved batch, warns, and reports
{ skipped: true, reason: 'concurrent cursor advance' }instead of silently rewinding another instance's progress.
Also repaired (broken on main, blocking the fixes above)
src/config.js—const config = module.exports;+ dangling object properties was asyntax error; restructured to
const config = module.exports = { … }./health— duplicatewebsocketkey and undefinedwsHealth.priceWebSocket.getHealth()— export lost in a merge, restored.priceOracle.fetchFreshPrice()—wrapperPromise/actualPromisewere never declared([bug] priceOracle inFlight map stores promise reference - concurrent callers get same rejection #417), which made single-flight pricing throw and silently failed cache warming.
Cache warm completelog asserted bycacheWarm.test.js.priceWebSocket,airdropExpiryjob health,saveEvents,apiKeyRateLimit.tiers, price-refresh scan pattern.