tigerbeetle-node 0.11.12 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +212 -196
  2. package/dist/bin/aarch64-linux-gnu/client.node +0 -0
  3. package/dist/bin/aarch64-linux-musl/client.node +0 -0
  4. package/dist/bin/aarch64-macos/client.node +0 -0
  5. package/dist/bin/x86_64-linux-gnu/client.node +0 -0
  6. package/dist/bin/x86_64-linux-musl/client.node +0 -0
  7. package/dist/bin/x86_64-macos/client.node +0 -0
  8. package/dist/index.js +33 -1
  9. package/dist/index.js.map +1 -1
  10. package/package-lock.json +66 -0
  11. package/package.json +8 -17
  12. package/src/index.ts +56 -1
  13. package/src/node.zig +10 -9
  14. package/dist/.client.node.sha256 +0 -1
  15. package/scripts/build_lib.sh +0 -61
  16. package/scripts/download_node_headers.sh +0 -32
  17. package/src/tigerbeetle/scripts/benchmark.bat +0 -48
  18. package/src/tigerbeetle/scripts/benchmark.sh +0 -66
  19. package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
  20. package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
  21. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
  22. package/src/tigerbeetle/scripts/install.bat +0 -7
  23. package/src/tigerbeetle/scripts/install.sh +0 -21
  24. package/src/tigerbeetle/scripts/install_zig.bat +0 -113
  25. package/src/tigerbeetle/scripts/install_zig.sh +0 -90
  26. package/src/tigerbeetle/scripts/lint.zig +0 -199
  27. package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
  28. package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -48
  29. package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
  30. package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
  31. package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
  32. package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
  33. package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
  34. package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
  35. package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
  36. package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
  37. package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
  38. package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
  39. package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
  40. package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
  41. package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
  42. package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
  43. package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
  44. package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
  45. package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
  46. package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
  47. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
  48. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
  49. package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
  50. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  51. package/src/tigerbeetle/src/benchmark.zig +0 -314
  52. package/src/tigerbeetle/src/config.zig +0 -234
  53. package/src/tigerbeetle/src/constants.zig +0 -436
  54. package/src/tigerbeetle/src/ewah.zig +0 -286
  55. package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
  56. package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
  57. package/src/tigerbeetle/src/fifo.zig +0 -120
  58. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  59. package/src/tigerbeetle/src/io/darwin.zig +0 -814
  60. package/src/tigerbeetle/src/io/linux.zig +0 -1062
  61. package/src/tigerbeetle/src/io/test.zig +0 -643
  62. package/src/tigerbeetle/src/io/windows.zig +0 -1183
  63. package/src/tigerbeetle/src/io.zig +0 -34
  64. package/src/tigerbeetle/src/iops.zig +0 -107
  65. package/src/tigerbeetle/src/lsm/README.md +0 -308
  66. package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
  67. package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
  68. package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
  69. package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
  70. package/src/tigerbeetle/src/lsm/direction.zig +0 -11
  71. package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
  72. package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
  73. package/src/tigerbeetle/src/lsm/forest.zig +0 -204
  74. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -401
  75. package/src/tigerbeetle/src/lsm/grid.zig +0 -573
  76. package/src/tigerbeetle/src/lsm/groove.zig +0 -972
  77. package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
  78. package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
  79. package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
  80. package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -877
  81. package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
  82. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
  83. package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
  84. package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
  85. package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -378
  86. package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1328
  87. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
  88. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
  89. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
  90. package/src/tigerbeetle/src/lsm/table.zig +0 -1031
  91. package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -203
  92. package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
  93. package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -220
  94. package/src/tigerbeetle/src/lsm/test.zig +0 -438
  95. package/src/tigerbeetle/src/lsm/tree.zig +0 -1193
  96. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -474
  97. package/src/tigerbeetle/src/message_bus.zig +0 -1012
  98. package/src/tigerbeetle/src/message_pool.zig +0 -156
  99. package/src/tigerbeetle/src/ring_buffer.zig +0 -399
  100. package/src/tigerbeetle/src/simulator.zig +0 -569
  101. package/src/tigerbeetle/src/state_machine/auditor.zig +0 -577
  102. package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
  103. package/src/tigerbeetle/src/state_machine.zig +0 -1881
  104. package/src/tigerbeetle/src/static_allocator.zig +0 -65
  105. package/src/tigerbeetle/src/stdx.zig +0 -162
  106. package/src/tigerbeetle/src/storage.zig +0 -393
  107. package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
  108. package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
  109. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
  110. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
  111. package/src/tigerbeetle/src/testing/cluster.zig +0 -443
  112. package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
  113. package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
  114. package/src/tigerbeetle/src/testing/id.zig +0 -99
  115. package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -364
  116. package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
  117. package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
  118. package/src/tigerbeetle/src/testing/state_machine.zig +0 -249
  119. package/src/tigerbeetle/src/testing/storage.zig +0 -757
  120. package/src/tigerbeetle/src/testing/table.zig +0 -247
  121. package/src/tigerbeetle/src/testing/time.zig +0 -84
  122. package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
  123. package/src/tigerbeetle/src/time.zig +0 -112
  124. package/src/tigerbeetle/src/tracer.zig +0 -529
  125. package/src/tigerbeetle/src/unit_tests.zig +0 -42
  126. package/src/tigerbeetle/src/vopr.zig +0 -495
  127. package/src/tigerbeetle/src/vsr/README.md +0 -209
  128. package/src/tigerbeetle/src/vsr/client.zig +0 -544
  129. package/src/tigerbeetle/src/vsr/clock.zig +0 -853
  130. package/src/tigerbeetle/src/vsr/journal.zig +0 -2413
  131. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
  132. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  133. package/src/tigerbeetle/src/vsr/replica.zig +0 -6381
  134. package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
  135. package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
  136. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
  137. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
  138. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
  139. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
  140. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
  141. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
  142. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
  143. package/src/tigerbeetle/src/vsr.zig +0 -1352
@@ -1,1031 +0,0 @@
1
- const std = @import("std");
2
- const mem = std.mem;
3
- const math = std.math;
4
- const assert = std.debug.assert;
5
-
6
- const constants = @import("../constants.zig");
7
- const vsr = @import("../vsr.zig");
8
- const binary_search = @import("binary_search.zig");
9
- const bloom_filter = @import("bloom_filter.zig");
10
-
11
- const stdx = @import("../stdx.zig");
12
- const div_ceil = stdx.div_ceil;
13
- const eytzinger = @import("eytzinger.zig").eytzinger;
14
- const snapshot_latest = @import("tree.zig").snapshot_latest;
15
-
16
- const BlockType = @import("grid.zig").BlockType;
17
- const alloc_block = @import("grid.zig").alloc_block;
18
- const TableInfoType = @import("manifest.zig").TableInfoType;
19
-
20
- pub const TableUsage = enum {
21
- /// General purpose table.
22
- general,
23
- /// If your usage fits this pattern:
24
- /// * Only put keys which are not present.
25
- /// * Only remove keys which are present.
26
- /// * TableKey == TableValue (modulo padding, eg CompositeKey)
27
- /// Then we can unlock additional optimizations.
28
- secondary_index,
29
- };
30
-
31
- /// A table is a set of blocks:
32
- ///
33
- /// * Index block (exactly 1)
34
- /// * Filter blocks (at least one, at most `filter_block_count_max`)
35
- /// Each filter block summarizes the keys for several adjacent (in terms of key) data blocks.
36
- /// * Data blocks (at least one, at most `data_block_count_max`)
37
- /// Store the actual keys/values, along with a small index of the keys to optimize lookups.
38
- ///
39
- ///
40
- /// Every block begins with a `vsr.Header` that includes:
41
- ///
42
- /// * `checksum`, `checksum_body` verify the data integrity.
43
- /// * `cluster` is the cluster id.
44
- /// * `command` is `.block`.
45
- /// * `op` is the block address.
46
- /// * `size` is the block size excluding padding.
47
- ///
48
- /// Index block schema:
49
- /// │ vsr.Header │ operation=BlockType.index
50
- /// │ vsr.Header │ commit=filter_block_count,
51
- /// │ │ request=data_block_count,
52
- /// │ │ timestamp=snapshot_min
53
- /// │ [filter_block_count_max]u128 │ checksums of filter blocks
54
- /// │ [data_block_count_max]u128 │ checksums of data blocks
55
- /// │ [data_block_count_max]Key │ the maximum/last key in the respective data block
56
- /// │ [filter_block_count_max]u64 │ addresses of filter blocks
57
- /// │ [data_block_count_max]u64 │ addresses of data blocks
58
- /// │ […]u8{0} │ padding (to end of block)
59
- ///
60
- /// Filter block schema:
61
- /// │ vsr.Header │ operation=BlockType.filter
62
- /// │ […]u8 │ A split-block Bloom filter, "containing" every key from as many as
63
- /// │ │ `filter_data_block_count_max` data blocks.
64
- ///
65
- /// Data block schema:
66
- /// │ vsr.Header │ operation=BlockType.data
67
- /// │ [block_key_count + 1]Key │ Eytzinger-layout keys from a subset of the values.
68
- /// │ [≤value_count_max]Value │ At least one value (no empty tables).
69
- /// │ […]u8{0} │ padding (to end of block)
70
- pub fn TableType(
71
- comptime TableKey: type,
72
- comptime TableValue: type,
73
- /// Returns the sort order between two keys.
74
- comptime table_compare_keys: fn (TableKey, TableKey) callconv(.Inline) math.Order,
75
- /// Returns the key for a value. For example, given `object` returns `object.id`.
76
- /// Since most objects contain an id, this avoids duplicating the key when storing the value.
77
- comptime table_key_from_value: fn (*const TableValue) callconv(.Inline) TableKey,
78
- /// Must compare greater than all other keys.
79
- comptime table_sentinel_key: TableKey,
80
- /// Returns whether a value is a tombstone value.
81
- comptime table_tombstone: fn (*const TableValue) callconv(.Inline) bool,
82
- /// Returns a tombstone value representation for a key.
83
- comptime table_tombstone_from_key: fn (TableKey) callconv(.Inline) TableValue,
84
- comptime usage: TableUsage,
85
- ) type {
86
- return struct {
87
- const Table = @This();
88
-
89
- // Re-export all the generic arguments.
90
- pub const Key = TableKey;
91
- pub const Value = TableValue;
92
- pub const compare_keys = table_compare_keys;
93
- pub const key_from_value = table_key_from_value;
94
- pub const sentinel_key = table_sentinel_key;
95
- pub const tombstone = table_tombstone;
96
- pub const tombstone_from_key = table_tombstone_from_key;
97
- pub const usage = usage;
98
-
99
- // Export hashmap context for Key and Value
100
- pub const HashMapContextValue = struct {
101
- pub fn eql(_: HashMapContextValue, a: Value, b: Value) bool {
102
- return compare_keys(key_from_value(&a), key_from_value(&b)) == .eq;
103
- }
104
-
105
- pub fn hash(_: HashMapContextValue, value: Value) u64 {
106
- // TODO(King): this erros out with "unable to hash type void" due to
107
- // CompositeKey(T) struct containing .padding which may be void at comptime.
108
- const key = key_from_value(&value);
109
- return std.hash_map.getAutoHashFn(Key, HashMapContextValue)(.{}, key);
110
- }
111
- };
112
-
113
- const block_size = constants.block_size;
114
- const BlockPtr = *align(constants.sector_size) [block_size]u8;
115
- const BlockPtrConst = *align(constants.sector_size) const [block_size]u8;
116
-
117
- pub const key_size = @sizeOf(Key);
118
- pub const value_size = @sizeOf(Value);
119
-
120
- comptime {
121
- assert(@alignOf(Key) == 8 or @alignOf(Key) == 16);
122
- // TODO(ifreund) What are our alignment expectations for Value?
123
-
124
- // There must be no padding in the Key/Value types to avoid buffer bleeds.
125
- assert(@bitSizeOf(Key) == @sizeOf(Key) * 8);
126
- assert(@bitSizeOf(Value) == @sizeOf(Value) * 8);
127
-
128
- // These impact our calculation of:
129
- // * the superblock trailer size, and
130
- // * the manifest log layout for alignment.
131
- assert(key_size >= 8);
132
- assert(key_size <= 32);
133
- assert(key_size == 8 or key_size == 16 or key_size == 24 or key_size == 32);
134
- }
135
-
136
- const address_size = @sizeOf(u64);
137
- const checksum_size = @sizeOf(u128);
138
- const table_size_max = constants.lsm_table_size_max;
139
- const table_block_count_max = @divExact(table_size_max, block_size);
140
- const block_body_size = block_size - @sizeOf(vsr.Header);
141
-
142
- pub const layout = layout: {
143
- @setEvalBranchQuota(10_000);
144
-
145
- assert(block_size % constants.sector_size == 0);
146
- assert(math.isPowerOfTwo(table_size_max));
147
- assert(math.isPowerOfTwo(block_size));
148
-
149
- // Searching the values array is more expensive than searching the per-block index
150
- // as the larger values size leads to more cache misses. We can therefore speed
151
- // up lookups by making the per block index larger at the cost of reducing the
152
- // number of values that may be stored per block.
153
- //
154
- // X = values per block
155
- // Y = keys per block
156
- //
157
- // R = constants.lsm_value_to_key_layout_ratio_min
158
- //
159
- // To maximize:
160
- // Y
161
- // Given constraints:
162
- // body >= X * value_size + Y * key_size
163
- // (X * value_size) / (Y * key_size) >= R
164
- // X >= Y
165
- //
166
- // Plots of above constraints:
167
- // https://www.desmos.com/calculator/elqqaalgbc
168
- //
169
- // body - X * value_size = Y * key_size
170
- // Y = (body - X * value_size) / key_size
171
- //
172
- // (X * value_size) / (body - X * value_size) = R
173
- // (X * value_size) = R * body - R * X * value_size
174
- // (R + 1) * X * value_size = R * body
175
- // X = R * body / ((R + 1)* value_size)
176
- //
177
- // Y = (body - (R * body / ((R + 1) * value_size)) * value_size) / key_size
178
- // Y = (body - (R / (R + 1)) * body) / key_size
179
- // Y = body / ((R + 1) * key_size)
180
- var block_keys_layout_count = math.min(
181
- block_body_size / ((constants.lsm_value_to_key_layout_ratio_min + 1) * key_size),
182
- block_body_size / (value_size + key_size),
183
- );
184
-
185
- // Round to the next lowest power of two. This speeds up lookups in the Eytzinger
186
- // layout and should help ensure better alignment for the following values.
187
- // We could round to the nearest power of two, but then we would need
188
- // care to avoid breaking e.g. the X >= Y invariant above.
189
- block_keys_layout_count = math.floorPowerOfTwo(u64, block_keys_layout_count);
190
-
191
- // If the index is smaller than 16 keys then there are key sizes >= 4 such that
192
- // the total index size is not 64 byte cache line aligned.
193
- assert(@sizeOf(Key) >= 4);
194
- assert(@sizeOf(Key) % 4 == 0);
195
- if (block_keys_layout_count < @divExact(constants.cache_line_size, 4)) {
196
- block_keys_layout_count = 0;
197
- }
198
- assert((block_keys_layout_count * key_size) % constants.cache_line_size == 0);
199
-
200
- const block_key_layout_size = block_keys_layout_count * key_size;
201
- const block_key_count =
202
- if (block_keys_layout_count == 0) 0 else block_keys_layout_count - 1;
203
-
204
- const block_value_count_max = @divFloor(
205
- block_body_size - block_key_layout_size,
206
- value_size,
207
- );
208
-
209
- const data_index_entry_size = key_size + address_size + checksum_size;
210
- const filter_index_entry_size = address_size + checksum_size;
211
-
212
- // TODO Audit/tune this number for split block bloom filters:
213
- const filter_bytes_per_key = 2;
214
- const filter_data_block_count_max = @divFloor(
215
- block_body_size,
216
- block_value_count_max * filter_bytes_per_key,
217
- );
218
-
219
- // Compute the number of data and filter blocks by solving the constraints:
220
- // * the cumulative table size must not exceed lsm_table_size_max
221
- // * the filter and data blocks' metadata must fix in the index block
222
- // * the filter blocks must index all data blocks
223
- // * minimize the number of filter blocks
224
- // * maximize the number of data blocks
225
- var data_blocks = table_block_count_max - index_block_count;
226
- var filter_blocks = 0;
227
- while (true) : (data_blocks -= 1) {
228
- filter_blocks = div_ceil(data_blocks, filter_data_block_count_max);
229
-
230
- const data_index_size = data_index_entry_size * data_blocks;
231
- const filter_index_size = filter_index_entry_size * filter_blocks;
232
-
233
- const index_size = @sizeOf(vsr.Header) + data_index_size + filter_index_size;
234
- const table_block_count = index_block_count + filter_blocks + data_blocks;
235
- if (index_size <= block_size and table_block_count <= table_block_count_max) {
236
- break;
237
- }
238
- }
239
-
240
- const table_block_count = index_block_count + filter_blocks + data_blocks;
241
- assert(table_block_count <= table_block_count_max);
242
-
243
- break :layout .{
244
- // The number of keys in the Eytzinger layout per data block.
245
- .block_key_count = block_key_count,
246
- // The number of bytes used by the keys in the data block.
247
- .block_key_layout_size = block_key_layout_size,
248
- // The maximum number of values in a data block.
249
- .block_value_count_max = block_value_count_max,
250
-
251
- .data_block_count_max = data_blocks,
252
- .filter_block_count_max = filter_blocks,
253
-
254
- // The number of data blocks covered by a single filter block.
255
- .filter_data_block_count_max = std.math.min(
256
- filter_data_block_count_max,
257
- data_blocks,
258
- ),
259
- };
260
- };
261
-
262
- const index_block_count = 1;
263
- pub const filter_block_count_max = layout.filter_block_count_max;
264
- pub const data_block_count_max = layout.data_block_count_max;
265
- pub const block_count_max =
266
- index_block_count + filter_block_count_max + data_block_count_max;
267
-
268
- const index = struct {
269
- const size = @sizeOf(vsr.Header) + filter_checksums_size + data_checksums_size +
270
- keys_size + filter_addresses_size + data_addresses_size;
271
-
272
- const filter_checksums_offset = @sizeOf(vsr.Header);
273
- const filter_checksums_size = filter_block_count_max * checksum_size;
274
-
275
- const data_checksums_offset = filter_checksums_offset + filter_checksums_size;
276
- const data_checksums_size = data_block_count_max * checksum_size;
277
-
278
- const keys_offset = data_checksums_offset + data_checksums_size;
279
- const keys_size = data_block_count_max * key_size;
280
-
281
- const filter_addresses_offset = keys_offset + keys_size;
282
- const filter_addresses_size = filter_block_count_max * address_size;
283
-
284
- const data_addresses_offset = filter_addresses_offset + filter_addresses_size;
285
- const data_addresses_size = data_block_count_max * address_size;
286
-
287
- const padding_offset = data_addresses_offset + data_addresses_size;
288
- const padding_size = block_size - padding_offset;
289
- };
290
-
291
- pub const filter = struct {
292
- pub const data_block_count_max = layout.filter_data_block_count_max;
293
-
294
- const filter_offset = @sizeOf(vsr.Header);
295
- const filter_size = block_size - filter_offset;
296
-
297
- const padding_offset = filter_offset + filter_size;
298
- const padding_size = block_size - padding_offset;
299
- };
300
-
301
- pub const data = struct {
302
- const key_count = layout.block_key_count;
303
- pub const value_count_max = layout.block_value_count_max;
304
-
305
- const key_layout_offset = @sizeOf(vsr.Header);
306
- const key_layout_size = layout.block_key_layout_size;
307
-
308
- const values_offset = key_layout_offset + key_layout_size;
309
- const values_size = value_count_max * value_size;
310
-
311
- const padding_offset = values_offset + values_size;
312
- const padding_size = block_size - padding_offset;
313
- };
314
-
315
- const compile_log_layout = false;
316
- comptime {
317
- if (compile_log_layout) {
318
- @compileError(std.fmt.comptimePrint(
319
- \\
320
- \\
321
- \\lsm parameters:
322
- \\ key size: {}
323
- \\ value size: {}
324
- \\ table size max: {}
325
- \\ block size: {}
326
- \\layout:
327
- \\ index block count: {}
328
- \\ filter block count max: {}
329
- \\ data block count max: {}
330
- \\index:
331
- \\ size: {}
332
- \\ filter_checksums_offset: {}
333
- \\ filter_checksums_size: {}
334
- \\ data_checksums_offset: {}
335
- \\ data_checksums_size: {}
336
- \\ keys_offset: {}
337
- \\ keys_size: {}
338
- \\ filter_addresses_offset: {}
339
- \\ filter_addresses_size: {}
340
- \\ data_addresses_offset: {}
341
- \\ data_addresses_size: {}
342
- \\filter:
343
- \\ data_block_count_max: {}
344
- \\ filter_offset: {}
345
- \\ filter_size: {}
346
- \\ padding_offset: {}
347
- \\ padding_size: {}
348
- \\data:
349
- \\ key_count: {}
350
- \\ value_count_max: {}
351
- \\ key_layout_offset: {}
352
- \\ key_layout_size: {}
353
- \\ values_offset: {}
354
- \\ values_size: {}
355
- \\ padding_offset: {}
356
- \\ padding_size: {}
357
- \\
358
- ,
359
- .{
360
- key_size,
361
- value_size,
362
- table_size_max,
363
- block_size,
364
-
365
- index_block_count,
366
- filter_block_count_max,
367
- data_block_count_max,
368
-
369
- index.size,
370
- index.filter_checksums_offset,
371
- index.filter_checksums_size,
372
- index.data_checksums_offset,
373
- index.data_checksums_size,
374
- index.keys_offset,
375
- index.keys_size,
376
- index.filter_addresses_offset,
377
- index.filter_addresses_size,
378
- index.data_addresses_offset,
379
- index.data_addresses_size,
380
-
381
- filter.data_block_count_max,
382
- filter.filter_offset,
383
- filter.filter_size,
384
- filter.padding_offset,
385
- filter.padding_size,
386
-
387
- data.key_count,
388
- data.value_count_max,
389
- data.key_layout_offset,
390
- data.key_layout_size,
391
- data.values_offset,
392
- data.values_size,
393
- data.padding_offset,
394
- data.padding_size,
395
- },
396
- ));
397
- }
398
- }
399
-
400
- comptime {
401
- assert(index_block_count > 0);
402
- assert(filter_block_count_max > 0);
403
- assert(data_block_count_max > 0);
404
- assert(index_block_count + filter_block_count_max +
405
- data_block_count_max <= table_block_count_max);
406
-
407
- assert(filter.data_block_count_max > 0);
408
- // There should not be more data blocks per filter block than there are data blocks:
409
- assert(filter.data_block_count_max <= data_block_count_max);
410
-
411
- const filter_bytes_per_key = 2;
412
- assert(filter_block_count_max * filter.filter_size >=
413
- data_block_count_max * data.value_count_max * filter_bytes_per_key);
414
-
415
- assert(index.size == @sizeOf(vsr.Header) +
416
- data_block_count_max * (key_size + address_size + checksum_size) +
417
- filter_block_count_max * (address_size + checksum_size));
418
- assert(index.size == index.data_addresses_offset + index.data_addresses_size);
419
- assert(index.size <= block_size);
420
- assert(index.keys_size > 0);
421
- assert(index.keys_size % key_size == 0);
422
- assert(@divExact(index.data_addresses_size, @sizeOf(u64)) == data_block_count_max);
423
- assert(@divExact(index.filter_addresses_size, @sizeOf(u64)) == filter_block_count_max);
424
- assert(@divExact(index.data_checksums_size, @sizeOf(u128)) == data_block_count_max);
425
- assert(@divExact(index.filter_checksums_size, @sizeOf(u128)) == filter_block_count_max);
426
- assert(block_size == index.padding_offset + index.padding_size);
427
- assert(block_size == index.size + index.padding_size);
428
-
429
- // Split block bloom filters require filters to be a multiple of 32 bytes as they
430
- // use 256 bit blocks.
431
- assert(filter.filter_size % 32 == 0);
432
- assert(filter.filter_size == block_body_size);
433
- assert(block_size == filter.padding_offset + filter.padding_size);
434
- assert(block_size == @sizeOf(vsr.Header) + filter.filter_size + filter.padding_size);
435
-
436
- if (data.key_count > 0) {
437
- assert(data.key_count >= 3);
438
- assert(math.isPowerOfTwo(data.key_count + 1));
439
- assert(data.key_count + 1 == @divExact(data.key_layout_size, key_size));
440
- assert(data.values_size / data.key_layout_size >=
441
- constants.lsm_value_to_key_layout_ratio_min);
442
- } else {
443
- assert(data.key_count == 0);
444
- assert(data.key_layout_size == 0);
445
- assert(data.values_offset == data.key_layout_offset);
446
- }
447
-
448
- assert(data.value_count_max > 0);
449
- assert(data.value_count_max >= data.key_count);
450
- assert(@divExact(data.values_size, value_size) == data.value_count_max);
451
- assert(data.values_offset % constants.cache_line_size == 0);
452
- // You can have any size value you want, as long as it fits
453
- // neatly into the CPU cache lines :)
454
- assert((data.value_count_max * value_size) % constants.cache_line_size == 0);
455
-
456
- assert(data.padding_size >= 0);
457
- assert(block_size == @sizeOf(vsr.Header) + data.key_layout_size +
458
- data.values_size + data.padding_size);
459
- assert(block_size == data.padding_offset + data.padding_size);
460
-
461
- // We expect no block padding at least for TigerBeetle's objects and indexes:
462
- if ((key_size == 8 and value_size == 128) or
463
- (key_size == 8 and value_size == 64) or
464
- (key_size == 16 and value_size == 16) or
465
- (key_size == 32 and value_size == 32))
466
- {
467
- assert(data.padding_size == 0);
468
- }
469
- }
470
-
471
- pub const Builder = struct {
472
- const TableInfo = TableInfoType(Table);
473
-
474
- key_min: Key = undefined, // Inclusive.
475
- key_max: Key = undefined, // Inclusive.
476
-
477
- index_block: BlockPtr,
478
- filter_block: BlockPtr,
479
- data_block: BlockPtr,
480
-
481
- data_block_count: u32 = 0,
482
- value: u32 = 0,
483
-
484
- filter_block_count: u32 = 0,
485
- data_blocks_in_filter: u32 = 0,
486
-
487
- pub fn init(allocator: mem.Allocator) !Builder {
488
- const index_block = try alloc_block(allocator);
489
- errdefer allocator.free(index_block);
490
-
491
- const filter_block = try alloc_block(allocator);
492
- errdefer allocator.free(filter_block);
493
-
494
- const data_block = try alloc_block(allocator);
495
- errdefer allocator.free(data_block);
496
-
497
- return Builder{
498
- .index_block = index_block[0..block_size],
499
- .filter_block = filter_block[0..block_size],
500
- .data_block = data_block[0..block_size],
501
- };
502
- }
503
-
504
- pub fn deinit(builder: *Builder, allocator: mem.Allocator) void {
505
- allocator.free(builder.index_block);
506
- allocator.free(builder.filter_block);
507
- allocator.free(builder.data_block);
508
-
509
- builder.* = undefined;
510
- }
511
-
512
- pub fn data_block_append(builder: *Builder, value: *const Value) void {
513
- const values_max = data_block_values(builder.data_block);
514
- assert(values_max.len == data.value_count_max);
515
-
516
- values_max[builder.value] = value.*;
517
- builder.value += 1;
518
-
519
- const key = key_from_value(value);
520
- const fingerprint = bloom_filter.Fingerprint.create(mem.asBytes(&key));
521
- bloom_filter.add(fingerprint, filter_block_filter(builder.filter_block));
522
- }
523
-
524
- pub fn data_block_append_slice(builder: *Builder, values: []const Value) void {
525
- assert(values.len > 0);
526
- assert(builder.value + values.len <= data.value_count_max);
527
-
528
- const values_max = data_block_values(builder.data_block);
529
- assert(values_max.len == data.value_count_max);
530
-
531
- stdx.copy_disjoint(.inexact, Value, values_max[builder.value..], values);
532
- builder.value += @intCast(u32, values.len);
533
-
534
- for (values) |*value| {
535
- const key = key_from_value(value);
536
- const fingerprint = bloom_filter.Fingerprint.create(mem.asBytes(&key));
537
- bloom_filter.add(fingerprint, filter_block_filter(builder.filter_block));
538
- }
539
- }
540
-
541
- pub fn data_block_empty(builder: Builder) bool {
542
- assert(builder.value <= data.value_count_max);
543
- return builder.value == 0;
544
- }
545
-
546
- pub fn data_block_full(builder: Builder) bool {
547
- assert(builder.value <= data.value_count_max);
548
- return builder.value == data.value_count_max;
549
- }
550
-
551
- const DataFinishOptions = struct {
552
- cluster: u32,
553
- address: u64,
554
- };
555
-
556
- pub fn data_block_finish(builder: *Builder, options: DataFinishOptions) void {
557
- // For each block we write the sorted values, initialize the Eytzinger layout,
558
- // complete the block header, and add the block's max key to the table index.
559
-
560
- assert(options.address > 0);
561
- assert(builder.value > 0);
562
-
563
- const block = builder.data_block;
564
- const values_max = data_block_values(block);
565
- assert(values_max.len == data.value_count_max);
566
-
567
- const values = values_max[0..builder.value];
568
- const key_max = key_from_value(&values[values.len - 1]);
569
-
570
- if (constants.verify) {
571
- var a = &values[0];
572
- for (values[1..]) |*b| {
573
- assert(compare_keys(key_from_value(a), key_from_value(b)) == .lt);
574
- a = b;
575
- }
576
- }
577
-
578
- if (data.key_count > 0) {
579
- assert(@divExact(data.key_layout_size, key_size) == data.key_count + 1);
580
-
581
- const key_layout_bytes = @alignCast(
582
- @alignOf(Key),
583
- block[data.key_layout_offset..][0..data.key_layout_size],
584
- );
585
- const key_layout = mem.bytesAsValue([data.key_count + 1]Key, key_layout_bytes);
586
-
587
- const e = eytzinger(data.key_count, data.value_count_max);
588
- e.layout_from_keys_or_values(
589
- Key,
590
- Value,
591
- key_from_value,
592
- sentinel_key,
593
- values,
594
- key_layout,
595
- );
596
- }
597
-
598
- const values_padding = mem.sliceAsBytes(values_max[builder.value..]);
599
- const block_padding = block[data.padding_offset..][0..data.padding_size];
600
- assert(compare_keys(key_from_value(&values[values.len - 1]), key_max) == .eq);
601
-
602
- const header_bytes = block[0..@sizeOf(vsr.Header)];
603
- const header = mem.bytesAsValue(vsr.Header, header_bytes);
604
-
605
- header.* = .{
606
- .cluster = options.cluster,
607
- .op = options.address,
608
- .request = @intCast(u32, values.len),
609
- .size = block_size - @intCast(u32, values_padding.len + block_padding.len),
610
- .command = .block,
611
- .operation = BlockType.data.operation(),
612
- };
613
-
614
- header.set_checksum_body(block[@sizeOf(vsr.Header)..header.size]);
615
- header.set_checksum();
616
-
617
- const current = builder.data_block_count;
618
- index_data_keys(builder.index_block)[current] = key_max;
619
- index_data_addresses(builder.index_block)[current] = options.address;
620
- index_data_checksums(builder.index_block)[current] = header.checksum;
621
-
622
- if (current == 0) builder.key_min = key_from_value(&values[0]);
623
- builder.key_max = key_max;
624
-
625
- if (current == 0 and values.len == 1) {
626
- assert(compare_keys(builder.key_min, builder.key_max) == .eq);
627
- } else {
628
- assert(compare_keys(builder.key_min, builder.key_max) == .lt);
629
- }
630
-
631
- if (current > 0) {
632
- const key_max_prev = index_data_keys(builder.index_block)[current - 1];
633
- assert(compare_keys(key_max_prev, key_from_value(&values[0])) == .lt);
634
- }
635
-
636
- builder.data_block_count += 1;
637
- builder.value = 0;
638
-
639
- builder.data_blocks_in_filter += 1;
640
- }
641
-
642
- pub fn filter_block_empty(builder: Builder) bool {
643
- assert(builder.data_blocks_in_filter <= filter.data_block_count_max);
644
- return builder.data_blocks_in_filter == 0;
645
- }
646
-
647
- pub fn filter_block_full(builder: Builder) bool {
648
- assert(builder.data_blocks_in_filter <= filter.data_block_count_max);
649
- return builder.data_blocks_in_filter == filter.data_block_count_max;
650
- }
651
-
652
- const FilterFinishOptions = struct {
653
- cluster: u32,
654
- address: u64,
655
- };
656
-
657
- pub fn filter_block_finish(builder: *Builder, options: FilterFinishOptions) void {
658
- assert(!builder.filter_block_empty());
659
- assert(builder.data_block_empty());
660
- assert(options.address > 0);
661
-
662
- const header_bytes = builder.filter_block[0..@sizeOf(vsr.Header)];
663
- const header = mem.bytesAsValue(vsr.Header, header_bytes);
664
- header.* = .{
665
- .cluster = options.cluster,
666
- .op = options.address,
667
- .size = block_size - filter.padding_size,
668
- .command = .block,
669
- .operation = BlockType.filter.operation(),
670
- };
671
-
672
- const body = builder.filter_block[@sizeOf(vsr.Header)..header.size];
673
- header.set_checksum_body(body);
674
- header.set_checksum();
675
-
676
- const current = builder.filter_block_count;
677
- index_filter_addresses(builder.index_block)[current] = options.address;
678
- index_filter_checksums(builder.index_block)[current] = header.checksum;
679
-
680
- builder.filter_block_count += 1;
681
- builder.data_blocks_in_filter = 0;
682
- }
683
-
684
- pub fn index_block_empty(builder: Builder) bool {
685
- assert(builder.data_block_count <= data_block_count_max);
686
- return builder.data_block_count == 0;
687
- }
688
-
689
- pub fn index_block_full(builder: Builder) bool {
690
- assert(builder.data_block_count <= data_block_count_max);
691
- return builder.data_block_count == data_block_count_max;
692
- }
693
-
694
- const IndexFinishOptions = struct {
695
- cluster: u32,
696
- address: u64,
697
- snapshot_min: u64,
698
- };
699
-
700
- pub fn index_block_finish(builder: *Builder, options: IndexFinishOptions) TableInfo {
701
- assert(options.address > 0);
702
- assert(builder.filter_block_empty());
703
- assert(builder.data_block_empty());
704
- assert(builder.data_block_count > 0);
705
- assert(builder.value == 0);
706
- assert(builder.data_blocks_in_filter == 0);
707
- assert(builder.filter_block_count == div_ceil(
708
- builder.data_block_count,
709
- filter.data_block_count_max,
710
- ));
711
-
712
- const index_block = builder.index_block;
713
-
714
- const header_bytes = index_block[0..@sizeOf(vsr.Header)];
715
- const header = mem.bytesAsValue(vsr.Header, header_bytes);
716
-
717
- header.* = .{
718
- .cluster = options.cluster,
719
- .op = options.address,
720
- .commit = builder.filter_block_count,
721
- .request = builder.data_block_count,
722
- .timestamp = options.snapshot_min,
723
- .size = index.size,
724
- .command = .block,
725
- .operation = BlockType.index.operation(),
726
- };
727
- header.set_checksum_body(index_block[@sizeOf(vsr.Header)..header.size]);
728
- header.set_checksum();
729
-
730
- const info: TableInfo = .{
731
- .checksum = header.checksum,
732
- .address = options.address,
733
- .snapshot_min = options.snapshot_min,
734
- .key_min = builder.key_min,
735
- .key_max = builder.key_max,
736
- };
737
-
738
- assert(info.snapshot_max == math.maxInt(u64));
739
-
740
- // Reset the builder to its initial state, leaving the buffers untouched.
741
- builder.* = .{
742
- .key_min = undefined,
743
- .key_max = undefined,
744
- .index_block = builder.index_block,
745
- .filter_block = builder.filter_block,
746
- .data_block = builder.data_block,
747
- };
748
-
749
- return info;
750
- }
751
- };
752
-
753
- pub inline fn index_block_address(index_block: BlockPtrConst) u64 {
754
- const header = mem.bytesAsValue(vsr.Header, index_block[0..@sizeOf(vsr.Header)]);
755
- const address = header.op;
756
- assert(address > 0);
757
- return address;
758
- }
759
-
760
- pub inline fn index_data_keys(index_block: BlockPtr) []Key {
761
- return mem.bytesAsSlice(Key, index_block[index.keys_offset..][0..index.keys_size]);
762
- }
763
-
764
- pub inline fn index_data_keys_used(index_block: BlockPtrConst) []const Key {
765
- const slice = mem.bytesAsSlice(
766
- Key,
767
- index_block[index.keys_offset..][0..index.keys_size],
768
- );
769
- return slice[0..index_data_blocks_used(index_block)];
770
- }
771
-
772
- pub inline fn index_data_addresses(index_block: BlockPtr) []u64 {
773
- return mem.bytesAsSlice(
774
- u64,
775
- index_block[index.data_addresses_offset..][0..index.data_addresses_size],
776
- );
777
- }
778
-
779
- pub inline fn index_data_addresses_used(index_block: BlockPtrConst) []const u64 {
780
- const slice = mem.bytesAsSlice(
781
- u64,
782
- index_block[index.data_addresses_offset..][0..index.data_addresses_size],
783
- );
784
- return slice[0..index_data_blocks_used(index_block)];
785
- }
786
-
787
- pub inline fn index_data_checksums(index_block: BlockPtr) []u128 {
788
- return mem.bytesAsSlice(
789
- u128,
790
- index_block[index.data_checksums_offset..][0..index.data_checksums_size],
791
- );
792
- }
793
-
794
- pub inline fn index_data_checksums_used(index_block: BlockPtrConst) []const u128 {
795
- const slice = mem.bytesAsSlice(
796
- u128,
797
- index_block[index.data_checksums_offset..][0..index.data_checksums_size],
798
- );
799
- return slice[0..index_data_blocks_used(index_block)];
800
- }
801
-
802
- inline fn index_filter_addresses(index_block: BlockPtr) []u64 {
803
- return mem.bytesAsSlice(
804
- u64,
805
- index_block[index.filter_addresses_offset..][0..index.filter_addresses_size],
806
- );
807
- }
808
-
809
- pub inline fn index_filter_addresses_used(index_block: BlockPtrConst) []const u64 {
810
- const slice = mem.bytesAsSlice(
811
- u64,
812
- index_block[index.filter_addresses_offset..][0..index.filter_addresses_size],
813
- );
814
- return slice[0..index_filter_blocks_used(index_block)];
815
- }
816
-
817
- inline fn index_filter_checksums(index_block: BlockPtr) []u128 {
818
- return mem.bytesAsSlice(
819
- u128,
820
- index_block[index.filter_checksums_offset..][0..index.filter_checksums_size],
821
- );
822
- }
823
-
824
- pub inline fn index_filter_checksums_used(index_block: BlockPtrConst) []const u128 {
825
- const slice = mem.bytesAsSlice(
826
- u128,
827
- index_block[index.filter_checksums_offset..][0..index.filter_checksums_size],
828
- );
829
- return slice[0..index_filter_blocks_used(index_block)];
830
- }
831
-
832
- inline fn index_blocks_used(index_block: BlockPtrConst) u32 {
833
- return index_block_count + index_filter_blocks_used(index_block) +
834
- index_data_blocks_used(index_block);
835
- }
836
-
837
- inline fn index_filter_blocks_used(index_block: BlockPtrConst) u32 {
838
- const header = mem.bytesAsValue(vsr.Header, index_block[0..@sizeOf(vsr.Header)]);
839
- const value = @intCast(u32, header.commit);
840
- assert(value > 0);
841
- assert(value <= filter_block_count_max);
842
- return value;
843
- }
844
-
845
- pub inline fn index_data_blocks_used(index_block: BlockPtrConst) u32 {
846
- const header = mem.bytesAsValue(vsr.Header, index_block[0..@sizeOf(vsr.Header)]);
847
- const value = @intCast(u32, header.request);
848
- assert(value > 0);
849
- assert(value <= data_block_count_max);
850
- return value;
851
- }
852
-
853
- /// Returns the zero-based index of the data block that may contain the key.
854
- /// May be called on an index block only when the key is in range of the table.
855
- inline fn index_data_block_for_key(index_block: BlockPtrConst, key: Key) u32 {
856
- // Because we store key_max in the index block we can use the raw binary search
857
- // here and avoid the extra comparison. If the search finds an exact match, we
858
- // want to return that data block. If the search does not find an exact match
859
- // it returns the index of the next greatest key, which again is the index of the
860
- // data block that may contain the key.
861
- const data_block_index = binary_search.binary_search_keys_raw(
862
- Key,
863
- compare_keys,
864
- Table.index_data_keys_used(index_block),
865
- key,
866
- .{},
867
- );
868
- assert(data_block_index < index_data_blocks_used(index_block));
869
- return data_block_index;
870
- }
871
-
872
- pub const IndexBlocks = struct {
873
- filter_block_address: u64,
874
- filter_block_checksum: u128,
875
- data_block_address: u64,
876
- data_block_checksum: u128,
877
- };
878
-
879
- /// Returns all data stored in the index block relating to a given key.
880
- /// May be called on an index block only when the key is in range of the table.
881
- pub inline fn index_blocks_for_key(index_block: BlockPtrConst, key: Key) IndexBlocks {
882
- const d = Table.index_data_block_for_key(index_block, key);
883
- const f = @divFloor(d, filter.data_block_count_max);
884
-
885
- return .{
886
- .filter_block_address = index_filter_addresses_used(index_block)[f],
887
- .filter_block_checksum = index_filter_checksums_used(index_block)[f],
888
- .data_block_address = index_data_addresses_used(index_block)[d],
889
- .data_block_checksum = index_data_checksums_used(index_block)[d],
890
- };
891
- }
892
-
893
- inline fn data_block_values(data_block: BlockPtr) []Value {
894
- return mem.bytesAsSlice(
895
- Value,
896
- data_block[data.values_offset..][0..data.values_size],
897
- );
898
- }
899
-
900
- pub inline fn data_block_values_used(data_block: BlockPtrConst) []const Value {
901
- const header = mem.bytesAsValue(vsr.Header, data_block[0..@sizeOf(vsr.Header)]);
902
- // TODO we should be able to cross-check this with the header size
903
- // for more safety.
904
- const used = @intCast(u32, header.request);
905
- assert(used <= data.value_count_max);
906
- const slice = mem.bytesAsSlice(
907
- Value,
908
- data_block[data.values_offset..][0..data.values_size],
909
- );
910
- return slice[0..used];
911
- }
912
-
913
- pub inline fn block_address(block: BlockPtrConst) u64 {
914
- const header = mem.bytesAsValue(vsr.Header, block[0..@sizeOf(vsr.Header)]);
915
- const address = header.op;
916
- assert(address > 0);
917
- return address;
918
- }
919
-
920
- pub inline fn filter_block_filter(filter_block: BlockPtr) []u8 {
921
- return filter_block[filter.filter_offset..][0..filter.filter_size];
922
- }
923
-
924
- pub inline fn filter_block_filter_const(filter_block: BlockPtrConst) []const u8 {
925
- return filter_block[filter.filter_offset..][0..filter.filter_size];
926
- }
927
-
928
- pub fn data_block_search(data_block: BlockPtrConst, key: Key) ?*const Value {
929
- const values = blk: {
930
- if (data.key_count == 0) break :blk data_block_values_used(data_block);
931
-
932
- assert(@divExact(data.key_layout_size, key_size) == data.key_count + 1);
933
- const key_layout_bytes = @alignCast(
934
- @alignOf(Key),
935
- data_block[data.key_layout_offset..][0..data.key_layout_size],
936
- );
937
- const key_layout = mem.bytesAsValue([data.key_count + 1]Key, key_layout_bytes);
938
-
939
- const e = eytzinger(data.key_count, data.value_count_max);
940
- break :blk e.search_values(
941
- Key,
942
- Value,
943
- compare_keys,
944
- key_layout,
945
- data_block_values_used(data_block),
946
- key,
947
- );
948
- };
949
-
950
- const result = binary_search.binary_search_values(
951
- Key,
952
- Value,
953
- key_from_value,
954
- compare_keys,
955
- values,
956
- key,
957
- .{},
958
- );
959
- if (result.exact) {
960
- const value = &values[result.index];
961
- if (constants.verify) {
962
- assert(compare_keys(key, key_from_value(value)) == .eq);
963
- }
964
- return value;
965
- }
966
-
967
- if (constants.verify) {
968
- for (data_block_values_used(data_block)) |*value| {
969
- assert(compare_keys(key, key_from_value(value)) != .eq);
970
- }
971
- }
972
-
973
- return null;
974
- }
975
-
976
- pub fn verify(
977
- comptime Storage: type,
978
- storage: *Storage,
979
- index_address: u64,
980
- key_min: ?Key,
981
- key_max: ?Key,
982
- ) void {
983
- if (Storage != @import("../testing/storage.zig").Storage)
984
- // Too complicated to do async verification
985
- return;
986
-
987
- const index_block = storage.grid_block(index_address);
988
- const addresses = index_data_addresses(index_block);
989
- const data_blocks_used = index_data_blocks_used(index_block);
990
- var data_block_index: usize = 0;
991
- while (data_block_index < data_blocks_used) : (data_block_index += 1) {
992
- const address = addresses[data_block_index];
993
- const data_block = storage.grid_block(address);
994
- const values = data_block_values_used(data_block);
995
- if (values.len > 0) {
996
- if (data_block_index == 0) {
997
- assert(key_min == null or
998
- compare_keys(key_min.?, key_from_value(&values[0])) == .eq);
999
- }
1000
- if (data_block_index == data_blocks_used - 1) {
1001
- assert(key_max == null or
1002
- compare_keys(key_from_value(&values[values.len - 1]), key_max.?) == .eq);
1003
- }
1004
- var a = &values[0];
1005
- for (values[1..]) |*b| {
1006
- assert(compare_keys(key_from_value(a), key_from_value(b)) == .lt);
1007
- a = b;
1008
- }
1009
- }
1010
- }
1011
- }
1012
- };
1013
- }
1014
-
1015
- test "Table" {
1016
- const Key = @import("composite_key.zig").CompositeKey(u128);
1017
-
1018
- const Table = TableType(
1019
- Key,
1020
- Key.Value,
1021
- Key.compare_keys,
1022
- Key.key_from_value,
1023
- Key.sentinel_key,
1024
- Key.tombstone,
1025
- Key.tombstone_from_key,
1026
- .general,
1027
- );
1028
-
1029
- _ = Table;
1030
- std.testing.refAllDecls(Table.Builder);
1031
- }