tigerbeetle-node 0.11.9 → 0.11.11

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.
@@ -38,7 +38,7 @@ pub const Clock = @import("vsr/clock.zig").Clock;
38
38
  pub const JournalType = @import("vsr/journal.zig").JournalType;
39
39
  pub const SlotRange = @import("vsr/journal.zig").SlotRange;
40
40
  pub const SuperBlockType = superblock.SuperBlockType;
41
- pub const VSRState = superblock.SuperBlockSector.VSRState;
41
+ pub const VSRState = superblock.SuperBlockHeader.VSRState;
42
42
 
43
43
  /// The version of our Viewstamped Replication protocol in use, including customizations.
44
44
  /// For backwards compatibility through breaking changes (e.g. upgrading checksums/ciphers).
@@ -106,9 +106,6 @@ pub const Command = enum(u8) {
106
106
  do_view_change,
107
107
  start_view,
108
108
 
109
- recovery,
110
- recovery_response,
111
-
112
109
  request_start_view,
113
110
  request_headers,
114
111
  request_prepare,
@@ -323,8 +320,6 @@ pub const Header = extern struct {
323
320
  .start_view_change => self.invalid_start_view_change(),
324
321
  .do_view_change => self.invalid_do_view_change(),
325
322
  .start_view => self.invalid_start_view(),
326
- .recovery => self.invalid_recovery(),
327
- .recovery_response => self.invalid_recovery_response(),
328
323
  .request_start_view => self.invalid_request_start_view(),
329
324
  .request_headers => self.invalid_request_headers(),
330
325
  .request_prepare => self.invalid_request_prepare(),
@@ -527,29 +522,6 @@ pub const Header = extern struct {
527
522
  return null;
528
523
  }
529
524
 
530
- fn invalid_recovery(self: *const Header) ?[]const u8 {
531
- assert(self.command == .recovery);
532
- if (self.parent != 0) return "parent != 0";
533
- if (self.client != 0) return "client != 0";
534
- if (self.request != 0) return "request != 0";
535
- if (self.view != 0) return "view != 0";
536
- if (self.op != 0) return "op != 0";
537
- if (self.commit != 0) return "commit != 0";
538
- if (self.timestamp != 0) return "timestamp != 0";
539
- if (self.operation != .reserved) return "operation != .reserved";
540
- return null;
541
- }
542
-
543
- fn invalid_recovery_response(self: *const Header) ?[]const u8 {
544
- assert(self.command == .recovery_response);
545
- if (self.parent != 0) return "parent != 0";
546
- if (self.client != 0) return "client != 0";
547
- if (self.request != 0) return "request != 0";
548
- if (self.timestamp != 0) return "timestamp != 0";
549
- if (self.operation != .reserved) return "operation != .reserved";
550
- return null;
551
- }
552
-
553
525
  fn invalid_request_start_view(self: *const Header) ?[]const u8 {
554
526
  assert(self.command == .request_start_view);
555
527
  if (self.parent != 0) return "parent != 0";
@@ -1026,3 +998,95 @@ pub fn quorums(replica_count: u8) struct {
1026
998
  .view_change = quorum_view_change,
1027
999
  };
1028
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
+ }