From e409a30a4085aed9bec4a542f907ad6f036dfe38 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Tue, 26 May 2026 19:39:16 -0400 Subject: [PATCH] =?UTF-8?q?revert=20#000067=20phase-2=20page=5Fsize=3D1638?= =?UTF-8?q?4=20=E2=80=94=20measured=20worse=20than=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3090 bench-max v3 run at 13:25 elapsed had shards at 696-760 MB; v2 at 8:46 elapsed (default 4 KB pages) had 549-662 MB. Larger pages made the chunk-content UPDATE workload SLOWER, not faster. Reason: chunk-content payloads average ~500 bytes after zstd. With 4 KB pages each UPDATE rewrites a 4 KB page; with 16 KB pages the same UPDATE rewrites a 16 KB page → 4× write amplification per row. ext4 journal traffic increases roughly proportionally. Bigger pages help SCAN-heavy workloads (fewer page reads to walk a B-tree). For narrow-row-UPDATE-heavy workloads like cold-pack chunk fill, they hurt. Comment in code captures the measurement so a future operator doesn't re-attempt this. The other bench-max measures stay in place: - synchronous = OFF (per-write fsync removed) - journal_mode = MEMORY (WAL in RAM) - temp_store = MEMORY (sort scratch in RAM) - cache_size = -524288 (512 MB page cache) - mmap_size = 536870912 (512 MB read mmap) - foreign_keys = OFF (no FK verify per row) - deferred FTS5 rebuild post-pass - batched 5000-row executemany UPDATEs - bench-max #5 (this commit's revert): page_size stays at 4 KB Next bench point: v4 against the new bucket packs that include fts (--include-fts producer run in flight). That eliminates the FTS rebuild step entirely on the consumer. --- arborist/cli.py | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/arborist/cli.py b/arborist/cli.py index 13a36b9..5041478 100644 --- a/arborist/cli.py +++ b/arborist/cli.py @@ -2995,29 +2995,16 @@ def _cmd_cold_unpack(args: argparse.Namespace) -> int: # directory; --hydrate-M picks the routing modulus. Each incoming # row lands on shard_for_document(root, M). if args.hydrate_shards_dir: - import sqlite3 target_dir = Path(args.hydrate_shards_dir).expanduser() target_dir.mkdir(parents=True, exist_ok=True) M = args.hydrate_M target_paths = [target_dir / f"{i:03d}.db" for i in range(M)] - - # #000067 bench-max: bump page_size before schema applies so the - # corpus uses 16 KB pages instead of SQLite's 4 KB default. - # Bigger pages → ~4× fewer page writes during chunk-fill → - # ~4× fewer ext4 metadata journal events (jbd2_log_wait_commit - # was the measured bottleneck on the 3090). page_size CAN ONLY - # be set on an empty DB; VACUUM finalises it. Idempotent on - # re-runs since fresh hydrate dirs are clean. - for p in target_paths: - if not p.exists() or p.stat().st_size == 0: - pre = sqlite3.connect(str(p)) - try: - pre.execute("PRAGMA page_size = 16384") - pre.execute("PRAGMA secure_delete = OFF") - pre.execute("VACUUM") # commit the page_size change - finally: - pre.close() - + # NOTE: I tried PRAGMA page_size = 16384 on hydrate targets + # to reduce ext4-journal contention by emitting fewer + # page-write events. Measured WORSE on 2026-05-26 — narrow + # UPDATEs (~500 byte content per row) rewrite the whole + # page; 16 KB pages = 4× write amplification per chunk- + # content UPDATE. Stay at SQLite default 4 KB. targets: list = [] for p in target_paths: t = connect(p)