Skip to content

Feat/allowance concurrency retry - #998

Merged
orunganiekan merged 2 commits into
SiLioLabs:masterfrom
ogaziedaniel80-droid:feat/allowance-concurrency-retry
Aug 31, 2026
Merged

Feat/allowance concurrency retry#998
orunganiekan merged 2 commits into
SiLioLabs:masterfrom
ogaziedaniel80-droid:feat/allowance-concurrency-retry

Conversation

@ogaziedaniel80-droid

Copy link
Copy Markdown
Contributor

Closes #891

Adds configurable bounded concurrency and per-call transient-error retry with backoff to both allowance scripts. A new shared allowance-utils.ts module carries all the reusable logic and stable JSON schema types.

New: scripts/allowance-utils.ts

  • withRetry<T>() — exponential backoff retry with jitter; propagates immediately on DefinitiveError and known non-transient HTTP errors (400/401/403/404); retries on network/timeout/5xx/429
  • pLimit(n) — token-bucket concurrency limiter; throws RangeError on n < 1
  • runConcurrent() — fan-out helper capping in-flight tasks, captures per-task errors as Error objects without aborting the batch
  • isTransientError() — classifier powering the retry gate
  • DefinitiveError — marker class for non-retryable business-logic results
  • Stable JSON schema types: AuditResult, AllowanceCheckReport, ExpiryEntry, ExpiryAlertReport

scripts/check-allowances.ts

  • getSubscription() and getAllowance() use withRetry() internally
  • auditSubscriber() — definitive outcomes (invalid_address, no_subscription) bypass retry
  • main() fans out all addresses via runConcurrent(CONCURRENCY)
  • --json writes AllowanceCheckReport to stdout; human summary always on stderr
  • New env knobs: CONCURRENCY (default 5), MAX_RETRIES (default 3), RETRY_BASE_MS (default 300)

scripts/alert-expiring-allowances.ts

  • Same retry wiring on all three RPC helpers
  • main() fans out via runConcurrent(CONCURRENCY), same env knobs
  • Always emits ExpiryAlertReport JSON to stdout

scripts/test-allowance-utils.ts (new, 16 test cases, no network)

Suites cover: transient/definitive classification, retry success/exhaustion/no-retry-on-definitive/no-retry-on-404/maxRetries=0, pLimit concurrency cap/serialisation/invalid-limit, runConcurrent ordering/partial-failures/cap, mock flaky RPC round-trip, onRetry callback numbering, empty task list.

Explicit non-goal: MultiEndpointServer failover is owned by rpc-client.ts (Issue 077) and is reused here unchanged.

Acceptance criteria

  • Both scripts support configurable concurrency
  • Transient RPC failures retry with backoff; definitive results do not
  • JSON and human outputs available
  • Unit tests cover retry and concurrency scheduling with mocks

…oLabs#97)

Add configurable concurrent execution and per-call transient-error
retry with backoff to check-allowances.ts and alert-expiring-allowances.ts.
Shared logic lives in the new allowance-utils.ts module.

New file: scripts/allowance-utils.ts
- withRetry<T>(): exponential backoff retry; skips DefinitiveError and
  known non-transient HTTP patterns (400/401/403/404); retries on
  network/timeout/5xx/429
- pLimit(n): token-bucket concurrency limiter; rejects limit < 1
- runConcurrent(): fan-out helper that caps in-flight tasks and captures
  per-task errors without aborting the batch
- isTransientError(): classifier for the retry gate
- DefinitiveError: marker class for business-logic (non-retryable) results
- Stable JSON schema types: AuditResult, AllowanceCheckReport,
  ExpiryEntry, ExpiryAlertReport

scripts/check-allowances.ts:
- getSubscription() and getAllowance() now call withRetry() internally;
  max retries / base delay controlled by MAX_RETRIES / RETRY_BASE_MS env vars
- auditSubscriber() runs both RPC calls with retry; definitive outcomes
  (invalid_address, no_subscription) bypass retry entirely
- main() fans out all addresses via runConcurrent(CONCURRENCY)
- --json writes AllowanceCheckReport to stdout; human summary always
  goes to stderr via logger so they never interleave
- Stable JSON field names: subscription_amount, allowance, gap, at_risk,
  error (replaces prior camelCase inconsistency)
- New env knobs: CONCURRENCY (default 5), MAX_RETRIES (default 3),
  RETRY_BASE_MS (default 300)

scripts/alert-expiring-allowances.ts:
- getSubscription(), getAllowanceAmount(), getAllowanceExpiryLedger()
  all call withRetry() with the same env knobs
- main() fans out via runConcurrent(CONCURRENCY)
- Always writes ExpiryAlertReport JSON to stdout; human summary to stderr
- Same CONCURRENCY / MAX_RETRIES / RETRY_BASE_MS env knobs

scripts/test-allowance-utils.ts (new):
16 test cases, no network required:
  Suite 1  — isTransientError classification (transient vs definitive)
  Suite 2  — withRetry succeeds on first attempt
  Suite 3  — withRetry retries on transient error, recovers
  Suite 4  — withRetry exhausts retries and rethrows
  Suite 5  — withRetry does NOT retry DefinitiveError
  Suite 6  — withRetry does NOT retry 404 non-transient error
  Suite 7  — withRetry maxRetries=0 propagates immediately
  Suite 8  — pLimit respects concurrency cap across 10 tasks
  Suite 9  — pLimit(1) serialises tasks
  Suite 10 — pLimit(0) throws RangeError
  Suite 11 — runConcurrent all-success preserves order
  Suite 12 — runConcurrent partial failures captured as Error objects
  Suite 13 — runConcurrent concurrency cap respected (20 tasks, cap 4)
  Suite 14 — mock flaky RPC (4 x 503 then success) round-trip
  Suite 15 — onRetry callback receives correct 1-based attempt numbers
  Suite 16 — runConcurrent empty task list returns []

scripts/package.json:
- Add test:allowance-utils script

Non-goal (explicit): MultiEndpointServer failover is owned by rpc-client.ts
(Issue 077) and is reused here unchanged — not reimplemented.

Closes SiLioLabs#97
@drips-wave

drips-wave Bot commented Aug 31, 2026

Copy link
Copy Markdown

@ogaziedaniel80-droid 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! 🚀

Learn more about application limits

@orunganiekan
orunganiekan merged commit dd9d88c into SiLioLabs:master Aug 31, 2026
0 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Issue 097: Add bounded concurrency and retries to allowance audit scripts with JSON output

2 participants