Skip to content

Latest commit

 

History

History
225 lines (166 loc) · 6.01 KB

File metadata and controls

225 lines (166 loc) · 6.01 KB

Chart Endpoint Optimization

Overview

Optimized the chart endpoint to use database-level aggregation instead of loading all transactions into memory and aggregating in JavaScript. This significantly improves performance and reduces memory usage for large datasets.

Changes Made

1. Database-Level Aggregation (analytics.service.ts)

Before:

  • Loaded all transactions into memory using findMany
  • Aggregated data in JavaScript using Map
  • Calculated metrics in application code

After:

  • Uses raw SQL with $queryRaw for database-level aggregation
  • Aggregates data at the database level using GROUP BY and SUM/COUNT
  • Only processes aggregated results in JavaScript

2. Timezone Support

  • Added timezone parameter to handle date grouping across timezone boundaries
  • Uses PostgreSQL's AT TIME ZONE for correct date conversion
  • Default timezone is UTC for backward compatibility

3. Gap Filling

  • Implemented fillDateGaps method to fill days with zero transactions
  • Ensures consistent time-series data even for inactive days
  • Maintains chronological order in results

4. Mock Prisma Service Update (prisma.service.ts)

  • Added $queryRaw mock implementation for testing
  • Supports basic aggregation queries
  • Maintains compatibility with in-memory test environment

5. Controller Update (analytics.controller.ts)

  • Added optional timezone query parameter
  • Maintains backward compatibility with UTC default

6. Unit Tests (analytics.service.spec.ts)

  • Tests for database-level aggregation
  • Tests for gap filling functionality
  • Tests for timezone boundary handling
  • Tests for correct aggregation calculations

7. Performance Benchmark (scripts/benchmark-chart-aggregation.ts)

  • Compares old vs new approach
  • Measures execution time and memory usage
  • Tests with varying record counts (100, 500, 1000, 5000)

SQL Query

The optimized query uses PostgreSQL's aggregation functions:

SELECT 
  DATE("createdAt" AT TIME ZONE ${timezone})::date as date,
  COALESCE(SUM("amount"), 0) as "totalVolume",
  COUNT(*) as "transactionCount",
  SUM(CASE WHEN "state" IN ('COMPLETED', 'RELEASED') THEN 1 ELSE 0 END) as "completedCount",
  SUM(CASE WHEN "state" = 'DISPUTED' THEN 1 ELSE 0 END) as "disputedCount"
FROM "Escrow"
WHERE 
  "vendorAddress" = ${vendorAddress}
  AND "createdAt" >= ${startDate}
  AND "createdAt" <= ${endDate}
GROUP BY DATE("createdAt" AT TIME ZONE ${timezone})::date
ORDER BY date ASC

Performance Improvements

Expected Improvements

For small datasets (< 100 records):

  • Minimal performance difference
  • Slight overhead from SQL query parsing

For medium datasets (100-1000 records):

  • 20-40% faster execution
  • 30-50% less memory usage

For large datasets (> 1000 records):

  • 50-80% faster execution
  • 60-90% less memory usage

Running the Benchmark

npm run benchmark:chart

Or directly:

ts-node scripts/benchmark-chart-aggregation.ts

API Usage

Request

GET /vendor/analytics/chart?days=30&timezone=America/New_York

Parameters

  • days (optional): Number of days to retrieve (default: 30, max: 365)
  • timezone (optional): Timezone for date grouping (default: UTC)

Response

{
  "data": [
    {
      "date": "2024-01-01",
      "totalVolume": 1500,
      "transactionCount": 15,
      "completedCount": 12,
      "disputedCount": 1,
      "averageTransactionValue": 100
    }
  ],
  "period": {
    "startDate": "2024-01-01",
    "endDate": "2024-01-30"
  },
  "summary": {
    "totalVolume": 45000,
    "totalTransactions": 450,
    "averageDaily": 1500
  }
}

Acceptance Criteria

Use Prisma groupBy or raw SQL for daily aggregation

  • Implemented using $queryRaw with PostgreSQL aggregation functions
  • Aggregates data at database level using GROUP BY, SUM, COUNT
  • Conditional aggregation for completed/disputed counts

Ensure correct date grouping across timezone boundaries

  • Added timezone parameter with UTC default
  • Uses PostgreSQL's AT TIME ZONE for correct date conversion
  • Implemented formatDateInTimezone helper method

Handle days with zero transactions (fill gaps)

  • Implemented fillDateGaps method
  • Iterates through entire date range
  • Inserts zero-value entries for missing dates
  • Maintains chronological order

Add unit test for aggregation logic

  • Created comprehensive test suite
  • Tests aggregation accuracy
  • Tests gap filling
  • Tests timezone handling
  • Tests edge cases (empty data, single day, etc.)

Benchmark performance before and after

  • Created benchmark script
  • Tests with varying record counts
  • Measures execution time and memory usage
  • Provides improvement percentages

Migration Notes

Database Requirements

  • PostgreSQL database with createdAt timestamp column
  • Index on (vendorAddress, createdAt) for optimal performance
  • Support for AT TIME ZONE function (PostgreSQL 9.3+)

Backward Compatibility

  • Default timezone is UTC (maintains existing behavior)
  • Existing API calls without timezone parameter work unchanged
  • Response format remains identical

Testing

Run unit tests:

npm test -- analytics.service.spec.ts

Run benchmark:

npm run benchmark:chart

Troubleshooting

Slow Performance

  1. Check database indexes: Ensure (vendorAddress, createdAt) index exists
  2. Verify PostgreSQL version: Requires 9.3+ for AT TIME ZONE
  3. Check connection pool: Ensure sufficient connections for concurrent requests

Incorrect Timezone Results

  1. Verify timezone string format (e.g., 'America/New_York', 'UTC')
  2. Check server timezone settings
  3. Test with UTC to isolate timezone issues

Gap Filling Issues

  1. Verify date range calculation
  2. Check timezone conversion logic
  3. Ensure fillDateGaps is called after aggregation

Future Improvements

  • Add caching for frequently accessed date ranges
  • Implement incremental updates for real-time dashboards
  • Add support for custom aggregation intervals (hourly, weekly, monthly)
  • Consider materialized views for very large datasets