tigerbeetle-node 0.11.8 → 0.11.9

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 (83) hide show
  1. package/dist/.client.node.sha256 +1 -1
  2. package/package.json +4 -3
  3. package/scripts/build_lib.sh +29 -0
  4. package/src/node.zig +1 -1
  5. package/src/tigerbeetle/scripts/validate_docs.sh +7 -1
  6. package/src/tigerbeetle/src/benchmark.zig +3 -3
  7. package/src/tigerbeetle/src/config.zig +29 -16
  8. package/src/tigerbeetle/src/constants.zig +30 -9
  9. package/src/tigerbeetle/src/ewah.zig +5 -5
  10. package/src/tigerbeetle/src/ewah_fuzz.zig +1 -1
  11. package/src/tigerbeetle/src/lsm/binary_search.zig +1 -1
  12. package/src/tigerbeetle/src/lsm/bloom_filter.zig +1 -1
  13. package/src/tigerbeetle/src/lsm/compaction.zig +34 -21
  14. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +85 -103
  15. package/src/tigerbeetle/src/lsm/grid.zig +19 -13
  16. package/src/tigerbeetle/src/lsm/manifest_log.zig +8 -10
  17. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +12 -8
  18. package/src/tigerbeetle/src/lsm/merge_iterator.zig +1 -1
  19. package/src/tigerbeetle/src/lsm/segmented_array.zig +17 -17
  20. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +1 -1
  21. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +1 -1
  22. package/src/tigerbeetle/src/lsm/table.zig +8 -20
  23. package/src/tigerbeetle/src/lsm/table_immutable.zig +1 -1
  24. package/src/tigerbeetle/src/lsm/table_iterator.zig +3 -3
  25. package/src/tigerbeetle/src/lsm/table_mutable.zig +14 -2
  26. package/src/tigerbeetle/src/lsm/tree.zig +31 -5
  27. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +86 -114
  28. package/src/tigerbeetle/src/message_bus.zig +4 -4
  29. package/src/tigerbeetle/src/message_pool.zig +7 -10
  30. package/src/tigerbeetle/src/ring_buffer.zig +22 -12
  31. package/src/tigerbeetle/src/simulator.zig +360 -214
  32. package/src/tigerbeetle/src/state_machine/auditor.zig +5 -5
  33. package/src/tigerbeetle/src/state_machine/workload.zig +3 -3
  34. package/src/tigerbeetle/src/state_machine.zig +190 -178
  35. package/src/tigerbeetle/src/{util.zig → stdx.zig} +2 -0
  36. package/src/tigerbeetle/src/storage.zig +13 -6
  37. package/src/tigerbeetle/src/{test → testing/cluster}/message_bus.zig +3 -3
  38. package/src/tigerbeetle/src/{test → testing/cluster}/network.zig +46 -22
  39. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +169 -0
  40. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +202 -0
  41. package/src/tigerbeetle/src/testing/cluster.zig +537 -0
  42. package/src/tigerbeetle/src/{test → testing}/fuzz.zig +0 -0
  43. package/src/tigerbeetle/src/testing/hash_log.zig +66 -0
  44. package/src/tigerbeetle/src/{test → testing}/id.zig +0 -0
  45. package/src/tigerbeetle/src/testing/packet_simulator.zig +365 -0
  46. package/src/tigerbeetle/src/{test → testing}/priority_queue.zig +1 -1
  47. package/src/tigerbeetle/src/testing/reply_sequence.zig +139 -0
  48. package/src/tigerbeetle/src/{test → testing}/state_machine.zig +3 -1
  49. package/src/tigerbeetle/src/testing/storage.zig +754 -0
  50. package/src/tigerbeetle/src/{test → testing}/table.zig +21 -0
  51. package/src/tigerbeetle/src/{test → testing}/time.zig +0 -0
  52. package/src/tigerbeetle/src/tigerbeetle.zig +2 -0
  53. package/src/tigerbeetle/src/tracer.zig +3 -3
  54. package/src/tigerbeetle/src/unit_tests.zig +4 -4
  55. package/src/tigerbeetle/src/vopr.zig +2 -2
  56. package/src/tigerbeetle/src/vsr/client.zig +5 -2
  57. package/src/tigerbeetle/src/vsr/clock.zig +93 -53
  58. package/src/tigerbeetle/src/vsr/journal.zig +29 -14
  59. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +2 -2
  60. package/src/tigerbeetle/src/vsr/replica.zig +1383 -774
  61. package/src/tigerbeetle/src/vsr/replica_format.zig +2 -2
  62. package/src/tigerbeetle/src/vsr/superblock.zig +59 -43
  63. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +7 -7
  64. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +1 -1
  65. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +1 -1
  66. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +15 -7
  67. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +38 -19
  68. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +1 -1
  69. package/src/tigerbeetle/src/vsr.zig +6 -4
  70. package/src/tigerbeetle/src/demo.zig +0 -132
  71. package/src/tigerbeetle/src/demo_01_create_accounts.zig +0 -35
  72. package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +0 -7
  73. package/src/tigerbeetle/src/demo_03_create_transfers.zig +0 -37
  74. package/src/tigerbeetle/src/demo_04_create_pending_transfers.zig +0 -61
  75. package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +0 -37
  76. package/src/tigerbeetle/src/demo_06_void_pending_transfers.zig +0 -24
  77. package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +0 -7
  78. package/src/tigerbeetle/src/test/cluster.zig +0 -352
  79. package/src/tigerbeetle/src/test/conductor.zig +0 -366
  80. package/src/tigerbeetle/src/test/packet_simulator.zig +0 -398
  81. package/src/tigerbeetle/src/test/state_checker.zig +0 -169
  82. package/src/tigerbeetle/src/test/storage.zig +0 -864
  83. package/src/tigerbeetle/src/test/storage_checker.zig +0 -204
@@ -1 +1 @@
1
- c82197101574fc1267be050abfa7d31ca355036eec3f7d3da9a43a6c83bf6b0c dist/client.node
1
+ ed71bbee01e27a1f4638bfc2b0542414c1fc8b503bce5bbcc45ae2b80b1edb26 dist/client.node
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tigerbeetle-node",
3
- "version": "0.11.8",
3
+ "version": "0.11.9",
4
4
  "description": "TigerBeetle Node.js client",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -18,13 +18,14 @@
18
18
  "package.json",
19
19
  "package-lock.json",
20
20
  "scripts/download_node_headers.sh",
21
+ "scripts/build_lib.sh",
21
22
  "src/c.zig",
22
23
  "src/benchmark.ts",
23
24
  "src/index.ts",
24
25
  "src/node.zig",
25
26
  "src/test.ts",
26
27
  "src/tigerbeetle/src/*.zig",
27
- "src/tigerbeetle/src/{c,io,lsm,test,vsr,state_machine}",
28
+ "src/tigerbeetle/src/{c,io,lsm,testing,vsr,state_machine}",
28
29
  "src/tigerbeetle/scripts",
29
30
  "src/translate.zig",
30
31
  "tsconfig.json"
@@ -40,7 +41,7 @@
40
41
  "download_node_headers": "sh ./scripts/download_node_headers.sh",
41
42
  "build": "npm run build_tsc && npm run build_lib",
42
43
  "build_tsc": "./node_modules/typescript/bin/tsc",
43
- "build_lib": "mkdir -p dist && zig/zig build-lib -mcpu=baseline -OReleaseSafe -dynamic -lc -isystem build/node-$(node --version)/include/node src/node.zig -fallow-shlib-undefined -femit-bin=dist/client.node",
44
+ "build_lib": "sh ./scripts/build_lib.sh",
44
45
  "prepack": "npm run build_tsc",
45
46
  "clean": "rm -rf build dist node_modules src/zig-cache zig"
46
47
  },
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env sh
2
+
3
+ set -e
4
+
5
+ # macOS 13 Ventura is not supported on Zig 0.9.x.
6
+ # Overriding -target is one workaround Andrew suggests.
7
+ # https://github.com/ziglang/zig/issues/10478#issuecomment-1294313967
8
+ # Cut everything after the first `.` in the target query result
9
+ # because the rest of it doesn't always seem to be valid when passed
10
+ # back in to `-target`.
11
+ target="$(./zig/zig targets | grep triple |cut -d '"' -f 4 | cut -d '.' -f 1)"
12
+ if [ "$(./zig/zig targets | grep triple |cut -d '"' -f 4 | cut -d '.' -f 1,2)" = "aarch64-macos.13" ]; then
13
+ target="native-macos.11"
14
+ fi
15
+
16
+ echo "Building for $target"
17
+
18
+ mkdir -p dist
19
+
20
+ ./zig/zig build-lib \
21
+ -mcpu=baseline \
22
+ -OReleaseSafe \
23
+ -dynamic \
24
+ -lc \
25
+ -isystem build/node-"$(node --version)"/include/node \
26
+ -fallow-shlib-undefined \
27
+ -femit-bin=dist/client.node \
28
+ -target "$target" \
29
+ src/node.zig
package/src/node.zig CHANGED
@@ -161,7 +161,7 @@ const Context = struct {
161
161
  .io = context.io,
162
162
  },
163
163
  );
164
- defer context.client.deinit(allocator);
164
+ errdefer context.client.deinit(allocator);
165
165
 
166
166
  return try translate.create_external(env, context);
167
167
  }
@@ -13,5 +13,11 @@ if [[ -z "$BRANCH" ]]; then
13
13
  # Otherwise fall back to git rev-parse
14
14
  BRANCH=$(git rev-parse --abbrev-ref HEAD)
15
15
  fi
16
- ( cd docs_website && npm install && ./scripts/build.sh "$BRANCH" )
16
+
17
+ REPO="https://github.com/tigerbeetledb/tigerbeetle"
18
+ if [[ -n "$SOURCE_REPO" ]]; then
19
+ REPO="${SOURCE_REPO}"
20
+ fi
21
+
22
+ ( cd docs_website && npm install && ./scripts/build.sh "$BRANCH" "$REPO" )
17
23
  rm -rf docs_website
@@ -8,7 +8,7 @@ pub const log_level: std.log.Level = .err;
8
8
 
9
9
  const IO = @import("io.zig").IO;
10
10
 
11
- const util = @import("util.zig");
11
+ const stdx = @import("stdx.zig");
12
12
  const Storage = @import("storage.zig").Storage;
13
13
  const MessagePool = @import("message_pool.zig").MessagePool;
14
14
  const MessageBus = @import("message_bus.zig").MessageBusClient;
@@ -211,7 +211,7 @@ const TimedQueue = struct {
211
211
  const message = self.client.get_message();
212
212
  defer self.client.unref(message);
213
213
 
214
- util.copy_disjoint(
214
+ stdx.copy_disjoint(
215
215
  .inexact,
216
216
  u8,
217
217
  message.buffer[@sizeOf(vsr.Header)..],
@@ -279,7 +279,7 @@ const TimedQueue = struct {
279
279
  const message = self.client.get_message();
280
280
  defer self.client.unref(message);
281
281
 
282
- util.copy_disjoint(
282
+ stdx.copy_disjoint(
283
283
  .inexact,
284
284
  u8,
285
285
  message.buffer[@sizeOf(vsr.Header)..],
@@ -1,8 +1,18 @@
1
+ //! Raw configuration values.
2
+ //!
3
+ //! Code which needs these values should use `constants.zig` instead.
4
+ //! Configuration values are set from a combination of:
5
+ //! - default values
6
+ //! - `root.tigerbeetle_config`
7
+ //! - `@import("tigerbeetle_options")`
8
+
1
9
  const builtin = @import("builtin");
2
10
  const std = @import("std");
3
11
 
4
- const build_options = @import("tigerbeetle_build_options");
5
12
  const root = @import("root");
13
+ // Allow setting build-time config either via `build.zig` `Options`, or via a struct in the root file.
14
+ const build_options =
15
+ if (@hasDecl(root, "vsr_options")) root.vsr_options else @import("vsr_options");
6
16
 
7
17
  const vsr = @import("vsr.zig");
8
18
  const sector_size = @import("constants.zig").sector_size;
@@ -25,6 +35,7 @@ pub const Config = struct {
25
35
  const ConfigProcess = struct {
26
36
  log_level: std.log.Level = .info,
27
37
  tracer_backend: TracerBackend = .none,
38
+ hash_log_mode: HashLogMode = .none,
28
39
  verify: bool,
29
40
  port: u16 = 3001,
30
41
  address: []const u8 = "127.0.0.1",
@@ -68,6 +79,7 @@ const ConfigProcess = struct {
68
79
  const ConfigCluster = struct {
69
80
  cache_line_size: comptime_int = 64,
70
81
  clients_max: usize,
82
+ pipeline_prepare_queue_max: usize = 8,
71
83
  quorum_replication_max: u8 = 3,
72
84
  journal_slot_count: usize = 1024,
73
85
  message_size_max: usize = 1 * 1024 * 1024,
@@ -80,7 +92,6 @@ const ConfigCluster = struct {
80
92
  lsm_batch_multiple: comptime_int = 4,
81
93
  lsm_snapshots_max: usize = 32,
82
94
  lsm_value_to_key_layout_ratio_min: comptime_int = 16,
83
- state_machine: StateMachine = .accounting,
84
95
 
85
96
  /// The WAL requires at least two sectors of redundant headers — otherwise we could lose them all to
86
97
  /// a single torn write. A replica needs at least one valid redundant header to determine an
@@ -91,7 +102,7 @@ const ConfigCluster = struct {
91
102
  pub const clients_max_min = 1;
92
103
 
93
104
  /// The smallest possible message_size_max (for use in the simulator to improve performance).
94
- /// The message body must have room for pipeline_max headers in the DVC.
105
+ /// The message body must have room for pipeline_prepare_queue_max headers in the DVC.
95
106
  pub fn message_size_max_min(clients_max: usize) usize {
96
107
  return std.math.max(
97
108
  sector_size,
@@ -118,9 +129,10 @@ pub const TracerBackend = enum {
118
129
  tracy,
119
130
  };
120
131
 
121
- pub const StateMachine = enum {
122
- accounting,
123
- testing,
132
+ pub const HashLogMode = enum {
133
+ none,
134
+ create,
135
+ check,
124
136
  };
125
137
 
126
138
  pub const configs = struct {
@@ -166,10 +178,11 @@ pub const configs = struct {
166
178
  .verify = true,
167
179
  },
168
180
  .cluster = .{
169
- .clients_max = 4,
181
+ .clients_max = 4 + 3,
182
+ .pipeline_prepare_queue_max = 4,
170
183
  .journal_slot_count = Config.Cluster.journal_slot_count_min,
171
- .message_size_max = Config.Cluster.message_size_max_min(2),
172
- .storage_size_max = 1024 * 1024 * 1024,
184
+ .message_size_max = Config.Cluster.message_size_max_min(4),
185
+ .storage_size_max = 4 * 1024 * 1024 * 1024,
173
186
 
174
187
  .block_size = sector_size,
175
188
  .lsm_growth_factor = 4,
@@ -196,13 +209,6 @@ pub const configs = struct {
196
209
  .test_min => test_min,
197
210
  };
198
211
 
199
- base.cluster.state_machine = if (@hasDecl(root, "decode_events"))
200
- // TODO(DJ) This is a hack to work around the absense of tigerbeetle_build_options.
201
- // This should be removed once the node client is built using `zig build`.
202
- .accounting
203
- else
204
- @intToEnum(StateMachine, @enumToInt(build_options.config_cluster_state_machine));
205
-
206
212
  // TODO Use additional build options to overwrite other fields.
207
213
  base.process.tracer_backend = if (@hasDecl(root, "tracer_backend"))
208
214
  // TODO(jamii)
@@ -214,6 +220,13 @@ pub const configs = struct {
214
220
  // but a different type from a nominal typing perspective.
215
221
  @intToEnum(TracerBackend, @enumToInt(build_options.tracer_backend));
216
222
 
223
+ base.process.hash_log_mode = if (@hasDecl(root, "decode_events"))
224
+ // TODO(DJ) This is a hack to work around the absense of tigerbeetle_build_options.
225
+ // This should be removed once the node client is built using `zig build`.
226
+ .none
227
+ else
228
+ @intToEnum(HashLogMode, @enumToInt(build_options.hash_log_mode));
229
+
217
230
  break :current base;
218
231
  };
219
232
  };
@@ -1,3 +1,7 @@
1
+ //! Constants are the configuration that the code actually imports — they include:
2
+ //! - all of the configuration values (flattened)
3
+ //! - derived configuration values,
4
+
1
5
  const std = @import("std");
2
6
  const assert = std.debug.assert;
3
7
  const vsr = @import("vsr.zig");
@@ -18,15 +22,12 @@ else
18
22
  // Default is `.none`.
19
23
  pub const tracer_backend = config.process.tracer_backend;
20
24
 
25
+ // Which mode to use for ./testing/hash_log.zig.
26
+ pub const hash_log_mode = config.process.hash_log_mode;
27
+
21
28
  /// The maximum number of replicas allowed in a cluster.
22
29
  pub const replicas_max = 6;
23
30
 
24
- pub const state_machine = config.cluster.state_machine;
25
- pub const StateMachineType = switch (config.cluster.state_machine) {
26
- .accounting => @import("state_machine.zig").StateMachineType,
27
- .testing => @import("test/state_machine.zig").StateMachineType,
28
- };
29
-
30
31
  /// The maximum number of clients allowed per cluster, where each client has a unique 128-bit ID.
31
32
  /// This impacts the amount of memory allocated at initialization by the server.
32
33
  /// This determines the size of the VR client table used to cache replies to clients by client ID.
@@ -131,7 +132,7 @@ comptime {
131
132
  assert(journal_slot_count >= Config.Cluster.journal_slot_count_min);
132
133
  assert(journal_slot_count >= lsm_batch_multiple * 2);
133
134
  assert(journal_slot_count % lsm_batch_multiple == 0);
134
- assert(journal_size_max == journal_size_headers + journal_size_prepares);
135
+ assert(journal_slot_count > pipeline_prepare_queue_max);
135
136
 
136
137
  assert(journal_size_max == journal_size_headers + journal_size_prepares);
137
138
  }
@@ -161,7 +162,27 @@ comptime {
161
162
  /// The maximum number of Viewstamped Replication prepare messages that can be inflight at a time.
162
163
  /// This is immutable once assigned per cluster, as replicas need to know how many operations might
163
164
  /// possibly be uncommitted during a view change, and this must be constant for all replicas.
164
- pub const pipeline_max = clients_max;
165
+ pub const pipeline_prepare_queue_max = config.cluster.pipeline_prepare_queue_max;
166
+
167
+ /// The maximum number of Viewstamped Replication request messages that can be queued at a primary,
168
+ /// waiting to prepare.
169
+ // TODO(Zig): After 0.10, change this to simply "clients_max -| pipeline_prepare_queue_max".
170
+ // In Zig 0.9 compilation fails with "operation caused overflow" despite the saturating subtraction.
171
+ // See: https://github.com/ziglang/zig/issues/10870
172
+ pub const pipeline_request_queue_max =
173
+ if (clients_max < pipeline_prepare_queue_max)
174
+ 0
175
+ else
176
+ clients_max - pipeline_prepare_queue_max;
177
+
178
+ comptime {
179
+ // A prepare-queue capacity larger than clients_max is wasted.
180
+ assert(pipeline_prepare_queue_max <= clients_max);
181
+ // A total queue capacity larger than clients_max is wasted.
182
+ assert(pipeline_prepare_queue_max + pipeline_request_queue_max <= clients_max);
183
+ assert(pipeline_prepare_queue_max > 0);
184
+ assert(pipeline_request_queue_max >= 0);
185
+ }
165
186
 
166
187
  /// The minimum and maximum amount of time in milliseconds to wait before initiating a connection.
167
188
  /// Exponential backoff and jitter are applied within this range.
@@ -263,7 +284,7 @@ pub const iops_write_max = journal_iops_write_max;
263
284
  /// The maximum number of concurrent WAL read I/O operations to allow at once.
264
285
  pub const journal_iops_read_max = config.process.journal_iops_read_max;
265
286
  /// The maximum number of concurrent WAL write I/O operations to allow at once.
266
- /// Ideally this is at least as high as pipeline_max, but it is safe to be lower.
287
+ /// Ideally this is at least as high as pipeline_prepare_queue_max, but it is safe to be lower.
267
288
  pub const journal_iops_write_max = config.process.journal_iops_write_max;
268
289
 
269
290
  /// The number of redundant copies of the superblock in the superblock storage zone.
@@ -2,9 +2,9 @@ const std = @import("std");
2
2
  const assert = std.debug.assert;
3
3
  const math = std.math;
4
4
  const mem = std.mem;
5
- const util = @import("util.zig");
6
- const div_ceil = util.div_ceil;
7
- const disjoint_slices = util.disjoint_slices;
5
+ const stdx = @import("stdx.zig");
6
+ const div_ceil = stdx.div_ceil;
7
+ const disjoint_slices = stdx.disjoint_slices;
8
8
 
9
9
  /// Encode or decode a bitset using Daniel Lemire's EWAH codec.
10
10
  /// ("Histogram-Aware Sorting for Enhanced Word-Aligned Compression in Bitmap Indexes")
@@ -78,7 +78,7 @@ pub fn ewah(comptime Word: type) type {
78
78
  if (marker.uniform_bit == 1) ~@as(Word, 0) else 0,
79
79
  );
80
80
  target_index += marker.uniform_word_count;
81
- util.copy_disjoint(
81
+ stdx.copy_disjoint(
82
82
  .exact,
83
83
  Word,
84
84
  target_words[target_index..][0..marker.literal_word_count],
@@ -129,7 +129,7 @@ pub fn ewah(comptime Word: type) type {
129
129
  .literal_word_count = @intCast(MarkerLiteralCount, literal_word_count),
130
130
  });
131
131
  target_index += 1;
132
- util.copy_disjoint(
132
+ stdx.copy_disjoint(
133
133
  .exact,
134
134
  Word,
135
135
  target_words[target_index..][0..literal_word_count],
@@ -4,7 +4,7 @@ const assert = std.debug.assert;
4
4
  const log = std.log.scoped(.fuzz_ewah);
5
5
 
6
6
  const ewah = @import("./ewah.zig");
7
- const fuzz = @import("./test/fuzz.zig");
7
+ const fuzz = @import("./testing/fuzz.zig");
8
8
 
9
9
  pub const tigerbeetle_config = @import("config.zig").configs.test_min;
10
10
 
@@ -137,7 +137,7 @@ pub inline fn binary_search_keys(
137
137
  }
138
138
 
139
139
  const test_binary_search = struct {
140
- const fuzz = @import("../test/fuzz.zig");
140
+ const fuzz = @import("../testing/fuzz.zig");
141
141
 
142
142
  const log = false;
143
143
 
@@ -82,7 +82,7 @@ test {
82
82
  }
83
83
 
84
84
  const test_bloom_filter = struct {
85
- const fuzz = @import("../test/fuzz.zig");
85
+ const fuzz = @import("../testing/fuzz.zig");
86
86
  const block_size = @import("../constants.zig").block_size;
87
87
 
88
88
  fn random_keys(random: std.rand.Random, iter: usize) !void {
@@ -62,7 +62,12 @@ pub fn CompactionType(
62
62
  const BlockWrite = struct {
63
63
  write: Grid.Write = undefined,
64
64
  block: *BlockPtr = undefined,
65
- writable: bool = false,
65
+ state: BlockState = .building,
66
+ };
67
+ const BlockState = enum {
68
+ building,
69
+ writable,
70
+ writing,
66
71
  };
67
72
 
68
73
  const Manifest = ManifestType(Table, Storage);
@@ -241,9 +246,9 @@ pub fn CompactionType(
241
246
  .level_a_input = if (table_a) |table| table.* else null,
242
247
  };
243
248
 
244
- assert(!compaction.index.writable);
245
- assert(!compaction.filter.writable);
246
- assert(!compaction.data.writable);
249
+ assert(compaction.index.state == .building);
250
+ assert(compaction.filter.state == .building);
251
+ assert(compaction.data.state == .building);
247
252
 
248
253
  // TODO Implement manifest.move_table() optimization if there's only range.table_count == 1.
249
254
  // This would do update_tables + insert_tables inline without going through the iterators.
@@ -342,6 +347,14 @@ pub fn CompactionType(
342
347
  const write_callback = struct {
343
348
  fn callback(write: *Grid.Write) void {
344
349
  const block_write = @fieldParentPtr(BlockWrite, "write", write);
350
+
351
+ assert(block_write.state == .writing);
352
+ block_write.state = .building;
353
+
354
+ if (constants.verify) {
355
+ // We've finished writing so the block should now be zeroed.
356
+ assert(mem.allEqual(u8, block_write.block.*, 0));
357
+ }
345
358
  block_write.block = undefined;
346
359
 
347
360
  const _compaction = @fieldParentPtr(Compaction, @tagName(field), block_write);
@@ -350,8 +363,8 @@ pub fn CompactionType(
350
363
  }.callback;
351
364
 
352
365
  const block_write: *BlockWrite = &@field(compaction, @tagName(field));
353
- if (block_write.writable) {
354
- block_write.writable = false;
366
+ if (block_write.state == .writable) {
367
+ block_write.state = .writing;
355
368
 
356
369
  compaction.io_start();
357
370
  compaction.grid.write_block(
@@ -405,9 +418,9 @@ pub fn CompactionType(
405
418
  assert(!compaction.merge_iterator.?.empty());
406
419
  }
407
420
 
408
- assert(!compaction.data.writable);
409
- assert(!compaction.filter.writable);
410
- assert(!compaction.index.writable);
421
+ assert(compaction.data.state == .building);
422
+ assert(compaction.filter.state == .building);
423
+ assert(compaction.index.state == .building);
411
424
 
412
425
  if (!compaction.merge_iterator.?.empty()) {
413
426
  compaction.cpu_merge();
@@ -444,9 +457,9 @@ pub fn CompactionType(
444
457
  // Ensure there are values to merge and that is it safe to do so.
445
458
  const merge_iterator = &compaction.merge_iterator.?;
446
459
  assert(!merge_iterator.empty());
447
- assert(!compaction.data.writable);
448
- assert(!compaction.filter.writable);
449
- assert(!compaction.index.writable);
460
+ assert(compaction.data.state == .building);
461
+ assert(compaction.filter.state == .building);
462
+ assert(compaction.index.state == .building);
450
463
 
451
464
  // Build up a data block with values merged from the read iterators.
452
465
  // This skips tombstone values if compaction was started with the intent to drop them.
@@ -470,8 +483,8 @@ pub fn CompactionType(
470
483
 
471
484
  // Mark the finished data block as writable for the next compact_tick() call.
472
485
  compaction.data.block = &compaction.table_builder.data_block;
473
- assert(!compaction.data.writable);
474
- compaction.data.writable = true;
486
+ assert(compaction.data.state == .building);
487
+ compaction.data.state = .writable;
475
488
  }
476
489
 
477
490
  // Finalize the filter block if it's full or if it contains pending data blocks
@@ -487,8 +500,8 @@ pub fn CompactionType(
487
500
 
488
501
  // Mark the finished filter block as writable for the next compact_tick() call.
489
502
  compaction.filter.block = &compaction.table_builder.filter_block;
490
- assert(!compaction.filter.writable);
491
- compaction.filter.writable = true;
503
+ assert(compaction.filter.state == .building);
504
+ compaction.filter.state = .writable;
492
505
  }
493
506
 
494
507
  // Finalize the index block if it's full or if it contains pending data blocks
@@ -507,8 +520,8 @@ pub fn CompactionType(
507
520
 
508
521
  // Mark the finished index block as writable for the next compact_tick() call.
509
522
  compaction.index.block = &compaction.table_builder.index_block;
510
- assert(!compaction.index.writable);
511
- compaction.index.writable = true;
523
+ assert(compaction.index.state == .building);
524
+ compaction.index.state = .writable;
512
525
 
513
526
  compaction.tables_output_count += 1;
514
527
  assert(compaction.tables_output_count <= compaction.range.table_count);
@@ -524,9 +537,9 @@ pub fn CompactionType(
524
537
 
525
538
  // Ensure merging is truly finished.
526
539
  assert(compaction.merge_iterator.?.empty());
527
- assert(!compaction.data.writable);
528
- assert(!compaction.filter.writable);
529
- assert(!compaction.index.writable);
540
+ assert(compaction.data.state == .building);
541
+ assert(compaction.filter.state == .building);
542
+ assert(compaction.index.state == .building);
530
543
 
531
544
  // Double check the iterators are finished as well.
532
545
  const stream_empty = struct {