Skip to content

feat: batch payout txs, background analytics, submissions pagination - #2134

Merged
RUKAYAT-CODER merged 2 commits into
EarnQuestOne:mainfrom
k-deejah:feat/perf-improvements-1981-2004-1973
Jul 29, 2026
Merged

feat: batch payout txs, background analytics, submissions pagination#2134
RUKAYAT-CODER merged 2 commits into
EarnQuestOne:mainfrom
k-deejah:feat/perf-improvements-1981-2004-1973

Conversation

@k-deejah

Copy link
Copy Markdown
Contributor

Description

This PR implements three performance improvement issues targeting batch payouts, background analytics computation, and submissions pagination.


Linked Issues


Changes Made

#1981 — Batch payout transactions

Problem: Individual payouts are submitted as separate transactions, incurring per-transaction fees and RPC round-trips.

Solution: Group eligible payouts into multi-operation Stellar transactions (up to 100 ops/tx).

  • StellarService.sendBatchPayments() (src/modules/stellar/stellar.service.ts): Builds a single TransactionBuilder with multiple Operation.payment() calls, signs once, submits once. Splits into multiple transactions when input exceeds 100 operations.
  • PayoutsService.processBatchPayouts() (src/modules/payouts/payouts.service.ts): Cron job (every 30s) that queries pending/retry payouts, groups by asset, delegates to sendBatchPayments(). Handles partial failures per-payout.
  • Updated claimPayout to set status to PENDING so batch cron picks it up.
  • Metrics: batch_payout_total, batch_payout_operations, batch_payout_size.

#2004 — Move analytics to background jobs

Problem: Synchronous analytics computation blocks request threads and risks timeouts.

Solution: Compute in background jobs, persist results, serve precomputed data.

  • PlatformAnalyticsService.getPlatformStats() (src/modules/analytics/services/platform-analytics.service.ts): First attempts to serve from the most recent AnalyticsSnapshot (within 5 min). Falls back to live computation when no fresh snapshot exists.
  • computeAndStorePlatformStats(): Computes full platform stats and persists to analytics_snapshots table via upsert.
  • @Cron(EVERY_5_MINUTES) computePlatformAnalytics(): Scheduled background job that pre-computes stats for the last 30 days.
  • Metrics: analytics_computation_total (source: snapshot|live), analytics_computation_duration_seconds.

#1973 — Pagination limit clamp for submissions

Problem: GET /quests/:questId/submissions accepted unbounded limits, returning all submissions.

Solution: Wired existing QuerySubmissionsDto to controller; cursor-based pagination with @Max(100) limit clamp.

  • Controller (src/modules/submissions/submissions.controller.ts): Accepts @Query() query: QuerySubmissionsDto.
  • Service findByQuest() (src/modules/submissions/submissions.service.ts): Cursor-based pagination with filters (status, userId), sort (sortBy, order), limit (default 10, max 100).
  • Returns PaginatedResponseDto<Submission> with data, nextCursor, hasMore.

Type of Change

  • New feature (non-breaking change that adds functionality)
  • Refactor (no functional change)
  • Bug fix
  • Breaking change
  • Security fix

Test Evidence

Unit Tests

  • New tests added for batch payouts (groups by asset, 100-op limit, partial failure)
  • New tests for sendBatchPayments (multi-op tx, >100 splits)
  • All existing tests pass (npm run test)

Test output:

PASS src/modules/payouts/payouts.service.spec.ts
PASS src/modules/stellar/stellar.service.spec.ts
Tests:       17 passed, 17 total

TypeScript Compilation

npx tsc --noEmit — no new errors introduced

Performance Impact

Area Before After Improvement
Payout tx submission 1 tx per payout 1 tx per 100 payouts ~100x fewer txs
Analytics endpoint Synchronous heavy query Serves from snapshot ~10-50x faster response
Submissions listing Returns all rows Cursor paginated (max 100) Bounded response size

Implements three performance improvement issues:

EarnQuestOne#1981 - Batch payout transactions
- Add sendBatchPayments() to StellarService for multi-op transactions
- Add processBatchPayouts() cron job in PayoutsService (every 30s)
- Groups eligible payouts by asset, up to 100 ops per tx
- Partial failure handling with per-op result tracking
- Metrics: batch_payout_total, batch_payout_operations, batch_payout_size

EarnQuestOne#2004 - Move analytics to background jobs
- PlatformAnalyticsService now serves from AnalyticsSnapshot first
- computeAndStorePlatformStats() persists results to DB snapshots
- Background cron (every 5 min) pre-computes platform stats
- Falls back to live computation when no fresh snapshot exists
- Metrics: analytics_computation_total, analytics_computation_duration_seconds

EarnQuestOne#1973 - Pagination limit clamp for submissions
- Wire QuerySubmissionsDto to GET /quests/:questId/submissions
- Cursor-based pagination with limit clamp (max 100 via @max(100))
- Supports status, userId filters and sortBy/order options
- Returns PaginatedResponseDto with nextCursor/hasMore
@k-deejah
k-deejah requested a review from RUKAYAT-CODER as a code owner July 28, 2026 20:05
@drips-wave

drips-wave Bot commented Jul 28, 2026

Copy link
Copy Markdown

@k-deejah 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

@RUKAYAT-CODER

Copy link
Copy Markdown
Contributor

kindly resolve conflict

@RUKAYAT-CODER

Copy link
Copy Markdown
Contributor

Thank you for contributing to the project.

@RUKAYAT-CODER
RUKAYAT-CODER merged commit e8d0136 into EarnQuestOne:main Jul 29, 2026
2 of 7 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

2 participants