Skip to main content
INDEPENDENT RESEARCH · WEBASSEMBLY · LWC · HIGH-VOLUME DATATier 1 Flagship

WebAssembly in Salesforce LWC: 500K Record Engine & Net-Zero DB

“When the platform caps storage, heap, and CPU, stop fighting the caps - change where the data lives and where the work runs.”

Independent research on a familiar ceiling: half a million catalog rows inside a sandboxed CRM component. Gzip transport, Wasm-assisted decoding, and an in-memory query engine - with edits stored as tiny deltas instead of a database.

WEBASSEMBLYRUSTLWCOBJECT STORAGEAST QUERYDELTA MERGE

Executive BriefSystems
Domain / ScopeSystems Engineering & High-Volume Web Architecture
Architectural RoleIndependent Systems Researcher & Builder
Timeline & InitiativeIndependent research · 2025–2026
Key Verified Invariant

500K-row synthetic fixture cold-loads, decodes, and indexes in the high tens of seconds on broadband in my tests - dominated by download, not parsing

01 · The Constraint

What Made the Problem Difficult

Independent work, synthetic data only. I have watched large quoting and catalog problems hit the same three walls: CRM storage priced per row, server heaps capped in single-digit megabytes, and browsers asked to parse hundreds of megabytes of row-oriented JSON on the main thread. I wanted a reproducible answer to a narrow question - how far can you get with compression, direct-to-browser streaming, and in-memory indexing - so I built the pipeline end to end on weekends and measured it.

Core Platform Constraint

Systemic Friction

The walls, stated plainly:

01
Row storage is the wrong home

Half a million catalog rows do not belong in per-row CRM storage. The cost and the import time both hurt.

02
Server heaps are small on purpose

A 100MB+ JSON payload cannot pass through a 6–12MB execution heap. It fails loudly.

03
Upstream APIs throttle

Catalog providers rate-limit. Synchronous pulls during busy hours return 429s.

04
Fat JSON over the wire is slow

Row-oriented JSON with repeated keys compresses badly and parses slowly.

05
Main-thread parsing freezes tabs

Hundreds of thousands of object allocations trigger long GC pauses.

06
Edits seem to demand a database

Price overrides and margin tweaks look like they need RDS - with bills and ops to match.

Boundary Invariants & Operational Limits:
  • !Stay inside the CRM component model: LWC under Lightning Web Security, no external portal redirect
  • !Cold open in tens of seconds, warm filtering instant: full load in under ~30s on broadband, queries in milliseconds
  • !No provisioned database for edits: overrides must work without RDS or similar
  • !No raw catalog rows in CRM storage: object storage holds the snapshot, CRM holds at most pointers
  • !Nothing heavy through the server heap: the browser streams bytes directly from storage
02 · The Architecture

What System Was Designed

The pipeline I built and measured: 1. Offline compaction: a small worker reshapes synthetic catalog JSON into columnar arrays and gzip-compresses it. On my fixtures this lands around 2–3% of the original byte size, staged immutably on S3-compatible storage. 2. Signed-URL streaming: the server side mints a short-lived signed URL and steps aside. The component fetches bytes directly - the CRM heap never sees the payload. 3. Wasm-assisted decode with JS fallback: a small Rust/Wasm decoder expands the stream into linear memory; where Wasm is unavailable the same path falls back to Pako. Either way the result lands in typed arrays, not object soup. 4. In-memory query engine: the json-query-lite lineage - lexer, parser, AST over typed arrays and inverted indexes - evaluates nested AND/OR/NOT filters in milliseconds. 5. Delta files for edits: overrides serialize to a tiny compressed delta (~KBs) beside the base snapshot and merge in memory at load. No database process exists anywhere. 6. Virtualized grid: roughly four dozen DOM rows in a sliding window, so scrolling half a million logical rows stays smooth.

03 · The Key Decision

Pivotal Architectural Choices

Key Architectural Decisions

ADR // 01DETERMINISTIC CHOICE

Wasm-assisted decode vs. main-thread JSON parsing

Context:

Expanding a multi-megabyte gzip into hundreds of thousands of JS objects on the main thread means seconds of GC pauses.

Alternatives Evaluated:
  • Server-side chunking through Apex
  • Main-thread loop with setTimeout slices
  • Web Worker running plain JSON.parse()
Chosen: Small Rust/Wasm decoder into linear memory, with a Pako fallback on the same interface.

Decoding outside the GC heap kept the UI alive during ingest; the fallback keeps the same code path working where Wasm is restricted.

ADR // 02DETERMINISTIC CHOICE

Direct signed-URL streaming vs. relaying through the server

Context:

Anything that touches the CRM heap inherits its limits.

Alternatives Evaluated:
  • Buffering chunks through server objects
  • Proxying the stream through middleware
  • Moving quoting to an external portal
Chosen: Short-lived signed URL; the component fetches bytes straight from object storage.

The payload never enters server memory. The server stays an auth gate, which is all it should be here.

ADR // 03DETERMINISTIC CHOICE

Delta files vs. a provisioned database for edits

Context:

Overrides need durability, but not necessarily a running database.

Alternatives Evaluated:
  • Hosted Postgres cluster with sync API
  • Document store with provisioned throughput
  • Storing edits as CRM rows
Chosen: Immutable base snapshot plus small versioned delta files merged in memory.

For single-editor quoting sessions this removes the entire database tier. Concurrent editing needs explicit merge handling - stated openly below.

ADR // 04DETERMINISTIC CHOICE

Typed-array AST engine vs. Array.filter() per keystroke

Context:

Naive per-keystroke scans across 500K rows lag Noticeably - a few hundred milliseconds per keypress.

Alternatives Evaluated:
  • Plain Array.filter() with string matching
  • IndexedDB queries
  • Embedded SQLite in Wasm
Chosen: Small lexer/parser compiling filters to AST evaluated over typed arrays and inverted indexes.

The same engine family as my json-query-lite work. Millisecond queries without leaving the tab.

04 · The Engineering

What Was Actually Built

01

Built the Wasm decoder from a minimal Rust crate (wasm-bindgen surface, streaming inflate) with an identical Pako JS fallback behind one interface, loaded as a static resource.

02

Kept authorization server-side and narrow: short-expiry signed URLs scoped to one snapshot prefix; no credentials reach the client.

03

Reshaped fixtures to columnar form before compression - shared keys factored out - which is where most of the byte savings actually come from.

04

Reused the json-query-lite query core (lexer, recursive-descent parser, BM25-aware AST) over typed arrays; nested AND/OR/NOT plus LIKE/IN/BETWEEN work the same in Node and in the component.

05

Implemented delta merge as a version-stamped hash join at load: base rows plus override rows reconcile before first paint, in milliseconds on test fixtures.

06

Built the grid as a sliding window of a few dozen rows positioned with transforms, so scroll cost is constant regardless of row count.

05 · The Trade-offs

Deliberate Architectural Compromises

Trade-offs & Mitigations

⚖Browser memory vs. server spend

Architectural Benefit:

No database or compute tier to operate for browsing and editing snapshots.

Associated Cost:

The tab holds the working set - on the order of 150–180MB for the largest fixture I tested.

Mitigation Strategy:

Columnar typed arrays (strings as dictionary IDs, money as integer minor units) plus prompt release of parse buffers.

⚖Wasm capability vs. sandbox reality

Architectural Benefit:

Near-native decode speed outside the GC heap.

Associated Cost:

Sandboxed environments restrict instantiation modes and shared memory.

Mitigation Strategy:

Conservative Wasm feature set and a zero-config JS fallback; the slow path still works.

⚖Delta simplicity vs. concurrent-write safety

Architectural Benefit:

No lock management, no connection pooling, full history for free.

Associated Cost:

Concurrent edits to one quote need optimistic concurrency, not row locks.

Mitigation Strategy:

Version-stamped deltas with hash checks and last-writer-wins plus manual re-merge surfaced in the UI - documented as a limit, not hidden.

06 · The Result

Verified Outcomes

✓ VERIFIED RESULT

500K-row synthetic fixture cold-loads, decodes, and indexes in the high tens of seconds on broadband in my tests - dominated by download, not parsing

✓ VERIFIED RESULT

Snapshot bytes shrink to roughly 2–3% of the row-oriented JSON on test fixtures via columnar layout plus gzip

✓ VERIFIED RESULT

Interactive filtering over the full set returns in milliseconds per keystroke in the harness - no server involved

✓ VERIFIED RESULT

Edits persist as KB-scale delta files with no database process to run or pay for

✓ VERIFIED RESULT

Scrolling cost stays flat: constant DOM size regardless of logical row count

✓ VERIFIED RESULT

Published lineage as open source: json-query-lite (TS) and a Rust/Wasm search sibling, both MIT

07 · Architecture Blueprint

System Schematic & Data Flow

System Topology Blueprint
High-Volume Catalog Engine & Delta Merge
INTERACTIVE SCHEMATIC
STAGE 01 · ASYNC INGESTION, GZIP COMPACTION & AWS S3 NET-ZERO DATA STORAGEConnectbase Salesforce Revenue Cloud (RCA) API500K+ Pricing RowsRaw JSON: ~120MB+Rate-Limited Endpointasync pollMuleSoft / Async WorkerColumnar Schema CompactionGZIP Compression Engine98% Compression: 120MB ? 3.2MBstream .gzAmazon S3 · Net-Zero DB Bucketpricing_base.json.gz500K Base Records3.2MB (98% storage saved)quote_delta.json.gzOverrides Only (~12KB)RDS Database CostDIRECT HTTP/2 STREAM VIA S3 PRE-SIGNED URL (ZERO APEX HEAP OVERHEAD)Salesforce Apex ControllerMints 60s AWS S3 Pre-signed URL (0 KB payload in heap)STAGE 02 · SALESFORCE LIGHTNING WEB COMPONENT (LWC) · WEBASSEMBLY IN-MEMORY RUNTIME01 · BARE-METAL DECOMPRESSIONWebAssembly (Rust / C++) Core• Loaded via LWC Static Resource• Fallback: Pako JS Stream Decoder• Zero V8 Garbage Collection PausesSUB-30s DECOMPRESSION500,000+ records unpacked into linear memory02 · AST QUERY & DSA ENGINEIn-Memory AST Query Engine• Lexer, Parser & AST Bytecode Core• Complex Multi-Field Boolean Filter• Radix / Typed Array Index ScansSUB-MILLISECOND RETRIEVALInstant search & sort over 500K records03 · RUNTIME DELTA MERGE & GRIDDelta Reconciler & 60 FPS Grid• Manual Price / Margin Overrides• Runtime DSA Hash Merge (Base + Delta)• Virtualized Viewport (~40 DOM nodes)NET-ZERO DB MUTATIONOverrides save to S3 delta; 0 DB costMicro Delta Upload (delta.json.gz ~12KB)
SUB-30s COLD LOAD500,000+ records decompressed and indexed inside native LWC in under 30 seconds.
98% STORAGE REDUCTION120MB raw JSON collapsed to 3.2MB gzip stream stored on AWS S3.
NET-ZERO DATABASE COSTrelational database overhead: runtime DSA hash merge of immutable base + S3 delta.
ZERO APEX HEAP IMPACTPre-signed URLs stream binary data straight into browser memory, bypassing 6MB/12MB caps.

Compressed snapshots stream direct to the browser, decode beside the GC heap, and merge with tiny edit deltas at render time.

Text alternative for screen readers: Architecture flow: Catalog API (throttled) to Offline Compactor via Async Ingest & Columnar Gzip; Object Store Snapshot to Component via Signed URL via Direct Streaming, No Server Heap; Wasm Decoder to AST Query over Typed Arrays via Decode Once, Filter in ms; Delta File (KBs) to In-Memory Merge via Edits Without a Database

08 · Why It Matters

Architectural Conclusion

“Platform limits tell you where the default path ends. Compression, direct streaming, and in-memory indexing are often enough to move the ceiling without new infrastructure.”

Key Lessons for Platform Scale:
  • •Measure the heap you actually have before designing the pipeline - it decides everything downstream.
  • •Columnar layout plus gzip beats clever parsing of row-oriented JSON every time.
  • •Delta files are a legitimate database replacement for single-editor snapshots - and an honest limitation for concurrent ones.
  • •Direct-to-browser streaming removes a whole class of middle-tier bottlenecks.