Skip to content

Reputation Trend Chart Canvas Freeze During Concurrent Slashing and Reward Events #37

Description

@JamesEjembi

Reputation Trend Chart Canvas Freeze During Concurrent Slashing and Reward Events

Problem Statement

The reputation trend chart in src/components/reputation/ReputationChart.tsx renders a time-series line chart of a node's reputation score over time using a <canvas> element with Chart.js. The updateChartData() function at line 80 receives new data points via WebSocket (one per reputation change event). When a node experiences a slashing (-500) followed immediately by multiple rewards (+10 each) as part of an automated recovery protocol, the chart receives 5-10 data points within 100ms. Each data point triggers chart.update('none') (no animation) which re-renders the canvas synchronously. The chart.update() call takes 3-5ms per invocation. With 10 updates within 100ms, the chart rendering consumes 30-50ms of main-thread time. If the browser's frame budget is 16ms (60fps), the chart causes 2-3 consecutive dropped frames. During active node recovery (50 nodes simultaneously), the chart component re-renders 50 × 10 = 500 times within 1 second, consuming 1.5-2.5s of main-thread time — freezing the entire dashboard for 2+ seconds.

State Invariants & Parameters

  • Data points per event: 1 (per reputation change)
  • Batch size: 10 points per node per recovery (5-10 events)
  • Concurrent nodes: up to 50 in recovery
  • chart.update() time: 3-5ms per call
  • Frame budget: 16ms (60fps)
  • Target: no single freeze > 50ms
  • Invariant: ∑(chart_update_time) < 100ms over any 500ms window

Affected Code Paths

  • src/components/reputation/ReputationChart.tsx:75-120updateChartData() calls chart.update() per point
  • src/components/reputation/chartConfig.ts:40-60 — Chart.js configuration with no batch update
  • src/hooks/useReputationStream.ts:50-80 — WebSocket events pushing to chart
  • src/components/reputation/tests/reputationChart.test.ts — No high-throughput test

Resolution Blueprint

  1. Implement batched chart updates: buffer incoming data points for 100ms using a useRef<DataPoint[]> buffer. Every 100ms (via setInterval), call chart.data.datasets[0].data.push(...buffer) (single push with spread) and chart.update('none') once. This reduces 50 updates to 10 per second — well within the frame budget.
  2. Use canvas offscreen rendering (OffscreenCanvas via Web Worker): the chart data is forwarded to a worker via postMessage(). The worker updates its local Chart.js instance (in a mock-compatible environment) and returns the rendered bitmap via transferControlToOffscreen(). The main thread only blits the final bitmap in < 1ms.
  3. Implement decimation at high event rates: when events exceed 10/s for a chart, aggregate consecutive points into a single point showing (timestamp: max, score: avg) at 1-second granularity. Display a visual indicator that the chart is in "high-frequency decimation mode."
  4. Use requestAnimationFrame-based rendering: instead of calling chart.update() on every event, set a dirty flag. The rAF loop checks the flag and calls chart.update() once per frame, ensuring at most 60 updates/s regardless of event rate.
  5. Add a performance benchmark that simulates 500 reputation events across 50 nodes within 1 second and measures the maximum single-frame freeze (target: < 50ms).

Labels

  • Complexity: Hardcore
  • Layer: Core-Engine
  • Type: Race-Condition

Metadata

Metadata

Assignees

Labels

Complexity: HardcoreExtremely difficult, high-complexity engineering taskGrantFox OSSIssue tracked in GrantFox OSSLayer: Core-EngineCore engine layerMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official CampaignType: Race-ConditionConcurrency and race condition related issues

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions