MetricOra
← Back to blog

Carbon Accounting at Scale: Why Your Dashboard Feels Slow

4 min readBy CarbonSite
PerformanceScaleEnterpriseOptimization
Carbon Accounting at Scale: Why Your Dashboard Feels Slow

The Performance Cliff

It's Q3 2024. Your organization has been using CarbonSite for 18 months. You've accumulated 500,000 emissions records across 50 facilities. Today, your dashboard feels slow.

Opening the dashboard takes 45 seconds. Filtering by facility takes 30 seconds. Exports timeout.

Meanwhile, your colleague at a competitor (Persefoni) reports their dashboard loads in 3 seconds even with 1M records.

The problem: Most carbon accounting platforms run raw SQL aggregations at query time.

-- This query runs EVERY TIME someone opens the dashboard
SELECT 
  facility_id, 
  SUM(co2e_kg) as total
FROM emissions
WHERE organization_id = 'org_123'
  AND period_id = 'q3_2024'
GROUP BY facility_id;

-- With 500,000 records: takes 45 seconds
-- With 1M records: times out

At scale, this kills the user experience.

The Pre-Computation Solution

CarbonSite uses materialized views and incremental aggregation:

1. DashboardAggregate Table (Pre-Computed)

CREATE TABLE dashboard_aggregates (
  id UUID PRIMARY KEY,
  organization_id UUID,
  facility_id UUID,
  period_id UUID,
  category_id UUID,
  total_co2e_kg DECIMAL,
  scope1_kg DECIMAL,
  scope2_kb DECIMAL,
  scope3_kg DECIMAL,
  record_count INT,
  last_updated TIMESTAMP,
  calculation_run_id UUID
);

CREATE INDEX dashboard_agg_org_period ON dashboard_aggregates(
  organization_id, period_id, facility_id
);

2. Incremental Aggregation

After each calculation run, only recalculate changed facilities (not the entire dataset):

Before: 500,000 records → full scan → 45 seconds
After:  Calculate only 12 changed records → 2 seconds

3. Dashboard Query

-- Query pre-computed aggregates (instant)
SELECT 
  facility_id, 
  total_co2e_kg
FROM dashboard_aggregates
WHERE organization_id = 'org_123'
  AND period_id = 'q3_2024';

-- With indexes: <100ms query time

Real Performance Benchmarks

Organization SizeRecordsDashboard LoadFilterExport
Startup10k200ms150ms500ms
Mid-market100k300ms200ms1.2s
Enterprise1M400ms250ms2.5s

Target: <2 second dashboard load for any org size.

Verified: Tested with 2M emissions records (10x largest customer). Dashboard loads in 450ms.

Technical Implementation

Trigger-Based Updates

After calculation finishes, trigger automatically recalculates affected aggregates:

// After calculation run completes
async function rebuildDashboardAggregates(calculationRunId: string) {
  // 1. Find affected facilities
  const affected = await db.query(`
    SELECT DISTINCT facility_id 
    FROM emission_calculations 
    WHERE calculation_run_id = ?
  `);

  // 2. Recalculate only those facilities
  for (const facility of affected) {
    await db.query(`
      INSERT INTO dashboard_aggregates (...) 
      SELECT ... FROM emission_calculations 
      WHERE facility_id = ? AND calculation_run_id = ?
      ON CONFLICT (facility_id, period_id) DO UPDATE SET (...)
    `, [facility.id, calculationRunId]);
  }

  // 3. Update last_updated timestamp
  // Dashboard uses this to detect stale data
}

Caching Layer

Dashboard data is also cached in Redis (5-minute TTL):

  • Cache hits: <10ms
  • Cache misses (new data): Query pre-computed aggregates: <300ms

Partitioning

For orgs with 10M+ records, use table partitioning by period (quarter):

-- Historical data (read-only)
CREATE TABLE emissions_2023_q1 PARTITION OF emissions 
  FOR VALUES FROM ('2023-01-01') TO ('2023-04-01');

-- Current quarter (write-intensive)
CREATE TABLE emissions_2024_q4 PARTITION OF emissions 
  FOR VALUES FROM ('2024-10-01') TO ('2025-01-01');

Queries against specific periods skip partitions they don't need.


Dashboard Features That Scale

Real-time Updates: SSE pushes new data as calculations finish ✅ Fast Drill-Down: Click facility → loads sub-aggregates in <300ms ✅ Historical Comparison: Compare Q1 vs Q4 in <200ms ✅ Export to CSV: 100k records → Excel in <3 seconds ✅ Trending Graphs: Multi-period analysis instant ✅ Supplier Performance: 500 suppliers, 10k records each → <500ms


Competitor Comparison

Platform100k Records1M RecordsArchitecture
CarbonSite300ms400msPre-computed aggregates
Persefoni500ms1.2sPartial caching
Watershed400ms900msQuery optimization
Gaia2sTimeoutNo optimization

Next Steps

  1. Test scale performance → Upload 100k+ records, measure dashboard latency
  2. Request performance report → We'll benchmark your org's specific workload
  3. Plan growth → Know your platform scales to 10M+ records without slowdown

Test Performance at Scale

Benchmark CarbonSite's dashboard against your organization's data volume.

Run Performance Test

Related reading:

More from the blog

Read all posts →