@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
@@ -10,15 +10,26 @@ use crate::binary_cas::{
10
10
  BlobBytesBatch, BlobExistsBatch, BlobHash, BlobLayout, BlobMetadata, BlobMetadataBatch,
11
11
  BlobWrite, BlobWriteReceipt,
12
12
  };
13
+ use crate::storage::{PointReadPlan, ScanPlan, StorageRead, StorageSpace, StorageWriteSet};
13
14
  use crate::storage::{
14
- KvGetGroup, KvGetRequest, KvScanRange, KvScanRequest, StorageReader, StorageWriteSet,
15
+ StorageGetOptions, StorageKey, StoragePrefix, StorageProjectedValue, StorageScanOptions,
16
+ StorageSpaceId, StorageValue,
15
17
  };
16
18
  use crate::LixError;
19
+ use bytes::Bytes;
17
20
  use std::collections::{HashMap, HashSet};
18
21
 
19
22
  pub(crate) const BINARY_CAS_MANIFEST_NAMESPACE: &str = "binary_cas.manifest";
20
23
  pub(crate) const BINARY_CAS_MANIFEST_CHUNK_NAMESPACE: &str = "binary_cas.manifest_chunk";
21
24
  pub(crate) const BINARY_CAS_CHUNK_NAMESPACE: &str = "binary_cas.chunk";
25
+ pub(crate) const BINARY_CAS_MANIFEST_SPACE: StorageSpace =
26
+ StorageSpace::new(StorageSpaceId(0x0005_0001), BINARY_CAS_MANIFEST_NAMESPACE);
27
+ pub(crate) const BINARY_CAS_MANIFEST_CHUNK_SPACE: StorageSpace = StorageSpace::new(
28
+ StorageSpaceId(0x0005_0002),
29
+ BINARY_CAS_MANIFEST_CHUNK_NAMESPACE,
30
+ );
31
+ pub(crate) const BINARY_CAS_CHUNK_SPACE: StorageSpace =
32
+ StorageSpace::new(StorageSpaceId(0x0005_0003), BINARY_CAS_CHUNK_NAMESPACE);
22
33
 
23
34
  #[derive(Debug, Clone, PartialEq, Eq)]
24
35
  pub(crate) struct KvBlobManifestChunk {
@@ -34,15 +45,10 @@ pub(crate) struct KvChunk {
34
45
  }
35
46
 
36
47
  pub(crate) async fn load_manifest(
37
- store: &mut impl StorageReader,
48
+ store: &impl StorageRead,
38
49
  blob_hash: BlobHash,
39
50
  ) -> Result<Option<BinaryCasManifest>, LixError> {
40
- let Some(bytes) = get_one(
41
- store,
42
- BINARY_CAS_MANIFEST_NAMESPACE,
43
- manifest_key(blob_hash),
44
- )
45
- .await?
51
+ let Some(bytes) = get_one(store, BINARY_CAS_MANIFEST_SPACE, manifest_key(blob_hash)).await?
46
52
  else {
47
53
  return Ok(None);
48
54
  };
@@ -50,14 +56,12 @@ pub(crate) async fn load_manifest(
50
56
  }
51
57
 
52
58
  #[cfg(feature = "storage-benches")]
53
- pub(crate) async fn count_manifests(store: &mut impl StorageReader) -> Result<usize, LixError> {
54
- Ok(scan_all_values(
55
- store,
56
- BINARY_CAS_MANIFEST_NAMESPACE,
57
- KvScanRange::Prefix(Vec::new()),
59
+ pub(crate) async fn count_manifests(store: &impl StorageRead) -> Result<usize, LixError> {
60
+ Ok(
61
+ scan_all_values(store, BINARY_CAS_MANIFEST_SPACE, Vec::new())
62
+ .await?
63
+ .len(),
58
64
  )
59
- .await?
60
- .len())
61
65
  }
62
66
 
63
67
  pub(crate) fn stage_manifest(
@@ -66,20 +70,20 @@ pub(crate) fn stage_manifest(
66
70
  manifest: &BinaryCasManifest,
67
71
  ) {
68
72
  writes.put(
69
- BINARY_CAS_MANIFEST_NAMESPACE,
70
- manifest_key(blob_hash),
71
- encode_binary_cas_manifest(manifest),
73
+ BINARY_CAS_MANIFEST_SPACE,
74
+ key(manifest_key(blob_hash)),
75
+ value(encode_binary_cas_manifest(manifest)),
72
76
  );
73
77
  }
74
78
 
75
79
  pub(crate) async fn scan_manifest_chunks(
76
- store: &mut impl StorageReader,
80
+ store: &impl StorageRead,
77
81
  blob_hash: BlobHash,
78
82
  ) -> Result<Vec<KvBlobManifestChunk>, LixError> {
79
83
  scan_all_values(
80
84
  store,
81
- BINARY_CAS_MANIFEST_CHUNK_NAMESPACE,
82
- KvScanRange::Prefix(manifest_chunk_prefix(blob_hash)),
85
+ BINARY_CAS_MANIFEST_CHUNK_SPACE,
86
+ manifest_chunk_prefix(blob_hash),
83
87
  )
84
88
  .await?
85
89
  .into_iter()
@@ -100,18 +104,20 @@ pub(crate) fn stage_manifest_chunk(
100
104
  chunk: &KvBlobManifestChunk,
101
105
  ) {
102
106
  writes.put(
103
- BINARY_CAS_MANIFEST_CHUNK_NAMESPACE,
104
- manifest_chunk_key(blob_hash, chunk_index),
105
- encode_binary_cas_manifest_chunk(&chunk.chunk_hash, chunk.chunk_size),
107
+ BINARY_CAS_MANIFEST_CHUNK_SPACE,
108
+ key(manifest_chunk_key(blob_hash, chunk_index)),
109
+ value(encode_binary_cas_manifest_chunk(
110
+ &chunk.chunk_hash,
111
+ chunk.chunk_size,
112
+ )),
106
113
  );
107
114
  }
108
115
 
109
116
  pub(crate) async fn load_chunk(
110
- store: &mut impl StorageReader,
117
+ store: &impl StorageRead,
111
118
  chunk_hash: BlobHash,
112
119
  ) -> Result<Option<KvChunk>, LixError> {
113
- let Some(bytes) = get_one(store, BINARY_CAS_CHUNK_NAMESPACE, chunk_key(chunk_hash)).await?
114
- else {
120
+ let Some(bytes) = get_one(store, BINARY_CAS_CHUNK_SPACE, chunk_key(chunk_hash)).await? else {
115
121
  return Ok(None);
116
122
  };
117
123
  let (codec, uncompressed_len, payload) = decode_binary_cas_chunk(&bytes)?;
@@ -124,73 +130,78 @@ pub(crate) async fn load_chunk(
124
130
 
125
131
  pub(crate) fn stage_chunk(writes: &mut StorageWriteSet, chunk_hash: BlobHash, chunk: &KvChunk) {
126
132
  writes.put(
127
- BINARY_CAS_CHUNK_NAMESPACE,
128
- chunk_key(chunk_hash),
129
- encode_binary_cas_chunk(chunk.codec, chunk.uncompressed_len, &chunk.data),
133
+ BINARY_CAS_CHUNK_SPACE,
134
+ key(chunk_key(chunk_hash)),
135
+ value(encode_binary_cas_chunk(
136
+ chunk.codec,
137
+ chunk.uncompressed_len,
138
+ &chunk.data,
139
+ )),
130
140
  );
131
141
  }
132
142
 
133
143
  async fn get_one(
134
- store: &mut impl StorageReader,
135
- namespace: &str,
144
+ store: &impl StorageRead,
145
+ space: StorageSpace,
136
146
  key: Vec<u8>,
137
147
  ) -> Result<Option<Vec<u8>>, LixError> {
138
- Ok(store
139
- .get_values(KvGetRequest {
140
- groups: vec![KvGetGroup {
141
- namespace: namespace.to_string(),
142
- keys: vec![key],
143
- }],
144
- })
145
- .await?
146
- .groups
148
+ let result = PointReadPlan::new(space, &[StorageKey(Bytes::from(key))])
149
+ .materialize(store, StorageGetOptions::default())?;
150
+ Ok(result
151
+ .value
147
152
  .into_iter()
148
153
  .next()
149
- .and_then(|group| group.single_value_owned()))
154
+ .flatten()
155
+ .and_then(full_value))
150
156
  }
151
157
 
152
158
  async fn scan_all_values(
153
- store: &mut impl StorageReader,
154
- namespace: &str,
155
- range: KvScanRange,
159
+ store: &impl StorageRead,
160
+ space: StorageSpace,
161
+ prefix: Vec<u8>,
156
162
  ) -> Result<Vec<Vec<u8>>, LixError> {
157
- let page = store
158
- .scan_values(KvScanRequest {
159
- namespace: namespace.to_string(),
160
- range,
161
- after: None,
162
- limit: usize::MAX,
163
- })
164
- .await?
165
- .values;
166
- Ok(page.iter().map(<[u8]>::to_vec).collect())
163
+ let plan = ScanPlan::prefix(
164
+ space,
165
+ StoragePrefix {
166
+ bytes: Bytes::from(prefix),
167
+ },
168
+ );
169
+ let mut values = Vec::new();
170
+ let mut resume_after = None;
171
+ loop {
172
+ let page = plan.collect(
173
+ store,
174
+ StorageScanOptions {
175
+ resume_after: resume_after.as_ref(),
176
+ ..StorageScanOptions::default()
177
+ },
178
+ )?;
179
+ resume_after = page.value.entries.last().map(|entry| entry.key.clone());
180
+ values.extend(
181
+ page.value
182
+ .entries
183
+ .into_iter()
184
+ .filter_map(|entry| full_value(entry.value)),
185
+ );
186
+ if !page.value.has_more || resume_after.is_none() {
187
+ break;
188
+ }
189
+ }
190
+ Ok(values)
167
191
  }
168
192
 
169
193
  pub(crate) async fn load_metadata_many(
170
- store: &mut impl StorageReader,
194
+ store: &impl StorageRead,
171
195
  hashes: &[BlobHash],
172
196
  ) -> Result<BlobMetadataBatch, LixError> {
173
197
  if hashes.is_empty() {
174
198
  return Ok(BlobMetadataBatch::new(Vec::new()));
175
199
  }
176
- let rows = store
177
- .get_values(KvGetRequest {
178
- groups: vec![KvGetGroup {
179
- namespace: BINARY_CAS_MANIFEST_NAMESPACE.to_string(),
180
- keys: hashes.iter().map(|hash| manifest_key(*hash)).collect(),
181
- }],
182
- })
183
- .await?
184
- .groups
185
- .into_iter()
186
- .next()
187
- .map(|group| {
188
- group
189
- .values_iter()
190
- .map(|value| value.map(<[u8]>::to_vec))
191
- .collect::<Vec<_>>()
192
- })
193
- .unwrap_or_default();
200
+ let rows = point_values(
201
+ store,
202
+ BINARY_CAS_MANIFEST_SPACE,
203
+ hashes.iter().map(|hash| manifest_key(*hash)).collect(),
204
+ )?;
194
205
  if rows.len() != hashes.len() {
195
206
  return Err(LixError::new(
196
207
  "LIX_ERROR_UNKNOWN",
@@ -216,7 +227,7 @@ pub(crate) async fn load_metadata_many(
216
227
  }
217
228
 
218
229
  pub(crate) async fn exists_many(
219
- store: &mut impl StorageReader,
230
+ store: &impl StorageRead,
220
231
  hashes: &[BlobHash],
221
232
  ) -> Result<BlobExistsBatch, LixError> {
222
233
  Ok(BlobExistsBatch::new(
@@ -230,7 +241,7 @@ pub(crate) async fn exists_many(
230
241
  }
231
242
 
232
243
  pub(crate) async fn load_bytes_many(
233
- store: &mut impl StorageReader,
244
+ store: &impl StorageRead,
234
245
  hashes: &[BlobHash],
235
246
  ) -> Result<BlobBytesBatch, LixError> {
236
247
  let metadata = load_metadata_many(store, hashes).await?.into_vec();
@@ -301,30 +312,52 @@ pub(crate) async fn load_bytes_many(
301
312
  }
302
313
 
303
314
  async fn load_chunk_rows(
304
- store: &mut impl StorageReader,
315
+ store: &impl StorageRead,
305
316
  hashes: &[BlobHash],
306
317
  ) -> Result<Vec<Option<Vec<u8>>>, LixError> {
307
318
  if hashes.is_empty() {
308
319
  return Ok(Vec::new());
309
320
  }
310
- Ok(store
311
- .get_values(KvGetRequest {
312
- groups: vec![KvGetGroup {
313
- namespace: BINARY_CAS_CHUNK_NAMESPACE.to_string(),
314
- keys: hashes.iter().map(|hash| chunk_key(*hash)).collect(),
315
- }],
316
- })
317
- .await?
318
- .groups
321
+ point_values(
322
+ store,
323
+ BINARY_CAS_CHUNK_SPACE,
324
+ hashes.iter().map(|hash| chunk_key(*hash)).collect(),
325
+ )
326
+ }
327
+
328
+ fn point_values(
329
+ store: &impl StorageRead,
330
+ space: StorageSpace,
331
+ keys: Vec<Vec<u8>>,
332
+ ) -> Result<Vec<Option<Vec<u8>>>, LixError> {
333
+ let keys = keys
319
334
  .into_iter()
320
- .next()
321
- .map(|group| {
322
- group
323
- .values_iter()
324
- .map(|value| value.map(<[u8]>::to_vec))
325
- .collect::<Vec<_>>()
326
- })
327
- .unwrap_or_default())
335
+ .map(|key| StorageKey(Bytes::from(key)))
336
+ .collect::<Vec<_>>();
337
+ let result =
338
+ PointReadPlan::new(space, &keys).materialize(store, StorageGetOptions::default())?;
339
+ Ok(result
340
+ .value
341
+ .into_iter()
342
+ .map(|value| value.and_then(full_value))
343
+ .collect())
344
+ }
345
+
346
+ fn key(bytes: Vec<u8>) -> StorageKey {
347
+ StorageKey(Bytes::from(bytes))
348
+ }
349
+
350
+ fn value(bytes: Vec<u8>) -> StorageValue {
351
+ StorageValue {
352
+ bytes: Bytes::from(bytes),
353
+ }
354
+ }
355
+
356
+ fn full_value(value: StorageProjectedValue) -> Option<Vec<u8>> {
357
+ match value {
358
+ StorageProjectedValue::FullValue(bytes) => Some(bytes.to_vec()),
359
+ StorageProjectedValue::KeyOnly => None,
360
+ }
328
361
  }
329
362
 
330
363
  fn assemble_blob_bytes(
@@ -653,28 +686,19 @@ fn persisted_size_to_usize(size: u64, label: &str) -> Result<usize, LixError> {
653
686
  #[cfg(test)]
654
687
  mod tests {
655
688
  use super::*;
656
- use crate::backend::testing::UnitTestBackend;
657
689
  use crate::binary_cas::BinaryCasContext;
658
- use crate::storage::{StorageContext, StorageWriteSet};
659
-
660
- fn stage_blob_to_writes(writes: &mut StorageWriteSet, data: &[u8]) {
661
- let mut writer = BinaryCasContext::new().writer(writes);
662
- writer.stage_bytes(data).expect("blob write should persist");
663
- }
690
+ use crate::storage::StorageContext;
691
+ use crate::storage::{InMemoryStorageBackend, StorageReadOptions, StorageWriteOptions};
664
692
 
665
693
  #[tokio::test]
666
694
  async fn stores_manifest_chunks_in_scan_order() {
667
- let storage = StorageContext::new(std::sync::Arc::new(UnitTestBackend::new()));
668
- let mut transaction = storage
669
- .begin_write_transaction()
670
- .await
671
- .expect("transaction should open");
695
+ let storage = StorageContext::new(InMemoryStorageBackend::new());
672
696
  let blob_hash = BlobHash::from_content(b"blob-a");
673
697
  let chunk_a_hash = BlobHash::from_content(b"chunk-a").into_bytes();
674
698
  let chunk_b_hash = BlobHash::from_content(b"chunk-b").into_bytes();
675
699
 
676
700
  {
677
- let mut writes = StorageWriteSet::new();
701
+ let mut writes = storage.new_write_set();
678
702
  stage_manifest(
679
703
  &mut writes,
680
704
  blob_hash,
@@ -701,19 +725,16 @@ mod tests {
701
725
  chunk_size: 6,
702
726
  },
703
727
  );
704
- writes
705
- .apply(&mut transaction.as_mut())
706
- .await
707
- .expect("manifest writes should apply");
728
+ storage
729
+ .commit_write_set(writes, StorageWriteOptions::default())
730
+ .expect("manifest writes should commit");
708
731
  }
709
- transaction.commit().await.expect("commit should succeed");
710
732
 
711
- let mut store = storage
712
- .begin_read_transaction()
713
- .await
714
- .expect("read transaction should open");
733
+ let store = storage
734
+ .begin_read(StorageReadOptions::default())
735
+ .expect("read should open");
715
736
  assert_eq!(
716
- load_manifest(&mut store, blob_hash)
737
+ load_manifest(&store, blob_hash)
717
738
  .await
718
739
  .expect("manifest should load"),
719
740
  Some(BinaryCasManifest::Chunked {
@@ -721,12 +742,11 @@ mod tests {
721
742
  chunk_count: 2,
722
743
  })
723
744
  );
724
- let mut store = storage
725
- .begin_read_transaction()
726
- .await
727
- .expect("read transaction should open");
745
+ let store = storage
746
+ .begin_read(StorageReadOptions::default())
747
+ .expect("read should open");
728
748
  assert_eq!(
729
- scan_manifest_chunks(&mut store, blob_hash)
749
+ scan_manifest_chunks(&store, blob_hash)
730
750
  .await
731
751
  .expect("manifest chunks should scan"),
732
752
  vec![
@@ -744,11 +764,7 @@ mod tests {
744
764
 
745
765
  #[tokio::test]
746
766
  async fn stores_encoded_chunks_by_chunk_hash() {
747
- let storage = StorageContext::new(std::sync::Arc::new(UnitTestBackend::new()));
748
- let mut transaction = storage
749
- .begin_write_transaction()
750
- .await
751
- .expect("transaction should open");
767
+ let storage = StorageContext::new(InMemoryStorageBackend::new());
752
768
  let chunk = KvChunk {
753
769
  codec: BinaryChunkCodec::Raw,
754
770
  uncompressed_len: 5,
@@ -757,21 +773,18 @@ mod tests {
757
773
  let chunk_hash = BlobHash::from_content(b"chunk-a");
758
774
 
759
775
  {
760
- let mut writes = StorageWriteSet::new();
776
+ let mut writes = storage.new_write_set();
761
777
  stage_chunk(&mut writes, chunk_hash, &chunk);
762
- writes
763
- .apply(&mut transaction.as_mut())
764
- .await
765
- .expect("chunk should apply");
778
+ storage
779
+ .commit_write_set(writes, StorageWriteOptions::default())
780
+ .expect("chunk should commit");
766
781
  }
767
- transaction.commit().await.expect("commit should succeed");
768
782
 
769
- let mut store = storage
770
- .begin_read_transaction()
771
- .await
772
- .expect("read transaction should open");
783
+ let store = storage
784
+ .begin_read(StorageReadOptions::default())
785
+ .expect("read should open");
773
786
  assert_eq!(
774
- load_chunk(&mut store, chunk_hash)
787
+ load_chunk(&store, chunk_hash)
775
788
  .await
776
789
  .expect("chunk should load"),
777
790
  Some(chunk)
@@ -796,41 +809,34 @@ mod tests {
796
809
 
797
810
  #[tokio::test]
798
811
  async fn public_kv_api_roundtrips_blob_bytes() {
799
- let storage = StorageContext::new(std::sync::Arc::new(UnitTestBackend::new()));
812
+ let storage = StorageContext::new(InMemoryStorageBackend::new());
800
813
  let data = b"hello chunked kv cas";
801
814
  let blob_hash = BlobHash::from_content(data);
802
- let mut transaction = storage
803
- .begin_write_transaction()
804
- .await
805
- .expect("transaction should open");
806
815
 
807
816
  {
808
- let mut writes = StorageWriteSet::new();
809
- stage_blob_to_writes(&mut writes, data);
810
- writes
811
- .apply(&mut transaction.as_mut())
812
- .await
813
- .expect("blob write should apply");
817
+ let mut writes = storage.new_write_set();
818
+ let mut writer = BinaryCasContext::new().writer(&mut writes);
819
+ writer.stage_bytes(data).expect("blob write should stage");
820
+ storage
821
+ .commit_write_set(writes, StorageWriteOptions::default())
822
+ .expect("blob write should commit");
814
823
  }
815
- transaction.commit().await.expect("commit should succeed");
816
824
 
817
- let mut store = storage
818
- .begin_read_transaction()
819
- .await
820
- .expect("read transaction should open");
825
+ let store = storage
826
+ .begin_read(StorageReadOptions::default())
827
+ .expect("read should open");
821
828
  assert_eq!(
822
- load_bytes_many(&mut store, &[blob_hash])
829
+ load_bytes_many(&store, &[blob_hash])
823
830
  .await
824
831
  .expect("blob should load")
825
832
  .into_vec(),
826
833
  vec![Some(data.to_vec())]
827
834
  );
828
- let mut store = storage
829
- .begin_read_transaction()
830
- .await
831
- .expect("read transaction should open");
835
+ let store = storage
836
+ .begin_read(StorageReadOptions::default())
837
+ .expect("read should open");
832
838
  assert_eq!(
833
- load_manifest(&mut store, blob_hash)
839
+ load_manifest(&store, blob_hash)
834
840
  .await
835
841
  .expect("manifest should load"),
836
842
  Some(BinaryCasManifest::SingleChunk {
@@ -838,22 +844,20 @@ mod tests {
838
844
  chunk_hash: BlobHash::from_content(data).into_bytes(),
839
845
  })
840
846
  );
841
- let mut store = storage
842
- .begin_read_transaction()
843
- .await
844
- .expect("read transaction should open");
847
+ let store = storage
848
+ .begin_read(StorageReadOptions::default())
849
+ .expect("read should open");
845
850
  assert_eq!(
846
- scan_manifest_chunks(&mut store, blob_hash)
851
+ scan_manifest_chunks(&store, blob_hash)
847
852
  .await
848
853
  .expect("single-chunk blob should not spill manifest chunks"),
849
854
  Vec::<KvBlobManifestChunk>::new()
850
855
  );
851
- let mut store = storage
852
- .begin_read_transaction()
853
- .await
854
- .expect("read transaction should open");
856
+ let store = storage
857
+ .begin_read(StorageReadOptions::default())
858
+ .expect("read should open");
855
859
  assert_eq!(
856
- exists_many(&mut store, &[blob_hash])
860
+ exists_many(&store, &[blob_hash])
857
861
  .await
858
862
  .expect("blob exists should succeed")
859
863
  .into_vec(),
@@ -863,48 +867,40 @@ mod tests {
863
867
 
864
868
  #[tokio::test]
865
869
  async fn read_rejects_chunk_bytes_that_do_not_match_manifest_hash() {
866
- let storage = StorageContext::new(std::sync::Arc::new(UnitTestBackend::new()));
870
+ let storage = StorageContext::new(InMemoryStorageBackend::new());
867
871
  let data = b"same length";
868
872
  let corrupted = b"SAME length";
869
873
  let blob_hash = BlobHash::from_content(data);
870
874
 
871
- let mut transaction = storage
872
- .begin_write_transaction()
873
- .await
874
- .expect("transaction should open");
875
875
  {
876
- let mut writes = StorageWriteSet::new();
877
- stage_blob_to_writes(&mut writes, data);
878
- writes
879
- .apply(&mut transaction.as_mut())
880
- .await
881
- .expect("blob write should apply");
876
+ let mut writes = storage.new_write_set();
877
+ let mut writer = BinaryCasContext::new().writer(&mut writes);
878
+ writer.stage_bytes(data).expect("blob write should stage");
879
+ storage
880
+ .commit_write_set(writes, StorageWriteOptions::default())
881
+ .expect("blob write should commit");
882
882
  }
883
- transaction.commit().await.expect("commit should succeed");
884
883
 
885
- let mut transaction = storage
886
- .begin_write_transaction()
887
- .await
888
- .expect("transaction should open");
889
884
  {
890
- let mut writes = StorageWriteSet::new();
885
+ let mut writes = storage.new_write_set();
891
886
  writes.put(
892
- BINARY_CAS_CHUNK_NAMESPACE,
893
- chunk_key(blob_hash),
894
- encode_binary_cas_chunk(BinaryChunkCodec::Raw, corrupted.len() as u64, corrupted),
887
+ BINARY_CAS_CHUNK_SPACE,
888
+ key(chunk_key(blob_hash)),
889
+ value(encode_binary_cas_chunk(
890
+ BinaryChunkCodec::Raw,
891
+ corrupted.len() as u64,
892
+ corrupted,
893
+ )),
895
894
  );
896
- writes
897
- .apply(&mut transaction.as_mut())
898
- .await
895
+ storage
896
+ .commit_write_set(writes, StorageWriteOptions::default())
899
897
  .expect("corrupt chunk should overwrite");
900
898
  }
901
- transaction.commit().await.expect("commit should succeed");
902
899
 
903
- let mut store = storage
904
- .begin_read_transaction()
905
- .await
906
- .expect("read transaction should open");
907
- let error = load_bytes_many(&mut store, &[blob_hash])
900
+ let store = storage
901
+ .begin_read(StorageReadOptions::default())
902
+ .expect("read should open");
903
+ let error = load_bytes_many(&store, &[blob_hash])
908
904
  .await
909
905
  .expect_err("corrupt chunk should be rejected");
910
906
  assert!(error
@@ -914,19 +910,15 @@ mod tests {
914
910
 
915
911
  #[tokio::test]
916
912
  async fn read_rejects_manifest_that_assembles_wrong_blob_hash() {
917
- let storage = StorageContext::new(std::sync::Arc::new(UnitTestBackend::new()));
913
+ let storage = StorageContext::new(InMemoryStorageBackend::new());
918
914
  let expected = b"expected bytes";
919
915
  let substituted = b"different byte";
920
916
  assert_eq!(expected.len(), substituted.len());
921
917
  let expected_blob_hash = BlobHash::from_content(expected);
922
918
  let substituted_chunk_hash = BlobHash::from_content(substituted);
923
919
 
924
- let mut transaction = storage
925
- .begin_write_transaction()
926
- .await
927
- .expect("transaction should open");
928
920
  {
929
- let mut writes = StorageWriteSet::new();
921
+ let mut writes = storage.new_write_set();
930
922
  stage_manifest(
931
923
  &mut writes,
932
924
  expected_blob_hash,
@@ -953,18 +945,15 @@ mod tests {
953
945
  data: substituted.to_vec(),
954
946
  },
955
947
  );
956
- writes
957
- .apply(&mut transaction.as_mut())
958
- .await
959
- .expect("wrong manifest fixture should apply");
948
+ storage
949
+ .commit_write_set(writes, StorageWriteOptions::default())
950
+ .expect("wrong manifest fixture should commit");
960
951
  }
961
- transaction.commit().await.expect("commit should succeed");
962
952
 
963
- let mut store = storage
964
- .begin_read_transaction()
965
- .await
966
- .expect("read transaction should open");
967
- let error = load_bytes_many(&mut store, &[expected_blob_hash])
953
+ let store = storage
954
+ .begin_read(StorageReadOptions::default())
955
+ .expect("read should open");
956
+ let error = load_bytes_many(&store, &[expected_blob_hash])
968
957
  .await
969
958
  .expect_err("wrong assembled blob should be rejected");
970
959
  assert!(error
@@ -974,41 +963,34 @@ mod tests {
974
963
 
975
964
  #[tokio::test]
976
965
  async fn public_kv_api_roundtrips_empty_blob() {
977
- let storage = StorageContext::new(std::sync::Arc::new(UnitTestBackend::new()));
966
+ let storage = StorageContext::new(InMemoryStorageBackend::new());
978
967
  let data = b"";
979
968
  let blob_hash = BlobHash::from_content(data);
980
- let mut transaction = storage
981
- .begin_write_transaction()
982
- .await
983
- .expect("transaction should open");
984
969
 
985
970
  {
986
- let mut writes = StorageWriteSet::new();
987
- stage_blob_to_writes(&mut writes, data);
988
- writes
989
- .apply(&mut transaction.as_mut())
990
- .await
991
- .expect("blob write should apply");
971
+ let mut writes = storage.new_write_set();
972
+ let mut writer = BinaryCasContext::new().writer(&mut writes);
973
+ writer.stage_bytes(data).expect("blob write should stage");
974
+ storage
975
+ .commit_write_set(writes, StorageWriteOptions::default())
976
+ .expect("blob write should commit");
992
977
  }
993
- transaction.commit().await.expect("commit should succeed");
994
978
 
995
- let mut store = storage
996
- .begin_read_transaction()
997
- .await
998
- .expect("read transaction should open");
979
+ let store = storage
980
+ .begin_read(StorageReadOptions::default())
981
+ .expect("read should open");
999
982
  assert_eq!(
1000
- load_bytes_many(&mut store, &[blob_hash])
983
+ load_bytes_many(&store, &[blob_hash])
1001
984
  .await
1002
985
  .expect("empty blob should load")
1003
986
  .into_vec(),
1004
987
  vec![Some(Vec::new())]
1005
988
  );
1006
- let mut store = storage
1007
- .begin_read_transaction()
1008
- .await
1009
- .expect("read transaction should open");
989
+ let store = storage
990
+ .begin_read(StorageReadOptions::default())
991
+ .expect("read should open");
1010
992
  assert_eq!(
1011
- scan_manifest_chunks(&mut store, blob_hash)
993
+ scan_manifest_chunks(&store, blob_hash)
1012
994
  .await
1013
995
  .expect("empty blob chunks should scan"),
1014
996
  Vec::<KvBlobManifestChunk>::new()
@@ -1017,43 +999,36 @@ mod tests {
1017
999
 
1018
1000
  #[tokio::test]
1019
1001
  async fn public_kv_api_roundtrips_multi_chunk_blob() {
1020
- let storage = StorageContext::new(std::sync::Arc::new(UnitTestBackend::new()));
1002
+ let storage = StorageContext::new(InMemoryStorageBackend::new());
1021
1003
  let data = (0..600_000)
1022
1004
  .map(|index| (index % 251) as u8)
1023
1005
  .collect::<Vec<_>>();
1024
1006
  let blob_hash = BlobHash::from_content(&data);
1025
- let mut transaction = storage
1026
- .begin_write_transaction()
1027
- .await
1028
- .expect("transaction should open");
1029
1007
 
1030
1008
  {
1031
- let mut writes = StorageWriteSet::new();
1032
- stage_blob_to_writes(&mut writes, &data);
1033
- writes
1034
- .apply(&mut transaction.as_mut())
1035
- .await
1036
- .expect("blob write should apply");
1009
+ let mut writes = storage.new_write_set();
1010
+ let mut writer = BinaryCasContext::new().writer(&mut writes);
1011
+ writer.stage_bytes(&data).expect("blob write should stage");
1012
+ storage
1013
+ .commit_write_set(writes, StorageWriteOptions::default())
1014
+ .expect("blob write should commit");
1037
1015
  }
1038
- transaction.commit().await.expect("commit should succeed");
1039
1016
 
1040
- let mut store = storage
1041
- .begin_read_transaction()
1042
- .await
1043
- .expect("read transaction should open");
1017
+ let store = storage
1018
+ .begin_read(StorageReadOptions::default())
1019
+ .expect("read should open");
1044
1020
  assert_eq!(
1045
- load_bytes_many(&mut store, &[blob_hash])
1021
+ load_bytes_many(&store, &[blob_hash])
1046
1022
  .await
1047
1023
  .expect("large blob should load")
1048
1024
  .into_vec(),
1049
1025
  vec![Some(data.clone())]
1050
1026
  );
1051
- let mut store = storage
1052
- .begin_read_transaction()
1053
- .await
1054
- .expect("read transaction should open");
1027
+ let store = storage
1028
+ .begin_read(StorageReadOptions::default())
1029
+ .expect("read should open");
1055
1030
  assert!(
1056
- scan_manifest_chunks(&mut store, blob_hash)
1031
+ scan_manifest_chunks(&store, blob_hash)
1057
1032
  .await
1058
1033
  .expect("large blob chunks should scan")
1059
1034
  .len()