MetaMask / MetaMask/metamask-extension

[State Sync Redesign 1.3] Preserve controller keys in transport, remove debounce, add `requestAnimationFrame` batching

Open
#40,036 0 comments 0 reactions 0 assignees View on GitHub
INVALID-ISSUE-TEMPLATE team-extension-platform
Dominant language
TypeScript
Stars
13.2k
Forks
5.6k
Avg merge
2d 5h
Merged PRs (30d)
451

Description

Replace background-side accumulation + 200ms debounce with per-controller-keyed patch delivery, background-side microtask batching, and UI-side `requestAnimationFrame` batching.

## Background changes

- In `setupControllerConnection`, subscribe to each controller via messenger.
- On `stateChange`, normalize and sanitize patches inline.
- Accumulate `{ [controllerKey]: patches }` in a pending batch. Flush via `queueMicrotask` — one port write per synchronous cascade, not one per controller.
- A single user action that touches N controllers synchronously produces 1 port write, not N.

## UI changes

- In `ui/index.js`, collect incoming keyed-patch messages in a pending batch.
- Flush on next `requestAnimationFrame` (~16ms). Multiple messages arriving within one frame are coalesced.
- Flatten `Record` to `Patch[]` for existing `updateMetamaskState` Redux dispatch.
- Attach `changedControllers: string[]` to the action payload (available for future use).

## Two-layer batching

| Layer | Batches | Latency | Handles |
|---|---|---|---|
| Background `queueMicrotask` | Synchronous cascades within one action | ~0ms | One action → N controllers |
| UI `requestAnimationFrame` | Multiple messages arriving within one frame | ≤16ms (React 17), sub-frame (React 18) | Async bursts, rapid-fire actions |

**Combined worst-case latency:** action completes → `stateChange` synchronously → microtask flush (~0ms) → IPC (sub-ms) → UI rAF (≤16ms) = **≤17ms**.

## Steps

1. In `setupControllerConnection`, subscribe to each controller via messenger. On `stateChange`, accumulate `{ [controllerKey]: patches }` in a pending batch.
2. Flush the pending batch via `queueMicrotask` — write one message per microtask boundary to `outStream`.
3. In `ui/index.js`, add pending batch queue + `requestAnimationFrame` flush before dispatching to Redux.
4. Remove `PatchStore` class.
5. Remove `this.sendUpdate = debounce(...)` and `privateSendUpdate`.
6. Retain Redux dispatch path (flatten keyed patches for existing `updateMetamaskState`).

## Files

- `app/scripts/metamask-controller.js`
- `app/scripts/lib/setupControllerConnection.ts` (or wherever per-controller subscriptions are wired)
- `app/scripts/lib/PatchStore.ts`
- `ui/index.js`

## Validation

- [ ] Latency improvement measurable in React DevTools profiler (200ms → ≤17ms)
- [ ] E2E tests pass with two-layer batching
- [ ] Controller keys visible in transport (debug logging)
- [ ] Startup burst: ~50+ controllers initializing produces ≤2-3 port writes (not 50+)
- [ ] Account sync burst: 100+ changes to same controller coalesced per frame on UI side
- [ ] No port message queue growth under sustained load (memory stable)

## Risk

Medium — changes the timing of all UI updates. Validate with E2E tests. Rollback: revert to debounce.

## Dependencies

None (Issues 1.1-1.2 are independent)

## Outcome

Latency drops from 0-200ms to 0-17ms. Controller keys preserved in transport. Redux path unchanged. Port message volume controlled via microtask coalescing.

## Context

Part of [State Sync Redesign — Epic 1: Transport optimization](#40059). See [ADR transport batching strategy](https://github.com/MetaMask/decisions/pull/124) for full design rationale.

Contributor guide

Open the contributing guide

Research direction

Read app/scripts/lib/setupControllerConnection.ts, app/scripts/lib/PatchStore.ts, app/scripts/metamask-controller.js, and ui/index.js to trace current subscriptions, debounce, and Redux dispatch flow. Run the existing E2E tests before and after the change; done means keyed transport, microtask and requestAnimationFrame batching, the PatchStore and debounce paths removed, and the listed startup and account-sync behavior validated.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, redux, typescript
Domain
backend, frontend, performance
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.