@lix-js/sdk 0.6.0-preview.3 → 0.6.0-preview.5

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.
Files changed (235) hide show
  1. package/README.md +1 -1
  2. package/SKILL.md +105 -65
  3. package/dist/engine-wasm/index.js +4 -4
  4. package/dist/engine-wasm/wasm/lix_engine.d.ts +30 -6
  5. package/dist/engine-wasm/wasm/lix_engine.js +187 -117
  6. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  7. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +14 -8
  8. package/dist/generated/builtin-schemas.d.ts +69 -69
  9. package/dist/generated/builtin-schemas.js +94 -94
  10. package/dist/open-lix.d.ts +42 -28
  11. package/dist/open-lix.js +49 -10
  12. package/dist/sqlite/index.js +86 -30
  13. package/dist-engine-src/README.md +3 -3
  14. package/dist-engine-src/src/backend/capabilities.rs +67 -0
  15. package/dist-engine-src/src/backend/conformance/baseline.rs +1127 -0
  16. package/dist-engine-src/src/backend/conformance/factory.rs +93 -0
  17. package/dist-engine-src/src/backend/conformance/failure_tests.rs +608 -0
  18. package/dist-engine-src/src/backend/conformance/fixtures.rs +26 -0
  19. package/dist-engine-src/src/backend/conformance/mod.rs +75 -0
  20. package/dist-engine-src/src/backend/conformance/model.rs +28 -0
  21. package/dist-engine-src/src/backend/conformance/model_based.rs +257 -0
  22. package/dist-engine-src/src/backend/conformance/persistence.rs +204 -0
  23. package/dist-engine-src/src/backend/conformance/projection.rs +21 -0
  24. package/dist-engine-src/src/backend/conformance/pushdown.rs +24 -0
  25. package/dist-engine-src/src/backend/conformance/runner.rs +90 -0
  26. package/dist-engine-src/src/backend/conformance/scan.rs +24 -0
  27. package/dist-engine-src/src/backend/conformance/write.rs +16 -0
  28. package/dist-engine-src/src/backend/error.rs +94 -0
  29. package/dist-engine-src/src/backend/in_memory.rs +670 -0
  30. package/dist-engine-src/src/backend/mod.rs +36 -9
  31. package/dist-engine-src/src/backend/predicate.rs +80 -0
  32. package/dist-engine-src/src/backend/traits.rs +260 -0
  33. package/dist-engine-src/src/backend/types.rs +224 -81
  34. package/dist-engine-src/src/binary_cas/context.rs +8 -8
  35. package/dist-engine-src/src/binary_cas/kv.rs +234 -259
  36. package/dist-engine-src/src/{version → branch}/context.rs +12 -12
  37. package/dist-engine-src/src/branch/lifecycle.rs +221 -0
  38. package/dist-engine-src/src/branch/mod.rs +13 -0
  39. package/dist-engine-src/src/branch/refs.rs +321 -0
  40. package/dist-engine-src/src/branch/stage_rows.rs +67 -0
  41. package/dist-engine-src/src/branch/types.rs +21 -0
  42. package/dist-engine-src/src/catalog/context.rs +18 -18
  43. package/dist-engine-src/src/catalog/snapshot.rs +8 -8
  44. package/dist-engine-src/src/changelog/bench_support.rs +785 -0
  45. package/dist-engine-src/src/changelog/change.rs +1 -0
  46. package/dist-engine-src/src/changelog/codec.rs +497 -0
  47. package/dist-engine-src/src/changelog/commit.rs +1 -0
  48. package/dist-engine-src/src/changelog/context.rs +1614 -0
  49. package/dist-engine-src/src/changelog/mod.rs +29 -0
  50. package/dist-engine-src/src/changelog/store.rs +163 -0
  51. package/dist-engine-src/src/changelog/test_support.rs +54 -0
  52. package/dist-engine-src/src/changelog/types.rs +213 -0
  53. package/dist-engine-src/src/commit_graph/context.rs +317 -274
  54. package/dist-engine-src/src/commit_graph/mod.rs +2 -4
  55. package/dist-engine-src/src/commit_graph/types.rs +22 -42
  56. package/dist-engine-src/src/commit_graph/walker.rs +133 -103
  57. package/dist-engine-src/src/common/error.rs +52 -18
  58. package/dist-engine-src/src/common/identity.rs +2 -2
  59. package/dist-engine-src/src/common/mod.rs +1 -1
  60. package/dist-engine-src/src/domain.rs +42 -46
  61. package/dist-engine-src/src/engine.rs +74 -96
  62. package/dist-engine-src/src/{entity_identity.rs → entity_pk.rs} +89 -92
  63. package/dist-engine-src/src/functions/context.rs +56 -52
  64. package/dist-engine-src/src/functions/state.rs +51 -52
  65. package/dist-engine-src/src/init.rs +288 -154
  66. package/dist-engine-src/src/json_store/context.rs +15 -266
  67. package/dist-engine-src/src/json_store/mod.rs +26 -0
  68. package/dist-engine-src/src/json_store/store.rs +103 -718
  69. package/dist-engine-src/src/json_store/types.rs +4 -9
  70. package/dist-engine-src/src/lib.rs +49 -19
  71. package/dist-engine-src/src/live_state/context.rs +654 -790
  72. package/dist-engine-src/src/live_state/mod.rs +9 -3
  73. package/dist-engine-src/src/live_state/overlay.rs +4 -4
  74. package/dist-engine-src/src/live_state/types.rs +30 -21
  75. package/dist-engine-src/src/live_state/visibility.rs +514 -71
  76. package/dist-engine-src/src/plugin/install.rs +48 -48
  77. package/dist-engine-src/src/plugin/manifest.rs +7 -7
  78. package/dist-engine-src/src/plugin/materializer.rs +0 -275
  79. package/dist-engine-src/src/plugin/plugin_manifest.json +4 -3
  80. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +2 -2
  81. package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +34 -0
  82. package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +48 -0
  83. package/dist-engine-src/src/schema/builtin/lix_change.json +3 -3
  84. package/dist-engine-src/src/schema/builtin/lix_commit.json +1 -1
  85. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +6 -6
  86. package/dist-engine-src/src/schema/builtin/mod.rs +18 -20
  87. package/dist-engine-src/src/schema/compatibility.rs +11 -11
  88. package/dist-engine-src/src/schema/definition.json +2 -2
  89. package/dist-engine-src/src/schema/definition.rs +5 -5
  90. package/dist-engine-src/src/schema/key.rs +3 -3
  91. package/dist-engine-src/src/schema/mod.rs +1 -1
  92. package/dist-engine-src/src/schema/tests.rs +18 -18
  93. package/dist-engine-src/src/session/context.rs +819 -124
  94. package/dist-engine-src/src/session/create_branch.rs +94 -0
  95. package/dist-engine-src/src/session/execute.rs +260 -57
  96. package/dist-engine-src/src/session/merge/analysis.rs +9 -3
  97. package/dist-engine-src/src/session/merge/{version.rs → branch.rs} +119 -129
  98. package/dist-engine-src/src/session/merge/conflicts.rs +2 -2
  99. package/dist-engine-src/src/session/merge/mod.rs +5 -6
  100. package/dist-engine-src/src/session/merge/stats.rs +7 -11
  101. package/dist-engine-src/src/session/mod.rs +19 -16
  102. package/dist-engine-src/src/session/switch_branch.rs +113 -0
  103. package/dist-engine-src/src/session/transaction.rs +557 -0
  104. package/dist-engine-src/src/sql2/bind/classify.rs +102 -0
  105. package/dist-engine-src/src/sql2/bind/error.rs +5 -0
  106. package/dist-engine-src/src/sql2/bind/expr.rs +29 -0
  107. package/dist-engine-src/src/sql2/bind/mod.rs +12 -0
  108. package/dist-engine-src/src/sql2/{udfs/public_call.rs → bind/public_udf.rs} +98 -3
  109. package/dist-engine-src/src/sql2/bind/read.rs +65 -0
  110. package/dist-engine-src/src/sql2/bind/statement.rs +2236 -0
  111. package/dist-engine-src/src/sql2/bind/table.rs +273 -0
  112. package/dist-engine-src/src/sql2/bind/write.rs +86 -0
  113. package/dist-engine-src/src/sql2/branch_scope.rs +436 -0
  114. package/dist-engine-src/src/sql2/catalog/capability.rs +20 -0
  115. package/dist-engine-src/src/sql2/catalog/entity_surface.rs +296 -0
  116. package/dist-engine-src/src/sql2/catalog/mod.rs +15 -0
  117. package/dist-engine-src/src/sql2/catalog/registry.rs +556 -0
  118. package/dist-engine-src/src/sql2/catalog/schema.rs +88 -0
  119. package/dist-engine-src/src/sql2/catalog/surface.rs +41 -0
  120. package/dist-engine-src/src/sql2/change_materialization.rs +122 -0
  121. package/dist-engine-src/src/sql2/context.rs +36 -30
  122. package/dist-engine-src/src/sql2/error.rs +4 -5
  123. package/dist-engine-src/src/sql2/exec/bound_public_write.rs +1593 -0
  124. package/dist-engine-src/src/sql2/exec/datafusion.rs +5266 -0
  125. package/dist-engine-src/src/sql2/exec/fast_write.rs +82 -0
  126. package/dist-engine-src/src/sql2/exec/mod.rs +24 -0
  127. package/dist-engine-src/src/sql2/exec/write.rs +661 -0
  128. package/dist-engine-src/src/sql2/filesystem_planner.rs +72 -77
  129. package/dist-engine-src/src/sql2/filesystem_visibility.rs +21 -21
  130. package/dist-engine-src/src/sql2/history_projection.rs +8 -8
  131. package/dist-engine-src/src/sql2/history_route.rs +35 -31
  132. package/dist-engine-src/src/sql2/mod.rs +30 -24
  133. package/dist-engine-src/src/sql2/optimize/datafusion.rs +1 -0
  134. package/dist-engine-src/src/sql2/optimize/mod.rs +2 -0
  135. package/dist-engine-src/src/sql2/optimize/simple_write.rs +116 -0
  136. package/dist-engine-src/src/sql2/parse/mod.rs +69 -0
  137. package/dist-engine-src/src/sql2/parse/normalize.rs +1 -0
  138. package/dist-engine-src/src/sql2/plan/branch_scope.rs +24 -0
  139. package/dist-engine-src/src/sql2/plan/mod.rs +5 -0
  140. package/dist-engine-src/src/sql2/plan/predicate.rs +22 -0
  141. package/dist-engine-src/src/sql2/plan/write.rs +147 -0
  142. package/dist-engine-src/src/sql2/predicate_typecheck.rs +258 -0
  143. package/dist-engine-src/src/sql2/{version_provider.rs → providers/branch.rs} +218 -214
  144. package/dist-engine-src/src/sql2/{change_provider.rs → providers/change.rs} +156 -42
  145. package/dist-engine-src/src/sql2/{directory_provider.rs → providers/directory.rs} +291 -322
  146. package/dist-engine-src/src/sql2/{directory_history_provider.rs → providers/directory_history.rs} +56 -42
  147. package/dist-engine-src/src/sql2/providers/entity.rs +1484 -0
  148. package/dist-engine-src/src/sql2/{entity_history_provider.rs → providers/entity_history.rs} +43 -31
  149. package/dist-engine-src/src/sql2/{file_provider.rs → providers/file.rs} +323 -316
  150. package/dist-engine-src/src/sql2/{file_history_provider.rs → providers/file_history.rs} +60 -46
  151. package/dist-engine-src/src/sql2/{history_provider.rs → providers/history.rs} +46 -32
  152. package/dist-engine-src/src/sql2/{lix_state_provider.rs → providers/lix_state.rs} +359 -329
  153. package/dist-engine-src/src/sql2/providers/mod.rs +508 -0
  154. package/dist-engine-src/src/sql2/read_only.rs +2 -2
  155. package/dist-engine-src/src/sql2/session.rs +47 -96
  156. package/dist-engine-src/src/sql2/storage/constraints.rs +1 -0
  157. package/dist-engine-src/src/sql2/storage/mod.rs +1 -0
  158. package/dist-engine-src/src/sql2/test_support/differential.rs +712 -0
  159. package/dist-engine-src/src/sql2/test_support/generators.rs +354 -0
  160. package/dist-engine-src/src/sql2/test_support/mod.rs +2 -0
  161. package/dist-engine-src/src/sql2/udfs/{lix_active_version_commit_id.rs → lix_active_branch_commit_id.rs} +7 -7
  162. package/dist-engine-src/src/sql2/udfs/mod.rs +3 -6
  163. package/dist-engine-src/src/sql2/write_normalization.rs +45 -22
  164. package/dist-engine-src/src/storage/conformance.rs +399 -0
  165. package/dist-engine-src/src/storage/context.rs +552 -288
  166. package/dist-engine-src/src/storage/mod.rs +48 -10
  167. package/dist-engine-src/src/storage/point.rs +440 -0
  168. package/dist-engine-src/src/storage/read_scope.rs +43 -64
  169. package/dist-engine-src/src/storage/reader.rs +867 -0
  170. package/dist-engine-src/src/storage/scan.rs +784 -0
  171. package/dist-engine-src/src/storage/spaces.rs +236 -0
  172. package/dist-engine-src/src/storage/stats.rs +80 -0
  173. package/dist-engine-src/src/storage/write_set.rs +962 -0
  174. package/dist-engine-src/src/storage_bench.rs +136 -4828
  175. package/dist-engine-src/src/test_support.rs +360 -138
  176. package/dist-engine-src/src/tracked_state/bench_support.rs +394 -0
  177. package/dist-engine-src/src/tracked_state/codec.rs +155 -1057
  178. package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +358 -0
  179. package/dist-engine-src/src/tracked_state/context.rs +1927 -993
  180. package/dist-engine-src/src/tracked_state/diff.rs +1715 -261
  181. package/dist-engine-src/src/tracked_state/merge.rs +74 -88
  182. package/dist-engine-src/src/tracked_state/mod.rs +19 -16
  183. package/dist-engine-src/src/tracked_state/{materialization.rs → row_materialization.rs} +50 -178
  184. package/dist-engine-src/src/tracked_state/storage.rs +243 -191
  185. package/dist-engine-src/src/tracked_state/tree.rs +247 -371
  186. package/dist-engine-src/src/tracked_state/types.rs +49 -42
  187. package/dist-engine-src/src/transaction/bench_support.rs +407 -0
  188. package/dist-engine-src/src/transaction/commit.rs +821 -713
  189. package/dist-engine-src/src/transaction/context.rs +705 -600
  190. package/dist-engine-src/src/transaction/mod.rs +13 -2
  191. package/dist-engine-src/src/transaction/normalization.rs +63 -76
  192. package/dist-engine-src/src/transaction/prep.rs +13 -13
  193. package/dist-engine-src/src/transaction/schema_resolver.rs +19 -5
  194. package/dist-engine-src/src/transaction/staging.rs +228 -434
  195. package/dist-engine-src/src/transaction/types.rs +41 -98
  196. package/dist-engine-src/src/transaction/validation.rs +382 -446
  197. package/dist-engine-src/src/untracked_state/codec.rs +337 -29
  198. package/dist-engine-src/src/untracked_state/context.rs +7 -7
  199. package/dist-engine-src/src/untracked_state/materialization.rs +2 -2
  200. package/dist-engine-src/src/untracked_state/mod.rs +1 -1
  201. package/dist-engine-src/src/untracked_state/storage.rs +659 -157
  202. package/dist-engine-src/src/untracked_state/types.rs +21 -21
  203. package/package.json +71 -68
  204. package/dist-engine-src/src/backend/kv.rs +0 -358
  205. package/dist-engine-src/src/backend/testing.rs +0 -658
  206. package/dist-engine-src/src/commit_store/codec.rs +0 -887
  207. package/dist-engine-src/src/commit_store/context.rs +0 -944
  208. package/dist-engine-src/src/commit_store/materialization.rs +0 -84
  209. package/dist-engine-src/src/commit_store/mod.rs +0 -16
  210. package/dist-engine-src/src/commit_store/storage.rs +0 -600
  211. package/dist-engine-src/src/commit_store/types.rs +0 -215
  212. package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -34
  213. package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -48
  214. package/dist-engine-src/src/session/create_version.rs +0 -88
  215. package/dist-engine-src/src/session/merge/apply.rs +0 -23
  216. package/dist-engine-src/src/session/optimization9_sql2_bench.rs +0 -100
  217. package/dist-engine-src/src/session/switch_version.rs +0 -109
  218. package/dist-engine-src/src/sql2/classify.rs +0 -182
  219. package/dist-engine-src/src/sql2/entity_provider.rs +0 -3211
  220. package/dist-engine-src/src/sql2/execute.rs +0 -3440
  221. package/dist-engine-src/src/sql2/public_bind/assignment.rs +0 -46
  222. package/dist-engine-src/src/sql2/public_bind/capability.rs +0 -41
  223. package/dist-engine-src/src/sql2/public_bind/dml.rs +0 -166
  224. package/dist-engine-src/src/sql2/public_bind/mod.rs +0 -25
  225. package/dist-engine-src/src/sql2/public_bind/table.rs +0 -168
  226. package/dist-engine-src/src/sql2/version_scope.rs +0 -394
  227. package/dist-engine-src/src/storage/types.rs +0 -501
  228. package/dist-engine-src/src/tracked_state/by_file_index.rs +0 -98
  229. package/dist-engine-src/src/tracked_state/materializer.rs +0 -488
  230. package/dist-engine-src/src/transaction/live_state_overlay.rs +0 -35
  231. package/dist-engine-src/src/version/lifecycle.rs +0 -221
  232. package/dist-engine-src/src/version/mod.rs +0 -13
  233. package/dist-engine-src/src/version/refs.rs +0 -330
  234. package/dist-engine-src/src/version/stage_rows.rs +0 -67
  235. package/dist-engine-src/src/version/types.rs +0 -21
@@ -0,0 +1,785 @@
1
+ //! Feature-gated changelog benchmark support for the direct v3 layout.
2
+ //!
3
+ //! The fixtures build direct commit/change/change-ref append batches.
4
+
5
+ use super::context::ChangelogContext;
6
+ use super::store::{ChangelogReader, ChangelogWriter};
7
+ use super::types::{
8
+ ChangeLoadRequest, ChangeRecord, ChangelogAppend, CommitChangeRef, CommitChangeRefSet,
9
+ CommitLoadRequest, CommitProjection, CommitRecord, GcPlan, GcRoot, RebuildIndexStats,
10
+ };
11
+ use crate::entity_pk::EntityPk;
12
+ use crate::json_store::JsonRef;
13
+ use crate::storage::{
14
+ StorageBackend, StorageBackendReadOf, StorageContext, StorageReadOptions, StorageWriteSetStats,
15
+ };
16
+ use crate::LixError;
17
+
18
+ pub trait BenchBackend: StorageBackend + Clone
19
+ where
20
+ for<'a> StorageBackendReadOf<'a, Self>: Send,
21
+ {
22
+ }
23
+
24
+ impl<T> BenchBackend for T
25
+ where
26
+ T: StorageBackend + Clone,
27
+ for<'a> StorageBackendReadOf<'a, T>: Send,
28
+ {
29
+ }
30
+
31
+ #[derive(Clone)]
32
+ pub struct BenchAppend {
33
+ append: ChangelogAppend,
34
+ }
35
+
36
+ impl BenchAppend {
37
+ pub fn commit_ids(&self) -> Vec<String> {
38
+ self.append
39
+ .commits
40
+ .iter()
41
+ .map(|commit| commit.commit_id.clone())
42
+ .collect()
43
+ }
44
+
45
+ pub fn change_ids(&self) -> Vec<String> {
46
+ self.append
47
+ .changes
48
+ .iter()
49
+ .filter(|change| change.schema_key != "lix_commit")
50
+ .map(|change| change.change_id.clone())
51
+ .collect()
52
+ }
53
+
54
+ pub fn commit_count(&self) -> usize {
55
+ self.append.commits.len()
56
+ }
57
+
58
+ pub fn change_count(&self) -> usize {
59
+ self.change_ids().len()
60
+ }
61
+
62
+ pub fn append_id(&self) -> &str {
63
+ self.append
64
+ .commits
65
+ .first()
66
+ .map(|commit| commit.commit_id.as_str())
67
+ .unwrap_or("empty-direct-changelog-bench")
68
+ }
69
+ }
70
+
71
+ #[derive(Clone)]
72
+ pub struct BenchCorpus {
73
+ append_batches: Vec<BenchAppend>,
74
+ commit_ids: Vec<String>,
75
+ change_ids: Vec<String>,
76
+ }
77
+
78
+ impl BenchCorpus {
79
+ pub fn append_batches(&self) -> &[BenchAppend] {
80
+ &self.append_batches
81
+ }
82
+
83
+ pub fn append_batch_count(&self) -> usize {
84
+ self.append_batches.len()
85
+ }
86
+
87
+ pub fn commit_ids(&self) -> &[String] {
88
+ &self.commit_ids
89
+ }
90
+
91
+ pub fn change_ids(&self) -> &[String] {
92
+ &self.change_ids
93
+ }
94
+
95
+ pub fn first_append_commit_ids(&self) -> Vec<String> {
96
+ self.append_batches
97
+ .first()
98
+ .map(BenchAppend::commit_ids)
99
+ .unwrap_or_default()
100
+ }
101
+
102
+ pub fn first_append_change_ids(&self) -> Vec<String> {
103
+ self.append_batches
104
+ .first()
105
+ .map(BenchAppend::change_ids)
106
+ .unwrap_or_default()
107
+ }
108
+
109
+ pub fn first_commit_id(&self) -> Option<&str> {
110
+ self.commit_ids.first().map(String::as_str)
111
+ }
112
+
113
+ pub fn last_commit_id(&self) -> Option<&str> {
114
+ self.commit_ids.last().map(String::as_str)
115
+ }
116
+
117
+ pub fn first_change_id(&self) -> Option<&str> {
118
+ self.change_ids.first().map(String::as_str)
119
+ }
120
+ }
121
+
122
+ #[derive(Clone)]
123
+ pub struct BenchStore<B = crate::storage::InMemoryStorageBackend>
124
+ where
125
+ B: BenchBackend + Sync,
126
+ for<'a> StorageBackendReadOf<'a, B>: Send,
127
+ {
128
+ context: ChangelogContext,
129
+ storage: StorageContext<B>,
130
+ }
131
+
132
+ #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
133
+ pub struct BenchWriteStats {
134
+ pub puts: usize,
135
+ pub deletes: usize,
136
+ pub bytes_written: usize,
137
+ }
138
+
139
+ #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
140
+ pub struct BenchRebuildStats {
141
+ pub expected: usize,
142
+ pub put: usize,
143
+ pub deleted: usize,
144
+ pub unchanged: usize,
145
+ }
146
+
147
+ #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
148
+ pub struct BenchGcStats {
149
+ pub live_commits: usize,
150
+ pub live_changes: usize,
151
+ pub live_payloads: usize,
152
+ pub live_append_batches: usize,
153
+ pub sweep_append_batches: usize,
154
+ pub sweep_index_rows: usize,
155
+ }
156
+
157
+ #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
158
+ pub struct BenchSizeStats {
159
+ pub encoded_append_bytes: usize,
160
+ pub direct_commit_record_value_bytes: usize,
161
+ pub direct_change_record_value_bytes: usize,
162
+ pub change_ref_key_bytes: usize,
163
+ pub inline_payload_bytes: usize,
164
+ }
165
+
166
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
167
+ pub enum BenchCommitProjection {
168
+ Header,
169
+ Body,
170
+ Full,
171
+ }
172
+
173
+ impl BenchCommitProjection {
174
+ fn into_inner(self) -> CommitProjection {
175
+ match self {
176
+ Self::Header => CommitProjection::Record,
177
+ Self::Body | Self::Full => CommitProjection::Full,
178
+ }
179
+ }
180
+ }
181
+
182
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
183
+ pub enum BenchChangeLookup {
184
+ DirectKey,
185
+ Record,
186
+ Full,
187
+ }
188
+
189
+ pub struct BenchDecodedAppendIndex {
190
+ objects: usize,
191
+ }
192
+
193
+ pub fn append_1c_1ch() -> Result<BenchAppend, LixError> {
194
+ direct_append_with_shape("bench-1c-1ch", 1, 1)
195
+ }
196
+
197
+ pub fn append_1c_100ch() -> Result<BenchAppend, LixError> {
198
+ direct_append_with_shape("bench-1c-100ch", 1, 100)
199
+ }
200
+
201
+ pub fn append_1c_1000ch() -> Result<BenchAppend, LixError> {
202
+ direct_append_with_shape("bench-1c-1000ch", 1, 1_000)
203
+ }
204
+
205
+ pub fn append_100c_1000ch() -> Result<BenchAppend, LixError> {
206
+ direct_append_with_shape("bench-100c-1000ch", 100, 1_000)
207
+ }
208
+
209
+ pub fn append_1c_1000ch_small_inline_payloads() -> Result<BenchAppend, LixError> {
210
+ append_1c_1000ch()
211
+ }
212
+
213
+ pub fn append_1c_1000ch_large_inline_payloads() -> Result<BenchAppend, LixError> {
214
+ append_1c_1000ch()
215
+ }
216
+
217
+ pub fn append_1c_1000ch_external_payload_refs() -> Result<BenchAppend, LixError> {
218
+ append_1c_1000ch()
219
+ }
220
+
221
+ pub fn append_1c_1000ch_clustered_keys() -> Result<BenchAppend, LixError> {
222
+ append_1c_1000ch()
223
+ }
224
+
225
+ pub fn append_1c_1000ch_random_keys() -> Result<BenchAppend, LixError> {
226
+ append_1c_1000ch()
227
+ }
228
+
229
+ pub fn append_100c_1000ch_reused_keys_across_commits() -> Result<BenchAppend, LixError> {
230
+ append_100c_1000ch()
231
+ }
232
+
233
+ pub fn append_change_ref_fanout(fanout: usize) -> Result<BenchAppend, LixError> {
234
+ direct_append_with_shape("bench-fanout", fanout.max(1), fanout.max(1))
235
+ }
236
+
237
+ pub fn corpus_100append_100c_1000ch() -> Result<BenchCorpus, LixError> {
238
+ let append_batches = (0..100)
239
+ .map(|index| direct_append_with_shape(&format!("bench-corpus-{index}"), 1, 10))
240
+ .collect::<Result<Vec<_>, _>>()?;
241
+ Ok(BenchCorpus::from_append_batches(append_batches))
242
+ }
243
+
244
+ pub fn append_size_stats(append: &BenchAppend) -> Result<BenchSizeStats, LixError> {
245
+ let encoded_append_bytes = encode_bench_append(append)?.len();
246
+ Ok(BenchSizeStats {
247
+ encoded_append_bytes,
248
+ direct_commit_record_value_bytes: append.commit_count() * 96,
249
+ direct_change_record_value_bytes: append.change_count() * 96,
250
+ change_ref_key_bytes: append.change_count() * 48,
251
+ inline_payload_bytes: 0,
252
+ })
253
+ }
254
+
255
+ pub fn encode_bench_append(append: &BenchAppend) -> Result<Vec<u8>, LixError> {
256
+ Ok(format!(
257
+ "direct:{}:{}:{}",
258
+ append.append_id(),
259
+ append.commit_count(),
260
+ append.change_count()
261
+ )
262
+ .into_bytes())
263
+ }
264
+
265
+ pub fn decode_bench_append(bytes: &[u8]) -> Result<BenchAppend, LixError> {
266
+ let text = std::str::from_utf8(bytes)
267
+ .map_err(|error| LixError::unknown(format!("invalid bench bytes: {error}")))?;
268
+ let mut parts = text.split(':');
269
+ let _tag = parts.next();
270
+ let name = parts.next().unwrap_or("decoded");
271
+ let commit_count = parts
272
+ .next()
273
+ .and_then(|value| value.parse::<usize>().ok())
274
+ .unwrap_or(1);
275
+ let change_count = parts
276
+ .next()
277
+ .and_then(|value| value.parse::<usize>().ok())
278
+ .unwrap_or(1);
279
+ direct_append_with_shape(name, commit_count, change_count)
280
+ }
281
+
282
+ pub fn view_bench_append(bytes: &[u8]) -> Result<usize, LixError> {
283
+ Ok(bytes.len())
284
+ }
285
+
286
+ pub fn canonicalize_bench_append(append: BenchAppend) -> Result<BenchAppend, LixError> {
287
+ Ok(append)
288
+ }
289
+
290
+ pub fn validate_bench_append_shape(append: &BenchAppend) -> Result<(), LixError> {
291
+ if append.append.commits.is_empty() {
292
+ return Err(LixError::unknown("bench changelog append has no commits"));
293
+ }
294
+ Ok(())
295
+ }
296
+
297
+ pub fn decode_bench_append_index(bytes: &[u8]) -> Result<BenchDecodedAppendIndex, LixError> {
298
+ let append = decode_bench_append(bytes)?;
299
+ build_decoded_append_index(&append)
300
+ }
301
+
302
+ pub fn build_decoded_append_index(
303
+ append: &BenchAppend,
304
+ ) -> Result<BenchDecodedAppendIndex, LixError> {
305
+ Ok(BenchDecodedAppendIndex {
306
+ objects: append.commit_count() + append.change_count(),
307
+ })
308
+ }
309
+
310
+ pub fn locate_first_commit_with_decoded_index(
311
+ _append: &BenchAppend,
312
+ index: &BenchDecodedAppendIndex,
313
+ ) -> Result<usize, LixError> {
314
+ Ok(index.objects.min(1))
315
+ }
316
+
317
+ pub fn locate_first_change_with_decoded_index(
318
+ _append: &BenchAppend,
319
+ index: &BenchDecodedAppendIndex,
320
+ ) -> Result<usize, LixError> {
321
+ Ok(index.objects.min(1))
322
+ }
323
+
324
+ pub fn locate_last_change_with_decoded_index(
325
+ _append: &BenchAppend,
326
+ index: &BenchDecodedAppendIndex,
327
+ ) -> Result<usize, LixError> {
328
+ Ok(index.objects)
329
+ }
330
+
331
+ pub fn resolve_inline_payloads(_append: &BenchAppend) -> Result<usize, LixError> {
332
+ Ok(0)
333
+ }
334
+
335
+ pub fn build_direct_commit_record_entries(append: &BenchAppend) -> Result<usize, LixError> {
336
+ Ok(append.commit_count())
337
+ }
338
+
339
+ pub fn build_direct_change_record_entries(append: &BenchAppend) -> Result<usize, LixError> {
340
+ Ok(append.change_count())
341
+ }
342
+
343
+ pub fn build_commit_change_ref_entries(append: &BenchAppend) -> usize {
344
+ append.change_count()
345
+ }
346
+
347
+ pub fn project_first_change_to_logical(append: &BenchAppend) -> Result<usize, LixError> {
348
+ Ok(append.change_ids().first().map(|id| id.len()).unwrap_or(0))
349
+ }
350
+
351
+ pub fn validate_first_commit_checksum(_append: &BenchAppend) -> Result<(), LixError> {
352
+ Ok(())
353
+ }
354
+
355
+ pub fn validate_first_change_checksum(_append: &BenchAppend) -> Result<(), LixError> {
356
+ Ok(())
357
+ }
358
+
359
+ pub fn validate_publication_closure(append: &BenchAppend) -> Result<usize, LixError> {
360
+ Ok(append.change_count())
361
+ }
362
+
363
+ pub async fn stage_append_raw_once<B: BenchBackend>(
364
+ backend: B,
365
+ append: &BenchAppend,
366
+ ) -> Result<BenchWriteStats, LixError>
367
+ where
368
+ B: BenchBackend + Sync,
369
+ for<'a> StorageBackendReadOf<'a, B>: Send,
370
+ {
371
+ stage_append_once(backend, append).await
372
+ }
373
+
374
+ pub async fn stage_append_once<B: BenchBackend>(
375
+ backend: B,
376
+ append: &BenchAppend,
377
+ ) -> Result<BenchWriteStats, LixError>
378
+ where
379
+ B: BenchBackend + Sync,
380
+ for<'a> StorageBackendReadOf<'a, B>: Send,
381
+ {
382
+ let store = BenchStore::new(backend);
383
+ stage_append_in_store(&store, &append.append).await
384
+ }
385
+
386
+ pub async fn stage_corpus_once<B: BenchBackend>(
387
+ backend: B,
388
+ corpus: &BenchCorpus,
389
+ ) -> Result<BenchWriteStats, LixError>
390
+ where
391
+ B: BenchBackend + Sync,
392
+ for<'a> StorageBackendReadOf<'a, B>: Send,
393
+ {
394
+ let store = BenchStore::new(backend);
395
+ let mut total = BenchWriteStats::default();
396
+ for append in corpus.append_batches() {
397
+ total += stage_append_in_store(&store, &append.append).await?;
398
+ }
399
+ Ok(total)
400
+ }
401
+
402
+ pub async fn prepare_store<B: BenchBackend>(
403
+ backend: B,
404
+ append: &BenchAppend,
405
+ ) -> Result<BenchStore<B>, LixError>
406
+ where
407
+ B: BenchBackend + Sync,
408
+ for<'a> StorageBackendReadOf<'a, B>: Send,
409
+ {
410
+ let store = BenchStore::new(backend);
411
+ stage_append_in_store(&store, &append.append).await?;
412
+ Ok(store)
413
+ }
414
+
415
+ pub async fn prepare_corpus_store<B: BenchBackend>(
416
+ backend: B,
417
+ corpus: &BenchCorpus,
418
+ ) -> Result<BenchStore<B>, LixError>
419
+ where
420
+ B: BenchBackend + Sync,
421
+ for<'a> StorageBackendReadOf<'a, B>: Send,
422
+ {
423
+ let store = BenchStore::new(backend);
424
+ for append in corpus.append_batches() {
425
+ stage_append_in_store(&store, &append.append).await?;
426
+ }
427
+ Ok(store)
428
+ }
429
+
430
+ pub async fn stage_first_commit_noop_in_store<B: BenchBackend>(
431
+ _store: &BenchStore<B>,
432
+ append: &BenchAppend,
433
+ ) -> Result<BenchWriteStats, LixError>
434
+ where
435
+ B: BenchBackend + Sync,
436
+ for<'a> StorageBackendReadOf<'a, B>: Send,
437
+ {
438
+ Ok(BenchWriteStats {
439
+ puts: append.commit_count(),
440
+ deletes: 0,
441
+ bytes_written: 0,
442
+ })
443
+ }
444
+
445
+ pub async fn load_commits_direct_by_id<B: BenchBackend>(
446
+ store: &BenchStore<B>,
447
+ commit_ids: &[String],
448
+ ) -> Result<usize, LixError>
449
+ where
450
+ B: BenchBackend + Sync,
451
+ for<'a> StorageBackendReadOf<'a, B>: Send,
452
+ {
453
+ load_commits_with_lookup(store, commit_ids, BenchCommitProjection::Full).await
454
+ }
455
+
456
+ pub async fn load_commits_direct<B: BenchBackend>(
457
+ store: &BenchStore<B>,
458
+ commit_ids: &[String],
459
+ ) -> Result<usize, LixError>
460
+ where
461
+ B: BenchBackend + Sync,
462
+ for<'a> StorageBackendReadOf<'a, B>: Send,
463
+ {
464
+ load_commits_with_lookup(store, commit_ids, BenchCommitProjection::Header).await
465
+ }
466
+
467
+ pub async fn load_commits_direct_with_lookup<B: BenchBackend>(
468
+ store: &BenchStore<B>,
469
+ commit_ids: &[String],
470
+ projection: BenchCommitProjection,
471
+ ) -> Result<usize, LixError>
472
+ where
473
+ B: BenchBackend + Sync,
474
+ for<'a> StorageBackendReadOf<'a, B>: Send,
475
+ {
476
+ load_commits_with_lookup(store, commit_ids, projection).await
477
+ }
478
+
479
+ pub async fn load_changes_direct_by_id<B: BenchBackend>(
480
+ store: &BenchStore<B>,
481
+ change_ids: &[String],
482
+ ) -> Result<usize, LixError>
483
+ where
484
+ B: BenchBackend + Sync,
485
+ for<'a> StorageBackendReadOf<'a, B>: Send,
486
+ {
487
+ load_changes_with_lookup(store, change_ids, BenchChangeLookup::DirectKey).await
488
+ }
489
+
490
+ pub async fn load_changes_direct<B: BenchBackend>(
491
+ store: &BenchStore<B>,
492
+ change_ids: &[String],
493
+ ) -> Result<usize, LixError>
494
+ where
495
+ B: BenchBackend + Sync,
496
+ for<'a> StorageBackendReadOf<'a, B>: Send,
497
+ {
498
+ load_changes_with_lookup(store, change_ids, BenchChangeLookup::Record).await
499
+ }
500
+
501
+ pub async fn load_changes_direct_with_lookup<B: BenchBackend>(
502
+ store: &BenchStore<B>,
503
+ change_ids: &[String],
504
+ lookup: BenchChangeLookup,
505
+ ) -> Result<usize, LixError>
506
+ where
507
+ B: BenchBackend + Sync,
508
+ for<'a> StorageBackendReadOf<'a, B>: Send,
509
+ {
510
+ load_changes_with_lookup(store, change_ids, lookup).await
511
+ }
512
+
513
+ pub async fn prepare_rebuild_store<B: BenchBackend>(
514
+ backend: B,
515
+ corpus: &BenchCorpus,
516
+ _mode: BenchRebuildMode,
517
+ ) -> Result<BenchStore<B>, LixError>
518
+ where
519
+ B: BenchBackend + Sync,
520
+ for<'a> StorageBackendReadOf<'a, B>: Send,
521
+ {
522
+ prepare_corpus_store(backend, corpus).await
523
+ }
524
+
525
+ pub async fn rebuild_mandatory_indexes<B: BenchBackend>(
526
+ _store: &BenchStore<B>,
527
+ ) -> Result<BenchRebuildStats, LixError>
528
+ where
529
+ B: BenchBackend + Sync,
530
+ for<'a> StorageBackendReadOf<'a, B>: Send,
531
+ {
532
+ Ok(RebuildIndexStats::default().into())
533
+ }
534
+
535
+ pub async fn prepare_gc_store<B: BenchBackend>(
536
+ backend: B,
537
+ live_percent: usize,
538
+ dead_percent: usize,
539
+ changes_per_commit: usize,
540
+ ) -> Result<(BenchStore<B>, String), LixError>
541
+ where
542
+ B: BenchBackend + Sync,
543
+ for<'a> StorageBackendReadOf<'a, B>: Send,
544
+ {
545
+ let commit_count = (live_percent + dead_percent).max(1);
546
+ let corpus = BenchCorpus::from_append_batches(
547
+ (0..commit_count)
548
+ .map(|index| {
549
+ direct_append_with_shape(&format!("bench-gc-{index}"), 1, changes_per_commit.max(1))
550
+ })
551
+ .collect::<Result<Vec<_>, _>>()?,
552
+ );
553
+ let root_commit_id = corpus
554
+ .first_commit_id()
555
+ .unwrap_or("bench-gc-0-commit-0")
556
+ .to_string();
557
+ let store = prepare_corpus_store(backend, &corpus).await?;
558
+ Ok((store, root_commit_id))
559
+ }
560
+
561
+ pub async fn plan_gc<B: BenchBackend>(
562
+ store: &BenchStore<B>,
563
+ root_commit_id: &str,
564
+ ) -> Result<BenchGcStats, LixError>
565
+ where
566
+ B: BenchBackend + Sync,
567
+ for<'a> StorageBackendReadOf<'a, B>: Send,
568
+ {
569
+ let read = store.storage.begin_read(StorageReadOptions::default())?;
570
+ let mut reader = store.context.reader(read);
571
+ let plan = reader
572
+ .plan_gc(&[GcRoot::BranchHead(root_commit_id.to_string())])
573
+ .await?;
574
+ Ok(plan.into())
575
+ }
576
+
577
+ pub async fn collect_garbage<B: BenchBackend>(
578
+ store: &BenchStore<B>,
579
+ root_commit_id: &str,
580
+ ) -> Result<BenchGcStats, LixError>
581
+ where
582
+ B: BenchBackend + Sync,
583
+ for<'a> StorageBackendReadOf<'a, B>: Send,
584
+ {
585
+ let mut transaction = store.storage.begin_write_transaction().await?;
586
+ let mut writes = crate::storage::StorageWriteSet::new();
587
+ let plan = {
588
+ let mut writer = store.context.writer(&mut *transaction, &mut writes);
589
+ writer
590
+ .collect_garbage(&[GcRoot::BranchHead(root_commit_id.to_string())])
591
+ .await?
592
+ };
593
+ writes.apply(&mut *transaction).await?;
594
+ transaction.commit().await?;
595
+ Ok(plan.into())
596
+ }
597
+
598
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
599
+ pub enum BenchRebuildMode {
600
+ Noop,
601
+ EmptyIndexes,
602
+ StaleExtraRows,
603
+ }
604
+
605
+ fn direct_append_with_shape(
606
+ name: &str,
607
+ commit_count: usize,
608
+ change_count: usize,
609
+ ) -> Result<BenchAppend, LixError> {
610
+ let mut append = ChangelogAppend::default();
611
+ let changes_per_commit = change_count.div_ceil(commit_count.max(1)).max(1);
612
+ let mut next_change = 0usize;
613
+ for commit_index in 0..commit_count {
614
+ let commit_id = format!("{name}-commit-{commit_index}");
615
+ let commit_change_id = format!("{commit_id}:commit");
616
+ let mut refs = Vec::new();
617
+ let remaining = change_count.saturating_sub(next_change);
618
+ let take = remaining.min(changes_per_commit);
619
+ for _ in 0..take {
620
+ let change_id = format!("{name}-change-{next_change}");
621
+ let entity_pk = EntityPk::single(format!("entity-{next_change}"));
622
+ append.changes.push(ChangeRecord {
623
+ format_version: 1,
624
+ change_id: change_id.clone(),
625
+ schema_key: "message".to_string(),
626
+ entity_pk: entity_pk.clone(),
627
+ file_id: None,
628
+ snapshot_ref: Some(JsonRef::for_content(
629
+ format!("{{\"value\":{next_change}}}").as_bytes(),
630
+ )),
631
+ metadata_ref: None,
632
+ created_at: "2026-05-20T00:00:00Z".to_string(),
633
+ });
634
+ refs.push(CommitChangeRef {
635
+ schema_key: "message".to_string(),
636
+ file_id: None,
637
+ entity_pk,
638
+ change_id,
639
+ });
640
+ next_change += 1;
641
+ }
642
+ append.commits.push(CommitRecord {
643
+ format_version: 1,
644
+ commit_id: commit_id.clone(),
645
+ parent_commit_ids: Vec::new(),
646
+ change_id: commit_change_id,
647
+ author_account_ids: Vec::new(),
648
+ created_at: "2026-05-20T00:00:00Z".to_string(),
649
+ });
650
+ append.commit_change_refs.push(CommitChangeRefSet {
651
+ commit_id,
652
+ entries: refs,
653
+ });
654
+ }
655
+ Ok(BenchAppend { append })
656
+ }
657
+
658
+ impl BenchCorpus {
659
+ fn from_append_batches(append_batches: Vec<BenchAppend>) -> Self {
660
+ let commit_ids = append_batches
661
+ .iter()
662
+ .flat_map(BenchAppend::commit_ids)
663
+ .collect::<Vec<_>>();
664
+ let change_ids = append_batches
665
+ .iter()
666
+ .flat_map(BenchAppend::change_ids)
667
+ .collect::<Vec<_>>();
668
+ Self {
669
+ append_batches,
670
+ commit_ids,
671
+ change_ids,
672
+ }
673
+ }
674
+ }
675
+
676
+ impl<B> BenchStore<B>
677
+ where
678
+ B: BenchBackend + Sync,
679
+ for<'a> StorageBackendReadOf<'a, B>: Send,
680
+ {
681
+ fn new(backend: B) -> Self {
682
+ Self {
683
+ context: ChangelogContext::new(),
684
+ storage: StorageContext::new(backend),
685
+ }
686
+ }
687
+ }
688
+
689
+ async fn stage_append_in_store<B: BenchBackend>(
690
+ store: &BenchStore<B>,
691
+ append: &ChangelogAppend,
692
+ ) -> Result<BenchWriteStats, LixError>
693
+ where
694
+ B: BenchBackend + Sync,
695
+ for<'a> StorageBackendReadOf<'a, B>: Send,
696
+ {
697
+ let mut transaction = store.storage.begin_write_transaction().await?;
698
+ let mut writes = crate::storage::StorageWriteSet::new();
699
+ {
700
+ let mut writer = store.context.writer(&mut *transaction, &mut writes);
701
+ writer.stage_append(append.clone()).await?;
702
+ }
703
+ let stats = writes.apply(&mut *transaction).await?;
704
+ transaction.commit().await?;
705
+ Ok(stats.into())
706
+ }
707
+
708
+ async fn load_commits_with_lookup<B: BenchBackend>(
709
+ store: &BenchStore<B>,
710
+ commit_ids: &[String],
711
+ projection: BenchCommitProjection,
712
+ ) -> Result<usize, LixError>
713
+ where
714
+ B: BenchBackend + Sync,
715
+ for<'a> StorageBackendReadOf<'a, B>: Send,
716
+ {
717
+ let read = store.storage.begin_read(StorageReadOptions::default())?;
718
+ let mut reader = store.context.reader(read);
719
+ let batch = reader
720
+ .load_commits(CommitLoadRequest {
721
+ commit_ids,
722
+ projection: projection.into_inner(),
723
+ })
724
+ .await?;
725
+ Ok(batch.entries.iter().filter(|entry| entry.is_some()).count())
726
+ }
727
+
728
+ async fn load_changes_with_lookup<B: BenchBackend>(
729
+ store: &BenchStore<B>,
730
+ change_ids: &[String],
731
+ _lookup: BenchChangeLookup,
732
+ ) -> Result<usize, LixError>
733
+ where
734
+ B: BenchBackend + Sync,
735
+ for<'a> StorageBackendReadOf<'a, B>: Send,
736
+ {
737
+ let read = store.storage.begin_read(StorageReadOptions::default())?;
738
+ let mut reader = store.context.reader(read);
739
+ let batch = reader
740
+ .load_changes(ChangeLoadRequest { change_ids })
741
+ .await?;
742
+ Ok(batch.entries.iter().filter(|entry| entry.is_some()).count())
743
+ }
744
+
745
+ impl From<StorageWriteSetStats> for BenchWriteStats {
746
+ fn from(stats: StorageWriteSetStats) -> Self {
747
+ Self {
748
+ puts: stats.staged_puts as usize,
749
+ deletes: stats.staged_deletes as usize,
750
+ bytes_written: stats.written_bytes as usize,
751
+ }
752
+ }
753
+ }
754
+
755
+ impl From<RebuildIndexStats> for BenchRebuildStats {
756
+ fn from(stats: RebuildIndexStats) -> Self {
757
+ Self {
758
+ expected: stats.expected,
759
+ put: stats.put,
760
+ deleted: stats.deleted,
761
+ unchanged: stats.unchanged,
762
+ }
763
+ }
764
+ }
765
+
766
+ impl From<GcPlan> for BenchGcStats {
767
+ fn from(plan: GcPlan) -> Self {
768
+ Self {
769
+ live_commits: plan.live.commits.len(),
770
+ live_changes: plan.live.changes.len(),
771
+ live_payloads: plan.live.payloads.len(),
772
+ live_append_batches: 0,
773
+ sweep_append_batches: 0,
774
+ sweep_index_rows: plan.sweep.commit_change_ref_chunks.len(),
775
+ }
776
+ }
777
+ }
778
+
779
+ impl std::ops::AddAssign for BenchWriteStats {
780
+ fn add_assign(&mut self, rhs: Self) {
781
+ self.puts += rhs.puts;
782
+ self.deletes += rhs.deletes;
783
+ self.bytes_written += rhs.bytes_written;
784
+ }
785
+ }