Experiment 019: Hybrid Reader Pool (SendPort + Sacrificial Isolate.exit)

Date: 2026-04-08

Status: Accepted

Commit:e07d95b

Hypothesis

A persistent reader pool eliminates the ~80μs isolate spawn overhead per query.

For small results, SendPort.send (copy) is cheaper than the spawn cost saved.

For large results, the worker sacrifices itself via Isolate.exit (zero-copy)

and the pool auto-respawns a replacement.

Design

Key Insight

Previous experiment 011 rejected the pool because it used List<Map> via SendPort,

which copies string keys per row. Using ResultSet (flat List<Object?> + RowSchema)

dramatically reduces the copy cost — the flat list contains only primitive values

(int/double/String/null), and RowSchema is shared across all rows.

Results

RowsIsolate.exitHybrid PoolPathDelta
10.11ms0.02msSendPort83% faster
100.13ms0.04msSendPort68% faster
500.12ms0.04msSendPort68% faster
1000.12ms0.04msSendPort64% faster
5000.24ms0.17msSacrifice28% faster
10000.40ms0.39msSacrificetied
50001.83ms1.76msSacrificetied

Point query: 45k qps (was 14k) — beats sqlite_reactive's 37k qps.

Why It Works Now (When Experiment 011 Rejected It)

Experiment 011 used List<Map<String, Object?>> for SendPort transfer. Each map

contains N string keys that must be serialized per row. At 5k rows × 6 columns,

that's 30k extra string copies.

This experiment uses ResultSet — a flat List<Object?> with a single RowSchema

containing column names once. SendPort copies the flat list (primitives only) and

the schema (6 strings). The copy cost is proportional to value count, not value

count × key count.

Code Changes