flatsql 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,9 @@ partition logs, one writer per partition on a pinned pool, durable acks,
5
5
  per-commit indexes, type owners, and durable-tail open. Part II (§12–§21) is
6
6
  T2: reader instances and lanes, the snapshot protocol, the SQL surface, the
7
7
  fan-out merge, admission and results. Part III (§23–§30) is T3: compaction,
8
- reclamation, meta-segment retirement, disk accounting and quota. The design is
8
+ reclamation, meta-segment retirement, disk accounting and quota. Part IV
9
+ (§31–) is T3b: store-migrate gseqs, the entry-size setting, hot-partition
10
+ splitting and arrivals compaction. The design is
9
11
  the stack's `docs/architecture/flatsql-partition-store.md` (§22 overrides
10
12
  §0–§21; §22.4 holds the owner rulings). This file records what was built, how
11
13
  to run it, the measured acceptance, and every place the build departs from the
@@ -978,3 +980,101 @@ checkout at the landed commit; the test enforces the 1.5× bounds on such a box:
978
980
  ```
979
981
  cpp/build/flatsql_ps_test --test=compaction_half_dead_T3_1_full --dir=<ext4 dir>
980
982
  ```
983
+
984
+ # Part IV: T3b
985
+
986
+ ## 31. store-migrate gseqs, the entry-size setting, tag conditions
987
+
988
+ store-migrate (T6, design §16.1-5) gives every migrated FIRST copy gseq = its
989
+ legacy `sdn_record_index.rowid`, so per-schema MaxRowID and the FTS rowids
990
+ carry over.
991
+
992
+ - **Input.** `RecordAttr.migrated_gseq` (`ulong`, field 6, appended to
993
+ `schemas/flatsql_attr.fbs`; absent = 0, so older attributes are
994
+ unchanged byte for byte). `buildRecordAttr(..., migratedGseq)` writes it.
995
+ - **Row.** A new PUT whose attribute carries one gets row flag
996
+ `MIGRATED_GSEQ` (0x80). The gseq stays in the attribute (in the same meta
997
+ batch, `ATTR_IN_M`): the RecRow has no spare u64 (offsets 108–127 hold
998
+ `supersedeHash`, `tagHash` and `aux`), and the attribute is as durable as
999
+ the row. Deviation from the T6 proposal "(b) the RecRow reserved u64 at
1000
+ offset 116", which T1's layout had already given to `tagHash`.
1001
+ - **Labeling.** The type owner reads the flagged rows' attributes in windows
1002
+ over the chunk it labels (one read per chunk of up to 4,096 rows when the
1003
+ attributes fit in 1 MiB). A flagged copy labeled FIRST keeps its gseq when it
1004
+ is above the type's committed `gseq_hi` and no arrival of the same type
1005
+ commit holds it; the engine's gseq counter is then raised to at least gseq
1006
+ + 1. Otherwise it gets an allocated gseq and counts a fallback; a flagged
1007
+ copy labeled REPEAT counts a fallback too. A type commit's new arrivals are
1008
+ sorted by gseq before they are appended, so arrivals stay strictly
1009
+ increasing whatever order partitions are labeled in.
1010
+ - **Counters.** `EngineStats::migratedGseqs` and `migratedGseqFallbacks`,
1011
+ appended to `flatsql_ps_stats` (entries 24 and 25). store-migrate's
1012
+ verification (§16.1-7) needs fallbacks = 0; they are counted per labeling
1013
+ decision, so a type commit that fails and is relabeled counts again.
1014
+ - **Crash.** The gseq is read from durable rows and attributes, so a crash
1015
+ that loses the type commit relabels the same copies with the same gseqs.
1016
+ An allocated fallback gseq that was never published can be reassigned
1017
+ (as for every allocated gseq).
1018
+ - **What T6 must do** (its (d)): feed each type's FIRST copies in rowid
1019
+ order and wait for `labeled_through` before crossing partitions, and run
1020
+ with fallbacks = 0. gseqs are unique per type; across types a fallback could
1021
+ reuse a legacy rowid another type migrates later, which is one more reason
1022
+ a fallback fails the migration.
1023
+ - **Entry size.** Writer TLV 19 = `maxEntryBytes` (u64; default 1 MiB +
1024
+ 4 KiB, clamped to [64 KiB, 1 GiB]): the largest ring entry (header,
1025
+ RecordAttr and frame). Each writer's arena grows to at least 4 ×
1026
+ (maxEntryBytes + 4 KiB), so the frames half holds two entries. Frames
1027
+ larger than a segment (A27 jumbo segments) are still not built.
1028
+
1029
+ Tests: `type_migrated_gseq_preserved_fallback_and_stats` (kept, fallback
1030
+ below `gseq_hi`, REPEAT copy, ordinary allocation continues above, flags on
1031
+ disk, reopen), `type_migrated_gseq_one_commit_sorted_and_crash_reopen` (two
1032
+ partitions' interleaved gseqs in one type commit come out sorted; a crash
1033
+ before and after the type commit gives the same arrivals),
1034
+ `writer_max_entry_bytes_configurable` (a 9 MiB frame refused by default,
1035
+ stored and read back at 10 MiB), `writer_capi_tlv19_max_entry_and_stats`
1036
+ (tag 19 through `flatsql_ps_init`, the 26-entry stats array).
1037
+
1038
+ ### 31.1 Tag conditions match any live tag instance (A2)
1039
+
1040
+ `_provider`, `_batch`, `_peer_id`, `_source` and `_source_name` conditions
1041
+ have the legacy tag table's ANY-row semantics (`indexedRecordWindowSQL`: a
1042
+ CID matches when one of its tag rows satisfies every requested condition).
1043
+ In format 2 a record's tag instances are its PUT's own tag and its RETAG rows
1044
+ (A2). As T2 built it, the vtab scanned tag postings (which name every
1045
+ instance, RETAGs included) but left each constraint for SQLite to re-check
1046
+ against the projected column, which shows only the PUT's own tag, so every
1047
+ record matched through a RETAG was dropped (T6 #7 on a migrated copy: a
1048
+ provider window of 200 read as 100; a source + batch window lost the
1049
+ re-tagged records).
1050
+
1051
+ Now `xBestIndex` consumes every tag condition (`omit = 1`) and the vtab
1052
+ evaluates them all on one instance:
1053
+
1054
+ - A tag or source posting scan (the plan's key) checks the remaining
1055
+ conditions on the instance the posting names (its RecordAttr), then emits
1056
+ the instance's record. A single condition that is the scanned key needs no
1057
+ attribute read unless the key was capped (over 512 bytes).
1058
+ - Any other plan (CID, column index, epoch, arrivals order, full scan)
1059
+ keeps a record when one of its live tag instances matches every condition
1060
+ (`RowFilter::hasLiveTag`, the TAG_OF postings), as the alias table's source
1061
+ filter already did.
1062
+ - An untagged record matches no tag condition (as the index-driven plans
1063
+ already behaved, T2 deviation 8), and conditions that contradict each other
1064
+ (two sources) return no rows.
1065
+ - `_source = '<TYPE>@<name>'` is compared as the column projects it: the type
1066
+ prefix is case-sensitive (a lower-case prefix returned no rows before
1067
+ either, after SQLite's re-check).
1068
+ - `_peer_id` conditions match the tag's producer peer (the TAG_PEER postings
1069
+ the T2 plan scanned); the `_peer_id` column still projects the storing
1070
+ call's `RecordAttr.peer_id`. Before, SQLite compared that BLOB column with
1071
+ the argument, so a text argument never matched.
1072
+ - `<TYPE>_current` groups first and filters after, as SQL says: its tag
1073
+ conditions stay SQLite's (the current version's own tag).
1074
+ - Projected columns are unchanged: they show the PUT's own tag.
1075
+
1076
+ Test: `lane_tag_conditions_match_any_instance_A2` (two partitions, PUT tags,
1077
+ RETAGs and REPEAT copies; eleven condition sets at type and partition level
1078
+ against an ANY-instance oracle, the full `_source` form, the alias, an
1079
+ epoch-ordered window, column and CID plans). With `omit = 0` restored it
1080
+ fails 18 checks.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flatsql",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "SQL queries over raw FlatBuffer storage: the SQLite-backed wasm engine and the multithreaded partition store (flatsql-ps-threads.wasm)",
5
5
  "type": "module",
6
6
  "main": "wasm/index.js",
Binary file
@@ -5,9 +5,9 @@
5
5
  "size": 2139350,
6
6
  "files": {
7
7
  "flatsql-ps-threads.wasm": {
8
- "sha256": "075dd0a104694df39b2776c34dc6444be224442717967d9cea5dd9af15964379",
9
- "sri": "sha384-6COvEIcsSPYQ2gaha+/n5bdRTDysR84pfSw92d4F+Sxss3kCxYj2XNkPIfA9zEhC",
10
- "size": 2237656
8
+ "sha256": "a90d9488187e527bccd3f02ce3f68be6443690bc1a2ee6c6786c64c907376e36",
9
+ "sri": "sha384-zzvS8qbCe33AJCklxaZX4xr1+QajJAaz0G0FuBv9WyEXh2rSKAz9+Rq4o2XksFaF",
10
+ "size": 2261085
11
11
  },
12
12
  "flatsql-wasi-noeh.wasm": {
13
13
  "sha256": "fd744d4c502310669bc74ab555a36de1692030a0fa6e22fdb1df744809eeacbe",