tigerbeetle-node 0.11.8 → 0.11.10

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 (85) hide show
  1. package/dist/.client.node.sha256 +1 -1
  2. package/package.json +4 -3
  3. package/scripts/build_lib.sh +41 -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 +31 -16
  8. package/src/tigerbeetle/src/constants.zig +48 -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 +84 -104
  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 +18 -13
  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/test.zig +5 -4
  27. package/src/tigerbeetle/src/lsm/tree.zig +1 -2
  28. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +85 -115
  29. package/src/tigerbeetle/src/message_bus.zig +4 -4
  30. package/src/tigerbeetle/src/message_pool.zig +7 -10
  31. package/src/tigerbeetle/src/ring_buffer.zig +22 -12
  32. package/src/tigerbeetle/src/simulator.zig +366 -239
  33. package/src/tigerbeetle/src/state_machine/auditor.zig +5 -5
  34. package/src/tigerbeetle/src/state_machine/workload.zig +3 -3
  35. package/src/tigerbeetle/src/state_machine.zig +190 -178
  36. package/src/tigerbeetle/src/{util.zig → stdx.zig} +2 -0
  37. package/src/tigerbeetle/src/storage.zig +13 -6
  38. package/src/tigerbeetle/src/{test → testing/cluster}/message_bus.zig +3 -3
  39. package/src/tigerbeetle/src/{test → testing/cluster}/network.zig +46 -22
  40. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +169 -0
  41. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +202 -0
  42. package/src/tigerbeetle/src/testing/cluster.zig +443 -0
  43. package/src/tigerbeetle/src/{test → testing}/fuzz.zig +0 -0
  44. package/src/tigerbeetle/src/testing/hash_log.zig +66 -0
  45. package/src/tigerbeetle/src/{test → testing}/id.zig +0 -0
  46. package/src/tigerbeetle/src/testing/packet_simulator.zig +365 -0
  47. package/src/tigerbeetle/src/{test → testing}/priority_queue.zig +1 -1
  48. package/src/tigerbeetle/src/testing/reply_sequence.zig +139 -0
  49. package/src/tigerbeetle/src/{test → testing}/state_machine.zig +3 -1
  50. package/src/tigerbeetle/src/testing/storage.zig +757 -0
  51. package/src/tigerbeetle/src/{test → testing}/table.zig +21 -0
  52. package/src/tigerbeetle/src/{test → testing}/time.zig +0 -0
  53. package/src/tigerbeetle/src/tigerbeetle.zig +2 -0
  54. package/src/tigerbeetle/src/tracer.zig +3 -3
  55. package/src/tigerbeetle/src/unit_tests.zig +4 -4
  56. package/src/tigerbeetle/src/vopr.zig +2 -2
  57. package/src/tigerbeetle/src/vsr/client.zig +5 -2
  58. package/src/tigerbeetle/src/vsr/clock.zig +93 -53
  59. package/src/tigerbeetle/src/vsr/journal.zig +109 -98
  60. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +2 -2
  61. package/src/tigerbeetle/src/vsr/replica.zig +1983 -1430
  62. package/src/tigerbeetle/src/vsr/replica_format.zig +13 -13
  63. package/src/tigerbeetle/src/vsr/superblock.zig +240 -142
  64. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +7 -7
  65. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +1 -1
  66. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +1 -1
  67. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +49 -14
  68. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +38 -19
  69. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +48 -48
  70. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +51 -51
  71. package/src/tigerbeetle/src/vsr.zig +99 -33
  72. package/src/tigerbeetle/src/demo.zig +0 -132
  73. package/src/tigerbeetle/src/demo_01_create_accounts.zig +0 -35
  74. package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +0 -7
  75. package/src/tigerbeetle/src/demo_03_create_transfers.zig +0 -37
  76. package/src/tigerbeetle/src/demo_04_create_pending_transfers.zig +0 -61
  77. package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +0 -37
  78. package/src/tigerbeetle/src/demo_06_void_pending_transfers.zig +0 -24
  79. package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +0 -7
  80. package/src/tigerbeetle/src/test/cluster.zig +0 -352
  81. package/src/tigerbeetle/src/test/conductor.zig +0 -366
  82. package/src/tigerbeetle/src/test/packet_simulator.zig +0 -398
  83. package/src/tigerbeetle/src/test/state_checker.zig +0 -169
  84. package/src/tigerbeetle/src/test/storage.zig +0 -864
  85. package/src/tigerbeetle/src/test/storage_checker.zig +0 -204
@@ -3,10 +3,10 @@ const assert = std.debug.assert;
3
3
  const log = std.log.scoped(.fuzz_vsr_superblock_quorums);
4
4
 
5
5
  const superblock = @import("./superblock.zig");
6
- const SuperBlockSector = superblock.SuperBlockSector;
6
+ const SuperBlockHeader = superblock.SuperBlockHeader;
7
7
  const SuperBlockVersion = superblock.SuperBlockVersion;
8
8
 
9
- const fuzz = @import("../test/fuzz.zig");
9
+ const fuzz = @import("../testing/fuzz.zig");
10
10
  const superblock_quorums = @import("superblock_quorums.zig");
11
11
  const QuorumsType = superblock_quorums.QuorumsType;
12
12
 
@@ -21,7 +21,7 @@ pub fn main() !void {
21
21
  try fuzz_quorums_working(prng.random());
22
22
 
23
23
  try fuzz_quorum_repairs(prng.random(), .{ .superblock_copies = 4 });
24
- // TODO: Enable these once SuperBlockSector is generic over its Constants.
24
+ // TODO: Enable these once SuperBlockHeader is generic over its Constants.
25
25
  // try fuzz_quorum_repairs(prng.random(), .{ .superblock_copies = 6 });
26
26
  // try fuzz_quorum_repairs(prng.random(), .{ .superblock_copies = 8 });
27
27
  }
@@ -108,18 +108,18 @@ fn test_quorums_working(
108
108
  const Quorums = QuorumsType(.{ .superblock_copies = 4 });
109
109
  const misdirect = random.boolean(); // true:cluster false:replica
110
110
  var quorums: Quorums = undefined;
111
- var sectors: [4]SuperBlockSector = undefined;
111
+ var headers: [4]SuperBlockHeader = undefined;
112
112
  // TODO(Zig): Ideally this would be a [6]?u128 and the access would be
113
113
  // "checksums[i] orelse random.int(u128)", but that currently causes the compiler to segfault
114
114
  // during code generation.
115
115
  var checksums: [6]u128 = undefined;
116
116
  for (checksums) |*c| c.* = random.int(u128);
117
117
 
118
- // Create sectors in ascending-sequence order to build the checksum/parent hash chain.
118
+ // Create headers in ascending-sequence order to build the checksum/parent hash chain.
119
119
  std.sort.sort(CopyTemplate, copies, {}, CopyTemplate.less_than);
120
120
 
121
- for (sectors) |*sector, i| {
122
- sector.* = std.mem.zeroInit(SuperBlockSector, .{
121
+ for (headers) |*header, i| {
122
+ header.* = std.mem.zeroInit(SuperBlockHeader, .{
123
123
  .copy = @intCast(u8, i),
124
124
  .version = SuperBlockVersion,
125
125
  .replica = 1,
@@ -131,32 +131,32 @@ fn test_quorums_working(
131
131
  var checksum: ?u128 = null;
132
132
  switch (copies[i].variant) {
133
133
  .valid => {},
134
- .valid_high_copy => sector.copy = 4,
134
+ .valid_high_copy => header.copy = 4,
135
135
  .invalid_broken => {
136
136
  if (random.boolean() and i > 0) {
137
- // Error: duplicate sector (if available).
138
- sector.* = sectors[random.uintLessThanBiased(usize, i)];
137
+ // Error: duplicate header (if available).
138
+ header.* = headers[random.uintLessThanBiased(usize, i)];
139
139
  checksum = random.int(u128);
140
140
  } else {
141
141
  // Error: invalid checksum.
142
142
  checksum = random.int(u128);
143
143
  }
144
144
  },
145
- .invalid_fork => sector.storage_size_max += 1, // Ensure we have a different checksum.
146
- .invalid_parent => sector.parent += 1,
145
+ .invalid_fork => header.storage_size_max += 1, // Ensure we have a different checksum.
146
+ .invalid_parent => header.parent += 1,
147
147
  .invalid_misdirect => {
148
148
  if (misdirect) {
149
- sector.cluster += 1;
149
+ header.cluster += 1;
150
150
  } else {
151
- sector.replica += 1;
151
+ header.replica += 1;
152
152
  }
153
153
  },
154
- .invalid_vsr_state => sector.vsr_state.view += 1,
154
+ .invalid_vsr_state => header.vsr_state.view += 1,
155
155
  }
156
- sector.checksum = checksum orelse sector.calculate_checksum();
156
+ header.checksum = checksum orelse header.calculate_checksum();
157
157
 
158
158
  if (copies[i].variant == .valid or copies[i].variant == .invalid_vsr_state) {
159
- checksums[sector.sequence] = sector.checksum;
159
+ checksums[header.sequence] = header.checksum;
160
160
  }
161
161
  }
162
162
 
@@ -165,7 +165,7 @@ fn test_quorums_working(
165
165
  } else {
166
166
  // Shuffling copies can only change the working quorum when we have a corrupt copy index,
167
167
  // because we guess that the true index is the slot.
168
- random.shuffle(SuperBlockSector, &sectors);
168
+ random.shuffle(SuperBlockHeader, &headers);
169
169
  }
170
170
 
171
171
  const threshold = switch (threshold_count) {
@@ -175,8 +175,8 @@ fn test_quorums_working(
175
175
  };
176
176
  assert(threshold.count() == threshold_count);
177
177
 
178
- if (quorums.working(&sectors, threshold)) |working| {
179
- try std.testing.expectEqual(result, working.sector.sequence);
178
+ if (quorums.working(&headers, threshold)) |working| {
179
+ try std.testing.expectEqual(result, working.header.sequence);
180
180
  } else |err| {
181
181
  try std.testing.expectEqual(result, err);
182
182
  }
@@ -238,7 +238,7 @@ pub const CopyTemplate = struct {
238
238
  }
239
239
  };
240
240
 
241
- // Verify that a torn sector write during repair never compromises the existing quorum.
241
+ // Verify that a torn header write during repair never compromises the existing quorum.
242
242
  pub fn fuzz_quorum_repairs(
243
243
  random: std.rand.Random,
244
244
  comptime options: superblock_quorums.Options,
@@ -249,66 +249,66 @@ pub fn fuzz_quorum_repairs(
249
249
  var q1: Quorums = undefined;
250
250
  var q2: Quorums = undefined;
251
251
 
252
- const sectors_valid = blk: {
253
- var sectors: [superblock_copies]SuperBlockSector = undefined;
254
- for (&sectors) |*sector, i| {
255
- sector.* = std.mem.zeroInit(SuperBlockSector, .{
252
+ const headers_valid = blk: {
253
+ var headers: [superblock_copies]SuperBlockHeader = undefined;
254
+ for (&headers) |*header, i| {
255
+ header.* = std.mem.zeroInit(SuperBlockHeader, .{
256
256
  .copy = @intCast(u8, i),
257
257
  .version = SuperBlockVersion,
258
258
  .replica = 1,
259
259
  .storage_size_max = superblock.data_file_size_min,
260
260
  .sequence = 123,
261
261
  });
262
- sector.set_checksum();
262
+ header.set_checksum();
263
263
  }
264
- break :blk sectors;
264
+ break :blk headers;
265
265
  };
266
266
 
267
- const sector_invalid = blk: {
268
- var sector = sectors_valid[0];
269
- sector.checksum = 456;
270
- break :blk sector;
267
+ const header_invalid = blk: {
268
+ var header = headers_valid[0];
269
+ header.checksum = 456;
270
+ break :blk header;
271
271
  };
272
272
 
273
273
  // Generate a random valid 2/4 quorum.
274
- // 1 bits indicate valid sectors.
275
- // 0 bits indicate invalid sectors.
274
+ // 1 bits indicate valid headers.
275
+ // 0 bits indicate invalid headers.
276
276
  var valid = std.bit_set.IntegerBitSet(superblock_copies).initEmpty();
277
277
  while (valid.count() < Quorums.Threshold.open.count() or random.boolean()) {
278
278
  valid.set(random.uintLessThan(usize, superblock_copies));
279
279
  }
280
280
 
281
- var working_sectors: [superblock_copies]SuperBlockSector = undefined;
282
- for (&working_sectors) |*sector, i| {
283
- sector.* = if (valid.isSet(i)) sectors_valid[i] else sector_invalid;
281
+ var working_headers: [superblock_copies]SuperBlockHeader = undefined;
282
+ for (&working_headers) |*header, i| {
283
+ header.* = if (valid.isSet(i)) headers_valid[i] else header_invalid;
284
284
  }
285
- random.shuffle(SuperBlockSector, &working_sectors);
286
- var repair_sectors = working_sectors;
285
+ random.shuffle(SuperBlockHeader, &working_headers);
286
+ var repair_headers = working_headers;
287
287
 
288
- const working_quorum = q1.working(&working_sectors, .open) catch unreachable;
288
+ const working_quorum = q1.working(&working_headers, .open) catch unreachable;
289
289
  var quorum_repairs = working_quorum.repairs();
290
290
  while (quorum_repairs.next()) |repair_copy| {
291
291
  {
292
- // Simulate a torn sector write, crash, recover sequence.
293
- var damaged_sectors = repair_sectors;
294
- damaged_sectors[repair_copy] = sector_invalid;
295
- const damaged_quorum = q2.working(&damaged_sectors, .open) catch unreachable;
296
- assert(damaged_quorum.sector.checksum == working_quorum.sector.checksum);
292
+ // Simulate a torn header write, crash, recover sequence.
293
+ var damaged_headers = repair_headers;
294
+ damaged_headers[repair_copy] = header_invalid;
295
+ const damaged_quorum = q2.working(&damaged_headers, .open) catch unreachable;
296
+ assert(damaged_quorum.header.checksum == working_quorum.header.checksum);
297
297
  }
298
298
 
299
299
  // "Finish" the write so that we can test the next repair.
300
- repair_sectors[repair_copy] = sectors_valid[repair_copy];
300
+ repair_headers[repair_copy] = headers_valid[repair_copy];
301
301
 
302
- const quorum_repaired = q2.working(&repair_sectors, .open) catch unreachable;
303
- assert(quorum_repaired.sector.checksum == working_quorum.sector.checksum);
302
+ const quorum_repaired = q2.working(&repair_headers, .open) catch unreachable;
303
+ assert(quorum_repaired.header.checksum == working_quorum.header.checksum);
304
304
  }
305
305
 
306
306
  // At the end of all repairs, we expect to have every copy of the superblock.
307
307
  // They do not need to be in their home slot.
308
308
  var copies = Quorums.QuorumCount.initEmpty();
309
- for (repair_sectors) |repair_sector| {
310
- assert(repair_sector.checksum == working_quorum.sector.checksum);
311
- copies.set(repair_sector.copy);
309
+ for (repair_headers) |repair_header| {
310
+ assert(repair_header.checksum == working_quorum.header.checksum);
311
+ copies.set(repair_header.copy);
312
312
  }
313
- assert(repair_sectors.len == copies.count());
313
+ assert(repair_headers.len == copies.count());
314
314
  }
@@ -17,6 +17,7 @@ pub const tigerbeetle = @import("tigerbeetle.zig");
17
17
  pub const time = @import("time.zig");
18
18
  pub const tracer = @import("tracer.zig");
19
19
  pub const config = @import("config.zig");
20
+ pub const stdx = @import("stdx.zig");
20
21
  pub const superblock = @import("vsr/superblock.zig");
21
22
  pub const lsm = .{
22
23
  .tree = @import("lsm/tree.zig"),
@@ -25,6 +26,9 @@ pub const lsm = .{
25
26
  .forest = @import("lsm/forest.zig"),
26
27
  .posted_groove = @import("lsm/posted_groove.zig"),
27
28
  };
29
+ pub const testing = .{
30
+ .cluster = @import("testing/cluster.zig"),
31
+ };
28
32
 
29
33
  pub const ReplicaType = @import("vsr/replica.zig").ReplicaType;
30
34
  pub const format = @import("vsr/replica_format.zig").format;
@@ -34,7 +38,7 @@ pub const Clock = @import("vsr/clock.zig").Clock;
34
38
  pub const JournalType = @import("vsr/journal.zig").JournalType;
35
39
  pub const SlotRange = @import("vsr/journal.zig").SlotRange;
36
40
  pub const SuperBlockType = superblock.SuperBlockType;
37
- pub const VSRState = superblock.SuperBlockSector.VSRState;
41
+ pub const VSRState = superblock.SuperBlockHeader.VSRState;
38
42
 
39
43
  /// The version of our Viewstamped Replication protocol in use, including customizations.
40
44
  /// For backwards compatibility through breaking changes (e.g. upgrading checksums/ciphers).
@@ -102,9 +106,6 @@ pub const Command = enum(u8) {
102
106
  do_view_change,
103
107
  start_view,
104
108
 
105
- recovery,
106
- recovery_response,
107
-
108
109
  request_start_view,
109
110
  request_headers,
110
111
  request_prepare,
@@ -319,8 +320,6 @@ pub const Header = extern struct {
319
320
  .start_view_change => self.invalid_start_view_change(),
320
321
  .do_view_change => self.invalid_do_view_change(),
321
322
  .start_view => self.invalid_start_view(),
322
- .recovery => self.invalid_recovery(),
323
- .recovery_response => self.invalid_recovery_response(),
324
323
  .request_start_view => self.invalid_request_start_view(),
325
324
  .request_headers => self.invalid_request_headers(),
326
325
  .request_prepare => self.invalid_request_prepare(),
@@ -523,29 +522,6 @@ pub const Header = extern struct {
523
522
  return null;
524
523
  }
525
524
 
526
- fn invalid_recovery(self: *const Header) ?[]const u8 {
527
- assert(self.command == .recovery);
528
- if (self.parent != 0) return "parent != 0";
529
- if (self.client != 0) return "client != 0";
530
- if (self.request != 0) return "request != 0";
531
- if (self.view != 0) return "view != 0";
532
- if (self.op != 0) return "op != 0";
533
- if (self.commit != 0) return "commit != 0";
534
- if (self.timestamp != 0) return "timestamp != 0";
535
- if (self.operation != .reserved) return "operation != .reserved";
536
- return null;
537
- }
538
-
539
- fn invalid_recovery_response(self: *const Header) ?[]const u8 {
540
- assert(self.command == .recovery_response);
541
- if (self.parent != 0) return "parent != 0";
542
- if (self.client != 0) return "client != 0";
543
- if (self.request != 0) return "request != 0";
544
- if (self.timestamp != 0) return "timestamp != 0";
545
- if (self.operation != .reserved) return "operation != .reserved";
546
- return null;
547
- }
548
-
549
525
  fn invalid_request_start_view(self: *const Header) ?[]const u8 {
550
526
  assert(self.command == .request_start_view);
551
527
  if (self.parent != 0) return "parent != 0";
@@ -826,8 +802,6 @@ pub fn exponential_backoff_with_jitter(
826
802
  }
827
803
 
828
804
  test "exponential_backoff_with_jitter" {
829
- const testing = std.testing;
830
-
831
805
  var prng = std.rand.DefaultPrng.init(0);
832
806
  const random = prng.random();
833
807
 
@@ -838,8 +812,8 @@ test "exponential_backoff_with_jitter" {
838
812
  var attempt = max - attempts;
839
813
  while (attempt < max) : (attempt += 1) {
840
814
  const ebwj = exponential_backoff_with_jitter(random, min, max, attempt);
841
- try testing.expect(ebwj >= min);
842
- try testing.expect(ebwj <= max);
815
+ try std.testing.expect(ebwj >= min);
816
+ try std.testing.expect(ebwj <= max);
843
817
  }
844
818
  }
845
819
 
@@ -1024,3 +998,95 @@ pub fn quorums(replica_count: u8) struct {
1024
998
  .view_change = quorum_view_change,
1025
999
  };
1026
1000
  }
1001
+
1002
+ /// The SuperBlock's persisted VSR headers.
1003
+ /// One of the following:
1004
+ ///
1005
+ /// - SV headers (consecutive chain)
1006
+ /// - DVC headers (disjoint chain)
1007
+ pub const ViewChangeHeaders = struct {
1008
+ /// Headers are ordered from high-to-low op.
1009
+ slice: []const Header,
1010
+
1011
+ pub const BoundedArray = std.BoundedArray(Header, constants.pipeline_prepare_queue_max);
1012
+
1013
+ pub fn init(slice: []const Header) ViewChangeHeaders {
1014
+ ViewChangeHeaders.verify(slice);
1015
+
1016
+ return .{ .slice = slice };
1017
+ }
1018
+
1019
+ pub fn verify(slice: []const Header) void {
1020
+ assert(slice.len > 0);
1021
+ assert(slice.len <= constants.pipeline_prepare_queue_max);
1022
+
1023
+ var child: ?*const Header = null;
1024
+ for (slice) |*header| {
1025
+ assert(header.valid_checksum());
1026
+ assert(header.command == .prepare);
1027
+
1028
+ if (child) |child_header| {
1029
+ assert(header.op < child_header.op);
1030
+ assert(header.view <= child_header.view);
1031
+ assert((header.op + 1 == child_header.op) ==
1032
+ (header.checksum == child_header.parent));
1033
+ assert(header.timestamp < child_header.timestamp);
1034
+ }
1035
+ child = header;
1036
+ }
1037
+ }
1038
+
1039
+ const ViewRange = struct {
1040
+ min: u32, // inclusive
1041
+ max: u32, // inclusive
1042
+
1043
+ pub fn contains(range: ViewRange, view: u32) bool {
1044
+ return range.min <= view and view <= range.max;
1045
+ }
1046
+ };
1047
+
1048
+ /// Returns the range of possible views (of prepare, not commit) for a message that is part of
1049
+ /// the same log_view as these headers.
1050
+ ///
1051
+ /// - When these are DVC headers for a log_view=V, we must be in view_change status working to
1052
+ /// transition to a view beyond V. So we will never prepare anything else as part of view V.
1053
+ /// - When these are SV headers for a log_view=V, we can continue to add to them (by preparing
1054
+ /// more ops), but those ops will laways be part of the log_view. If they were prepared during
1055
+ /// a view prior to the log_view, they would already be part of the headers.
1056
+ pub fn view_for_op(headers: ViewChangeHeaders, op: u64, log_view: u32) ViewRange {
1057
+ const header_newest = &headers.slice[0];
1058
+ const header_oldest = &headers.slice[headers.slice.len - 1];
1059
+
1060
+ if (op < header_oldest.op) return .{ .min = 0, .max = header_oldest.view };
1061
+ if (op > header_newest.op) return .{ .min = log_view, .max = log_view };
1062
+
1063
+ for (headers.slice) |*header| {
1064
+ if (header.op == op) return .{ .min = header.view, .max = header.view };
1065
+ }
1066
+
1067
+ for (headers.slice[0 .. headers.slice.len - 1]) |*header_next, header_next_index| {
1068
+ const header_prev = headers.slice[header_next_index + 1];
1069
+ if (header_prev.op < op and op < header_next.op) {
1070
+ return .{ .min = header_prev.view, .max = header_next.view };
1071
+ }
1072
+ }
1073
+ unreachable;
1074
+ }
1075
+ };
1076
+
1077
+ test "ViewChangeHeaders.view_for_op" {
1078
+ var headers_array = [_]Header{
1079
+ std.mem.zeroInit(Header, .{ .op = 9, .view = 10 }),
1080
+ std.mem.zeroInit(Header, .{ .op = 6, .view = 7 }),
1081
+ };
1082
+
1083
+ const headers = ViewChangeHeaders{ .slice = &headers_array };
1084
+ try std.testing.expect(std.meta.eql(headers.view_for_op(11, 12), .{ .min = 12, .max = 12 }));
1085
+ try std.testing.expect(std.meta.eql(headers.view_for_op(10, 12), .{ .min = 12, .max = 12 }));
1086
+ try std.testing.expect(std.meta.eql(headers.view_for_op(9, 12), .{ .min = 10, .max = 10 }));
1087
+ try std.testing.expect(std.meta.eql(headers.view_for_op(8, 12), .{ .min = 7, .max = 10 }));
1088
+ try std.testing.expect(std.meta.eql(headers.view_for_op(7, 12), .{ .min = 7, .max = 10 }));
1089
+ try std.testing.expect(std.meta.eql(headers.view_for_op(6, 12), .{ .min = 7, .max = 7 }));
1090
+ try std.testing.expect(std.meta.eql(headers.view_for_op(5, 12), .{ .min = 0, .max = 7 }));
1091
+ try std.testing.expect(std.meta.eql(headers.view_for_op(0, 12), .{ .min = 0, .max = 7 }));
1092
+ }
@@ -1,132 +0,0 @@
1
- const std = @import("std");
2
- const assert = std.debug.assert;
3
-
4
- const constants = @import("constants.zig");
5
-
6
- const tb = @import("tigerbeetle.zig");
7
- const Account = tb.Account;
8
- const Transfer = tb.Transfer;
9
-
10
- const CreateAccountsResult = tb.CreateAccountsResult;
11
- const CreateTransfersResult = tb.CreateTransfersResult;
12
-
13
- const util = @import("util.zig");
14
- const IO = @import("io.zig").IO;
15
- const Storage = @import("storage.zig").Storage;
16
- const MessagePool = @import("message_pool.zig").MessagePool;
17
- const MessageBus = @import("message_bus.zig").MessageBusClient;
18
- const StateMachine = @import("state_machine.zig").StateMachineType(Storage, .{
19
- .message_body_size_max = constants.message_body_size_max,
20
- });
21
-
22
- const vsr = @import("vsr.zig");
23
- const Header = vsr.Header;
24
- const Client = vsr.Client(StateMachine, MessageBus);
25
-
26
- pub const log_level: std.log.Level = .alert;
27
-
28
- pub fn request(
29
- operation: StateMachine.Operation,
30
- batch: anytype,
31
- on_reply: fn (
32
- user_data: u128,
33
- operation: StateMachine.Operation,
34
- results: Client.Error![]const u8,
35
- ) void,
36
- ) !void {
37
- const allocator = std.heap.page_allocator;
38
- const client_id = std.crypto.random.int(u128);
39
- const cluster_id: u32 = 0;
40
- var addresses = [_]std.net.Address{try std.net.Address.parseIp4("127.0.0.1", constants.port)};
41
-
42
- var io = try IO.init(32, 0);
43
- defer io.deinit();
44
-
45
- var message_pool = try MessagePool.init(allocator, .client);
46
- defer message_pool.deinit(allocator);
47
-
48
- var client = try Client.init(
49
- allocator,
50
- client_id,
51
- cluster_id,
52
- @intCast(u8, addresses.len),
53
- &message_pool,
54
- .{
55
- .configuration = &addresses,
56
- .io = &io,
57
- },
58
- );
59
- defer client.deinit(allocator);
60
-
61
- const message = client.get_message();
62
- defer client.unref(message);
63
-
64
- const body = std.mem.asBytes(&batch);
65
- util.copy_disjoint(.inexact, u8, message.buffer[@sizeOf(Header)..], body);
66
-
67
- client.request(
68
- 0,
69
- on_reply,
70
- operation,
71
- message,
72
- body.len,
73
- );
74
-
75
- while (client.request_queue.count > 0) {
76
- client.tick();
77
- try io.run_for_ns(constants.tick_ms * std.time.ns_per_ms);
78
- }
79
- }
80
-
81
- pub fn on_create_accounts(
82
- user_data: u128,
83
- operation: StateMachine.Operation,
84
- results: Client.Error![]const u8,
85
- ) void {
86
- _ = user_data;
87
- _ = operation;
88
-
89
- print_results(CreateAccountsResult, results);
90
- }
91
-
92
- pub fn on_lookup_accounts(
93
- user_data: u128,
94
- operation: StateMachine.Operation,
95
- results: Client.Error![]const u8,
96
- ) void {
97
- _ = user_data;
98
- _ = operation;
99
-
100
- print_results(Account, results);
101
- }
102
-
103
- pub fn on_lookup_transfers(
104
- user_data: u128,
105
- operation: StateMachine.Operation,
106
- results: Client.Error![]const u8,
107
- ) void {
108
- _ = user_data;
109
- _ = operation;
110
-
111
- print_results(Transfer, results);
112
- }
113
-
114
- pub fn on_create_transfers(
115
- user_data: u128,
116
- operation: StateMachine.Operation,
117
- results: Client.Error![]const u8,
118
- ) void {
119
- _ = user_data;
120
- _ = operation;
121
-
122
- print_results(CreateTransfersResult, results);
123
- }
124
-
125
- fn print_results(comptime Results: type, results: Client.Error![]const u8) void {
126
- const body = results catch unreachable;
127
- const slice = std.mem.bytesAsSlice(Results, body);
128
- for (slice) |result| {
129
- std.debug.print("{}\n", .{result});
130
- }
131
- if (slice.len == 0) std.debug.print("OK\n", .{});
132
- }
@@ -1,35 +0,0 @@
1
- const tb = @import("tigerbeetle.zig");
2
- const demo = @import("demo.zig");
3
-
4
- const Account = tb.Account;
5
-
6
- pub fn main() !void {
7
- const accounts = [_]Account{
8
- Account{
9
- .id = 1,
10
- .user_data = 0,
11
- .reserved = [_]u8{0} ** 48,
12
- .ledger = 710, // Let's use the ISO-4217 Code Number for ZAR
13
- .code = 1000, // A chart of accounts code to describe this as a clearing account.
14
- .flags = .{ .debits_must_not_exceed_credits = true },
15
- .debits_pending = 0,
16
- .debits_posted = 0,
17
- .credits_pending = 0,
18
- .credits_posted = 0,
19
- },
20
- Account{
21
- .id = 2,
22
- .user_data = 0,
23
- .reserved = [_]u8{0} ** 48,
24
- .ledger = 710, // Let's use the ISO-4217 Code Number for ZAR
25
- .code = 2000, // A chart of accounts code to describe this as a payable account.
26
- .flags = .{},
27
- .debits_pending = 0,
28
- .debits_posted = 0,
29
- .credits_pending = 0,
30
- .credits_posted = 0,
31
- },
32
- };
33
-
34
- try demo.request(.create_accounts, accounts, demo.on_create_accounts);
35
- }
@@ -1,7 +0,0 @@
1
- const demo = @import("demo.zig");
2
-
3
- pub fn main() !void {
4
- const ids = [_]u128{ 1, 2 };
5
-
6
- try demo.request(.lookup_accounts, ids, demo.on_lookup_accounts);
7
- }
@@ -1,37 +0,0 @@
1
- const tb = @import("tigerbeetle.zig");
2
- const demo = @import("demo.zig");
3
-
4
- const Transfer = tb.Transfer;
5
-
6
- pub fn main() !void {
7
- const transfers = [_]Transfer{
8
- Transfer{
9
- .id = 1,
10
- .debit_account_id = 2,
11
- .credit_account_id = 1,
12
- .user_data = 0,
13
- .reserved = 0,
14
- .pending_id = 0,
15
- .timeout = 0,
16
- .ledger = 710, // Let's use the ISO-4217 Code Number for ZAR
17
- .code = 1,
18
- .flags = .{},
19
- .amount = 10000, // Let's start with some liquidity in account 1.
20
- },
21
- Transfer{
22
- .id = 2,
23
- .debit_account_id = 1,
24
- .credit_account_id = 2,
25
- .user_data = 0,
26
- .reserved = 0,
27
- .pending_id = 0,
28
- .timeout = 0,
29
- .ledger = 710, // Let's use the ISO-4217 Code Number for ZAR
30
- .code = 1,
31
- .flags = .{},
32
- .amount = 1000,
33
- },
34
- };
35
-
36
- try demo.request(.create_transfers, transfers, demo.on_create_transfers);
37
- }
@@ -1,61 +0,0 @@
1
- const std = @import("std");
2
-
3
- const tb = @import("tigerbeetle.zig");
4
- const demo = @import("demo.zig");
5
-
6
- const Transfer = tb.Transfer;
7
-
8
- pub fn main() !void {
9
- const transfers = [_]Transfer{
10
- Transfer{
11
- .id = 1001,
12
- .debit_account_id = 1,
13
- .credit_account_id = 2,
14
- .user_data = 0,
15
- .reserved = 0,
16
- .pending_id = 0,
17
- .timeout = std.time.ns_per_hour,
18
- .ledger = 710,
19
- .code = 1,
20
- .flags = .{
21
- .pending = true, // Set this transfer to be two-phase.
22
- },
23
- .amount = 8000,
24
- },
25
- Transfer{
26
- .id = 1002,
27
- .debit_account_id = 1,
28
- .credit_account_id = 2,
29
- .user_data = 0,
30
- .reserved = 0,
31
- .pending_id = 0,
32
- .timeout = std.time.ns_per_hour,
33
- .ledger = 710,
34
- .code = 1,
35
- .flags = .{
36
- .pending = true, // Set this transfer to be two-phase.
37
- .linked = true, // Link this transfer with the next transfer 1003.
38
- },
39
- .amount = 500,
40
- },
41
- Transfer{
42
- .id = 1003,
43
- .debit_account_id = 1,
44
- .credit_account_id = 2,
45
- .user_data = 0,
46
- .reserved = 0,
47
- .pending_id = 0,
48
- .timeout = std.time.ns_per_hour,
49
- .ledger = 710,
50
- .code = 1,
51
- .flags = .{
52
- .pending = true, // Set this transfer to be two-phase.
53
- // The last transfer in a linked chain has .linked set to false to close the chain.
54
- // This transfer will succeed or fail together with transfer 1002 above.
55
- },
56
- .amount = 500,
57
- },
58
- };
59
-
60
- try demo.request(.create_transfers, transfers, demo.on_create_transfers);
61
- }