Skip to content

feat:improve performance improvements#544

Merged
Smartdevs17 merged 5 commits into
Smartdevs17:mainfrom
Samuel1505:deployments
Jun 27, 2026
Merged

feat:improve performance improvements#544
Smartdevs17 merged 5 commits into
Smartdevs17:mainfrom
Samuel1505:deployments

Conversation

@Samuel1505

Copy link
Copy Markdown
Contributor

Performance improvements: DB partitioning, optimistic UI, CDN edge caching, PWA offline queue

Summary

This PR implements four performance issues (#501#504) to improve query performance, perceived latency, API response time for distributed users, and offline resilience.


closes #504
closes #503
closes #502
closes #501

#504 — Database Partitioning by Tenant

Files changed

  • backend/prisma/migrations/20260627000000_tenant_partitioning/migration.sql
  • backend/src/db/partition-manager.ts

What was done

Added PostgreSQL LIST partitioning by tenant_id for the four high-volume tables (payments, invoices, audit_logs, webhook_logs).

  • audit_logs gains a tenant_id column with backfill from users.tenant_id (falls back to 'system' for non-user actors).
  • A tenant_partitions registry table tracks every partition with row counts, byte sizes, and last-analyzed timestamps.
  • Two reusable PostgreSQL functions: create_tenant_partition(table, tenant) (idempotent) and refresh_partition_stats().
  • Partitioned shadow tables (payments_partitioned, invoices_partitioned, audit_logs_partitioned, webhook_logs_partitioned) are seeded from the originals with ON CONFLICT DO NOTHING.
  • PartitionManager TypeScript class exposes:
    • ensurePartitionsForTenant(tenantId) — call when a new tenant registers
    • dropTenantPartitions(tenantId) — archive/delete a tenant's data by dropping their partition
    • getPartitionStats([tenantId]) — monitoring: size and row counts per partition
    • getPartitionDistribution() — aggregate by table for dashboards
    • migrateExistingTenantData(tenantId, table) — backfill per-tenant with error result
    • migrateAllTenants(table) — full table backfill
    • getQueryMetrics(tenantId, table) — pruning efficiency metric

Zero-downtime strategy: shadow tables run in parallel with originals. The cutover (rename) is deferred to a maintenance window and driven by PartitionManager, not the migration itself, so rollback is a single rename.


#503 — Optimistic UI Updates with Background Revalidation

Files changed

  • frontend/src/hooks/mutations/usePaymentMutations.ts
  • frontend/src/components/common/optimistic-status.tsx
  • frontend/lib/query-keys.ts

What was done

TanStack Query v5 optimistic update pattern applied to all payment mutations.

  • useCreatePayment — immediately prepends an optimistic payment to the cache list; skips optimistic path for amounts >= OPTIMISTIC_THRESHOLD (10 000) to avoid false positives on critical transactions.
  • useUpdatePayment — patches both list and detail caches; onError restores both from snapshot.
  • useCancelPayment — sets status to 'cancelled' optimistically; rolls back on error.
  • useRetryPayment — no optimistic update (server drives the retry outcome).
  • All hooks: onMutate to snapshot and apply, onError to rollback, onSettled to invalidate for server revalidation.
  • OptimisticStatus badge component: shows spinner/label during pending, checkmark on success, error label on error; auto-hides after configurable duration; marks optimistic items with (optimistic) suffix.
  • StaleBadge and ConflictBanner components for stale-data and server-vs-optimistic conflict UX.
  • useMutationState helper derives MutationState from TanStack Query's boolean flags.
  • query-keys.ts extended with invoices, webhooks, and payments.infinite namespaces.

#502 — CDN-Powered Geo-Distributed API Edge Caching

Files changed

  • backend/src/middleware/cache-headers.ts
  • infra/cdn.tf

What was done

Adds Cache-Control / Surrogate-Control header management and a CloudFront distribution.

Middleware (cache-headers.ts)

  • cacheHeaders(options) middleware sets Cache-Control and Surrogate-Control with configurable TTL, stale-while-revalidate, and stale-if-error.
  • Authorization header is SHA-256 hashed into X-Auth-Hash; Vary includes this hash so CDN caches per-user without storing raw tokens in cache keys.
  • Surrogate-Key / Cache-Tag headers enable tag-based purge after mutations.
  • purgeCdnCache({ provider, paths?, surrogateKeys? }) supports CloudFront, Cloudflare, and Fastly.
  • Pre-built presets: cdnCache.none(), cdnCache.realtime(), cdnCache.userData(), cdnCache.staticData().
  • getCacheMetrics() and getCacheHitRatioSummary() for monitoring dashboards.

Terraform (cdn.tf)

  • CloudFront distribution with origin shield (reduces origin load by coalescing edge requests).
  • Three cache policies: no-store (mutations/auth), 30 s (user data), 5 min (static config).
  • Separate ordered behaviours for /api/v1/payments*, /api/v1/invoices*, /api/v1/config*, /api/v1/currencies*.
  • CloudFront Function replaces the Authorization header with its hash before the cache key is computed.
  • S3 log bucket with 90-day lifecycle expiry for cache hit/miss analysis.
  • cdn_distribution_id, cdn_domain_name, cdn_hosted_zone_id outputs for Route53 alias wiring.

#501 — Progressive Web App with Offline Transaction Queuing

Files changed

  • frontend/src/lib/offline-queue.ts
  • frontend/src/hooks/useOnlineStatus.ts
  • frontend/public/sw.js

What was done

IndexedDB-backed offline transaction queue with automatic background sync.

offline-queue.ts

  • enqueue(input) — writes to IndexedDB, registers a SyncManager tag, and dispatches a CustomEvent for cross-component reactivity.
  • flush(apiBaseUrl) — submits all due items (respects retryAt), marks synced or applies exponential backoff (5 s -> 15 s -> 1 min -> 5 min -> 10 min); after 5 retries status is 'failed'.
  • HTTP 409 responses are treated as success (idempotent replay: server already processed the request).
  • getSnapshot() — counts by status for UI indicators.
  • subscribeToQueue(listener) — event-based subscription; unsubscribe function returned.

useOnlineStatus.ts

  • Tracks navigator.onLine via online / offline window events.
  • On reconnect (online event): calls flush() automatically.
  • Listens for PAYMENT_QUEUE_CHANGED and PAYMENT_QUEUE_SYNCED messages from the service worker.
  • Exposes flushNow() for manual sync (e.g. a "Sync now" button in the UI).
  • Returns { isOnline, wasOffline, queue, isFlushing, lastFlushResult, flushNow }.

sw.js updates

  • Bumped APP_VERSION to 2026-06-27.1 to invalidate old caches on deploy.
  • Added /dashboard/payments and /dashboard/transactions to the precache App Shell.
  • IndexedDB upgraded to v2 with retryAt index for efficient due-item queries.
  • flushPaymentQueue filters by retryAt <= now() before submitting; applies same exponential backoff; stops retrying on offline detection.

Test plan

…#504)

- Add tenant_id column to audit_logs with backfill from users table
- Create tenant_partitions registry table for partition lifecycle tracking
- Add create_tenant_partition() and refresh_partition_stats() PG functions
- Create partitioned shadow tables for payments, invoices, audit_logs, webhook_logs
- Seed shadow tables from originals with ON CONFLICT DO NOTHING for safety
- Add PartitionManager class with createPartition, dropTenantPartitions,
  getPartitionStats, migrateExistingTenantData, and getQueryMetrics APIs
- Zero-downtime strategy: shadow tables run alongside originals; cutover
  handled by partition-manager outside of migration to allow rollback
…martdevs17#503)

- Add useCreatePayment, useUpdatePayment, useCancelPayment, useRetryPayment
  mutation hooks with TanStack Query optimistic update pattern
- onMutate snapshots cache and applies optimistic state immediately
- onError rolls back to snapshot; onSettled invalidates for server sync
- Payments above OPTIMISTIC_THRESHOLD (10k) skip optimistic path
- OptimisticStatus badge component shows pending/success/error with spinner
- StaleBadge and ConflictBanner components for stale/conflict UX
- useMutationState helper derives MutationState from isPending/isSuccess/isError
- Extend query-keys with invoices, webhooks, and payments.infinite namespaces
- Add cache-headers.ts middleware with configurable Cache-Control and
  Surrogate-Control header management per CDN TTL tier (none/user/static)
- Vary header includes Accept, Accept-Language plus hashed X-Auth-Hash so CDN
  can cache per-user responses without exposing raw tokens in cache keys
- Surrogate-Key / Cache-Tag headers enable tag-based purge on data mutation
- purgeCdnCache() supports CloudFront, Cloudflare, and Fastly providers
- Pre-built cdnCache presets: none, realtime, userData, staticData
- getCacheMetrics() and getCacheHitRatioSummary() for monitoring
- cdn.tf: CloudFront distribution with per-path behaviours: no-cache for
  mutations, 30 s TTL for user data, 5 min for static, with origin shield
- CloudFront Function hashes Authorization into X-Auth-Hash before caching
- CDN access logs stored in S3 with 90-day lifecycle expiry
…ync (Smartdevs17#501)

- Add offline-queue.ts: IndexedDB-backed queue with enqueue, flush, getSnapshot,
  removeItem, clearAll, and subscribeToQueue APIs
- Exponential backoff on retry: 5 s, 15 s, 1 min, 5 min, 10 min; max 5 retries
- 409 Conflict treated as success (idempotent replay protection)
- SyncManager registration on enqueue for automatic background sync
- subscribeToQueue dispatches CustomEvent for cross-component reactivity
- Add useOnlineStatus hook: tracks online/offline state, auto-flushes on
  reconnect, listens to SW PAYMENT_QUEUE_CHANGED/SYNCED messages, exposes
  flushNow() for manual sync trigger and queue snapshot for UI display
- Update sw.js: bump to APP_VERSION 2026-06-27.1, add /dashboard/payments and
  /dashboard/transactions to precache shell, upgrade IndexedDB to v2 with
  retryAt index, apply exponential backoff in flushPaymentQueue, honour
  retryAt filter to skip items not yet due for retry
@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

@Samuel1505 is attempting to deploy a commit to the smartdevs17's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Jun 26, 2026

Copy link
Copy Markdown

@Samuel1505 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

@Smartdevs17 Smartdevs17 merged commit 0d72ee9 into Smartdevs17:main Jun 27, 2026
17 of 38 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