| … | … | @@ -3543,6 +3543,63 @@ injected per layer: |
| 3543 | 3543 | from that destroy). |
| 3544 | 3544 | - **Next: R4b** (rn/: rocksdb-native's JS layer over librocksdb's async mode, completions |
| 3545 | 3545 | inline for now, the drain scaffold for unconverted callers). |
| 3546 | - **R4b DESIGN (2026-10-06; approved by the user 2026-10-06).** Read in full first: rocksdb-native |
| 3547 | 3.17.4 (index.js, lib/*, binding.cc), ready-resource 1.2.0, refcounter 1.0.0, |
| 3548 | resolve-reject-promise 1.1.0, signal-promise 1.0.3, librocksdb 1a00e82's rocksdb.h. |
| 3549 | - **Measured on Bare 1.28 (scratch probe tp1.js):** overlapping read batches complete out of |
| 3550 | submission order 277 / 302 times in 1000 adjacent pairs with the default pool, and 0 / 2000 |
| 3551 | with `UV_THREADPOOL_SIZE=1`. Bare honours the variable; one pool thread is FIFO by |
| 3552 | construction. That is the gate's JS reference, unpatched. |
| 3553 | - **What gets ported (all of it, the 100 % rule):** |
| 3554 | - `rn::RocksDB` (index.js): sessions (`session` / `columnFamily` / `snapshot`, the index-swap |
| 3555 | session list), `ready`, `close({ force })`, `suspend` / `resume`, `isIdle` / `idle`, |
| 3556 | `iterator`, `keys` (an async generator: an iterator object over `iterator`), `peek`, |
| 3557 | `read` / `write` (batches), `flush`, `get` / `put` / `delete` / `deleteRange` (one-op |
| 3558 | batches, their exact `async` shapes), `compact`, `compactRange`, `approximateSize`, |
| 3559 | `currentWalFile`, `get/setStatsLevel`, `getProperty`, `diagnostics`, `stats`, |
| 3560 | `maybeClosed`, key / value encodings (a codec hook: encode / decode over bytes, as M5g-4a). |
| 3561 | - `rn::State` (lib/state.js) on literal ports of ready-resource (open / close, `opening` / |
| 3562 | `closing`, 'ready' / 'close'), refcounter (`io`, `handles`, `idle()`), signal-promise |
| 3563 | and resolve-reject-promise: column-family upsert / lookup, `_open` (the |
| 3564 | `await Promise.resolve()`, the lock's `ready` / `transfer` / `close`, deferred snapshot |
| 3565 | init), `_close` (updating wait, io / handles idle, sessions, CF destroy), suspend / |
| 3566 | resume / `update` / `waitForResume`, flush, compact, compactRange, approximateSize, |
| 3567 | currentWalFile, the batch reuse pools (capacity ≤ 16, at most 64). |
| 3568 | - Batches (lib/batch.js): ReadBatch / WriteBatch, `_reuse`, `ready`, `flush` / `tryFlush` |
| 3569 | (the one-hop submit), `_onread` / `_onwrite` (per-op resolution in index order, then the |
| 3570 | flush promise; AggregateError / `Batch was not applied`), `_abort`, `destroy`, `try*`, |
| 3571 | `_resize`, stats. |
| 3572 | - Iterator (lib/iterator.js): a streamx Readable in object mode, capacity 8, the |
| 3573 | `_open` / `_read` / `_destroy` shapes, `limit`, keys-only, ranges. |
| 3574 | - Snapshot, ColumnFamily (+ `cloneSettings`), Bloom / Ribbon filter policies, constants. |
| 3575 | - binding.cc's glue: librocksdb options and CF descriptors, the lock fd hand-over (closed if |
| 3576 | open fails to submit), errors as `Error(message)` with `code = uv_err_name(status)`, a |
| 3577 | missing key = null vs an empty value = a zero-length buffer, the idle callback that |
| 3578 | destroys the remaining CFs and snapshots on close. Process-exit teardown (`exiting`) has |
| 3579 | no keetd equivalent and is not ported. |
| 3580 | - **streamx, generalized:** the Iterator is an object-mode Readable, and streamx counts an |
| 3581 | entry as `byteLength(entry)`: its length for a typed array, else 1024 (`defaultByteLength`), |
| 3582 | after `map` (applied at push). That decides when `_read`, an iterator refill, runs. |
| 3583 | `ss::Duplex` becomes `ss::BasicDuplex<T>` with streamx's `byteLength` and `map` options |
| 3584 | (`Duplex = BasicDuplex<Buf>`, its API unchanged); `ss::AsyncIterator` likewise. The duplex, |
| 3585 | secret-stream and interop gates must stay identical; the duplex gate gains object-mode |
| 3586 | scripts. (hypercore-storage sets `_readableState.map` on every stream: R4c needs `map`.) |
| 3587 | - **Completions in R4b, both runtimes, as the R4 migration says:** librocksdb runs in SYNC |
| 3588 | mode on the loop thread at the moment JS calls the binding (the work is done at submit); |
| 3589 | its completion is PARKED in a new rt completion FIFO and delivered in the next poll phase, |
| 3590 | one native callback each (`rt::callback`: its own checkpoint), as `uv__work_done` delivers |
| 3591 | one JS call per completed work item. Real: a `uv_async_t`; Virtual: the poll phase; |
| 3592 | `alive` counts parked completions. R4i swaps Real to librocksdb's async mode on |
| 3593 | `rt::uvLoop()` (the real pool) and gives Virtual its scripted order. |
| 3594 | - **Gate `tools/rn`:** op scripts (batches, one-op calls, iterators, sessions / snapshots, |
| 3595 | suspend / resume, close, errors) run on Bare's rocksdb-native at `UV_THREADPOOL_SIZE=1` and |
| 3596 | on rn over rt real AND virtual. Each trace logs every per-op settlement, batch flush, iterator |
| 3597 | entry and event, beside a microtask ruler (hops); then the stores are dumped and compared. |
| 3598 | Scripts never order a storage completion against a timer or an immediate (pool timing). |
| 3599 | Then a drill. |
| 3600 | - **Order:** R4b-1 streamx generalized (gates unchanged + object mode) → R4b-2 rt completion |
| 3601 | FIFO + State / batches / sessions / snapshots / CFs → R4b-3 Iterator, `keys`, `peek` → |
| 3602 | R4b-4 gate + drill. L2 keeps its synchronous Db until R4c. |
| 3546 | 3603 | - **Then 4e-4** (`createProtocolStream` / `replicate`): the first gate that composes the stack |
| 3547 | 3604 | is a COMPOSED trace against JS (secret-stream + protomux + replicator), now meaningful. |
| 3548 | 3605 | |