@lix-js/sdk 0.6.0-preview.1 → 0.6.0-preview.2
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.
- package/SKILL.md +305 -320
- package/dist/engine-wasm/wasm/lix_engine.d.ts +5 -0
- package/dist/engine-wasm/wasm/lix_engine.js +9 -13
- package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
- package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +1 -0
- package/dist/open-lix.d.ts +103 -14
- package/dist/open-lix.js +3 -0
- package/dist/sqlite/index.js +99 -22
- package/dist-engine-src/README.md +18 -0
- package/dist-engine-src/src/backend/kv.rs +358 -0
- package/dist-engine-src/src/backend/mod.rs +12 -0
- package/dist-engine-src/src/backend/testing.rs +658 -0
- package/dist-engine-src/src/backend/types.rs +96 -0
- package/dist-engine-src/src/binary_cas/chunking.rs +31 -0
- package/dist-engine-src/src/binary_cas/codec.rs +346 -0
- package/dist-engine-src/src/binary_cas/context.rs +139 -0
- package/dist-engine-src/src/binary_cas/kv.rs +1063 -0
- package/dist-engine-src/src/binary_cas/mod.rs +11 -0
- package/dist-engine-src/src/binary_cas/types.rs +127 -0
- package/dist-engine-src/src/cel/context.rs +86 -0
- package/dist-engine-src/src/cel/error.rs +19 -0
- package/dist-engine-src/src/cel/mod.rs +8 -0
- package/dist-engine-src/src/cel/provider.rs +9 -0
- package/dist-engine-src/src/cel/runtime.rs +167 -0
- package/dist-engine-src/src/cel/value.rs +50 -0
- package/dist-engine-src/src/changelog/codec.rs +321 -0
- package/dist-engine-src/src/changelog/context.rs +92 -0
- package/dist-engine-src/src/changelog/materialization.rs +121 -0
- package/dist-engine-src/src/changelog/mod.rs +13 -0
- package/dist-engine-src/src/changelog/reader.rs +20 -0
- package/dist-engine-src/src/changelog/storage.rs +220 -0
- package/dist-engine-src/src/changelog/types.rs +38 -0
- package/dist-engine-src/src/commit_graph/context.rs +1588 -0
- package/dist-engine-src/src/commit_graph/mod.rs +12 -0
- package/dist-engine-src/src/commit_graph/types.rs +145 -0
- package/dist-engine-src/src/commit_graph/walker.rs +780 -0
- package/dist-engine-src/src/common/error.rs +313 -0
- package/dist-engine-src/src/common/fingerprint.rs +3 -0
- package/dist-engine-src/src/common/fs_path.rs +1336 -0
- package/dist-engine-src/src/common/identity.rs +135 -0
- package/dist-engine-src/src/common/metadata.rs +35 -0
- package/dist-engine-src/src/common/mod.rs +23 -0
- package/dist-engine-src/src/common/types.rs +105 -0
- package/dist-engine-src/src/common/wire.rs +222 -0
- package/dist-engine-src/src/engine.rs +239 -0
- package/dist-engine-src/src/entity_identity.rs +285 -0
- package/dist-engine-src/src/functions/context.rs +327 -0
- package/dist-engine-src/src/functions/deterministic.rs +113 -0
- package/dist-engine-src/src/functions/mod.rs +18 -0
- package/dist-engine-src/src/functions/provider.rs +130 -0
- package/dist-engine-src/src/functions/state.rs +363 -0
- package/dist-engine-src/src/functions/types.rs +37 -0
- package/dist-engine-src/src/init.rs +505 -0
- package/dist-engine-src/src/json_store/compression.rs +77 -0
- package/dist-engine-src/src/json_store/context.rs +129 -0
- package/dist-engine-src/src/json_store/encoded.rs +15 -0
- package/dist-engine-src/src/json_store/mod.rs +9 -0
- package/dist-engine-src/src/json_store/store.rs +236 -0
- package/dist-engine-src/src/json_store/types.rs +52 -0
- package/dist-engine-src/src/lib.rs +61 -0
- package/dist-engine-src/src/live_state/context.rs +2241 -0
- package/dist-engine-src/src/live_state/mod.rs +15 -0
- package/dist-engine-src/src/live_state/overlay.rs +75 -0
- package/dist-engine-src/src/live_state/reader.rs +23 -0
- package/dist-engine-src/src/live_state/types.rs +239 -0
- package/dist-engine-src/src/live_state/visibility.rs +218 -0
- package/dist-engine-src/src/plugin/archive.rs +441 -0
- package/dist-engine-src/src/plugin/component.rs +183 -0
- package/dist-engine-src/src/plugin/install.rs +637 -0
- package/dist-engine-src/src/plugin/manifest.rs +516 -0
- package/dist-engine-src/src/plugin/materializer.rs +477 -0
- package/dist-engine-src/src/plugin/mod.rs +33 -0
- package/dist-engine-src/src/plugin/plugin_manifest.json +119 -0
- package/dist-engine-src/src/plugin/storage.rs +74 -0
- package/dist-engine-src/src/schema/annotations/defaults.rs +280 -0
- package/dist-engine-src/src/schema/annotations/mod.rs +1 -0
- package/dist-engine-src/src/schema/builtin/lix_account.json +22 -0
- package/dist-engine-src/src/schema/builtin/lix_active_account.json +30 -0
- package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +30 -0
- package/dist-engine-src/src/schema/builtin/lix_change.json +62 -0
- package/dist-engine-src/src/schema/builtin/lix_change_author.json +46 -0
- package/dist-engine-src/src/schema/builtin/lix_change_set.json +18 -0
- package/dist-engine-src/src/schema/builtin/lix_change_set_element.json +75 -0
- package/dist-engine-src/src/schema/builtin/lix_commit.json +62 -0
- package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +46 -0
- package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +53 -0
- package/dist-engine-src/src/schema/builtin/lix_entity_label.json +63 -0
- package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +53 -0
- package/dist-engine-src/src/schema/builtin/lix_key_value.json +41 -0
- package/dist-engine-src/src/schema/builtin/lix_label.json +22 -0
- package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +31 -0
- package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +35 -0
- package/dist-engine-src/src/schema/builtin/lix_version_ref.json +49 -0
- package/dist-engine-src/src/schema/builtin/mod.rs +271 -0
- package/dist-engine-src/src/schema/definition.json +157 -0
- package/dist-engine-src/src/schema/definition.rs +636 -0
- package/dist-engine-src/src/schema/key.rs +206 -0
- package/dist-engine-src/src/schema/mod.rs +20 -0
- package/dist-engine-src/src/schema/seed.rs +14 -0
- package/dist-engine-src/src/schema/tests.rs +739 -0
- package/dist-engine-src/src/schema_registry.rs +294 -0
- package/dist-engine-src/src/session/context.rs +366 -0
- package/dist-engine-src/src/session/create_version.rs +80 -0
- package/dist-engine-src/src/session/execute.rs +447 -0
- package/dist-engine-src/src/session/merge/analysis.rs +102 -0
- package/dist-engine-src/src/session/merge/apply.rs +23 -0
- package/dist-engine-src/src/session/merge/conflicts.rs +62 -0
- package/dist-engine-src/src/session/merge/mod.rs +11 -0
- package/dist-engine-src/src/session/merge/stats.rs +65 -0
- package/dist-engine-src/src/session/merge/version.rs +437 -0
- package/dist-engine-src/src/session/mod.rs +25 -0
- package/dist-engine-src/src/session/switch_version.rs +121 -0
- package/dist-engine-src/src/sql2/change_provider.rs +337 -0
- package/dist-engine-src/src/sql2/classify.rs +147 -0
- package/dist-engine-src/src/sql2/commit_derived_provider.rs +591 -0
- package/dist-engine-src/src/sql2/context.rs +307 -0
- package/dist-engine-src/src/sql2/directory_history_provider.rs +623 -0
- package/dist-engine-src/src/sql2/directory_provider.rs +2405 -0
- package/dist-engine-src/src/sql2/dml.rs +148 -0
- package/dist-engine-src/src/sql2/entity_history_provider.rs +444 -0
- package/dist-engine-src/src/sql2/entity_provider.rs +2700 -0
- package/dist-engine-src/src/sql2/error.rs +196 -0
- package/dist-engine-src/src/sql2/execute.rs +3379 -0
- package/dist-engine-src/src/sql2/file_history_provider.rs +902 -0
- package/dist-engine-src/src/sql2/file_provider.rs +3254 -0
- package/dist-engine-src/src/sql2/filesystem_planner.rs +1526 -0
- package/dist-engine-src/src/sql2/filesystem_predicates.rs +159 -0
- package/dist-engine-src/src/sql2/filesystem_visibility.rs +369 -0
- package/dist-engine-src/src/sql2/history_projection.rs +80 -0
- package/dist-engine-src/src/sql2/history_provider.rs +418 -0
- package/dist-engine-src/src/sql2/history_route.rs +643 -0
- package/dist-engine-src/src/sql2/lix_state_provider.rs +2430 -0
- package/dist-engine-src/src/sql2/mod.rs +43 -0
- package/dist-engine-src/src/sql2/read_only.rs +65 -0
- package/dist-engine-src/src/sql2/record_batch.rs +17 -0
- package/dist-engine-src/src/sql2/result_metadata.rs +29 -0
- package/dist-engine-src/src/sql2/runtime.rs +60 -0
- package/dist-engine-src/src/sql2/session.rs +135 -0
- package/dist-engine-src/src/sql2/udfs/common.rs +295 -0
- package/dist-engine-src/src/sql2/udfs/lix_active_version_commit_id.rs +53 -0
- package/dist-engine-src/src/sql2/udfs/lix_empty_blob.rs +47 -0
- package/dist-engine-src/src/sql2/udfs/lix_json.rs +100 -0
- package/dist-engine-src/src/sql2/udfs/lix_json_get.rs +99 -0
- package/dist-engine-src/src/sql2/udfs/lix_json_get_text.rs +99 -0
- package/dist-engine-src/src/sql2/udfs/lix_text_decode.rs +82 -0
- package/dist-engine-src/src/sql2/udfs/lix_text_encode.rs +85 -0
- package/dist-engine-src/src/sql2/udfs/lix_uuid_v7.rs +76 -0
- package/dist-engine-src/src/sql2/udfs/mod.rs +82 -0
- package/dist-engine-src/src/sql2/version_provider.rs +1187 -0
- package/dist-engine-src/src/sql2/version_scope.rs +394 -0
- package/dist-engine-src/src/sql2/write_normalization.rs +345 -0
- package/dist-engine-src/src/storage/context.rs +356 -0
- package/dist-engine-src/src/storage/mod.rs +14 -0
- package/dist-engine-src/src/storage/read_scope.rs +88 -0
- package/dist-engine-src/src/storage/types.rs +501 -0
- package/dist-engine-src/src/storage_bench.rs +3406 -0
- package/dist-engine-src/src/test_support.rs +81 -0
- package/dist-engine-src/src/tracked_state/by_file_index.rs +102 -0
- package/dist-engine-src/src/tracked_state/codec.rs +747 -0
- package/dist-engine-src/src/tracked_state/context.rs +983 -0
- package/dist-engine-src/src/tracked_state/diff.rs +494 -0
- package/dist-engine-src/src/tracked_state/materialization.rs +141 -0
- package/dist-engine-src/src/tracked_state/merge.rs +474 -0
- package/dist-engine-src/src/tracked_state/mod.rs +31 -0
- package/dist-engine-src/src/tracked_state/rebuild.rs +771 -0
- package/dist-engine-src/src/tracked_state/storage.rs +243 -0
- package/dist-engine-src/src/tracked_state/tree.rs +2744 -0
- package/dist-engine-src/src/tracked_state/tree_types.rs +176 -0
- package/dist-engine-src/src/tracked_state/types.rs +61 -0
- package/dist-engine-src/src/transaction/commit.rs +1224 -0
- package/dist-engine-src/src/transaction/context.rs +1307 -0
- package/dist-engine-src/src/transaction/live_state_overlay.rs +34 -0
- package/dist-engine-src/src/transaction/mod.rs +11 -0
- package/dist-engine-src/src/transaction/normalization.rs +1026 -0
- package/dist-engine-src/src/transaction/schema_resolver.rs +127 -0
- package/dist-engine-src/src/transaction/staging.rs +1436 -0
- package/dist-engine-src/src/transaction/types.rs +351 -0
- package/dist-engine-src/src/transaction/validation.rs +4811 -0
- package/dist-engine-src/src/untracked_state/codec.rs +363 -0
- package/dist-engine-src/src/untracked_state/context.rs +82 -0
- package/dist-engine-src/src/untracked_state/materialization.rs +157 -0
- package/dist-engine-src/src/untracked_state/mod.rs +17 -0
- package/dist-engine-src/src/untracked_state/storage.rs +348 -0
- package/dist-engine-src/src/untracked_state/types.rs +96 -0
- package/dist-engine-src/src/version/context.rs +52 -0
- package/dist-engine-src/src/version/mod.rs +12 -0
- package/dist-engine-src/src/version/refs.rs +421 -0
- package/dist-engine-src/src/version/stage_rows.rs +71 -0
- package/dist-engine-src/src/version/types.rs +21 -0
- package/dist-engine-src/src/wasm/mod.rs +60 -0
- package/package.json +68 -64
|
@@ -0,0 +1,3406 @@
|
|
|
1
|
+
use crate::binary_cas::{BinaryCasContext, BlobHash, BlobWrite};
|
|
2
|
+
use crate::changelog::{
|
|
3
|
+
canonicalize_materialized_change, CanonicalChange, ChangelogContext, ChangelogScanRequest,
|
|
4
|
+
MaterializedCanonicalChange,
|
|
5
|
+
};
|
|
6
|
+
use crate::entity_identity::EntityIdentity;
|
|
7
|
+
use crate::entity_identity::EntityIdentityPart;
|
|
8
|
+
use crate::json_store::context::JsonStoreContext;
|
|
9
|
+
use crate::json_store::types::{JsonProjectionPath, JsonRef};
|
|
10
|
+
use crate::storage::{
|
|
11
|
+
KvGetGroup, KvGetRequest, KvScanRange, KvScanRequest, KvWriteBatch, StorageContext,
|
|
12
|
+
StorageWriteSet,
|
|
13
|
+
};
|
|
14
|
+
use crate::tracked_state::{
|
|
15
|
+
TrackedStateContext, TrackedStateDiffRequest, TrackedStateFilter, TrackedStateProjection,
|
|
16
|
+
TrackedStateRow, TrackedStateRowRequest, TrackedStateScanRequest,
|
|
17
|
+
};
|
|
18
|
+
use crate::untracked_state::{
|
|
19
|
+
canonicalize_materialized_row, MaterializedUntrackedStateRow, UntrackedStateContext,
|
|
20
|
+
UntrackedStateFilter, UntrackedStateProjection, UntrackedStateRowRequest,
|
|
21
|
+
UntrackedStateScanRequest,
|
|
22
|
+
};
|
|
23
|
+
use crate::{Backend, LixError, NullableKeyFilter};
|
|
24
|
+
use std::sync::Arc;
|
|
25
|
+
use std::time::{Duration, Instant};
|
|
26
|
+
|
|
27
|
+
#[derive(Debug, Clone, Copy)]
|
|
28
|
+
pub struct StorageBenchConfig {
|
|
29
|
+
pub rows: usize,
|
|
30
|
+
pub blob_bytes: usize,
|
|
31
|
+
pub state_payload_bytes: usize,
|
|
32
|
+
pub key_pattern: StorageBenchKeyPattern,
|
|
33
|
+
pub selectivity: StorageBenchSelectivity,
|
|
34
|
+
pub update_fraction: StorageBenchUpdateFraction,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
impl StorageBenchConfig {
|
|
38
|
+
pub fn with_rows(mut self, rows: usize) -> Self {
|
|
39
|
+
self.rows = rows;
|
|
40
|
+
self
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pub fn with_blob_bytes(mut self, blob_bytes: usize) -> Self {
|
|
44
|
+
self.blob_bytes = blob_bytes;
|
|
45
|
+
self
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub fn with_state_payload_bytes(mut self, state_payload_bytes: usize) -> Self {
|
|
49
|
+
self.state_payload_bytes = state_payload_bytes;
|
|
50
|
+
self
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
pub fn with_key_pattern(mut self, key_pattern: StorageBenchKeyPattern) -> Self {
|
|
54
|
+
self.key_pattern = key_pattern;
|
|
55
|
+
self
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
pub fn with_selectivity(mut self, selectivity: StorageBenchSelectivity) -> Self {
|
|
59
|
+
self.selectivity = selectivity;
|
|
60
|
+
self
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
pub fn with_update_fraction(mut self, update_fraction: StorageBenchUpdateFraction) -> Self {
|
|
64
|
+
self.update_fraction = update_fraction;
|
|
65
|
+
self
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#[derive(Debug, Clone, Copy)]
|
|
70
|
+
pub enum StorageBenchKeyPattern {
|
|
71
|
+
Sequential,
|
|
72
|
+
Random,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[derive(Debug, Clone, Copy)]
|
|
76
|
+
pub enum StorageBenchSelectivity {
|
|
77
|
+
Percent1,
|
|
78
|
+
Percent10,
|
|
79
|
+
Percent100,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
impl StorageBenchSelectivity {
|
|
83
|
+
fn matches(self, index: usize) -> bool {
|
|
84
|
+
match self {
|
|
85
|
+
Self::Percent1 => index % 100 == 0,
|
|
86
|
+
Self::Percent10 => index % 10 == 0,
|
|
87
|
+
Self::Percent100 => true,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
fn expected_rows(self, rows: usize) -> usize {
|
|
92
|
+
(0..rows).filter(|index| self.matches(*index)).count()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#[derive(Debug, Clone, Copy)]
|
|
97
|
+
pub enum StorageBenchUpdateFraction {
|
|
98
|
+
Percent10,
|
|
99
|
+
Percent100,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
impl StorageBenchUpdateFraction {
|
|
103
|
+
fn rows(self, total_rows: usize) -> usize {
|
|
104
|
+
match self {
|
|
105
|
+
Self::Percent10 => total_rows.div_ceil(10),
|
|
106
|
+
Self::Percent100 => total_rows,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
#[derive(Debug, Clone, Copy)]
|
|
112
|
+
pub struct StorageBenchReport {
|
|
113
|
+
pub measured_rows: usize,
|
|
114
|
+
pub verified_rows: usize,
|
|
115
|
+
pub elapsed: Duration,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
pub struct StorageApiFixture {
|
|
119
|
+
storage: StorageContext,
|
|
120
|
+
rows: usize,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const STORAGE_API_NAMESPACE: &str = "bench.storage_api";
|
|
124
|
+
const STORAGE_API_ALT_NAMESPACE: &str = "bench.storage_api.alt";
|
|
125
|
+
|
|
126
|
+
pub async fn storage_api_write_kv_batch_puts(
|
|
127
|
+
backend: Arc<dyn Backend + Send + Sync>,
|
|
128
|
+
rows: usize,
|
|
129
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
130
|
+
let storage = StorageContext::new(backend);
|
|
131
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
132
|
+
let mut batch = KvWriteBatch::new();
|
|
133
|
+
for index in 0..rows {
|
|
134
|
+
batch.put(
|
|
135
|
+
STORAGE_API_NAMESPACE,
|
|
136
|
+
storage_api_key(index),
|
|
137
|
+
storage_api_value(index),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
let started_at = Instant::now();
|
|
141
|
+
let stats = transaction.write_kv_batch(batch).await?;
|
|
142
|
+
transaction.commit().await?;
|
|
143
|
+
Ok(StorageBenchReport {
|
|
144
|
+
measured_rows: stats.puts,
|
|
145
|
+
verified_rows: rows,
|
|
146
|
+
elapsed: started_at.elapsed(),
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
pub async fn storage_api_write_kv_batch_mixed_put_delete(
|
|
151
|
+
backend: Arc<dyn Backend + Send + Sync>,
|
|
152
|
+
rows: usize,
|
|
153
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
154
|
+
let fixture = prepare_storage_api_read(backend, rows).await?;
|
|
155
|
+
let mut transaction = fixture.storage.begin_write_transaction().await?;
|
|
156
|
+
let mut batch = KvWriteBatch::new();
|
|
157
|
+
for index in 0..rows {
|
|
158
|
+
if index % 2 == 0 {
|
|
159
|
+
batch.put(
|
|
160
|
+
STORAGE_API_NAMESPACE,
|
|
161
|
+
storage_api_key(index),
|
|
162
|
+
storage_api_updated_value(index),
|
|
163
|
+
);
|
|
164
|
+
} else {
|
|
165
|
+
batch.delete(STORAGE_API_NAMESPACE, storage_api_key(index));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
let started_at = Instant::now();
|
|
169
|
+
let stats = transaction.write_kv_batch(batch).await?;
|
|
170
|
+
transaction.commit().await?;
|
|
171
|
+
Ok(StorageBenchReport {
|
|
172
|
+
measured_rows: stats.puts + stats.deletes,
|
|
173
|
+
verified_rows: rows,
|
|
174
|
+
elapsed: started_at.elapsed(),
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
pub async fn storage_api_write_kv_batch_multi_namespace(
|
|
179
|
+
backend: Arc<dyn Backend + Send + Sync>,
|
|
180
|
+
rows: usize,
|
|
181
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
182
|
+
let storage = StorageContext::new(backend);
|
|
183
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
184
|
+
let mut batch = KvWriteBatch::new();
|
|
185
|
+
for index in 0..rows {
|
|
186
|
+
let namespace = if index % 2 == 0 {
|
|
187
|
+
STORAGE_API_NAMESPACE
|
|
188
|
+
} else {
|
|
189
|
+
STORAGE_API_ALT_NAMESPACE
|
|
190
|
+
};
|
|
191
|
+
batch.put(namespace, storage_api_key(index), storage_api_value(index));
|
|
192
|
+
}
|
|
193
|
+
let started_at = Instant::now();
|
|
194
|
+
let stats = transaction.write_kv_batch(batch).await?;
|
|
195
|
+
transaction.commit().await?;
|
|
196
|
+
Ok(StorageBenchReport {
|
|
197
|
+
measured_rows: stats.puts,
|
|
198
|
+
verified_rows: rows,
|
|
199
|
+
elapsed: started_at.elapsed(),
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
pub async fn storage_api_write_kv_batch_duplicate_keys(
|
|
204
|
+
backend: Arc<dyn Backend + Send + Sync>,
|
|
205
|
+
rows: usize,
|
|
206
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
207
|
+
let storage = StorageContext::new(backend);
|
|
208
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
209
|
+
let mut batch = KvWriteBatch::new();
|
|
210
|
+
for index in 0..rows {
|
|
211
|
+
batch.put(
|
|
212
|
+
STORAGE_API_NAMESPACE,
|
|
213
|
+
storage_api_key(index % 100),
|
|
214
|
+
storage_api_value(index),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
let started_at = Instant::now();
|
|
218
|
+
let stats = transaction.write_kv_batch(batch).await?;
|
|
219
|
+
transaction.commit().await?;
|
|
220
|
+
Ok(StorageBenchReport {
|
|
221
|
+
measured_rows: stats.puts,
|
|
222
|
+
verified_rows: rows,
|
|
223
|
+
elapsed: started_at.elapsed(),
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
pub async fn storage_api_write_kv_batch_value_size(
|
|
228
|
+
backend: Arc<dyn Backend + Send + Sync>,
|
|
229
|
+
rows: usize,
|
|
230
|
+
value_bytes: usize,
|
|
231
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
232
|
+
let storage = StorageContext::new(backend);
|
|
233
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
234
|
+
let mut batch = KvWriteBatch::new();
|
|
235
|
+
for index in 0..rows {
|
|
236
|
+
batch.put(
|
|
237
|
+
STORAGE_API_NAMESPACE,
|
|
238
|
+
storage_api_key(index),
|
|
239
|
+
storage_api_value_with_bytes(index, value_bytes),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
let started_at = Instant::now();
|
|
243
|
+
let stats = transaction.write_kv_batch(batch).await?;
|
|
244
|
+
transaction.commit().await?;
|
|
245
|
+
Ok(StorageBenchReport {
|
|
246
|
+
measured_rows: stats.puts,
|
|
247
|
+
verified_rows: rows,
|
|
248
|
+
elapsed: started_at.elapsed(),
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
pub async fn storage_api_write_and_commit(
|
|
253
|
+
backend: Arc<dyn Backend + Send + Sync>,
|
|
254
|
+
rows: usize,
|
|
255
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
256
|
+
let storage = StorageContext::new(backend);
|
|
257
|
+
let started_at = Instant::now();
|
|
258
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
259
|
+
let mut batch = KvWriteBatch::new();
|
|
260
|
+
for index in 0..rows {
|
|
261
|
+
batch.put(
|
|
262
|
+
STORAGE_API_NAMESPACE,
|
|
263
|
+
storage_api_key(index),
|
|
264
|
+
storage_api_value(index),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
let stats = transaction.write_kv_batch(batch).await?;
|
|
268
|
+
transaction.commit().await?;
|
|
269
|
+
Ok(StorageBenchReport {
|
|
270
|
+
measured_rows: stats.puts,
|
|
271
|
+
verified_rows: rows,
|
|
272
|
+
elapsed: started_at.elapsed(),
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
pub async fn storage_api_rollback_after_write(
|
|
277
|
+
backend: Arc<dyn Backend + Send + Sync>,
|
|
278
|
+
rows: usize,
|
|
279
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
280
|
+
let storage = StorageContext::new(backend);
|
|
281
|
+
let started_at = Instant::now();
|
|
282
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
283
|
+
let mut batch = KvWriteBatch::new();
|
|
284
|
+
for index in 0..rows {
|
|
285
|
+
batch.put(
|
|
286
|
+
STORAGE_API_NAMESPACE,
|
|
287
|
+
storage_api_key(index),
|
|
288
|
+
storage_api_value(index),
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
let stats = transaction.write_kv_batch(batch).await?;
|
|
292
|
+
transaction.rollback().await?;
|
|
293
|
+
Ok(StorageBenchReport {
|
|
294
|
+
measured_rows: stats.puts,
|
|
295
|
+
verified_rows: rows,
|
|
296
|
+
elapsed: started_at.elapsed(),
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
pub async fn prepare_storage_api_read(
|
|
301
|
+
backend: Arc<dyn Backend + Send + Sync>,
|
|
302
|
+
rows: usize,
|
|
303
|
+
) -> Result<StorageApiFixture, LixError> {
|
|
304
|
+
let storage = StorageContext::new(backend);
|
|
305
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
306
|
+
let mut batch = KvWriteBatch::new();
|
|
307
|
+
for index in 0..rows {
|
|
308
|
+
batch.put(
|
|
309
|
+
STORAGE_API_NAMESPACE,
|
|
310
|
+
storage_api_key(index),
|
|
311
|
+
storage_api_value(index),
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
transaction.write_kv_batch(batch).await?;
|
|
315
|
+
transaction.commit().await?;
|
|
316
|
+
Ok(StorageApiFixture { storage, rows })
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
pub async fn storage_api_get_values_hits_prepared(
|
|
320
|
+
fixture: &StorageApiFixture,
|
|
321
|
+
reads: usize,
|
|
322
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
323
|
+
let mut transaction = fixture.storage.begin_read_transaction().await?;
|
|
324
|
+
let keys = (0..reads)
|
|
325
|
+
.map(|index| storage_api_key(index % fixture.rows))
|
|
326
|
+
.collect::<Vec<_>>();
|
|
327
|
+
let started_at = Instant::now();
|
|
328
|
+
let result = transaction
|
|
329
|
+
.get_values(KvGetRequest {
|
|
330
|
+
groups: vec![KvGetGroup {
|
|
331
|
+
namespace: STORAGE_API_NAMESPACE.to_string(),
|
|
332
|
+
keys,
|
|
333
|
+
}],
|
|
334
|
+
})
|
|
335
|
+
.await?;
|
|
336
|
+
transaction.rollback().await?;
|
|
337
|
+
let verified_rows = result.groups[0]
|
|
338
|
+
.values_iter()
|
|
339
|
+
.filter(|value| value.is_some())
|
|
340
|
+
.count();
|
|
341
|
+
Ok(StorageBenchReport {
|
|
342
|
+
measured_rows: reads,
|
|
343
|
+
verified_rows,
|
|
344
|
+
elapsed: started_at.elapsed(),
|
|
345
|
+
})
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
pub async fn storage_api_exists_many_prepared(
|
|
349
|
+
fixture: &StorageApiFixture,
|
|
350
|
+
reads: usize,
|
|
351
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
352
|
+
let mut transaction = fixture.storage.begin_read_transaction().await?;
|
|
353
|
+
let keys = (0..reads)
|
|
354
|
+
.map(|index| storage_api_key(index % fixture.rows))
|
|
355
|
+
.collect::<Vec<_>>();
|
|
356
|
+
let started_at = Instant::now();
|
|
357
|
+
let result = transaction
|
|
358
|
+
.exists_many(KvGetRequest {
|
|
359
|
+
groups: vec![KvGetGroup {
|
|
360
|
+
namespace: STORAGE_API_NAMESPACE.to_string(),
|
|
361
|
+
keys,
|
|
362
|
+
}],
|
|
363
|
+
})
|
|
364
|
+
.await?;
|
|
365
|
+
transaction.rollback().await?;
|
|
366
|
+
let verified_rows = result.groups[0]
|
|
367
|
+
.exists
|
|
368
|
+
.iter()
|
|
369
|
+
.filter(|exists| **exists)
|
|
370
|
+
.count();
|
|
371
|
+
Ok(StorageBenchReport {
|
|
372
|
+
measured_rows: reads,
|
|
373
|
+
verified_rows,
|
|
374
|
+
elapsed: started_at.elapsed(),
|
|
375
|
+
})
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
pub async fn storage_api_get_values_misses_prepared(
|
|
379
|
+
fixture: &StorageApiFixture,
|
|
380
|
+
reads: usize,
|
|
381
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
382
|
+
let mut transaction = fixture.storage.begin_read_transaction().await?;
|
|
383
|
+
let keys = (0..reads)
|
|
384
|
+
.map(|index| storage_api_missing_key(index))
|
|
385
|
+
.collect::<Vec<_>>();
|
|
386
|
+
let started_at = Instant::now();
|
|
387
|
+
let result = transaction
|
|
388
|
+
.get_values(KvGetRequest {
|
|
389
|
+
groups: vec![KvGetGroup {
|
|
390
|
+
namespace: STORAGE_API_NAMESPACE.to_string(),
|
|
391
|
+
keys,
|
|
392
|
+
}],
|
|
393
|
+
})
|
|
394
|
+
.await?;
|
|
395
|
+
transaction.rollback().await?;
|
|
396
|
+
let verified_rows = result.groups[0]
|
|
397
|
+
.values_iter()
|
|
398
|
+
.filter(|value| value.is_none())
|
|
399
|
+
.count();
|
|
400
|
+
Ok(StorageBenchReport {
|
|
401
|
+
measured_rows: reads,
|
|
402
|
+
verified_rows,
|
|
403
|
+
elapsed: started_at.elapsed(),
|
|
404
|
+
})
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
pub async fn storage_api_get_values_mixed_hit_miss_prepared(
|
|
408
|
+
fixture: &StorageApiFixture,
|
|
409
|
+
reads: usize,
|
|
410
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
411
|
+
let mut transaction = fixture.storage.begin_read_transaction().await?;
|
|
412
|
+
let keys = (0..reads)
|
|
413
|
+
.map(|index| {
|
|
414
|
+
if index % 2 == 0 {
|
|
415
|
+
storage_api_key(index % fixture.rows)
|
|
416
|
+
} else {
|
|
417
|
+
storage_api_missing_key(index)
|
|
418
|
+
}
|
|
419
|
+
})
|
|
420
|
+
.collect::<Vec<_>>();
|
|
421
|
+
let started_at = Instant::now();
|
|
422
|
+
let result = transaction
|
|
423
|
+
.get_values(KvGetRequest {
|
|
424
|
+
groups: vec![KvGetGroup {
|
|
425
|
+
namespace: STORAGE_API_NAMESPACE.to_string(),
|
|
426
|
+
keys,
|
|
427
|
+
}],
|
|
428
|
+
})
|
|
429
|
+
.await?;
|
|
430
|
+
transaction.rollback().await?;
|
|
431
|
+
let verified_rows = result.groups[0]
|
|
432
|
+
.values_iter()
|
|
433
|
+
.filter(|value| value.is_some())
|
|
434
|
+
.count();
|
|
435
|
+
Ok(StorageBenchReport {
|
|
436
|
+
measured_rows: reads,
|
|
437
|
+
verified_rows,
|
|
438
|
+
elapsed: started_at.elapsed(),
|
|
439
|
+
})
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
pub async fn storage_api_get_values_multi_namespace(
|
|
443
|
+
backend: Arc<dyn Backend + Send + Sync>,
|
|
444
|
+
reads: usize,
|
|
445
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
446
|
+
let storage = StorageContext::new(backend);
|
|
447
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
448
|
+
let mut batch = KvWriteBatch::new();
|
|
449
|
+
for index in 0..reads {
|
|
450
|
+
let namespace = if index % 2 == 0 {
|
|
451
|
+
STORAGE_API_NAMESPACE
|
|
452
|
+
} else {
|
|
453
|
+
STORAGE_API_ALT_NAMESPACE
|
|
454
|
+
};
|
|
455
|
+
batch.put(namespace, storage_api_key(index), storage_api_value(index));
|
|
456
|
+
}
|
|
457
|
+
transaction.write_kv_batch(batch).await?;
|
|
458
|
+
transaction.commit().await?;
|
|
459
|
+
|
|
460
|
+
let mut transaction = storage.begin_read_transaction().await?;
|
|
461
|
+
let even_keys = (0..reads)
|
|
462
|
+
.step_by(2)
|
|
463
|
+
.map(storage_api_key)
|
|
464
|
+
.collect::<Vec<_>>();
|
|
465
|
+
let odd_keys = (1..reads)
|
|
466
|
+
.step_by(2)
|
|
467
|
+
.map(storage_api_key)
|
|
468
|
+
.collect::<Vec<_>>();
|
|
469
|
+
let started_at = Instant::now();
|
|
470
|
+
let result = transaction
|
|
471
|
+
.get_values(KvGetRequest {
|
|
472
|
+
groups: vec![
|
|
473
|
+
KvGetGroup {
|
|
474
|
+
namespace: STORAGE_API_NAMESPACE.to_string(),
|
|
475
|
+
keys: even_keys,
|
|
476
|
+
},
|
|
477
|
+
KvGetGroup {
|
|
478
|
+
namespace: STORAGE_API_ALT_NAMESPACE.to_string(),
|
|
479
|
+
keys: odd_keys,
|
|
480
|
+
},
|
|
481
|
+
],
|
|
482
|
+
})
|
|
483
|
+
.await?;
|
|
484
|
+
transaction.rollback().await?;
|
|
485
|
+
let verified_rows = result
|
|
486
|
+
.groups
|
|
487
|
+
.iter()
|
|
488
|
+
.map(|group| group.values_iter().filter(|value| value.is_some()).count())
|
|
489
|
+
.sum();
|
|
490
|
+
Ok(StorageBenchReport {
|
|
491
|
+
measured_rows: reads,
|
|
492
|
+
verified_rows,
|
|
493
|
+
elapsed: started_at.elapsed(),
|
|
494
|
+
})
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
pub async fn storage_api_get_values_duplicate_keys_prepared(
|
|
498
|
+
fixture: &StorageApiFixture,
|
|
499
|
+
reads: usize,
|
|
500
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
501
|
+
let mut transaction = fixture.storage.begin_read_transaction().await?;
|
|
502
|
+
let keys = (0..reads)
|
|
503
|
+
.map(|index| storage_api_key(index % 100))
|
|
504
|
+
.collect::<Vec<_>>();
|
|
505
|
+
let started_at = Instant::now();
|
|
506
|
+
let result = transaction
|
|
507
|
+
.get_values(KvGetRequest {
|
|
508
|
+
groups: vec![KvGetGroup {
|
|
509
|
+
namespace: STORAGE_API_NAMESPACE.to_string(),
|
|
510
|
+
keys,
|
|
511
|
+
}],
|
|
512
|
+
})
|
|
513
|
+
.await?;
|
|
514
|
+
transaction.rollback().await?;
|
|
515
|
+
let verified_rows = result.groups[0]
|
|
516
|
+
.values_iter()
|
|
517
|
+
.filter(|value| value.is_some())
|
|
518
|
+
.count();
|
|
519
|
+
Ok(StorageBenchReport {
|
|
520
|
+
measured_rows: reads,
|
|
521
|
+
verified_rows,
|
|
522
|
+
elapsed: started_at.elapsed(),
|
|
523
|
+
})
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
pub async fn storage_api_scan_keys_prefix_prepared(
|
|
527
|
+
fixture: &StorageApiFixture,
|
|
528
|
+
limit: usize,
|
|
529
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
530
|
+
let mut transaction = fixture.storage.begin_read_transaction().await?;
|
|
531
|
+
let started_at = Instant::now();
|
|
532
|
+
let result = transaction
|
|
533
|
+
.scan_keys(KvScanRequest {
|
|
534
|
+
namespace: STORAGE_API_NAMESPACE.to_string(),
|
|
535
|
+
range: KvScanRange::prefix(b"key/".to_vec()),
|
|
536
|
+
after: None,
|
|
537
|
+
limit,
|
|
538
|
+
})
|
|
539
|
+
.await?;
|
|
540
|
+
transaction.rollback().await?;
|
|
541
|
+
Ok(StorageBenchReport {
|
|
542
|
+
measured_rows: result.keys.len(),
|
|
543
|
+
verified_rows: limit.min(fixture.rows),
|
|
544
|
+
elapsed: started_at.elapsed(),
|
|
545
|
+
})
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
pub async fn storage_api_scan_keys_after_pages_prepared(
|
|
549
|
+
fixture: &StorageApiFixture,
|
|
550
|
+
page_size: usize,
|
|
551
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
552
|
+
let mut transaction = fixture.storage.begin_read_transaction().await?;
|
|
553
|
+
let started_at = Instant::now();
|
|
554
|
+
let mut after = None;
|
|
555
|
+
let mut measured_rows = 0usize;
|
|
556
|
+
loop {
|
|
557
|
+
let result = transaction
|
|
558
|
+
.scan_keys(KvScanRequest {
|
|
559
|
+
namespace: STORAGE_API_NAMESPACE.to_string(),
|
|
560
|
+
range: KvScanRange::prefix(b"key/".to_vec()),
|
|
561
|
+
after,
|
|
562
|
+
limit: page_size,
|
|
563
|
+
})
|
|
564
|
+
.await?;
|
|
565
|
+
if result.keys.is_empty() {
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
measured_rows += result.keys.len();
|
|
569
|
+
let Some(resume_after) = result.resume_after else {
|
|
570
|
+
break;
|
|
571
|
+
};
|
|
572
|
+
after = Some(resume_after);
|
|
573
|
+
}
|
|
574
|
+
transaction.rollback().await?;
|
|
575
|
+
Ok(StorageBenchReport {
|
|
576
|
+
measured_rows,
|
|
577
|
+
verified_rows: fixture.rows,
|
|
578
|
+
elapsed: started_at.elapsed(),
|
|
579
|
+
})
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
pub async fn storage_api_scan_keys_empty_range_prepared(
|
|
583
|
+
fixture: &StorageApiFixture,
|
|
584
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
585
|
+
let mut transaction = fixture.storage.begin_read_transaction().await?;
|
|
586
|
+
let started_at = Instant::now();
|
|
587
|
+
let result = transaction
|
|
588
|
+
.scan_keys(KvScanRequest {
|
|
589
|
+
namespace: STORAGE_API_NAMESPACE.to_string(),
|
|
590
|
+
range: KvScanRange::prefix(b"absent/".to_vec()),
|
|
591
|
+
after: None,
|
|
592
|
+
limit: fixture.rows,
|
|
593
|
+
})
|
|
594
|
+
.await?;
|
|
595
|
+
transaction.rollback().await?;
|
|
596
|
+
Ok(StorageBenchReport {
|
|
597
|
+
measured_rows: result.keys.len(),
|
|
598
|
+
verified_rows: 0,
|
|
599
|
+
elapsed: started_at.elapsed(),
|
|
600
|
+
})
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
pub async fn prepare_storage_api_selective_scan(
|
|
604
|
+
backend: Arc<dyn Backend + Send + Sync>,
|
|
605
|
+
rows: usize,
|
|
606
|
+
selectivity: StorageBenchSelectivity,
|
|
607
|
+
) -> Result<StorageApiFixture, LixError> {
|
|
608
|
+
let storage = StorageContext::new(backend);
|
|
609
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
610
|
+
let mut batch = KvWriteBatch::new();
|
|
611
|
+
for index in 0..rows {
|
|
612
|
+
let key = if selectivity.matches(index) {
|
|
613
|
+
storage_api_selective_key(index)
|
|
614
|
+
} else {
|
|
615
|
+
storage_api_key(index)
|
|
616
|
+
};
|
|
617
|
+
batch.put(STORAGE_API_NAMESPACE, key, storage_api_value(index));
|
|
618
|
+
}
|
|
619
|
+
transaction.write_kv_batch(batch).await?;
|
|
620
|
+
transaction.commit().await?;
|
|
621
|
+
Ok(StorageApiFixture { storage, rows })
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
pub async fn storage_api_scan_keys_selective_prefix_prepared(
|
|
625
|
+
fixture: &StorageApiFixture,
|
|
626
|
+
selectivity: StorageBenchSelectivity,
|
|
627
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
628
|
+
let mut transaction = fixture.storage.begin_read_transaction().await?;
|
|
629
|
+
let started_at = Instant::now();
|
|
630
|
+
let result = transaction
|
|
631
|
+
.scan_keys(KvScanRequest {
|
|
632
|
+
namespace: STORAGE_API_NAMESPACE.to_string(),
|
|
633
|
+
range: KvScanRange::prefix(b"selective/".to_vec()),
|
|
634
|
+
after: None,
|
|
635
|
+
limit: fixture.rows,
|
|
636
|
+
})
|
|
637
|
+
.await?;
|
|
638
|
+
transaction.rollback().await?;
|
|
639
|
+
Ok(StorageBenchReport {
|
|
640
|
+
measured_rows: result.keys.len(),
|
|
641
|
+
verified_rows: selectivity.expected_rows(fixture.rows),
|
|
642
|
+
elapsed: started_at.elapsed(),
|
|
643
|
+
})
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
pub async fn storage_api_transaction_commit_empty(
|
|
647
|
+
backend: Arc<dyn Backend + Send + Sync>,
|
|
648
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
649
|
+
let storage = StorageContext::new(backend);
|
|
650
|
+
let started_at = Instant::now();
|
|
651
|
+
let transaction = storage.begin_write_transaction().await?;
|
|
652
|
+
transaction.commit().await?;
|
|
653
|
+
Ok(StorageBenchReport {
|
|
654
|
+
measured_rows: 0,
|
|
655
|
+
verified_rows: 0,
|
|
656
|
+
elapsed: started_at.elapsed(),
|
|
657
|
+
})
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
fn storage_api_key(index: usize) -> Vec<u8> {
|
|
661
|
+
format!("key/{index:08}").into_bytes()
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
fn storage_api_selective_key(index: usize) -> Vec<u8> {
|
|
665
|
+
format!("selective/{index:08}").into_bytes()
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
fn storage_api_missing_key(index: usize) -> Vec<u8> {
|
|
669
|
+
format!("missing/{index:08}").into_bytes()
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
fn storage_api_value(index: usize) -> Vec<u8> {
|
|
673
|
+
format!("value/{index:08}/{}", "x".repeat(64)).into_bytes()
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
fn storage_api_value_with_bytes(index: usize, value_bytes: usize) -> Vec<u8> {
|
|
677
|
+
let prefix = format!("value/{index:08}/");
|
|
678
|
+
if value_bytes <= prefix.len() {
|
|
679
|
+
return prefix.into_bytes();
|
|
680
|
+
}
|
|
681
|
+
let mut value = prefix.into_bytes();
|
|
682
|
+
value.extend(std::iter::repeat_n(b'x', value_bytes - value.len()));
|
|
683
|
+
value
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
fn storage_api_updated_value(index: usize) -> Vec<u8> {
|
|
687
|
+
format!("updated/{index:08}/{}", "y".repeat(64)).into_bytes()
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
pub struct TrackedStateWriteRootFixture {
|
|
691
|
+
context: TrackedStateContext,
|
|
692
|
+
rows: Vec<TrackedStateRow>,
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
pub struct TrackedStateReadFixture {
|
|
696
|
+
context: TrackedStateContext,
|
|
697
|
+
rows: usize,
|
|
698
|
+
commit_id: String,
|
|
699
|
+
key_pattern: StorageBenchKeyPattern,
|
|
700
|
+
selectivity: StorageBenchSelectivity,
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
pub struct TrackedStateUpdateFixture {
|
|
704
|
+
context: TrackedStateContext,
|
|
705
|
+
rows: Vec<TrackedStateRow>,
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
pub struct TrackedStateDiffFixture {
|
|
709
|
+
context: TrackedStateContext,
|
|
710
|
+
left_commit_id: String,
|
|
711
|
+
right_commit_id: String,
|
|
712
|
+
expected_entries: usize,
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
pub struct UntrackedStateWriteFixture {
|
|
716
|
+
context: UntrackedStateContext,
|
|
717
|
+
rows: Vec<MaterializedUntrackedStateRow>,
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
pub struct UntrackedStateReadFixture {
|
|
721
|
+
context: UntrackedStateContext,
|
|
722
|
+
rows: usize,
|
|
723
|
+
key_pattern: StorageBenchKeyPattern,
|
|
724
|
+
selectivity: StorageBenchSelectivity,
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
pub struct ChangelogAppendFixture {
|
|
728
|
+
context: ChangelogContext,
|
|
729
|
+
changes: Vec<MaterializedCanonicalChange>,
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
pub struct ChangelogReadFixture {
|
|
733
|
+
context: ChangelogContext,
|
|
734
|
+
rows: usize,
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
pub struct ChangelogCodecFixture {
|
|
738
|
+
changes: Vec<CanonicalChange>,
|
|
739
|
+
encoded_changes: Vec<Vec<u8>>,
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
pub struct BinaryCasWriteFixture {
|
|
743
|
+
context: BinaryCasContext,
|
|
744
|
+
file_ids: Vec<String>,
|
|
745
|
+
payloads: Vec<Vec<u8>>,
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
pub struct BinaryCasReadFixture {
|
|
749
|
+
context: BinaryCasContext,
|
|
750
|
+
rows: usize,
|
|
751
|
+
hashes: Vec<BlobHash>,
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
#[derive(Debug, Clone, Copy)]
|
|
755
|
+
pub enum JsonStorePayloadShape {
|
|
756
|
+
SmallRaw1k,
|
|
757
|
+
MediumStructured16k,
|
|
758
|
+
LargeStructured128k,
|
|
759
|
+
LargeArray128k,
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
#[derive(Debug, Clone, Copy)]
|
|
763
|
+
pub enum JsonStoreProjectionShape {
|
|
764
|
+
TopLevelTarget,
|
|
765
|
+
TopLevelTenProps,
|
|
766
|
+
NestedTarget,
|
|
767
|
+
ArrayItem999,
|
|
768
|
+
Status,
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
pub struct JsonStoreWriteFixture {
|
|
772
|
+
context: JsonStoreContext,
|
|
773
|
+
documents: Vec<Vec<u8>>,
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
pub struct JsonStoreReadFixture {
|
|
777
|
+
context: JsonStoreContext,
|
|
778
|
+
refs: Vec<JsonRef>,
|
|
779
|
+
paths: Vec<JsonProjectionPath>,
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
pub async fn prepare_tracked_state_write_root(
|
|
783
|
+
config: StorageBenchConfig,
|
|
784
|
+
) -> Result<TrackedStateWriteRootFixture, LixError> {
|
|
785
|
+
Ok(TrackedStateWriteRootFixture {
|
|
786
|
+
context: TrackedStateContext::new(),
|
|
787
|
+
rows: tracked_rows(config, "bench-tracked-commit"),
|
|
788
|
+
})
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
pub async fn tracked_state_write_root_prepared(
|
|
792
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
793
|
+
fixture: &TrackedStateWriteRootFixture,
|
|
794
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
795
|
+
write_tracked_root(
|
|
796
|
+
backend,
|
|
797
|
+
&fixture.context,
|
|
798
|
+
"bench-tracked-commit",
|
|
799
|
+
None,
|
|
800
|
+
&fixture.rows,
|
|
801
|
+
)
|
|
802
|
+
.await?;
|
|
803
|
+
Ok(report(
|
|
804
|
+
fixture.rows.len(),
|
|
805
|
+
fixture.rows.len(),
|
|
806
|
+
Duration::ZERO,
|
|
807
|
+
))
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
pub async fn prepare_tracked_state_read(
|
|
811
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
812
|
+
config: StorageBenchConfig,
|
|
813
|
+
) -> Result<TrackedStateReadFixture, LixError> {
|
|
814
|
+
let context = TrackedStateContext::new();
|
|
815
|
+
let rows = tracked_rows(config, "bench-tracked-commit");
|
|
816
|
+
write_tracked_root(backend, &context, "bench-tracked-commit", None, &rows).await?;
|
|
817
|
+
Ok(TrackedStateReadFixture {
|
|
818
|
+
context,
|
|
819
|
+
rows: config.rows,
|
|
820
|
+
commit_id: "bench-tracked-commit".to_string(),
|
|
821
|
+
key_pattern: config.key_pattern,
|
|
822
|
+
selectivity: config.selectivity,
|
|
823
|
+
})
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
pub async fn prepare_tracked_state_read_file_selective(
|
|
827
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
828
|
+
config: StorageBenchConfig,
|
|
829
|
+
) -> Result<TrackedStateReadFixture, LixError> {
|
|
830
|
+
let context = TrackedStateContext::new();
|
|
831
|
+
let rows = tracked_rows_file_selective(config, "bench-tracked-commit");
|
|
832
|
+
write_tracked_root(backend, &context, "bench-tracked-commit", None, &rows).await?;
|
|
833
|
+
Ok(TrackedStateReadFixture {
|
|
834
|
+
context,
|
|
835
|
+
rows: config.rows,
|
|
836
|
+
commit_id: "bench-tracked-commit".to_string(),
|
|
837
|
+
key_pattern: config.key_pattern,
|
|
838
|
+
selectivity: config.selectivity,
|
|
839
|
+
})
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
pub async fn tracked_state_read_point_hit_prepared(
|
|
843
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
844
|
+
fixture: &TrackedStateReadFixture,
|
|
845
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
846
|
+
let mut verified_rows = 0;
|
|
847
|
+
let mut reader = fixture
|
|
848
|
+
.context
|
|
849
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
850
|
+
for index in 0..fixture.rows {
|
|
851
|
+
if reader
|
|
852
|
+
.load_row_at_commit(
|
|
853
|
+
&fixture.commit_id,
|
|
854
|
+
&TrackedStateRowRequest {
|
|
855
|
+
schema_key: tracked_schema_key(index, StorageBenchSelectivity::Percent100),
|
|
856
|
+
entity_id: EntityIdentity::single(entity_id(
|
|
857
|
+
"tracked",
|
|
858
|
+
index,
|
|
859
|
+
fixture.key_pattern,
|
|
860
|
+
)),
|
|
861
|
+
file_id: NullableKeyFilter::Value("bench.json".to_string()),
|
|
862
|
+
},
|
|
863
|
+
)
|
|
864
|
+
.await?
|
|
865
|
+
.is_some()
|
|
866
|
+
{
|
|
867
|
+
verified_rows += 1;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
pub async fn tracked_state_read_point_hit_constant_prepared(
|
|
874
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
875
|
+
fixture: &TrackedStateReadFixture,
|
|
876
|
+
measured_reads: usize,
|
|
877
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
878
|
+
let mut verified_rows = 0;
|
|
879
|
+
let mut reader = fixture
|
|
880
|
+
.context
|
|
881
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
882
|
+
for index in 0..measured_reads.min(fixture.rows) {
|
|
883
|
+
if reader
|
|
884
|
+
.load_row_at_commit(
|
|
885
|
+
&fixture.commit_id,
|
|
886
|
+
&TrackedStateRowRequest {
|
|
887
|
+
schema_key: tracked_schema_key(index, StorageBenchSelectivity::Percent100),
|
|
888
|
+
entity_id: EntityIdentity::single(entity_id(
|
|
889
|
+
"tracked",
|
|
890
|
+
index,
|
|
891
|
+
fixture.key_pattern,
|
|
892
|
+
)),
|
|
893
|
+
file_id: NullableKeyFilter::Value("bench.json".to_string()),
|
|
894
|
+
},
|
|
895
|
+
)
|
|
896
|
+
.await?
|
|
897
|
+
.is_some()
|
|
898
|
+
{
|
|
899
|
+
verified_rows += 1;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
Ok(report(
|
|
903
|
+
measured_reads.min(fixture.rows),
|
|
904
|
+
verified_rows,
|
|
905
|
+
Duration::ZERO,
|
|
906
|
+
))
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
pub async fn tracked_state_read_point_miss_prepared(
|
|
910
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
911
|
+
fixture: &TrackedStateReadFixture,
|
|
912
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
913
|
+
let mut misses = 0;
|
|
914
|
+
let mut reader = fixture
|
|
915
|
+
.context
|
|
916
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
917
|
+
for index in 0..fixture.rows {
|
|
918
|
+
if reader
|
|
919
|
+
.load_row_at_commit(
|
|
920
|
+
&fixture.commit_id,
|
|
921
|
+
&TrackedStateRowRequest {
|
|
922
|
+
schema_key: "bench_tracked_entity".to_string(),
|
|
923
|
+
entity_id: EntityIdentity::single(format!("missing-{index}")),
|
|
924
|
+
file_id: NullableKeyFilter::Value("bench.json".to_string()),
|
|
925
|
+
},
|
|
926
|
+
)
|
|
927
|
+
.await?
|
|
928
|
+
.is_none()
|
|
929
|
+
{
|
|
930
|
+
misses += 1;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
Ok(report(fixture.rows, misses, Duration::ZERO))
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
pub async fn tracked_state_scan_all_prepared(
|
|
937
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
938
|
+
fixture: &TrackedStateReadFixture,
|
|
939
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
940
|
+
let verified_rows = scan_tracked(backend, &fixture.context, &fixture.commit_id)
|
|
941
|
+
.await?
|
|
942
|
+
.len();
|
|
943
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
pub async fn tracked_state_scan_keys_only_prepared(
|
|
947
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
948
|
+
fixture: &TrackedStateReadFixture,
|
|
949
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
950
|
+
let mut reader = fixture
|
|
951
|
+
.context
|
|
952
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
953
|
+
let verified_rows = reader
|
|
954
|
+
.scan_rows_at_commit(
|
|
955
|
+
&fixture.commit_id,
|
|
956
|
+
&TrackedStateScanRequest {
|
|
957
|
+
projection: TrackedStateProjection {
|
|
958
|
+
columns: vec!["entity_id".to_string()],
|
|
959
|
+
},
|
|
960
|
+
..Default::default()
|
|
961
|
+
},
|
|
962
|
+
)
|
|
963
|
+
.await?
|
|
964
|
+
.len();
|
|
965
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
pub async fn tracked_state_scan_headers_only_prepared(
|
|
969
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
970
|
+
fixture: &TrackedStateReadFixture,
|
|
971
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
972
|
+
let mut reader = fixture
|
|
973
|
+
.context
|
|
974
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
975
|
+
let verified_rows = reader
|
|
976
|
+
.scan_rows_at_commit(
|
|
977
|
+
&fixture.commit_id,
|
|
978
|
+
&TrackedStateScanRequest {
|
|
979
|
+
projection: TrackedStateProjection {
|
|
980
|
+
columns: tracked_state_header_columns(),
|
|
981
|
+
},
|
|
982
|
+
..Default::default()
|
|
983
|
+
},
|
|
984
|
+
)
|
|
985
|
+
.await?
|
|
986
|
+
.len();
|
|
987
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
pub async fn tracked_state_scan_full_rows_prepared(
|
|
991
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
992
|
+
fixture: &TrackedStateReadFixture,
|
|
993
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
994
|
+
tracked_state_scan_all_prepared(backend, fixture).await
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
pub async fn tracked_state_scan_schema_prepared(
|
|
998
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
999
|
+
fixture: &TrackedStateReadFixture,
|
|
1000
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1001
|
+
let mut reader = fixture
|
|
1002
|
+
.context
|
|
1003
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1004
|
+
let verified_rows = reader
|
|
1005
|
+
.scan_rows_at_commit(
|
|
1006
|
+
&fixture.commit_id,
|
|
1007
|
+
&TrackedStateScanRequest {
|
|
1008
|
+
filter: TrackedStateFilter {
|
|
1009
|
+
schema_keys: vec![tracked_schema_key(0, StorageBenchSelectivity::Percent100)],
|
|
1010
|
+
..Default::default()
|
|
1011
|
+
},
|
|
1012
|
+
..Default::default()
|
|
1013
|
+
},
|
|
1014
|
+
)
|
|
1015
|
+
.await?
|
|
1016
|
+
.len();
|
|
1017
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
pub async fn tracked_state_scan_schema_selective_prepared(
|
|
1021
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1022
|
+
fixture: &TrackedStateReadFixture,
|
|
1023
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1024
|
+
let mut reader = fixture
|
|
1025
|
+
.context
|
|
1026
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1027
|
+
let verified_rows = reader
|
|
1028
|
+
.scan_rows_at_commit(
|
|
1029
|
+
&fixture.commit_id,
|
|
1030
|
+
&TrackedStateScanRequest {
|
|
1031
|
+
filter: TrackedStateFilter {
|
|
1032
|
+
schema_keys: vec![TRACKED_MATCH_SCHEMA_KEY.to_string()],
|
|
1033
|
+
..Default::default()
|
|
1034
|
+
},
|
|
1035
|
+
..Default::default()
|
|
1036
|
+
},
|
|
1037
|
+
)
|
|
1038
|
+
.await?
|
|
1039
|
+
.len();
|
|
1040
|
+
Ok(report(
|
|
1041
|
+
fixture.selectivity.expected_rows(fixture.rows),
|
|
1042
|
+
verified_rows,
|
|
1043
|
+
Duration::ZERO,
|
|
1044
|
+
))
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
pub async fn tracked_state_scan_file_prepared(
|
|
1048
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1049
|
+
fixture: &TrackedStateReadFixture,
|
|
1050
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1051
|
+
let mut reader = fixture
|
|
1052
|
+
.context
|
|
1053
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1054
|
+
let verified_rows = reader
|
|
1055
|
+
.scan_rows_at_commit(
|
|
1056
|
+
&fixture.commit_id,
|
|
1057
|
+
&TrackedStateScanRequest {
|
|
1058
|
+
filter: TrackedStateFilter {
|
|
1059
|
+
file_ids: vec![NullableKeyFilter::Value("bench.json".to_string())],
|
|
1060
|
+
..Default::default()
|
|
1061
|
+
},
|
|
1062
|
+
..Default::default()
|
|
1063
|
+
},
|
|
1064
|
+
)
|
|
1065
|
+
.await?
|
|
1066
|
+
.len();
|
|
1067
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
pub async fn tracked_state_scan_file_selective_prepared(
|
|
1071
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1072
|
+
fixture: &TrackedStateReadFixture,
|
|
1073
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1074
|
+
let mut reader = fixture
|
|
1075
|
+
.context
|
|
1076
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1077
|
+
let verified_rows = reader
|
|
1078
|
+
.scan_rows_at_commit(
|
|
1079
|
+
&fixture.commit_id,
|
|
1080
|
+
&TrackedStateScanRequest {
|
|
1081
|
+
filter: TrackedStateFilter {
|
|
1082
|
+
file_ids: vec![NullableKeyFilter::Value("bench-match.json".to_string())],
|
|
1083
|
+
..Default::default()
|
|
1084
|
+
},
|
|
1085
|
+
..Default::default()
|
|
1086
|
+
},
|
|
1087
|
+
)
|
|
1088
|
+
.await?
|
|
1089
|
+
.len();
|
|
1090
|
+
Ok(report(
|
|
1091
|
+
fixture.selectivity.expected_rows(fixture.rows),
|
|
1092
|
+
verified_rows,
|
|
1093
|
+
Duration::ZERO,
|
|
1094
|
+
))
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
pub async fn tracked_state_scan_file_header_selective_prepared(
|
|
1098
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1099
|
+
fixture: &TrackedStateReadFixture,
|
|
1100
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1101
|
+
let mut reader = fixture
|
|
1102
|
+
.context
|
|
1103
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1104
|
+
let verified_rows = reader
|
|
1105
|
+
.scan_rows_at_commit(
|
|
1106
|
+
&fixture.commit_id,
|
|
1107
|
+
&TrackedStateScanRequest {
|
|
1108
|
+
filter: TrackedStateFilter {
|
|
1109
|
+
file_ids: vec![NullableKeyFilter::Value("bench-match.json".to_string())],
|
|
1110
|
+
..Default::default()
|
|
1111
|
+
},
|
|
1112
|
+
projection: TrackedStateProjection {
|
|
1113
|
+
columns: tracked_state_header_columns(),
|
|
1114
|
+
},
|
|
1115
|
+
..Default::default()
|
|
1116
|
+
},
|
|
1117
|
+
)
|
|
1118
|
+
.await?
|
|
1119
|
+
.len();
|
|
1120
|
+
Ok(report(
|
|
1121
|
+
fixture.selectivity.expected_rows(fixture.rows),
|
|
1122
|
+
verified_rows,
|
|
1123
|
+
Duration::ZERO,
|
|
1124
|
+
))
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
pub async fn prepare_tracked_state_update(
|
|
1128
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1129
|
+
config: StorageBenchConfig,
|
|
1130
|
+
) -> Result<TrackedStateUpdateFixture, LixError> {
|
|
1131
|
+
prepare_tracked_state_update_rows(backend, config, config.update_fraction.rows(config.rows))
|
|
1132
|
+
.await
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
pub async fn prepare_tracked_state_update_rows(
|
|
1136
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1137
|
+
config: StorageBenchConfig,
|
|
1138
|
+
updated_rows: usize,
|
|
1139
|
+
) -> Result<TrackedStateUpdateFixture, LixError> {
|
|
1140
|
+
let context = TrackedStateContext::new();
|
|
1141
|
+
let rows = tracked_rows(config, "bench-tracked-parent");
|
|
1142
|
+
write_tracked_root(backend, &context, "bench-tracked-parent", None, &rows).await?;
|
|
1143
|
+
let mut updated_rows = tracked_rows(
|
|
1144
|
+
config.with_rows(updated_rows.min(config.rows)),
|
|
1145
|
+
"bench-tracked-child",
|
|
1146
|
+
);
|
|
1147
|
+
for (index, row) in updated_rows.iter_mut().enumerate() {
|
|
1148
|
+
row.snapshot_content = Some(updated_snapshot_content(index, config.state_payload_bytes));
|
|
1149
|
+
}
|
|
1150
|
+
Ok(TrackedStateUpdateFixture {
|
|
1151
|
+
context,
|
|
1152
|
+
rows: updated_rows,
|
|
1153
|
+
})
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
pub async fn prepare_tracked_state_partial_snapshot_update_rows(
|
|
1157
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1158
|
+
config: StorageBenchConfig,
|
|
1159
|
+
updated_rows: usize,
|
|
1160
|
+
) -> Result<TrackedStateUpdateFixture, LixError> {
|
|
1161
|
+
let context = TrackedStateContext::new();
|
|
1162
|
+
let rows = tracked_rows(config, "bench-tracked-parent");
|
|
1163
|
+
write_tracked_root(backend, &context, "bench-tracked-parent", None, &rows).await?;
|
|
1164
|
+
let mut updated_rows = tracked_rows(
|
|
1165
|
+
config.with_rows(updated_rows.min(config.rows)),
|
|
1166
|
+
"bench-tracked-child",
|
|
1167
|
+
);
|
|
1168
|
+
for (index, row) in updated_rows.iter_mut().enumerate() {
|
|
1169
|
+
row.snapshot_content = Some(partial_updated_snapshot_content(
|
|
1170
|
+
index,
|
|
1171
|
+
config.state_payload_bytes,
|
|
1172
|
+
));
|
|
1173
|
+
}
|
|
1174
|
+
Ok(TrackedStateUpdateFixture {
|
|
1175
|
+
context,
|
|
1176
|
+
rows: updated_rows,
|
|
1177
|
+
})
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
pub async fn prepare_tracked_state_append_child(
|
|
1181
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1182
|
+
config: StorageBenchConfig,
|
|
1183
|
+
) -> Result<TrackedStateUpdateFixture, LixError> {
|
|
1184
|
+
prepare_tracked_state_append_child_rows(backend, config, config.rows).await
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
pub async fn prepare_tracked_state_append_child_rows(
|
|
1188
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1189
|
+
config: StorageBenchConfig,
|
|
1190
|
+
appended_rows: usize,
|
|
1191
|
+
) -> Result<TrackedStateUpdateFixture, LixError> {
|
|
1192
|
+
let context = TrackedStateContext::new();
|
|
1193
|
+
let rows = tracked_rows(config, "bench-tracked-parent");
|
|
1194
|
+
write_tracked_root(backend, &context, "bench-tracked-parent", None, &rows).await?;
|
|
1195
|
+
let mut appended_rows = tracked_rows(
|
|
1196
|
+
config.with_rows(appended_rows.min(config.rows)),
|
|
1197
|
+
"bench-tracked-child",
|
|
1198
|
+
);
|
|
1199
|
+
for (index, row) in appended_rows.iter_mut().enumerate() {
|
|
1200
|
+
row.entity_id = EntityIdentity::single(entity_id("tracked-new", index, config.key_pattern));
|
|
1201
|
+
row.change_id = format!("tracked-new-change-{index}");
|
|
1202
|
+
}
|
|
1203
|
+
Ok(TrackedStateUpdateFixture {
|
|
1204
|
+
context,
|
|
1205
|
+
rows: appended_rows,
|
|
1206
|
+
})
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
pub async fn prepare_tracked_state_tombstone_rows(
|
|
1210
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1211
|
+
config: StorageBenchConfig,
|
|
1212
|
+
tombstone_rows: usize,
|
|
1213
|
+
) -> Result<TrackedStateUpdateFixture, LixError> {
|
|
1214
|
+
let context = TrackedStateContext::new();
|
|
1215
|
+
let rows = tracked_rows(config, "bench-tracked-parent");
|
|
1216
|
+
write_tracked_root(backend, &context, "bench-tracked-parent", None, &rows).await?;
|
|
1217
|
+
let mut tombstones = tracked_rows(
|
|
1218
|
+
config.with_rows(tombstone_rows.min(config.rows)),
|
|
1219
|
+
"bench-tracked-child",
|
|
1220
|
+
);
|
|
1221
|
+
for row in &mut tombstones {
|
|
1222
|
+
row.snapshot_content = None;
|
|
1223
|
+
}
|
|
1224
|
+
Ok(TrackedStateUpdateFixture {
|
|
1225
|
+
context,
|
|
1226
|
+
rows: tombstones,
|
|
1227
|
+
})
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
pub async fn tracked_state_update_existing_prepared(
|
|
1231
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1232
|
+
fixture: &TrackedStateUpdateFixture,
|
|
1233
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1234
|
+
write_tracked_root(
|
|
1235
|
+
backend,
|
|
1236
|
+
&fixture.context,
|
|
1237
|
+
"bench-tracked-child",
|
|
1238
|
+
Some("bench-tracked-parent"),
|
|
1239
|
+
&fixture.rows,
|
|
1240
|
+
)
|
|
1241
|
+
.await?;
|
|
1242
|
+
Ok(report(
|
|
1243
|
+
fixture.rows.len(),
|
|
1244
|
+
fixture.rows.len(),
|
|
1245
|
+
Duration::ZERO,
|
|
1246
|
+
))
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
pub async fn prepare_tracked_state_diff_update_rows(
|
|
1250
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1251
|
+
config: StorageBenchConfig,
|
|
1252
|
+
updated_rows: usize,
|
|
1253
|
+
) -> Result<TrackedStateDiffFixture, LixError> {
|
|
1254
|
+
let fixture = prepare_tracked_state_update_rows(backend, config, updated_rows).await?;
|
|
1255
|
+
tracked_state_update_existing_prepared(backend, &fixture).await?;
|
|
1256
|
+
Ok(TrackedStateDiffFixture {
|
|
1257
|
+
context: fixture.context,
|
|
1258
|
+
left_commit_id: "bench-tracked-parent".to_string(),
|
|
1259
|
+
right_commit_id: "bench-tracked-child".to_string(),
|
|
1260
|
+
expected_entries: fixture.rows.len(),
|
|
1261
|
+
})
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
pub async fn prepare_tracked_state_diff_tombstone_rows(
|
|
1265
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1266
|
+
config: StorageBenchConfig,
|
|
1267
|
+
tombstone_rows: usize,
|
|
1268
|
+
) -> Result<TrackedStateDiffFixture, LixError> {
|
|
1269
|
+
let fixture = prepare_tracked_state_tombstone_rows(backend, config, tombstone_rows).await?;
|
|
1270
|
+
tracked_state_update_existing_prepared(backend, &fixture).await?;
|
|
1271
|
+
Ok(TrackedStateDiffFixture {
|
|
1272
|
+
context: fixture.context,
|
|
1273
|
+
left_commit_id: "bench-tracked-parent".to_string(),
|
|
1274
|
+
right_commit_id: "bench-tracked-child".to_string(),
|
|
1275
|
+
expected_entries: fixture.rows.len(),
|
|
1276
|
+
})
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
pub async fn prepare_tracked_state_diff_equal(
|
|
1280
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1281
|
+
config: StorageBenchConfig,
|
|
1282
|
+
) -> Result<TrackedStateDiffFixture, LixError> {
|
|
1283
|
+
let context = TrackedStateContext::new();
|
|
1284
|
+
let rows = tracked_rows(config, "bench-tracked-parent");
|
|
1285
|
+
write_tracked_root(backend, &context, "bench-tracked-parent", None, &rows).await?;
|
|
1286
|
+
Ok(TrackedStateDiffFixture {
|
|
1287
|
+
context,
|
|
1288
|
+
left_commit_id: "bench-tracked-parent".to_string(),
|
|
1289
|
+
right_commit_id: "bench-tracked-parent".to_string(),
|
|
1290
|
+
expected_entries: 0,
|
|
1291
|
+
})
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
pub async fn tracked_state_diff_commits_prepared(
|
|
1295
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1296
|
+
fixture: &TrackedStateDiffFixture,
|
|
1297
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1298
|
+
let mut reader = fixture
|
|
1299
|
+
.context
|
|
1300
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1301
|
+
let diff = reader
|
|
1302
|
+
.diff_commits(
|
|
1303
|
+
&fixture.left_commit_id,
|
|
1304
|
+
&fixture.right_commit_id,
|
|
1305
|
+
&TrackedStateDiffRequest::default(),
|
|
1306
|
+
)
|
|
1307
|
+
.await?;
|
|
1308
|
+
Ok(report(
|
|
1309
|
+
fixture.expected_entries,
|
|
1310
|
+
diff.entries.len(),
|
|
1311
|
+
Duration::ZERO,
|
|
1312
|
+
))
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
pub async fn prepare_untracked_state_write_rows(
|
|
1316
|
+
config: StorageBenchConfig,
|
|
1317
|
+
) -> Result<UntrackedStateWriteFixture, LixError> {
|
|
1318
|
+
Ok(UntrackedStateWriteFixture {
|
|
1319
|
+
context: UntrackedStateContext::new(),
|
|
1320
|
+
rows: untracked_rows(config),
|
|
1321
|
+
})
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
pub async fn untracked_state_write_rows_prepared(
|
|
1325
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1326
|
+
fixture: &UntrackedStateWriteFixture,
|
|
1327
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1328
|
+
write_untracked_rows(backend, &fixture.context, &fixture.rows).await?;
|
|
1329
|
+
let verified_rows = scan_untracked(
|
|
1330
|
+
backend,
|
|
1331
|
+
&fixture.context,
|
|
1332
|
+
UntrackedStateScanRequest::default(),
|
|
1333
|
+
)
|
|
1334
|
+
.await?
|
|
1335
|
+
.len();
|
|
1336
|
+
Ok(report(fixture.rows.len(), verified_rows, Duration::ZERO))
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
pub async fn prepare_untracked_state_read(
|
|
1340
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1341
|
+
config: StorageBenchConfig,
|
|
1342
|
+
) -> Result<UntrackedStateReadFixture, LixError> {
|
|
1343
|
+
let context = UntrackedStateContext::new();
|
|
1344
|
+
let rows = untracked_rows(config);
|
|
1345
|
+
write_untracked_rows(backend, &context, &rows).await?;
|
|
1346
|
+
Ok(UntrackedStateReadFixture {
|
|
1347
|
+
context,
|
|
1348
|
+
rows: config.rows,
|
|
1349
|
+
key_pattern: config.key_pattern,
|
|
1350
|
+
selectivity: config.selectivity,
|
|
1351
|
+
})
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
pub async fn untracked_state_read_point_hit_prepared(
|
|
1355
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1356
|
+
fixture: &UntrackedStateReadFixture,
|
|
1357
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1358
|
+
let mut verified_rows = 0;
|
|
1359
|
+
let mut reader = fixture
|
|
1360
|
+
.context
|
|
1361
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1362
|
+
for index in 0..fixture.rows {
|
|
1363
|
+
if reader
|
|
1364
|
+
.load_row(&UntrackedStateRowRequest {
|
|
1365
|
+
schema_key: untracked_schema_key(index, StorageBenchSelectivity::Percent100),
|
|
1366
|
+
version_id: "bench-version".to_string(),
|
|
1367
|
+
entity_id: EntityIdentity::single(entity_id(
|
|
1368
|
+
"untracked",
|
|
1369
|
+
index,
|
|
1370
|
+
fixture.key_pattern,
|
|
1371
|
+
)),
|
|
1372
|
+
file_id: NullableKeyFilter::Value("bench.json".to_string()),
|
|
1373
|
+
})
|
|
1374
|
+
.await?
|
|
1375
|
+
.is_some()
|
|
1376
|
+
{
|
|
1377
|
+
verified_rows += 1;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
pub async fn untracked_state_read_point_hit_constant_prepared(
|
|
1384
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1385
|
+
fixture: &UntrackedStateReadFixture,
|
|
1386
|
+
measured_reads: usize,
|
|
1387
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1388
|
+
let mut verified_rows = 0;
|
|
1389
|
+
let mut reader = fixture
|
|
1390
|
+
.context
|
|
1391
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1392
|
+
for index in 0..measured_reads.min(fixture.rows) {
|
|
1393
|
+
if reader
|
|
1394
|
+
.load_row(&UntrackedStateRowRequest {
|
|
1395
|
+
schema_key: untracked_schema_key(index, StorageBenchSelectivity::Percent100),
|
|
1396
|
+
version_id: "bench-version".to_string(),
|
|
1397
|
+
entity_id: EntityIdentity::single(entity_id(
|
|
1398
|
+
"untracked",
|
|
1399
|
+
index,
|
|
1400
|
+
fixture.key_pattern,
|
|
1401
|
+
)),
|
|
1402
|
+
file_id: NullableKeyFilter::Value("bench.json".to_string()),
|
|
1403
|
+
})
|
|
1404
|
+
.await?
|
|
1405
|
+
.is_some()
|
|
1406
|
+
{
|
|
1407
|
+
verified_rows += 1;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
Ok(report(
|
|
1411
|
+
measured_reads.min(fixture.rows),
|
|
1412
|
+
verified_rows,
|
|
1413
|
+
Duration::ZERO,
|
|
1414
|
+
))
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
pub async fn untracked_state_read_point_miss_prepared(
|
|
1418
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1419
|
+
fixture: &UntrackedStateReadFixture,
|
|
1420
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1421
|
+
let mut misses = 0;
|
|
1422
|
+
let mut reader = fixture
|
|
1423
|
+
.context
|
|
1424
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1425
|
+
for index in 0..fixture.rows {
|
|
1426
|
+
if reader
|
|
1427
|
+
.load_row(&UntrackedStateRowRequest {
|
|
1428
|
+
schema_key: "bench_untracked_entity".to_string(),
|
|
1429
|
+
version_id: "bench-version".to_string(),
|
|
1430
|
+
entity_id: EntityIdentity::single(format!("missing-{index}")),
|
|
1431
|
+
file_id: NullableKeyFilter::Value("bench.json".to_string()),
|
|
1432
|
+
})
|
|
1433
|
+
.await?
|
|
1434
|
+
.is_none()
|
|
1435
|
+
{
|
|
1436
|
+
misses += 1;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
Ok(report(fixture.rows, misses, Duration::ZERO))
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
pub async fn untracked_state_scan_all_prepared(
|
|
1443
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1444
|
+
fixture: &UntrackedStateReadFixture,
|
|
1445
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1446
|
+
let verified_rows = scan_untracked(
|
|
1447
|
+
backend,
|
|
1448
|
+
&fixture.context,
|
|
1449
|
+
UntrackedStateScanRequest::default(),
|
|
1450
|
+
)
|
|
1451
|
+
.await?
|
|
1452
|
+
.len();
|
|
1453
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
pub async fn untracked_state_scan_keys_only_prepared(
|
|
1457
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1458
|
+
fixture: &UntrackedStateReadFixture,
|
|
1459
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1460
|
+
let verified_rows = scan_untracked(
|
|
1461
|
+
backend,
|
|
1462
|
+
&fixture.context,
|
|
1463
|
+
UntrackedStateScanRequest {
|
|
1464
|
+
projection: UntrackedStateProjection {
|
|
1465
|
+
columns: vec!["entity_id".to_string()],
|
|
1466
|
+
},
|
|
1467
|
+
..Default::default()
|
|
1468
|
+
},
|
|
1469
|
+
)
|
|
1470
|
+
.await?
|
|
1471
|
+
.len();
|
|
1472
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
pub async fn untracked_state_scan_headers_only_prepared(
|
|
1476
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1477
|
+
fixture: &UntrackedStateReadFixture,
|
|
1478
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1479
|
+
let verified_rows = scan_untracked(
|
|
1480
|
+
backend,
|
|
1481
|
+
&fixture.context,
|
|
1482
|
+
UntrackedStateScanRequest {
|
|
1483
|
+
projection: UntrackedStateProjection {
|
|
1484
|
+
columns: untracked_state_header_columns(),
|
|
1485
|
+
},
|
|
1486
|
+
..Default::default()
|
|
1487
|
+
},
|
|
1488
|
+
)
|
|
1489
|
+
.await?
|
|
1490
|
+
.len();
|
|
1491
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
pub async fn untracked_state_scan_full_rows_prepared(
|
|
1495
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1496
|
+
fixture: &UntrackedStateReadFixture,
|
|
1497
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1498
|
+
untracked_state_scan_all_prepared(backend, fixture).await
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
pub async fn untracked_state_scan_version_prepared(
|
|
1502
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1503
|
+
fixture: &UntrackedStateReadFixture,
|
|
1504
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1505
|
+
let verified_rows = scan_untracked(
|
|
1506
|
+
backend,
|
|
1507
|
+
&fixture.context,
|
|
1508
|
+
UntrackedStateScanRequest {
|
|
1509
|
+
filter: UntrackedStateFilter {
|
|
1510
|
+
version_ids: vec!["bench-version".to_string()],
|
|
1511
|
+
..Default::default()
|
|
1512
|
+
},
|
|
1513
|
+
..Default::default()
|
|
1514
|
+
},
|
|
1515
|
+
)
|
|
1516
|
+
.await?
|
|
1517
|
+
.len();
|
|
1518
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
pub async fn untracked_state_scan_schema_prepared(
|
|
1522
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1523
|
+
fixture: &UntrackedStateReadFixture,
|
|
1524
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1525
|
+
let verified_rows = scan_untracked(
|
|
1526
|
+
backend,
|
|
1527
|
+
&fixture.context,
|
|
1528
|
+
UntrackedStateScanRequest {
|
|
1529
|
+
filter: UntrackedStateFilter {
|
|
1530
|
+
schema_keys: vec![untracked_schema_key(0, StorageBenchSelectivity::Percent100)],
|
|
1531
|
+
..Default::default()
|
|
1532
|
+
},
|
|
1533
|
+
..Default::default()
|
|
1534
|
+
},
|
|
1535
|
+
)
|
|
1536
|
+
.await?
|
|
1537
|
+
.len();
|
|
1538
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
pub async fn untracked_state_scan_schema_selective_prepared(
|
|
1542
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1543
|
+
fixture: &UntrackedStateReadFixture,
|
|
1544
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1545
|
+
let verified_rows = scan_untracked(
|
|
1546
|
+
backend,
|
|
1547
|
+
&fixture.context,
|
|
1548
|
+
UntrackedStateScanRequest {
|
|
1549
|
+
filter: UntrackedStateFilter {
|
|
1550
|
+
schema_keys: vec![UNTRACKED_MATCH_SCHEMA_KEY.to_string()],
|
|
1551
|
+
..Default::default()
|
|
1552
|
+
},
|
|
1553
|
+
..Default::default()
|
|
1554
|
+
},
|
|
1555
|
+
)
|
|
1556
|
+
.await?
|
|
1557
|
+
.len();
|
|
1558
|
+
Ok(report(
|
|
1559
|
+
fixture.selectivity.expected_rows(fixture.rows),
|
|
1560
|
+
verified_rows,
|
|
1561
|
+
Duration::ZERO,
|
|
1562
|
+
))
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
pub async fn prepare_untracked_state_overwrite(
|
|
1566
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1567
|
+
config: StorageBenchConfig,
|
|
1568
|
+
) -> Result<UntrackedStateWriteFixture, LixError> {
|
|
1569
|
+
let context = UntrackedStateContext::new();
|
|
1570
|
+
let rows = untracked_rows(config);
|
|
1571
|
+
write_untracked_rows(backend, &context, &rows).await?;
|
|
1572
|
+
let mut updated_rows =
|
|
1573
|
+
untracked_rows(config.with_rows(config.update_fraction.rows(config.rows)));
|
|
1574
|
+
for (index, row) in updated_rows.iter_mut().enumerate() {
|
|
1575
|
+
row.snapshot_content = Some(updated_snapshot_content(index, config.state_payload_bytes));
|
|
1576
|
+
}
|
|
1577
|
+
Ok(UntrackedStateWriteFixture {
|
|
1578
|
+
context,
|
|
1579
|
+
rows: updated_rows,
|
|
1580
|
+
})
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
pub async fn prepare_untracked_state_insert_new_keys(
|
|
1584
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1585
|
+
config: StorageBenchConfig,
|
|
1586
|
+
) -> Result<UntrackedStateWriteFixture, LixError> {
|
|
1587
|
+
let context = UntrackedStateContext::new();
|
|
1588
|
+
let rows = untracked_rows(config);
|
|
1589
|
+
write_untracked_rows(backend, &context, &rows).await?;
|
|
1590
|
+
let mut new_rows = untracked_rows(config);
|
|
1591
|
+
for (index, row) in new_rows.iter_mut().enumerate() {
|
|
1592
|
+
row.entity_id =
|
|
1593
|
+
EntityIdentity::single(entity_id("untracked-new", index, config.key_pattern));
|
|
1594
|
+
}
|
|
1595
|
+
Ok(UntrackedStateWriteFixture {
|
|
1596
|
+
context,
|
|
1597
|
+
rows: new_rows,
|
|
1598
|
+
})
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
pub async fn untracked_state_overwrite_existing_prepared(
|
|
1602
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1603
|
+
fixture: &UntrackedStateWriteFixture,
|
|
1604
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1605
|
+
write_untracked_rows(backend, &fixture.context, &fixture.rows).await?;
|
|
1606
|
+
let verified_rows = scan_untracked(
|
|
1607
|
+
backend,
|
|
1608
|
+
&fixture.context,
|
|
1609
|
+
UntrackedStateScanRequest::default(),
|
|
1610
|
+
)
|
|
1611
|
+
.await?
|
|
1612
|
+
.len();
|
|
1613
|
+
Ok(report(fixture.rows.len(), verified_rows, Duration::ZERO))
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
pub async fn prepare_changelog_append_changes(
|
|
1617
|
+
config: StorageBenchConfig,
|
|
1618
|
+
) -> Result<ChangelogAppendFixture, LixError> {
|
|
1619
|
+
Ok(ChangelogAppendFixture {
|
|
1620
|
+
context: ChangelogContext::new(),
|
|
1621
|
+
changes: changelog_materialized_changes(config),
|
|
1622
|
+
})
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
pub async fn prepare_changelog_append_tombstones(
|
|
1626
|
+
config: StorageBenchConfig,
|
|
1627
|
+
) -> Result<ChangelogAppendFixture, LixError> {
|
|
1628
|
+
Ok(ChangelogAppendFixture {
|
|
1629
|
+
context: ChangelogContext::new(),
|
|
1630
|
+
changes: changelog_tombstone_changes(config),
|
|
1631
|
+
})
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
pub async fn prepare_changelog_append_metadata(
|
|
1635
|
+
config: StorageBenchConfig,
|
|
1636
|
+
) -> Result<ChangelogAppendFixture, LixError> {
|
|
1637
|
+
Ok(ChangelogAppendFixture {
|
|
1638
|
+
context: ChangelogContext::new(),
|
|
1639
|
+
changes: changelog_metadata_changes(config),
|
|
1640
|
+
})
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
pub async fn prepare_changelog_append_shared_payload(
|
|
1644
|
+
config: StorageBenchConfig,
|
|
1645
|
+
) -> Result<ChangelogAppendFixture, LixError> {
|
|
1646
|
+
Ok(ChangelogAppendFixture {
|
|
1647
|
+
context: ChangelogContext::new(),
|
|
1648
|
+
changes: changelog_shared_payload_changes(config),
|
|
1649
|
+
})
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
pub async fn prepare_changelog_append_shared_metadata(
|
|
1653
|
+
config: StorageBenchConfig,
|
|
1654
|
+
) -> Result<ChangelogAppendFixture, LixError> {
|
|
1655
|
+
Ok(ChangelogAppendFixture {
|
|
1656
|
+
context: ChangelogContext::new(),
|
|
1657
|
+
changes: changelog_shared_metadata_changes(config),
|
|
1658
|
+
})
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
pub async fn prepare_changelog_append_shared_payload_and_metadata(
|
|
1662
|
+
config: StorageBenchConfig,
|
|
1663
|
+
) -> Result<ChangelogAppendFixture, LixError> {
|
|
1664
|
+
Ok(ChangelogAppendFixture {
|
|
1665
|
+
context: ChangelogContext::new(),
|
|
1666
|
+
changes: changelog_shared_payload_and_metadata_changes(config),
|
|
1667
|
+
})
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
pub async fn prepare_changelog_append_composite_entity_ids(
|
|
1671
|
+
config: StorageBenchConfig,
|
|
1672
|
+
) -> Result<ChangelogAppendFixture, LixError> {
|
|
1673
|
+
Ok(ChangelogAppendFixture {
|
|
1674
|
+
context: ChangelogContext::new(),
|
|
1675
|
+
changes: changelog_composite_entity_id_changes(config),
|
|
1676
|
+
})
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
pub async fn prepare_changelog_codec(
|
|
1680
|
+
config: StorageBenchConfig,
|
|
1681
|
+
) -> Result<ChangelogCodecFixture, LixError> {
|
|
1682
|
+
let changes = changelog_changes(config);
|
|
1683
|
+
let encoded_changes = changes
|
|
1684
|
+
.iter()
|
|
1685
|
+
.map(crate::changelog::codec::encode_change)
|
|
1686
|
+
.collect::<Result<Vec<_>, _>>()?;
|
|
1687
|
+
Ok(ChangelogCodecFixture {
|
|
1688
|
+
changes,
|
|
1689
|
+
encoded_changes,
|
|
1690
|
+
})
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
pub async fn changelog_append_changes_prepared(
|
|
1694
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1695
|
+
fixture: &ChangelogAppendFixture,
|
|
1696
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1697
|
+
append_changelog_changes(backend, &fixture.context, &fixture.changes).await?;
|
|
1698
|
+
let reader = fixture
|
|
1699
|
+
.context
|
|
1700
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1701
|
+
let verified_rows = reader
|
|
1702
|
+
.scan_changes(&ChangelogScanRequest::default())
|
|
1703
|
+
.await?
|
|
1704
|
+
.len();
|
|
1705
|
+
Ok(report(fixture.changes.len(), verified_rows, Duration::ZERO))
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
pub async fn prepare_changelog_read(
|
|
1709
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1710
|
+
config: StorageBenchConfig,
|
|
1711
|
+
) -> Result<ChangelogReadFixture, LixError> {
|
|
1712
|
+
let context = ChangelogContext::new();
|
|
1713
|
+
let changes = changelog_materialized_changes(config);
|
|
1714
|
+
append_changelog_changes(backend, &context, &changes).await?;
|
|
1715
|
+
Ok(ChangelogReadFixture {
|
|
1716
|
+
context,
|
|
1717
|
+
rows: config.rows,
|
|
1718
|
+
})
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
pub async fn prepare_changelog_read_with_selectivity(
|
|
1722
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1723
|
+
config: StorageBenchConfig,
|
|
1724
|
+
) -> Result<ChangelogReadFixture, LixError> {
|
|
1725
|
+
let context = ChangelogContext::new();
|
|
1726
|
+
let changes = changelog_selective_changes(config);
|
|
1727
|
+
append_changelog_changes(backend, &context, &changes).await?;
|
|
1728
|
+
Ok(ChangelogReadFixture {
|
|
1729
|
+
context,
|
|
1730
|
+
rows: config.rows,
|
|
1731
|
+
})
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
pub async fn prepare_changelog_read_entity_history(
|
|
1735
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1736
|
+
config: StorageBenchConfig,
|
|
1737
|
+
) -> Result<ChangelogReadFixture, LixError> {
|
|
1738
|
+
let context = ChangelogContext::new();
|
|
1739
|
+
let changes = changelog_entity_history_changes(config);
|
|
1740
|
+
append_changelog_changes(backend, &context, &changes).await?;
|
|
1741
|
+
Ok(ChangelogReadFixture {
|
|
1742
|
+
context,
|
|
1743
|
+
rows: config.rows,
|
|
1744
|
+
})
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
pub async fn changelog_encode_only_prepared(
|
|
1748
|
+
fixture: &ChangelogCodecFixture,
|
|
1749
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1750
|
+
let mut verified_rows = 0;
|
|
1751
|
+
let mut encoded_bytes = 0;
|
|
1752
|
+
for change in &fixture.changes {
|
|
1753
|
+
encoded_bytes += crate::changelog::codec::encode_change(change)?.len();
|
|
1754
|
+
verified_rows += 1;
|
|
1755
|
+
}
|
|
1756
|
+
Ok(report(
|
|
1757
|
+
fixture.changes.len(),
|
|
1758
|
+
verified_rows + usize::from(encoded_bytes == 0),
|
|
1759
|
+
Duration::ZERO,
|
|
1760
|
+
))
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
pub async fn changelog_decode_only_prepared(
|
|
1764
|
+
fixture: &ChangelogCodecFixture,
|
|
1765
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1766
|
+
let mut verified_rows = 0;
|
|
1767
|
+
let mut decoded_bytes = 0;
|
|
1768
|
+
for bytes in &fixture.encoded_changes {
|
|
1769
|
+
let change = crate::changelog::codec::decode_change(bytes)?;
|
|
1770
|
+
decoded_bytes += change.schema_key.len();
|
|
1771
|
+
verified_rows += 1;
|
|
1772
|
+
}
|
|
1773
|
+
Ok(report(
|
|
1774
|
+
fixture.encoded_changes.len(),
|
|
1775
|
+
verified_rows + usize::from(decoded_bytes == 0),
|
|
1776
|
+
Duration::ZERO,
|
|
1777
|
+
))
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
pub async fn changelog_load_change_hit_prepared(
|
|
1781
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1782
|
+
fixture: &ChangelogReadFixture,
|
|
1783
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1784
|
+
let reader = fixture
|
|
1785
|
+
.context
|
|
1786
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1787
|
+
let mut verified_rows = 0;
|
|
1788
|
+
for index in 0..fixture.rows {
|
|
1789
|
+
if reader
|
|
1790
|
+
.load_change(&format!("bench-change-{index}"))
|
|
1791
|
+
.await?
|
|
1792
|
+
.is_some()
|
|
1793
|
+
{
|
|
1794
|
+
verified_rows += 1;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
pub async fn changelog_load_change_miss_prepared(
|
|
1801
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1802
|
+
fixture: &ChangelogReadFixture,
|
|
1803
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1804
|
+
let reader = fixture
|
|
1805
|
+
.context
|
|
1806
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1807
|
+
let mut misses = 0;
|
|
1808
|
+
for index in 0..fixture.rows {
|
|
1809
|
+
if reader
|
|
1810
|
+
.load_change(&format!("missing-change-{index}"))
|
|
1811
|
+
.await?
|
|
1812
|
+
.is_none()
|
|
1813
|
+
{
|
|
1814
|
+
misses += 1;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
Ok(report(fixture.rows, misses, Duration::ZERO))
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
pub async fn changelog_scan_all_prepared(
|
|
1821
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1822
|
+
fixture: &ChangelogReadFixture,
|
|
1823
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1824
|
+
let reader = fixture
|
|
1825
|
+
.context
|
|
1826
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1827
|
+
let verified_rows = reader
|
|
1828
|
+
.scan_changes(&ChangelogScanRequest::default())
|
|
1829
|
+
.await?
|
|
1830
|
+
.len();
|
|
1831
|
+
Ok(report(fixture.rows, verified_rows, Duration::ZERO))
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
pub async fn changelog_scan_full_changes_prepared(
|
|
1835
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1836
|
+
fixture: &ChangelogReadFixture,
|
|
1837
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1838
|
+
changelog_scan_all_prepared(backend, fixture).await
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
pub async fn changelog_scan_limit_100_prepared(
|
|
1842
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1843
|
+
fixture: &ChangelogReadFixture,
|
|
1844
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1845
|
+
let reader = fixture
|
|
1846
|
+
.context
|
|
1847
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1848
|
+
let expected = fixture.rows.min(100);
|
|
1849
|
+
let verified_rows = reader
|
|
1850
|
+
.scan_changes(&ChangelogScanRequest {
|
|
1851
|
+
limit: Some(expected),
|
|
1852
|
+
})
|
|
1853
|
+
.await?
|
|
1854
|
+
.len();
|
|
1855
|
+
Ok(report(expected, verified_rows, Duration::ZERO))
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
pub async fn changelog_scan_schema_prepared(
|
|
1859
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1860
|
+
fixture: &ChangelogReadFixture,
|
|
1861
|
+
selectivity: StorageBenchSelectivity,
|
|
1862
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1863
|
+
let reader = fixture
|
|
1864
|
+
.context
|
|
1865
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1866
|
+
let changes = reader
|
|
1867
|
+
.scan_changes(&ChangelogScanRequest::default())
|
|
1868
|
+
.await?;
|
|
1869
|
+
let verified_rows = changes
|
|
1870
|
+
.iter()
|
|
1871
|
+
.filter(|change| change.schema_key == CHANGELOG_MATCH_SCHEMA_KEY)
|
|
1872
|
+
.count();
|
|
1873
|
+
Ok(report(
|
|
1874
|
+
selectivity.expected_rows(fixture.rows),
|
|
1875
|
+
verified_rows,
|
|
1876
|
+
Duration::ZERO,
|
|
1877
|
+
))
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
pub async fn changelog_scan_entity_history_prepared(
|
|
1881
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1882
|
+
fixture: &ChangelogReadFixture,
|
|
1883
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1884
|
+
let reader = fixture
|
|
1885
|
+
.context
|
|
1886
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1887
|
+
let changes = reader
|
|
1888
|
+
.scan_changes(&ChangelogScanRequest::default())
|
|
1889
|
+
.await?;
|
|
1890
|
+
let target = EntityIdentity::single(CHANGELOG_HISTORY_ENTITY_ID);
|
|
1891
|
+
let verified_rows = changes
|
|
1892
|
+
.iter()
|
|
1893
|
+
.filter(|change| change.entity_id == target)
|
|
1894
|
+
.count();
|
|
1895
|
+
Ok(report(
|
|
1896
|
+
fixture.rows.div_ceil(10),
|
|
1897
|
+
verified_rows,
|
|
1898
|
+
Duration::ZERO,
|
|
1899
|
+
))
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
pub async fn prepare_binary_cas_write_blobs(
|
|
1903
|
+
config: StorageBenchConfig,
|
|
1904
|
+
) -> Result<BinaryCasWriteFixture, LixError> {
|
|
1905
|
+
Ok(BinaryCasWriteFixture {
|
|
1906
|
+
context: BinaryCasContext::new(),
|
|
1907
|
+
file_ids: binary_file_ids(config.rows),
|
|
1908
|
+
payloads: binary_payloads(config.rows, config.blob_bytes),
|
|
1909
|
+
})
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
pub async fn prepare_binary_cas_write_duplicate_payload(
|
|
1913
|
+
config: StorageBenchConfig,
|
|
1914
|
+
) -> Result<BinaryCasWriteFixture, LixError> {
|
|
1915
|
+
let payload = binary_payload(0, config.blob_bytes);
|
|
1916
|
+
Ok(BinaryCasWriteFixture {
|
|
1917
|
+
context: BinaryCasContext::new(),
|
|
1918
|
+
file_ids: binary_file_ids(config.rows),
|
|
1919
|
+
payloads: (0..config.rows).map(|_| payload.clone()).collect(),
|
|
1920
|
+
})
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
pub async fn prepare_binary_cas_write_half_duplicate_payload(
|
|
1924
|
+
config: StorageBenchConfig,
|
|
1925
|
+
) -> Result<BinaryCasWriteFixture, LixError> {
|
|
1926
|
+
Ok(BinaryCasWriteFixture {
|
|
1927
|
+
context: BinaryCasContext::new(),
|
|
1928
|
+
file_ids: binary_file_ids(config.rows),
|
|
1929
|
+
payloads: binary_half_duplicate_payloads(config.rows, config.blob_bytes),
|
|
1930
|
+
})
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
pub async fn binary_cas_write_blobs_prepared(
|
|
1934
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1935
|
+
fixture: &BinaryCasWriteFixture,
|
|
1936
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1937
|
+
let writes = binary_blob_writes(&fixture.file_ids, &fixture.payloads);
|
|
1938
|
+
write_binary_blob_writes(backend, &fixture.context, &writes).await?;
|
|
1939
|
+
let verified_rows = count_binary_cas_manifests(backend).await?;
|
|
1940
|
+
Ok(report(writes.len(), verified_rows, Duration::ZERO))
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
pub async fn prepare_binary_cas_read(
|
|
1944
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1945
|
+
config: StorageBenchConfig,
|
|
1946
|
+
) -> Result<BinaryCasReadFixture, LixError> {
|
|
1947
|
+
let context = BinaryCasContext::new();
|
|
1948
|
+
let payloads = binary_payloads(config.rows, config.blob_bytes);
|
|
1949
|
+
let file_ids = binary_file_ids(config.rows);
|
|
1950
|
+
let writes = binary_blob_writes(&file_ids, &payloads);
|
|
1951
|
+
write_binary_blob_writes(backend, &context, &writes).await?;
|
|
1952
|
+
let hashes = payloads
|
|
1953
|
+
.iter()
|
|
1954
|
+
.map(|payload| BlobHash::from_content(payload))
|
|
1955
|
+
.collect::<Vec<_>>();
|
|
1956
|
+
Ok(BinaryCasReadFixture {
|
|
1957
|
+
context,
|
|
1958
|
+
rows: config.rows,
|
|
1959
|
+
hashes,
|
|
1960
|
+
})
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
pub async fn binary_cas_read_blob_hit_prepared(
|
|
1964
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1965
|
+
fixture: &BinaryCasReadFixture,
|
|
1966
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1967
|
+
let mut reader = fixture
|
|
1968
|
+
.context
|
|
1969
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1970
|
+
let verified_rows = reader
|
|
1971
|
+
.load_bytes_many(&fixture.hashes)
|
|
1972
|
+
.await?
|
|
1973
|
+
.into_vec()
|
|
1974
|
+
.into_iter()
|
|
1975
|
+
.filter(|row| row.is_some())
|
|
1976
|
+
.count();
|
|
1977
|
+
Ok(report(fixture.hashes.len(), verified_rows, Duration::ZERO))
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
pub async fn binary_cas_read_blob_miss_prepared(
|
|
1981
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
1982
|
+
fixture: &BinaryCasReadFixture,
|
|
1983
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
1984
|
+
let mut misses = 0;
|
|
1985
|
+
let mut reader = fixture
|
|
1986
|
+
.context
|
|
1987
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
1988
|
+
for index in 0..fixture.rows {
|
|
1989
|
+
let missing_hash = BlobHash::from_hex(&format!("{index:064x}"))?;
|
|
1990
|
+
if reader
|
|
1991
|
+
.load_bytes_many(&[missing_hash])
|
|
1992
|
+
.await?
|
|
1993
|
+
.get(0)
|
|
1994
|
+
.is_none()
|
|
1995
|
+
{
|
|
1996
|
+
misses += 1;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
Ok(report(fixture.rows, misses, Duration::ZERO))
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
pub async fn prepare_json_store_write(
|
|
2003
|
+
shape: JsonStorePayloadShape,
|
|
2004
|
+
rows: usize,
|
|
2005
|
+
) -> Result<JsonStoreWriteFixture, LixError> {
|
|
2006
|
+
Ok(JsonStoreWriteFixture {
|
|
2007
|
+
context: JsonStoreContext::new(),
|
|
2008
|
+
documents: json_documents(shape, rows),
|
|
2009
|
+
})
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
pub async fn prepare_json_store_write_dedupe(
|
|
2013
|
+
shape: JsonStorePayloadShape,
|
|
2014
|
+
rows: usize,
|
|
2015
|
+
) -> Result<JsonStoreWriteFixture, LixError> {
|
|
2016
|
+
let document = json_document(shape, 0);
|
|
2017
|
+
Ok(JsonStoreWriteFixture {
|
|
2018
|
+
context: JsonStoreContext::new(),
|
|
2019
|
+
documents: (0..rows).map(|_| document.clone()).collect(),
|
|
2020
|
+
})
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
pub async fn json_store_write_prepared(
|
|
2024
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2025
|
+
fixture: &JsonStoreWriteFixture,
|
|
2026
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2027
|
+
let storage = StorageContext::new(Arc::clone(backend));
|
|
2028
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
2029
|
+
{
|
|
2030
|
+
let mut writes = StorageWriteSet::new();
|
|
2031
|
+
let mut writer = fixture.context.writer();
|
|
2032
|
+
for document in &fixture.documents {
|
|
2033
|
+
writer.stage_bytes(&mut writes, document)?;
|
|
2034
|
+
}
|
|
2035
|
+
writes.apply(&mut transaction.as_mut()).await?;
|
|
2036
|
+
}
|
|
2037
|
+
transaction.commit().await?;
|
|
2038
|
+
Ok(report(
|
|
2039
|
+
fixture.documents.len(),
|
|
2040
|
+
fixture.documents.len(),
|
|
2041
|
+
Duration::ZERO,
|
|
2042
|
+
))
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
pub async fn prepare_json_store_read(
|
|
2046
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2047
|
+
shape: JsonStorePayloadShape,
|
|
2048
|
+
rows: usize,
|
|
2049
|
+
) -> Result<JsonStoreReadFixture, LixError> {
|
|
2050
|
+
prepare_json_store_projection_read(
|
|
2051
|
+
backend,
|
|
2052
|
+
shape,
|
|
2053
|
+
rows,
|
|
2054
|
+
JsonStoreProjectionShape::TopLevelTarget,
|
|
2055
|
+
)
|
|
2056
|
+
.await
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
pub async fn prepare_json_store_projection_read(
|
|
2060
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2061
|
+
shape: JsonStorePayloadShape,
|
|
2062
|
+
rows: usize,
|
|
2063
|
+
projection: JsonStoreProjectionShape,
|
|
2064
|
+
) -> Result<JsonStoreReadFixture, LixError> {
|
|
2065
|
+
let context = JsonStoreContext::new();
|
|
2066
|
+
let documents = json_documents(shape, rows);
|
|
2067
|
+
let mut refs = Vec::with_capacity(documents.len());
|
|
2068
|
+
let storage = StorageContext::new(Arc::clone(backend));
|
|
2069
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
2070
|
+
{
|
|
2071
|
+
let mut writes = StorageWriteSet::new();
|
|
2072
|
+
let mut writer = context.writer();
|
|
2073
|
+
for document in documents {
|
|
2074
|
+
refs.push(writer.stage_bytes(&mut writes, &document)?);
|
|
2075
|
+
}
|
|
2076
|
+
writes.apply(&mut transaction.as_mut()).await?;
|
|
2077
|
+
}
|
|
2078
|
+
transaction.commit().await?;
|
|
2079
|
+
Ok(JsonStoreReadFixture {
|
|
2080
|
+
context,
|
|
2081
|
+
refs,
|
|
2082
|
+
paths: json_projection_paths(projection),
|
|
2083
|
+
})
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
pub async fn json_store_read_bytes_prepared(
|
|
2087
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2088
|
+
fixture: &JsonStoreReadFixture,
|
|
2089
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2090
|
+
let mut verified_rows = 0;
|
|
2091
|
+
let mut reader = fixture
|
|
2092
|
+
.context
|
|
2093
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
2094
|
+
for json_ref in &fixture.refs {
|
|
2095
|
+
if reader.load_bytes(json_ref).await?.is_some() {
|
|
2096
|
+
verified_rows += 1;
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
Ok(report(fixture.refs.len(), verified_rows, Duration::ZERO))
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
pub async fn json_store_read_value_prepared(
|
|
2103
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2104
|
+
fixture: &JsonStoreReadFixture,
|
|
2105
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2106
|
+
let mut verified_rows = 0;
|
|
2107
|
+
let mut reader = fixture
|
|
2108
|
+
.context
|
|
2109
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
2110
|
+
for json_ref in &fixture.refs {
|
|
2111
|
+
if reader.load_json_value(json_ref).await?.is_some() {
|
|
2112
|
+
verified_rows += 1;
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
Ok(report(fixture.refs.len(), verified_rows, Duration::ZERO))
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
pub async fn json_store_read_projection_prepared(
|
|
2119
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2120
|
+
fixture: &JsonStoreReadFixture,
|
|
2121
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2122
|
+
let mut verified_rows = 0;
|
|
2123
|
+
let mut reader = fixture
|
|
2124
|
+
.context
|
|
2125
|
+
.reader(StorageContext::new(Arc::clone(backend)));
|
|
2126
|
+
for json_ref in &fixture.refs {
|
|
2127
|
+
if reader
|
|
2128
|
+
.load_json_projection(json_ref, &fixture.paths)
|
|
2129
|
+
.await?
|
|
2130
|
+
.is_some()
|
|
2131
|
+
{
|
|
2132
|
+
verified_rows += 1;
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
Ok(report(fixture.refs.len(), verified_rows, Duration::ZERO))
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
pub async fn prepare_json_store_base_update_object(
|
|
2139
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2140
|
+
rows: usize,
|
|
2141
|
+
) -> Result<JsonStoreReadFixture, LixError> {
|
|
2142
|
+
prepare_json_store_base_update(backend, JsonStorePayloadShape::LargeStructured128k, rows).await
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
pub async fn prepare_json_store_base_update_array(
|
|
2146
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2147
|
+
rows: usize,
|
|
2148
|
+
) -> Result<JsonStoreReadFixture, LixError> {
|
|
2149
|
+
prepare_json_store_base_update(backend, JsonStorePayloadShape::LargeArray128k, rows).await
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
async fn prepare_json_store_base_update(
|
|
2153
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2154
|
+
shape: JsonStorePayloadShape,
|
|
2155
|
+
rows: usize,
|
|
2156
|
+
) -> Result<JsonStoreReadFixture, LixError> {
|
|
2157
|
+
let context = JsonStoreContext::new();
|
|
2158
|
+
let documents = json_documents(shape, rows);
|
|
2159
|
+
let mut refs = Vec::with_capacity(documents.len());
|
|
2160
|
+
let storage = StorageContext::new(Arc::clone(backend));
|
|
2161
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
2162
|
+
{
|
|
2163
|
+
let mut writes = StorageWriteSet::new();
|
|
2164
|
+
let mut writer = context.writer();
|
|
2165
|
+
for document in documents {
|
|
2166
|
+
refs.push(writer.stage_bytes(&mut writes, &document)?);
|
|
2167
|
+
}
|
|
2168
|
+
writes.apply(&mut transaction.as_mut()).await?;
|
|
2169
|
+
}
|
|
2170
|
+
transaction.commit().await?;
|
|
2171
|
+
Ok(JsonStoreReadFixture {
|
|
2172
|
+
context,
|
|
2173
|
+
refs,
|
|
2174
|
+
paths: json_projection_paths(JsonStoreProjectionShape::TopLevelTarget),
|
|
2175
|
+
})
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
pub async fn json_store_write_against_base_object_prepared(
|
|
2179
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2180
|
+
fixture: &JsonStoreReadFixture,
|
|
2181
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2182
|
+
json_store_write_against_base_prepared(
|
|
2183
|
+
backend,
|
|
2184
|
+
fixture,
|
|
2185
|
+
JsonStorePayloadShape::LargeStructured128k,
|
|
2186
|
+
)
|
|
2187
|
+
.await
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
pub async fn json_store_write_against_base_array_prepared(
|
|
2191
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2192
|
+
fixture: &JsonStoreReadFixture,
|
|
2193
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2194
|
+
json_store_write_against_base_prepared(backend, fixture, JsonStorePayloadShape::LargeArray128k)
|
|
2195
|
+
.await
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
async fn json_store_write_against_base_prepared(
|
|
2199
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2200
|
+
fixture: &JsonStoreReadFixture,
|
|
2201
|
+
shape: JsonStorePayloadShape,
|
|
2202
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2203
|
+
let storage = StorageContext::new(Arc::clone(backend));
|
|
2204
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
2205
|
+
{
|
|
2206
|
+
let mut writes = StorageWriteSet::new();
|
|
2207
|
+
let mut writer = fixture.context.writer();
|
|
2208
|
+
for (index, _json_ref) in fixture.refs.iter().enumerate() {
|
|
2209
|
+
let updated = updated_json_document(shape, index);
|
|
2210
|
+
writer.stage_bytes(&mut writes, &updated)?;
|
|
2211
|
+
}
|
|
2212
|
+
writes.apply(&mut transaction.as_mut()).await?;
|
|
2213
|
+
}
|
|
2214
|
+
transaction.commit().await?;
|
|
2215
|
+
Ok(report(
|
|
2216
|
+
fixture.refs.len(),
|
|
2217
|
+
fixture.refs.len(),
|
|
2218
|
+
Duration::ZERO,
|
|
2219
|
+
))
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
pub async fn tracked_state_write_root(
|
|
2223
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2224
|
+
config: StorageBenchConfig,
|
|
2225
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2226
|
+
let rows = tracked_rows(config, "bench-tracked-commit");
|
|
2227
|
+
let context = TrackedStateContext::new();
|
|
2228
|
+
let started = Instant::now();
|
|
2229
|
+
write_tracked_root(backend, &context, "bench-tracked-commit", None, &rows).await?;
|
|
2230
|
+
let elapsed = started.elapsed();
|
|
2231
|
+
let verified_rows = scan_tracked(backend, &context, "bench-tracked-commit")
|
|
2232
|
+
.await?
|
|
2233
|
+
.len();
|
|
2234
|
+
Ok(report(rows.len(), verified_rows, elapsed))
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
pub async fn tracked_state_read_point_hit(
|
|
2238
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2239
|
+
config: StorageBenchConfig,
|
|
2240
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2241
|
+
let context = TrackedStateContext::new();
|
|
2242
|
+
let rows = tracked_rows(config, "bench-tracked-commit");
|
|
2243
|
+
write_tracked_root(backend, &context, "bench-tracked-commit", None, &rows).await?;
|
|
2244
|
+
|
|
2245
|
+
let started = Instant::now();
|
|
2246
|
+
let mut verified_rows = 0;
|
|
2247
|
+
let mut reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2248
|
+
for index in 0..config.rows {
|
|
2249
|
+
if reader
|
|
2250
|
+
.load_row_at_commit(
|
|
2251
|
+
"bench-tracked-commit",
|
|
2252
|
+
&TrackedStateRowRequest {
|
|
2253
|
+
schema_key: tracked_schema_key(index, StorageBenchSelectivity::Percent100),
|
|
2254
|
+
entity_id: EntityIdentity::single(entity_id(
|
|
2255
|
+
"tracked",
|
|
2256
|
+
index,
|
|
2257
|
+
config.key_pattern,
|
|
2258
|
+
)),
|
|
2259
|
+
file_id: NullableKeyFilter::Value("bench.json".to_string()),
|
|
2260
|
+
},
|
|
2261
|
+
)
|
|
2262
|
+
.await?
|
|
2263
|
+
.is_some()
|
|
2264
|
+
{
|
|
2265
|
+
verified_rows += 1;
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
Ok(report(config.rows, verified_rows, started.elapsed()))
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
pub async fn tracked_state_read_point_miss(
|
|
2272
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2273
|
+
config: StorageBenchConfig,
|
|
2274
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2275
|
+
let context = TrackedStateContext::new();
|
|
2276
|
+
let rows = tracked_rows(config, "bench-tracked-commit");
|
|
2277
|
+
write_tracked_root(backend, &context, "bench-tracked-commit", None, &rows).await?;
|
|
2278
|
+
|
|
2279
|
+
let started = Instant::now();
|
|
2280
|
+
let mut misses = 0;
|
|
2281
|
+
let mut reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2282
|
+
for index in 0..config.rows {
|
|
2283
|
+
if reader
|
|
2284
|
+
.load_row_at_commit(
|
|
2285
|
+
"bench-tracked-commit",
|
|
2286
|
+
&TrackedStateRowRequest {
|
|
2287
|
+
schema_key: tracked_schema_key(index, StorageBenchSelectivity::Percent100),
|
|
2288
|
+
entity_id: EntityIdentity::single(format!("missing-{index}")),
|
|
2289
|
+
file_id: NullableKeyFilter::Value("bench.json".to_string()),
|
|
2290
|
+
},
|
|
2291
|
+
)
|
|
2292
|
+
.await?
|
|
2293
|
+
.is_none()
|
|
2294
|
+
{
|
|
2295
|
+
misses += 1;
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
Ok(report(config.rows, misses, started.elapsed()))
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
pub async fn tracked_state_scan_all(
|
|
2302
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2303
|
+
config: StorageBenchConfig,
|
|
2304
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2305
|
+
let context = TrackedStateContext::new();
|
|
2306
|
+
let rows = tracked_rows(config, "bench-tracked-commit");
|
|
2307
|
+
write_tracked_root(backend, &context, "bench-tracked-commit", None, &rows).await?;
|
|
2308
|
+
|
|
2309
|
+
let started = Instant::now();
|
|
2310
|
+
let verified_rows = scan_tracked(backend, &context, "bench-tracked-commit")
|
|
2311
|
+
.await?
|
|
2312
|
+
.len();
|
|
2313
|
+
Ok(report(config.rows, verified_rows, started.elapsed()))
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
pub async fn tracked_state_scan_schema(
|
|
2317
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2318
|
+
config: StorageBenchConfig,
|
|
2319
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2320
|
+
let context = TrackedStateContext::new();
|
|
2321
|
+
let rows = tracked_rows(config, "bench-tracked-commit");
|
|
2322
|
+
write_tracked_root(backend, &context, "bench-tracked-commit", None, &rows).await?;
|
|
2323
|
+
|
|
2324
|
+
let started = Instant::now();
|
|
2325
|
+
let mut reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2326
|
+
let verified_rows = reader
|
|
2327
|
+
.scan_rows_at_commit(
|
|
2328
|
+
"bench-tracked-commit",
|
|
2329
|
+
&TrackedStateScanRequest {
|
|
2330
|
+
filter: TrackedStateFilter {
|
|
2331
|
+
schema_keys: vec![tracked_schema_key(0, StorageBenchSelectivity::Percent100)],
|
|
2332
|
+
..Default::default()
|
|
2333
|
+
},
|
|
2334
|
+
..Default::default()
|
|
2335
|
+
},
|
|
2336
|
+
)
|
|
2337
|
+
.await?
|
|
2338
|
+
.len();
|
|
2339
|
+
Ok(report(config.rows, verified_rows, started.elapsed()))
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2342
|
+
pub async fn tracked_state_scan_file(
|
|
2343
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2344
|
+
config: StorageBenchConfig,
|
|
2345
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2346
|
+
let context = TrackedStateContext::new();
|
|
2347
|
+
let rows = tracked_rows(config, "bench-tracked-commit");
|
|
2348
|
+
write_tracked_root(backend, &context, "bench-tracked-commit", None, &rows).await?;
|
|
2349
|
+
|
|
2350
|
+
let started = Instant::now();
|
|
2351
|
+
let mut reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2352
|
+
let verified_rows = reader
|
|
2353
|
+
.scan_rows_at_commit(
|
|
2354
|
+
"bench-tracked-commit",
|
|
2355
|
+
&TrackedStateScanRequest {
|
|
2356
|
+
filter: TrackedStateFilter {
|
|
2357
|
+
file_ids: vec![NullableKeyFilter::Value("bench.json".to_string())],
|
|
2358
|
+
..Default::default()
|
|
2359
|
+
},
|
|
2360
|
+
..Default::default()
|
|
2361
|
+
},
|
|
2362
|
+
)
|
|
2363
|
+
.await?
|
|
2364
|
+
.len();
|
|
2365
|
+
Ok(report(config.rows, verified_rows, started.elapsed()))
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
pub async fn tracked_state_update_existing(
|
|
2369
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2370
|
+
config: StorageBenchConfig,
|
|
2371
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2372
|
+
let context = TrackedStateContext::new();
|
|
2373
|
+
let rows = tracked_rows(config, "bench-tracked-parent");
|
|
2374
|
+
write_tracked_root(backend, &context, "bench-tracked-parent", None, &rows).await?;
|
|
2375
|
+
let mut updated_rows = tracked_rows(config, "bench-tracked-child");
|
|
2376
|
+
for (index, row) in updated_rows.iter_mut().enumerate() {
|
|
2377
|
+
row.snapshot_content = Some(updated_snapshot_content(index, config.state_payload_bytes));
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
let started = Instant::now();
|
|
2381
|
+
write_tracked_root(
|
|
2382
|
+
backend,
|
|
2383
|
+
&context,
|
|
2384
|
+
"bench-tracked-child",
|
|
2385
|
+
Some("bench-tracked-parent"),
|
|
2386
|
+
&updated_rows,
|
|
2387
|
+
)
|
|
2388
|
+
.await?;
|
|
2389
|
+
let elapsed = started.elapsed();
|
|
2390
|
+
let verified_rows = scan_tracked(backend, &context, "bench-tracked-child")
|
|
2391
|
+
.await?
|
|
2392
|
+
.len();
|
|
2393
|
+
Ok(report(updated_rows.len(), verified_rows, elapsed))
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
pub async fn untracked_state_write_rows(
|
|
2397
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2398
|
+
config: StorageBenchConfig,
|
|
2399
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2400
|
+
let rows = untracked_rows(config);
|
|
2401
|
+
let context = UntrackedStateContext::new();
|
|
2402
|
+
let started = Instant::now();
|
|
2403
|
+
write_untracked_rows(backend, &context, &rows).await?;
|
|
2404
|
+
let elapsed = started.elapsed();
|
|
2405
|
+
let verified_rows = scan_untracked(backend, &context, UntrackedStateScanRequest::default())
|
|
2406
|
+
.await?
|
|
2407
|
+
.len();
|
|
2408
|
+
Ok(report(rows.len(), verified_rows, elapsed))
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
pub async fn untracked_state_read_point_hit(
|
|
2412
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2413
|
+
config: StorageBenchConfig,
|
|
2414
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2415
|
+
let context = UntrackedStateContext::new();
|
|
2416
|
+
let rows = untracked_rows(config);
|
|
2417
|
+
write_untracked_rows(backend, &context, &rows).await?;
|
|
2418
|
+
|
|
2419
|
+
let started = Instant::now();
|
|
2420
|
+
let mut verified_rows = 0;
|
|
2421
|
+
let mut reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2422
|
+
for index in 0..config.rows {
|
|
2423
|
+
if reader
|
|
2424
|
+
.load_row(&UntrackedStateRowRequest {
|
|
2425
|
+
schema_key: untracked_schema_key(index, StorageBenchSelectivity::Percent100),
|
|
2426
|
+
version_id: "bench-version".to_string(),
|
|
2427
|
+
entity_id: EntityIdentity::single(entity_id(
|
|
2428
|
+
"untracked",
|
|
2429
|
+
index,
|
|
2430
|
+
config.key_pattern,
|
|
2431
|
+
)),
|
|
2432
|
+
file_id: NullableKeyFilter::Value("bench.json".to_string()),
|
|
2433
|
+
})
|
|
2434
|
+
.await?
|
|
2435
|
+
.is_some()
|
|
2436
|
+
{
|
|
2437
|
+
verified_rows += 1;
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
Ok(report(config.rows, verified_rows, started.elapsed()))
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
pub async fn untracked_state_read_point_miss(
|
|
2444
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2445
|
+
config: StorageBenchConfig,
|
|
2446
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2447
|
+
let context = UntrackedStateContext::new();
|
|
2448
|
+
let rows = untracked_rows(config);
|
|
2449
|
+
write_untracked_rows(backend, &context, &rows).await?;
|
|
2450
|
+
|
|
2451
|
+
let started = Instant::now();
|
|
2452
|
+
let mut misses = 0;
|
|
2453
|
+
let mut reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2454
|
+
for index in 0..config.rows {
|
|
2455
|
+
if reader
|
|
2456
|
+
.load_row(&UntrackedStateRowRequest {
|
|
2457
|
+
schema_key: "bench_untracked_entity".to_string(),
|
|
2458
|
+
version_id: "bench-version".to_string(),
|
|
2459
|
+
entity_id: EntityIdentity::single(format!("missing-{index}")),
|
|
2460
|
+
file_id: NullableKeyFilter::Value("bench.json".to_string()),
|
|
2461
|
+
})
|
|
2462
|
+
.await?
|
|
2463
|
+
.is_none()
|
|
2464
|
+
{
|
|
2465
|
+
misses += 1;
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
Ok(report(config.rows, misses, started.elapsed()))
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
pub async fn untracked_state_scan_all(
|
|
2472
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2473
|
+
config: StorageBenchConfig,
|
|
2474
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2475
|
+
let context = UntrackedStateContext::new();
|
|
2476
|
+
let rows = untracked_rows(config);
|
|
2477
|
+
write_untracked_rows(backend, &context, &rows).await?;
|
|
2478
|
+
|
|
2479
|
+
let started = Instant::now();
|
|
2480
|
+
let verified_rows = scan_untracked(backend, &context, UntrackedStateScanRequest::default())
|
|
2481
|
+
.await?
|
|
2482
|
+
.len();
|
|
2483
|
+
Ok(report(config.rows, verified_rows, started.elapsed()))
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2486
|
+
pub async fn untracked_state_scan_version(
|
|
2487
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2488
|
+
config: StorageBenchConfig,
|
|
2489
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2490
|
+
let context = UntrackedStateContext::new();
|
|
2491
|
+
let rows = untracked_rows(config);
|
|
2492
|
+
write_untracked_rows(backend, &context, &rows).await?;
|
|
2493
|
+
|
|
2494
|
+
let started = Instant::now();
|
|
2495
|
+
let verified_rows = scan_untracked(
|
|
2496
|
+
backend,
|
|
2497
|
+
&context,
|
|
2498
|
+
UntrackedStateScanRequest {
|
|
2499
|
+
filter: UntrackedStateFilter {
|
|
2500
|
+
version_ids: vec!["bench-version".to_string()],
|
|
2501
|
+
..Default::default()
|
|
2502
|
+
},
|
|
2503
|
+
..Default::default()
|
|
2504
|
+
},
|
|
2505
|
+
)
|
|
2506
|
+
.await?
|
|
2507
|
+
.len();
|
|
2508
|
+
Ok(report(config.rows, verified_rows, started.elapsed()))
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
pub async fn untracked_state_scan_schema(
|
|
2512
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2513
|
+
config: StorageBenchConfig,
|
|
2514
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2515
|
+
let context = UntrackedStateContext::new();
|
|
2516
|
+
let rows = untracked_rows(config);
|
|
2517
|
+
write_untracked_rows(backend, &context, &rows).await?;
|
|
2518
|
+
|
|
2519
|
+
let started = Instant::now();
|
|
2520
|
+
let verified_rows = scan_untracked(
|
|
2521
|
+
backend,
|
|
2522
|
+
&context,
|
|
2523
|
+
UntrackedStateScanRequest {
|
|
2524
|
+
filter: UntrackedStateFilter {
|
|
2525
|
+
schema_keys: vec![untracked_schema_key(0, StorageBenchSelectivity::Percent100)],
|
|
2526
|
+
..Default::default()
|
|
2527
|
+
},
|
|
2528
|
+
..Default::default()
|
|
2529
|
+
},
|
|
2530
|
+
)
|
|
2531
|
+
.await?
|
|
2532
|
+
.len();
|
|
2533
|
+
Ok(report(config.rows, verified_rows, started.elapsed()))
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
pub async fn untracked_state_overwrite_existing(
|
|
2537
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2538
|
+
config: StorageBenchConfig,
|
|
2539
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2540
|
+
let context = UntrackedStateContext::new();
|
|
2541
|
+
let rows = untracked_rows(config);
|
|
2542
|
+
write_untracked_rows(backend, &context, &rows).await?;
|
|
2543
|
+
let mut updated_rows = untracked_rows(config);
|
|
2544
|
+
for (index, row) in updated_rows.iter_mut().enumerate() {
|
|
2545
|
+
row.snapshot_content = Some(updated_snapshot_content(index, config.state_payload_bytes));
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
let started = Instant::now();
|
|
2549
|
+
write_untracked_rows(backend, &context, &updated_rows).await?;
|
|
2550
|
+
let elapsed = started.elapsed();
|
|
2551
|
+
let verified_rows = scan_untracked(backend, &context, UntrackedStateScanRequest::default())
|
|
2552
|
+
.await?
|
|
2553
|
+
.len();
|
|
2554
|
+
Ok(report(updated_rows.len(), verified_rows, elapsed))
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
pub async fn changelog_append_changes(
|
|
2558
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2559
|
+
config: StorageBenchConfig,
|
|
2560
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2561
|
+
let changes = changelog_materialized_changes(config);
|
|
2562
|
+
let context = ChangelogContext::new();
|
|
2563
|
+
let started = Instant::now();
|
|
2564
|
+
append_changelog_changes(backend, &context, &changes).await?;
|
|
2565
|
+
let elapsed = started.elapsed();
|
|
2566
|
+
let reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2567
|
+
let verified_rows = reader
|
|
2568
|
+
.scan_changes(&ChangelogScanRequest::default())
|
|
2569
|
+
.await?
|
|
2570
|
+
.len();
|
|
2571
|
+
Ok(report(changes.len(), verified_rows, elapsed))
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
pub async fn changelog_load_change_hit(
|
|
2575
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2576
|
+
config: StorageBenchConfig,
|
|
2577
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2578
|
+
let context = ChangelogContext::new();
|
|
2579
|
+
let changes = changelog_materialized_changes(config);
|
|
2580
|
+
append_changelog_changes(backend, &context, &changes).await?;
|
|
2581
|
+
let reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2582
|
+
|
|
2583
|
+
let started = Instant::now();
|
|
2584
|
+
let mut verified_rows = 0;
|
|
2585
|
+
for index in 0..config.rows {
|
|
2586
|
+
if reader
|
|
2587
|
+
.load_change(&format!("bench-change-{index}"))
|
|
2588
|
+
.await?
|
|
2589
|
+
.is_some()
|
|
2590
|
+
{
|
|
2591
|
+
verified_rows += 1;
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
Ok(report(config.rows, verified_rows, started.elapsed()))
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
pub async fn changelog_load_change_miss(
|
|
2598
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2599
|
+
config: StorageBenchConfig,
|
|
2600
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2601
|
+
let context = ChangelogContext::new();
|
|
2602
|
+
let changes = changelog_materialized_changes(config);
|
|
2603
|
+
append_changelog_changes(backend, &context, &changes).await?;
|
|
2604
|
+
let reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2605
|
+
|
|
2606
|
+
let started = Instant::now();
|
|
2607
|
+
let mut misses = 0;
|
|
2608
|
+
for index in 0..config.rows {
|
|
2609
|
+
if reader
|
|
2610
|
+
.load_change(&format!("missing-change-{index}"))
|
|
2611
|
+
.await?
|
|
2612
|
+
.is_none()
|
|
2613
|
+
{
|
|
2614
|
+
misses += 1;
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
Ok(report(config.rows, misses, started.elapsed()))
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
pub async fn changelog_scan_all(
|
|
2621
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2622
|
+
config: StorageBenchConfig,
|
|
2623
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2624
|
+
let context = ChangelogContext::new();
|
|
2625
|
+
let changes = changelog_materialized_changes(config);
|
|
2626
|
+
append_changelog_changes(backend, &context, &changes).await?;
|
|
2627
|
+
let reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2628
|
+
|
|
2629
|
+
let started = Instant::now();
|
|
2630
|
+
let verified_rows = reader
|
|
2631
|
+
.scan_changes(&ChangelogScanRequest::default())
|
|
2632
|
+
.await?
|
|
2633
|
+
.len();
|
|
2634
|
+
Ok(report(config.rows, verified_rows, started.elapsed()))
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
pub async fn changelog_scan_limit_100(
|
|
2638
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2639
|
+
config: StorageBenchConfig,
|
|
2640
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2641
|
+
let context = ChangelogContext::new();
|
|
2642
|
+
let changes = changelog_materialized_changes(config);
|
|
2643
|
+
append_changelog_changes(backend, &context, &changes).await?;
|
|
2644
|
+
let reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2645
|
+
let expected = config.rows.min(100);
|
|
2646
|
+
|
|
2647
|
+
let started = Instant::now();
|
|
2648
|
+
let verified_rows = reader
|
|
2649
|
+
.scan_changes(&ChangelogScanRequest {
|
|
2650
|
+
limit: Some(expected),
|
|
2651
|
+
})
|
|
2652
|
+
.await?
|
|
2653
|
+
.len();
|
|
2654
|
+
Ok(report(expected, verified_rows, started.elapsed()))
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
pub async fn binary_cas_write_blobs(
|
|
2658
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2659
|
+
config: StorageBenchConfig,
|
|
2660
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2661
|
+
let payloads = binary_payloads(config.rows, config.blob_bytes);
|
|
2662
|
+
let file_ids = binary_file_ids(config.rows);
|
|
2663
|
+
let writes = binary_blob_writes(&file_ids, &payloads);
|
|
2664
|
+
let context = BinaryCasContext::new();
|
|
2665
|
+
|
|
2666
|
+
let started = Instant::now();
|
|
2667
|
+
write_binary_blob_writes(backend, &context, &writes).await?;
|
|
2668
|
+
let elapsed = started.elapsed();
|
|
2669
|
+
let verified_rows = count_binary_cas_manifests(backend).await?;
|
|
2670
|
+
Ok(report(writes.len(), verified_rows, elapsed))
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
pub async fn binary_cas_read_blob_hit(
|
|
2674
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2675
|
+
config: StorageBenchConfig,
|
|
2676
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2677
|
+
let context = BinaryCasContext::new();
|
|
2678
|
+
let payloads = binary_payloads(config.rows, config.blob_bytes);
|
|
2679
|
+
let file_ids = binary_file_ids(config.rows);
|
|
2680
|
+
let writes = binary_blob_writes(&file_ids, &payloads);
|
|
2681
|
+
write_binary_blob_writes(backend, &context, &writes).await?;
|
|
2682
|
+
let hashes = payloads
|
|
2683
|
+
.iter()
|
|
2684
|
+
.map(|payload| BlobHash::from_content(payload))
|
|
2685
|
+
.collect::<Vec<_>>();
|
|
2686
|
+
|
|
2687
|
+
let started = Instant::now();
|
|
2688
|
+
let mut reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2689
|
+
let verified_rows = reader
|
|
2690
|
+
.load_bytes_many(&hashes)
|
|
2691
|
+
.await?
|
|
2692
|
+
.into_vec()
|
|
2693
|
+
.into_iter()
|
|
2694
|
+
.filter(|row| row.is_some())
|
|
2695
|
+
.count();
|
|
2696
|
+
Ok(report(hashes.len(), verified_rows, started.elapsed()))
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
pub async fn binary_cas_read_blob_miss(
|
|
2700
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2701
|
+
config: StorageBenchConfig,
|
|
2702
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2703
|
+
let context = BinaryCasContext::new();
|
|
2704
|
+
let payloads = binary_payloads(config.rows, config.blob_bytes);
|
|
2705
|
+
let file_ids = binary_file_ids(config.rows);
|
|
2706
|
+
let writes = binary_blob_writes(&file_ids, &payloads);
|
|
2707
|
+
write_binary_blob_writes(backend, &context, &writes).await?;
|
|
2708
|
+
|
|
2709
|
+
let started = Instant::now();
|
|
2710
|
+
let mut misses = 0;
|
|
2711
|
+
let mut reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2712
|
+
for index in 0..config.rows {
|
|
2713
|
+
let missing_hash = BlobHash::from_hex(&format!("{index:064x}"))?;
|
|
2714
|
+
if reader
|
|
2715
|
+
.load_bytes_many(&[missing_hash])
|
|
2716
|
+
.await?
|
|
2717
|
+
.get(0)
|
|
2718
|
+
.is_none()
|
|
2719
|
+
{
|
|
2720
|
+
misses += 1;
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
Ok(report(config.rows, misses, started.elapsed()))
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2726
|
+
pub async fn binary_cas_write_duplicate_payload(
|
|
2727
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2728
|
+
config: StorageBenchConfig,
|
|
2729
|
+
) -> Result<StorageBenchReport, LixError> {
|
|
2730
|
+
let payload = binary_payload(0, config.blob_bytes);
|
|
2731
|
+
let payloads = (0..config.rows)
|
|
2732
|
+
.map(|_| payload.clone())
|
|
2733
|
+
.collect::<Vec<_>>();
|
|
2734
|
+
let file_ids = binary_file_ids(config.rows);
|
|
2735
|
+
let writes = binary_blob_writes(&file_ids, &payloads);
|
|
2736
|
+
let context = BinaryCasContext::new();
|
|
2737
|
+
|
|
2738
|
+
let started = Instant::now();
|
|
2739
|
+
write_binary_blob_writes(backend, &context, &writes).await?;
|
|
2740
|
+
let elapsed = started.elapsed();
|
|
2741
|
+
let verified_rows = count_binary_cas_manifests(backend).await?;
|
|
2742
|
+
Ok(report(writes.len(), verified_rows, elapsed))
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2745
|
+
async fn write_tracked_root(
|
|
2746
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2747
|
+
context: &TrackedStateContext,
|
|
2748
|
+
commit_id: &str,
|
|
2749
|
+
parent_commit_id: Option<&str>,
|
|
2750
|
+
rows: &[TrackedStateRow],
|
|
2751
|
+
) -> Result<(), LixError> {
|
|
2752
|
+
let storage = StorageContext::new(Arc::clone(backend));
|
|
2753
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
2754
|
+
{
|
|
2755
|
+
let mut writes = StorageWriteSet::new();
|
|
2756
|
+
{
|
|
2757
|
+
let mut json_writer = JsonStoreContext::new().writer();
|
|
2758
|
+
context
|
|
2759
|
+
.writer()
|
|
2760
|
+
.stage_root(
|
|
2761
|
+
&mut transaction.as_mut(),
|
|
2762
|
+
&mut writes,
|
|
2763
|
+
&mut json_writer,
|
|
2764
|
+
commit_id,
|
|
2765
|
+
parent_commit_id,
|
|
2766
|
+
rows,
|
|
2767
|
+
)
|
|
2768
|
+
.await?;
|
|
2769
|
+
}
|
|
2770
|
+
writes.apply(&mut transaction.as_mut()).await?;
|
|
2771
|
+
}
|
|
2772
|
+
transaction.commit().await
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
async fn scan_tracked(
|
|
2776
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2777
|
+
context: &TrackedStateContext,
|
|
2778
|
+
commit_id: &str,
|
|
2779
|
+
) -> Result<Vec<TrackedStateRow>, LixError> {
|
|
2780
|
+
let mut reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2781
|
+
reader
|
|
2782
|
+
.scan_rows_at_commit(commit_id, &TrackedStateScanRequest::default())
|
|
2783
|
+
.await
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
async fn write_untracked_rows(
|
|
2787
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2788
|
+
context: &UntrackedStateContext,
|
|
2789
|
+
rows: &[MaterializedUntrackedStateRow],
|
|
2790
|
+
) -> Result<(), LixError> {
|
|
2791
|
+
let storage = StorageContext::new(Arc::clone(backend));
|
|
2792
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
2793
|
+
{
|
|
2794
|
+
let mut writes = StorageWriteSet::new();
|
|
2795
|
+
let canonical_rows = {
|
|
2796
|
+
let mut json_writer = JsonStoreContext::new().writer();
|
|
2797
|
+
rows.iter()
|
|
2798
|
+
.map(|row| canonicalize_materialized_row(&mut writes, &mut json_writer, row))
|
|
2799
|
+
.collect::<Result<Vec<_>, _>>()?
|
|
2800
|
+
};
|
|
2801
|
+
let mut writer = context.writer(&mut writes);
|
|
2802
|
+
writer.stage_rows(&canonical_rows)?;
|
|
2803
|
+
writes.apply(&mut transaction.as_mut()).await?;
|
|
2804
|
+
}
|
|
2805
|
+
transaction.commit().await
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2808
|
+
async fn scan_untracked(
|
|
2809
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2810
|
+
context: &UntrackedStateContext,
|
|
2811
|
+
request: UntrackedStateScanRequest,
|
|
2812
|
+
) -> Result<Vec<MaterializedUntrackedStateRow>, LixError> {
|
|
2813
|
+
let mut reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2814
|
+
reader.scan_rows(&request).await
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
async fn append_changelog_changes(
|
|
2818
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2819
|
+
context: &ChangelogContext,
|
|
2820
|
+
changes: &[MaterializedCanonicalChange],
|
|
2821
|
+
) -> Result<(), LixError> {
|
|
2822
|
+
let storage = StorageContext::new(Arc::clone(backend));
|
|
2823
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
2824
|
+
{
|
|
2825
|
+
let mut writes = StorageWriteSet::new();
|
|
2826
|
+
let canonical_changes = {
|
|
2827
|
+
let mut json_writer = JsonStoreContext::new().writer();
|
|
2828
|
+
changes
|
|
2829
|
+
.iter()
|
|
2830
|
+
.map(|change| {
|
|
2831
|
+
canonicalize_materialized_change(&mut writes, &mut json_writer, change)
|
|
2832
|
+
})
|
|
2833
|
+
.collect::<Result<Vec<_>, _>>()?
|
|
2834
|
+
};
|
|
2835
|
+
let mut writer = context.writer(&mut writes);
|
|
2836
|
+
writer.stage_changes(&canonical_changes)?;
|
|
2837
|
+
writes.apply(&mut transaction.as_mut()).await?;
|
|
2838
|
+
}
|
|
2839
|
+
transaction.commit().await
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
async fn write_binary_blob_writes(
|
|
2843
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2844
|
+
context: &BinaryCasContext,
|
|
2845
|
+
writes: &[BlobWrite<'_>],
|
|
2846
|
+
) -> Result<(), LixError> {
|
|
2847
|
+
let storage = StorageContext::new(Arc::clone(backend));
|
|
2848
|
+
let mut transaction = storage.begin_write_transaction().await?;
|
|
2849
|
+
{
|
|
2850
|
+
let mut writeset = StorageWriteSet::new();
|
|
2851
|
+
let mut writer = context.writer(&mut writeset);
|
|
2852
|
+
writer.stage_many(writes)?;
|
|
2853
|
+
writeset.apply(&mut transaction.as_mut()).await?;
|
|
2854
|
+
}
|
|
2855
|
+
transaction.commit().await
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
async fn count_binary_cas_manifests(
|
|
2859
|
+
backend: &Arc<dyn Backend + Send + Sync>,
|
|
2860
|
+
) -> Result<usize, LixError> {
|
|
2861
|
+
let context = BinaryCasContext::new();
|
|
2862
|
+
let mut reader = context.reader(StorageContext::new(Arc::clone(backend)));
|
|
2863
|
+
reader.count_blob_manifests().await
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
fn report(measured_rows: usize, verified_rows: usize, elapsed: Duration) -> StorageBenchReport {
|
|
2867
|
+
StorageBenchReport {
|
|
2868
|
+
measured_rows,
|
|
2869
|
+
verified_rows,
|
|
2870
|
+
elapsed,
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
const TRACKED_MATCH_SCHEMA_KEY: &str = "bench_tracked_entity";
|
|
2875
|
+
const TRACKED_OTHER_SCHEMA_KEY: &str = "bench_tracked_other_entity";
|
|
2876
|
+
const UNTRACKED_MATCH_SCHEMA_KEY: &str = "bench_untracked_entity";
|
|
2877
|
+
const UNTRACKED_OTHER_SCHEMA_KEY: &str = "bench_untracked_other_entity";
|
|
2878
|
+
const CHANGELOG_MATCH_SCHEMA_KEY: &str = "bench_changelog_entity";
|
|
2879
|
+
const CHANGELOG_OTHER_SCHEMA_KEY: &str = "bench_changelog_other_entity";
|
|
2880
|
+
const CHANGELOG_HISTORY_ENTITY_ID: &str = "change-entity-history-target";
|
|
2881
|
+
|
|
2882
|
+
fn tracked_rows(config: StorageBenchConfig, commit_id: &str) -> Vec<TrackedStateRow> {
|
|
2883
|
+
(0..config.rows)
|
|
2884
|
+
.map(|index| TrackedStateRow {
|
|
2885
|
+
entity_id: EntityIdentity::single(entity_id("tracked", index, config.key_pattern)),
|
|
2886
|
+
schema_key: tracked_schema_key(index, config.selectivity),
|
|
2887
|
+
file_id: Some("bench.json".to_string()),
|
|
2888
|
+
snapshot_content: Some(snapshot_content(index, config.state_payload_bytes)),
|
|
2889
|
+
metadata: None,
|
|
2890
|
+
schema_version: "1".to_string(),
|
|
2891
|
+
created_at: timestamp(index),
|
|
2892
|
+
updated_at: timestamp(index),
|
|
2893
|
+
change_id: format!("tracked-change-{index}"),
|
|
2894
|
+
commit_id: commit_id.to_string(),
|
|
2895
|
+
})
|
|
2896
|
+
.collect()
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
fn tracked_rows_file_selective(
|
|
2900
|
+
config: StorageBenchConfig,
|
|
2901
|
+
commit_id: &str,
|
|
2902
|
+
) -> Vec<TrackedStateRow> {
|
|
2903
|
+
(0..config.rows)
|
|
2904
|
+
.map(|index| TrackedStateRow {
|
|
2905
|
+
entity_id: EntityIdentity::single(entity_id("tracked", index, config.key_pattern)),
|
|
2906
|
+
schema_key: TRACKED_MATCH_SCHEMA_KEY.to_string(),
|
|
2907
|
+
file_id: Some(
|
|
2908
|
+
if config.selectivity.matches(index) {
|
|
2909
|
+
"bench-match.json"
|
|
2910
|
+
} else {
|
|
2911
|
+
"bench-other.json"
|
|
2912
|
+
}
|
|
2913
|
+
.to_string(),
|
|
2914
|
+
),
|
|
2915
|
+
snapshot_content: Some(snapshot_content(index, config.state_payload_bytes)),
|
|
2916
|
+
metadata: None,
|
|
2917
|
+
schema_version: "1".to_string(),
|
|
2918
|
+
created_at: timestamp(index),
|
|
2919
|
+
updated_at: timestamp(index),
|
|
2920
|
+
change_id: format!("tracked-change-{index}"),
|
|
2921
|
+
commit_id: commit_id.to_string(),
|
|
2922
|
+
})
|
|
2923
|
+
.collect()
|
|
2924
|
+
}
|
|
2925
|
+
|
|
2926
|
+
fn untracked_rows(config: StorageBenchConfig) -> Vec<MaterializedUntrackedStateRow> {
|
|
2927
|
+
(0..config.rows)
|
|
2928
|
+
.map(|index| MaterializedUntrackedStateRow {
|
|
2929
|
+
entity_id: EntityIdentity::single(entity_id("untracked", index, config.key_pattern)),
|
|
2930
|
+
schema_key: untracked_schema_key(index, config.selectivity),
|
|
2931
|
+
file_id: Some("bench.json".to_string()),
|
|
2932
|
+
snapshot_content: Some(snapshot_content(index, config.state_payload_bytes)),
|
|
2933
|
+
metadata: None,
|
|
2934
|
+
schema_version: "1".to_string(),
|
|
2935
|
+
created_at: timestamp(index),
|
|
2936
|
+
updated_at: timestamp(index),
|
|
2937
|
+
global: false,
|
|
2938
|
+
version_id: "bench-version".to_string(),
|
|
2939
|
+
})
|
|
2940
|
+
.collect()
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
fn changelog_changes(config: StorageBenchConfig) -> Vec<CanonicalChange> {
|
|
2944
|
+
changelog_materialized_changes(config)
|
|
2945
|
+
.into_iter()
|
|
2946
|
+
.map(canonical_changelog_bench_change)
|
|
2947
|
+
.collect()
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
fn changelog_materialized_changes(config: StorageBenchConfig) -> Vec<MaterializedCanonicalChange> {
|
|
2951
|
+
(0..config.rows)
|
|
2952
|
+
.map(|index| MaterializedCanonicalChange {
|
|
2953
|
+
id: format!("bench-change-{index}"),
|
|
2954
|
+
entity_id: EntityIdentity::single(entity_id(
|
|
2955
|
+
"change-entity",
|
|
2956
|
+
index,
|
|
2957
|
+
config.key_pattern,
|
|
2958
|
+
)),
|
|
2959
|
+
schema_key: "bench_changelog_entity".to_string(),
|
|
2960
|
+
schema_version: "1".to_string(),
|
|
2961
|
+
file_id: Some("bench.json".to_string()),
|
|
2962
|
+
snapshot_content: Some(snapshot_content(index, config.state_payload_bytes)),
|
|
2963
|
+
metadata: None,
|
|
2964
|
+
created_at: timestamp(index),
|
|
2965
|
+
})
|
|
2966
|
+
.collect()
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2969
|
+
fn canonical_changelog_bench_change(change: MaterializedCanonicalChange) -> CanonicalChange {
|
|
2970
|
+
let snapshot_ref = change
|
|
2971
|
+
.snapshot_content
|
|
2972
|
+
.as_ref()
|
|
2973
|
+
.map(|value| JsonRef::from_hash(blake3::hash(value.as_bytes())));
|
|
2974
|
+
let metadata_ref = change.metadata.as_ref().map(|value| {
|
|
2975
|
+
let bytes =
|
|
2976
|
+
serde_json::to_vec(value).expect("bench metadata should serialize to JSON bytes");
|
|
2977
|
+
JsonRef::from_hash(blake3::hash(&bytes))
|
|
2978
|
+
});
|
|
2979
|
+
CanonicalChange {
|
|
2980
|
+
id: change.id,
|
|
2981
|
+
entity_id: change.entity_id,
|
|
2982
|
+
schema_key: change.schema_key,
|
|
2983
|
+
schema_version: change.schema_version,
|
|
2984
|
+
file_id: change.file_id,
|
|
2985
|
+
snapshot_ref,
|
|
2986
|
+
metadata_ref,
|
|
2987
|
+
created_at: change.created_at,
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
fn changelog_tombstone_changes(config: StorageBenchConfig) -> Vec<MaterializedCanonicalChange> {
|
|
2992
|
+
changelog_materialized_changes(config)
|
|
2993
|
+
.into_iter()
|
|
2994
|
+
.map(|mut change| {
|
|
2995
|
+
change.snapshot_content = None;
|
|
2996
|
+
change.metadata = None;
|
|
2997
|
+
change
|
|
2998
|
+
})
|
|
2999
|
+
.collect()
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
fn changelog_metadata_changes(config: StorageBenchConfig) -> Vec<MaterializedCanonicalChange> {
|
|
3003
|
+
changelog_materialized_changes(config)
|
|
3004
|
+
.into_iter()
|
|
3005
|
+
.enumerate()
|
|
3006
|
+
.map(|(index, mut change)| {
|
|
3007
|
+
change.metadata = Some(snapshot_metadata(index, config.state_payload_bytes));
|
|
3008
|
+
change
|
|
3009
|
+
})
|
|
3010
|
+
.collect()
|
|
3011
|
+
}
|
|
3012
|
+
|
|
3013
|
+
fn changelog_shared_payload_changes(
|
|
3014
|
+
config: StorageBenchConfig,
|
|
3015
|
+
) -> Vec<MaterializedCanonicalChange> {
|
|
3016
|
+
let shared_snapshot_content = snapshot_content(0, config.state_payload_bytes);
|
|
3017
|
+
changelog_materialized_changes(config)
|
|
3018
|
+
.into_iter()
|
|
3019
|
+
.map(|mut change| {
|
|
3020
|
+
change.snapshot_content = Some(shared_snapshot_content.clone());
|
|
3021
|
+
change
|
|
3022
|
+
})
|
|
3023
|
+
.collect()
|
|
3024
|
+
}
|
|
3025
|
+
|
|
3026
|
+
fn changelog_shared_metadata_changes(
|
|
3027
|
+
config: StorageBenchConfig,
|
|
3028
|
+
) -> Vec<MaterializedCanonicalChange> {
|
|
3029
|
+
let shared_metadata = snapshot_metadata(0, config.state_payload_bytes);
|
|
3030
|
+
changelog_materialized_changes(config)
|
|
3031
|
+
.into_iter()
|
|
3032
|
+
.map(|mut change| {
|
|
3033
|
+
change.snapshot_content = None;
|
|
3034
|
+
change.metadata = Some(shared_metadata.clone());
|
|
3035
|
+
change
|
|
3036
|
+
})
|
|
3037
|
+
.collect()
|
|
3038
|
+
}
|
|
3039
|
+
|
|
3040
|
+
fn changelog_shared_payload_and_metadata_changes(
|
|
3041
|
+
config: StorageBenchConfig,
|
|
3042
|
+
) -> Vec<MaterializedCanonicalChange> {
|
|
3043
|
+
let shared_snapshot_content = snapshot_content(0, config.state_payload_bytes);
|
|
3044
|
+
let shared_metadata = snapshot_metadata(1, config.state_payload_bytes);
|
|
3045
|
+
changelog_materialized_changes(config)
|
|
3046
|
+
.into_iter()
|
|
3047
|
+
.map(|mut change| {
|
|
3048
|
+
change.snapshot_content = Some(shared_snapshot_content.clone());
|
|
3049
|
+
change.metadata = Some(shared_metadata.clone());
|
|
3050
|
+
change
|
|
3051
|
+
})
|
|
3052
|
+
.collect()
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
fn changelog_composite_entity_id_changes(
|
|
3056
|
+
config: StorageBenchConfig,
|
|
3057
|
+
) -> Vec<MaterializedCanonicalChange> {
|
|
3058
|
+
changelog_materialized_changes(config)
|
|
3059
|
+
.into_iter()
|
|
3060
|
+
.enumerate()
|
|
3061
|
+
.map(|(index, mut change)| {
|
|
3062
|
+
change.entity_id = EntityIdentity {
|
|
3063
|
+
parts: vec![
|
|
3064
|
+
EntityIdentityPart::String(entity_id(
|
|
3065
|
+
"change-composite",
|
|
3066
|
+
index,
|
|
3067
|
+
config.key_pattern,
|
|
3068
|
+
)),
|
|
3069
|
+
EntityIdentityPart::Number(index.to_string()),
|
|
3070
|
+
EntityIdentityPart::Bool(index % 2 == 0),
|
|
3071
|
+
],
|
|
3072
|
+
};
|
|
3073
|
+
change
|
|
3074
|
+
})
|
|
3075
|
+
.collect()
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
fn changelog_selective_changes(config: StorageBenchConfig) -> Vec<MaterializedCanonicalChange> {
|
|
3079
|
+
changelog_materialized_changes(config)
|
|
3080
|
+
.into_iter()
|
|
3081
|
+
.enumerate()
|
|
3082
|
+
.map(|(index, mut change)| {
|
|
3083
|
+
change.schema_key = changelog_schema_key(index, config.selectivity);
|
|
3084
|
+
change
|
|
3085
|
+
})
|
|
3086
|
+
.collect()
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
fn changelog_entity_history_changes(
|
|
3090
|
+
config: StorageBenchConfig,
|
|
3091
|
+
) -> Vec<MaterializedCanonicalChange> {
|
|
3092
|
+
changelog_materialized_changes(config)
|
|
3093
|
+
.into_iter()
|
|
3094
|
+
.enumerate()
|
|
3095
|
+
.map(|(index, mut change)| {
|
|
3096
|
+
if index % 10 == 0 {
|
|
3097
|
+
change.entity_id = EntityIdentity::single(CHANGELOG_HISTORY_ENTITY_ID);
|
|
3098
|
+
}
|
|
3099
|
+
change
|
|
3100
|
+
})
|
|
3101
|
+
.collect()
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3104
|
+
fn tracked_schema_key(index: usize, selectivity: StorageBenchSelectivity) -> String {
|
|
3105
|
+
if selectivity.matches(index) {
|
|
3106
|
+
TRACKED_MATCH_SCHEMA_KEY
|
|
3107
|
+
} else {
|
|
3108
|
+
TRACKED_OTHER_SCHEMA_KEY
|
|
3109
|
+
}
|
|
3110
|
+
.to_string()
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
fn untracked_schema_key(index: usize, selectivity: StorageBenchSelectivity) -> String {
|
|
3114
|
+
if selectivity.matches(index) {
|
|
3115
|
+
UNTRACKED_MATCH_SCHEMA_KEY
|
|
3116
|
+
} else {
|
|
3117
|
+
UNTRACKED_OTHER_SCHEMA_KEY
|
|
3118
|
+
}
|
|
3119
|
+
.to_string()
|
|
3120
|
+
}
|
|
3121
|
+
|
|
3122
|
+
fn changelog_schema_key(index: usize, selectivity: StorageBenchSelectivity) -> String {
|
|
3123
|
+
if selectivity.matches(index) {
|
|
3124
|
+
CHANGELOG_MATCH_SCHEMA_KEY
|
|
3125
|
+
} else {
|
|
3126
|
+
CHANGELOG_OTHER_SCHEMA_KEY
|
|
3127
|
+
}
|
|
3128
|
+
.to_string()
|
|
3129
|
+
}
|
|
3130
|
+
|
|
3131
|
+
fn entity_id(prefix: &str, index: usize, key_pattern: StorageBenchKeyPattern) -> String {
|
|
3132
|
+
match key_pattern {
|
|
3133
|
+
StorageBenchKeyPattern::Sequential => format!("{prefix}-{index}"),
|
|
3134
|
+
StorageBenchKeyPattern::Random => format!("{prefix}-{:016x}", randomish_index(index)),
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
fn randomish_index(index: usize) -> u64 {
|
|
3139
|
+
let mut value = index as u64;
|
|
3140
|
+
value ^= value >> 30;
|
|
3141
|
+
value = value.wrapping_mul(0xbf58_476d_1ce4_e5b9);
|
|
3142
|
+
value ^= value >> 27;
|
|
3143
|
+
value = value.wrapping_mul(0x94d0_49bb_1331_11eb);
|
|
3144
|
+
value ^ (value >> 31)
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3147
|
+
fn binary_file_ids(rows: usize) -> Vec<String> {
|
|
3148
|
+
(0..rows)
|
|
3149
|
+
.map(|index| format!("bench-file-{index}"))
|
|
3150
|
+
.collect()
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
fn binary_payloads(rows: usize, blob_bytes: usize) -> Vec<Vec<u8>> {
|
|
3154
|
+
(0..rows)
|
|
3155
|
+
.map(|index| binary_payload(index, blob_bytes))
|
|
3156
|
+
.collect()
|
|
3157
|
+
}
|
|
3158
|
+
|
|
3159
|
+
fn binary_half_duplicate_payloads(rows: usize, blob_bytes: usize) -> Vec<Vec<u8>> {
|
|
3160
|
+
(0..rows)
|
|
3161
|
+
.map(|index| {
|
|
3162
|
+
if index % 2 == 0 {
|
|
3163
|
+
binary_payload(0, blob_bytes)
|
|
3164
|
+
} else {
|
|
3165
|
+
binary_payload(index, blob_bytes)
|
|
3166
|
+
}
|
|
3167
|
+
})
|
|
3168
|
+
.collect()
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
fn binary_blob_writes<'a>(_file_ids: &'a [String], payloads: &'a [Vec<u8>]) -> Vec<BlobWrite<'a>> {
|
|
3172
|
+
payloads
|
|
3173
|
+
.iter()
|
|
3174
|
+
.map(|payload| BlobWrite {
|
|
3175
|
+
bytes: payload.as_slice(),
|
|
3176
|
+
})
|
|
3177
|
+
.collect()
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
fn snapshot_content(index: usize, target_bytes: usize) -> String {
|
|
3181
|
+
let mut value = serde_json::json!({
|
|
3182
|
+
"id": format!("entity-{index}"),
|
|
3183
|
+
"value": format!("value-{index}"),
|
|
3184
|
+
"index": index
|
|
3185
|
+
});
|
|
3186
|
+
pad_snapshot_content(&mut value, target_bytes);
|
|
3187
|
+
value.to_string()
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
fn snapshot_metadata(index: usize, target_bytes: usize) -> serde_json::Value {
|
|
3191
|
+
serde_json::from_str(&snapshot_content(index, target_bytes))
|
|
3192
|
+
.expect("bench snapshot content should be valid JSON metadata")
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
fn tracked_state_header_columns() -> Vec<String> {
|
|
3196
|
+
[
|
|
3197
|
+
"entity_id",
|
|
3198
|
+
"schema_key",
|
|
3199
|
+
"schema_version",
|
|
3200
|
+
"file_id",
|
|
3201
|
+
"metadata",
|
|
3202
|
+
"created_at",
|
|
3203
|
+
"updated_at",
|
|
3204
|
+
"change_id",
|
|
3205
|
+
"commit_id",
|
|
3206
|
+
]
|
|
3207
|
+
.into_iter()
|
|
3208
|
+
.map(str::to_string)
|
|
3209
|
+
.collect()
|
|
3210
|
+
}
|
|
3211
|
+
|
|
3212
|
+
fn untracked_state_header_columns() -> Vec<String> {
|
|
3213
|
+
[
|
|
3214
|
+
"entity_id",
|
|
3215
|
+
"schema_key",
|
|
3216
|
+
"schema_version",
|
|
3217
|
+
"file_id",
|
|
3218
|
+
"metadata",
|
|
3219
|
+
"created_at",
|
|
3220
|
+
"updated_at",
|
|
3221
|
+
"global",
|
|
3222
|
+
"version_id",
|
|
3223
|
+
]
|
|
3224
|
+
.into_iter()
|
|
3225
|
+
.map(str::to_string)
|
|
3226
|
+
.collect()
|
|
3227
|
+
}
|
|
3228
|
+
|
|
3229
|
+
fn updated_snapshot_content(index: usize, target_bytes: usize) -> String {
|
|
3230
|
+
let mut value = serde_json::json!({
|
|
3231
|
+
"id": format!("entity-{index}"),
|
|
3232
|
+
"value": format!("updated-{index}"),
|
|
3233
|
+
"index": index
|
|
3234
|
+
});
|
|
3235
|
+
pad_snapshot_content(&mut value, target_bytes);
|
|
3236
|
+
value.to_string()
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3239
|
+
fn partial_updated_snapshot_content(index: usize, target_bytes: usize) -> String {
|
|
3240
|
+
let mut value = serde_json::json!({
|
|
3241
|
+
"id": format!("entity-{index}"),
|
|
3242
|
+
"value": format!("value-{index}"),
|
|
3243
|
+
"index": index,
|
|
3244
|
+
"done": true
|
|
3245
|
+
});
|
|
3246
|
+
pad_snapshot_content(&mut value, target_bytes);
|
|
3247
|
+
value.to_string()
|
|
3248
|
+
}
|
|
3249
|
+
|
|
3250
|
+
fn pad_snapshot_content(value: &mut serde_json::Value, target_bytes: usize) {
|
|
3251
|
+
let current = value.to_string().len();
|
|
3252
|
+
if target_bytes <= current {
|
|
3253
|
+
return;
|
|
3254
|
+
}
|
|
3255
|
+
value["padding"] = serde_json::Value::String("x".repeat(target_bytes - current));
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
fn timestamp(index: usize) -> String {
|
|
3259
|
+
format!(
|
|
3260
|
+
"2026-05-01T00:{:02}:{:02}.000Z",
|
|
3261
|
+
(index / 60) % 60,
|
|
3262
|
+
index % 60
|
|
3263
|
+
)
|
|
3264
|
+
}
|
|
3265
|
+
|
|
3266
|
+
fn binary_payload(index: usize, len: usize) -> Vec<u8> {
|
|
3267
|
+
let mut payload = (0..len)
|
|
3268
|
+
.map(|offset| {
|
|
3269
|
+
((index as u64)
|
|
3270
|
+
.wrapping_mul(31)
|
|
3271
|
+
.wrapping_add((offset as u64).wrapping_mul(17))
|
|
3272
|
+
& 0xff) as u8
|
|
3273
|
+
})
|
|
3274
|
+
.collect::<Vec<_>>();
|
|
3275
|
+
for (offset, byte) in (index as u64).to_le_bytes().into_iter().enumerate() {
|
|
3276
|
+
if offset < payload.len() {
|
|
3277
|
+
payload[offset] = byte;
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
payload
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3283
|
+
fn json_documents(shape: JsonStorePayloadShape, rows: usize) -> Vec<Vec<u8>> {
|
|
3284
|
+
(0..rows).map(|index| json_document(shape, index)).collect()
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
fn json_document(shape: JsonStorePayloadShape, index: usize) -> Vec<u8> {
|
|
3288
|
+
match shape {
|
|
3289
|
+
JsonStorePayloadShape::SmallRaw1k => json_object_document(index, 1_024, 8),
|
|
3290
|
+
JsonStorePayloadShape::MediumStructured16k => json_object_document(index, 16 * 1024, 128),
|
|
3291
|
+
JsonStorePayloadShape::LargeStructured128k => {
|
|
3292
|
+
json_object_document(index, 128 * 1024, 1_000)
|
|
3293
|
+
}
|
|
3294
|
+
JsonStorePayloadShape::LargeArray128k => json_array_document(index, 128 * 1024, 1_000),
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
|
|
3298
|
+
fn updated_json_document(shape: JsonStorePayloadShape, index: usize) -> Vec<u8> {
|
|
3299
|
+
let bytes = json_document(shape, index);
|
|
3300
|
+
let mut value: serde_json::Value =
|
|
3301
|
+
serde_json::from_slice(&bytes).expect("storage bench JSON document should parse");
|
|
3302
|
+
match shape {
|
|
3303
|
+
JsonStorePayloadShape::LargeArray128k => {
|
|
3304
|
+
value["items"][999]["value"] =
|
|
3305
|
+
serde_json::Value::String(format!("updated-array-value-{index}"));
|
|
3306
|
+
}
|
|
3307
|
+
JsonStorePayloadShape::SmallRaw1k
|
|
3308
|
+
| JsonStorePayloadShape::MediumStructured16k
|
|
3309
|
+
| JsonStorePayloadShape::LargeStructured128k => {
|
|
3310
|
+
value["field_999"] = serde_json::Value::String(format!("updated-object-value-{index}"));
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
serde_json::to_vec(&value).expect("storage bench updated JSON should serialize")
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3316
|
+
fn json_object_document(index: usize, target_bytes: usize, fields: usize) -> Vec<u8> {
|
|
3317
|
+
let mut object = serde_json::Map::new();
|
|
3318
|
+
object.insert(
|
|
3319
|
+
"id".to_string(),
|
|
3320
|
+
serde_json::Value::String(format!("json-{index}")),
|
|
3321
|
+
);
|
|
3322
|
+
object.insert(
|
|
3323
|
+
"target".to_string(),
|
|
3324
|
+
serde_json::Value::String(format!("target-{index}")),
|
|
3325
|
+
);
|
|
3326
|
+
object.insert(
|
|
3327
|
+
"status".to_string(),
|
|
3328
|
+
serde_json::Value::String(if index % 2 == 0 { "open" } else { "closed" }.to_string()),
|
|
3329
|
+
);
|
|
3330
|
+
object.insert(
|
|
3331
|
+
"nested".to_string(),
|
|
3332
|
+
serde_json::json!({
|
|
3333
|
+
"target": format!("nested-target-{index}"),
|
|
3334
|
+
"revision": index,
|
|
3335
|
+
}),
|
|
3336
|
+
);
|
|
3337
|
+
for field_index in 0..fields {
|
|
3338
|
+
object.insert(
|
|
3339
|
+
format!("field_{field_index}"),
|
|
3340
|
+
serde_json::Value::String(format!("value-{index}-{field_index}")),
|
|
3341
|
+
);
|
|
3342
|
+
}
|
|
3343
|
+
pad_json_object(&mut object, target_bytes);
|
|
3344
|
+
serde_json::to_vec(&serde_json::Value::Object(object))
|
|
3345
|
+
.expect("storage bench object JSON should serialize")
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
fn json_array_document(index: usize, target_bytes: usize, items: usize) -> Vec<u8> {
|
|
3349
|
+
let mut object = serde_json::Map::new();
|
|
3350
|
+
object.insert(
|
|
3351
|
+
"id".to_string(),
|
|
3352
|
+
serde_json::Value::String(format!("json-array-{index}")),
|
|
3353
|
+
);
|
|
3354
|
+
object.insert(
|
|
3355
|
+
"target".to_string(),
|
|
3356
|
+
serde_json::Value::String(format!("target-{index}")),
|
|
3357
|
+
);
|
|
3358
|
+
object.insert(
|
|
3359
|
+
"status".to_string(),
|
|
3360
|
+
serde_json::Value::String(if index % 2 == 0 { "open" } else { "closed" }.to_string()),
|
|
3361
|
+
);
|
|
3362
|
+
object.insert(
|
|
3363
|
+
"items".to_string(),
|
|
3364
|
+
serde_json::Value::Array(
|
|
3365
|
+
(0..items)
|
|
3366
|
+
.map(|item_index| {
|
|
3367
|
+
serde_json::json!({
|
|
3368
|
+
"index": item_index,
|
|
3369
|
+
"status": if item_index % 2 == 0 { "ready" } else { "blocked" },
|
|
3370
|
+
"value": format!("item-{index}-{item_index}"),
|
|
3371
|
+
})
|
|
3372
|
+
})
|
|
3373
|
+
.collect(),
|
|
3374
|
+
),
|
|
3375
|
+
);
|
|
3376
|
+
pad_json_object(&mut object, target_bytes);
|
|
3377
|
+
serde_json::to_vec(&serde_json::Value::Object(object))
|
|
3378
|
+
.expect("storage bench array JSON should serialize")
|
|
3379
|
+
}
|
|
3380
|
+
|
|
3381
|
+
fn pad_json_object(object: &mut serde_json::Map<String, serde_json::Value>, target_bytes: usize) {
|
|
3382
|
+
let current = serde_json::to_vec(&serde_json::Value::Object(object.clone()))
|
|
3383
|
+
.expect("storage bench JSON should serialize")
|
|
3384
|
+
.len();
|
|
3385
|
+
if target_bytes <= current {
|
|
3386
|
+
return;
|
|
3387
|
+
}
|
|
3388
|
+
object.insert(
|
|
3389
|
+
"padding".to_string(),
|
|
3390
|
+
serde_json::Value::String("x".repeat(target_bytes - current)),
|
|
3391
|
+
);
|
|
3392
|
+
}
|
|
3393
|
+
|
|
3394
|
+
fn json_projection_paths(projection: JsonStoreProjectionShape) -> Vec<JsonProjectionPath> {
|
|
3395
|
+
match projection {
|
|
3396
|
+
JsonStoreProjectionShape::TopLevelTarget => vec![JsonProjectionPath::new("/target")],
|
|
3397
|
+
JsonStoreProjectionShape::TopLevelTenProps => (0..10)
|
|
3398
|
+
.map(|index| JsonProjectionPath::new(format!("/field_{index}")))
|
|
3399
|
+
.collect(),
|
|
3400
|
+
JsonStoreProjectionShape::NestedTarget => vec![JsonProjectionPath::new("/nested/target")],
|
|
3401
|
+
JsonStoreProjectionShape::ArrayItem999 => {
|
|
3402
|
+
vec![JsonProjectionPath::new("/items/999/value")]
|
|
3403
|
+
}
|
|
3404
|
+
JsonStoreProjectionShape::Status => vec![JsonProjectionPath::new("/status")],
|
|
3405
|
+
}
|
|
3406
|
+
}
|