M1: doc-level free list, sessions, and a spec runner that no longer overstates #1

Merged
dev merged 37 commits from m1-cursors into main 2026-08-09 16:15:34 +00:00
2 changed files with 192 additions and 0 deletions
Showing only changes of commit 0034da4293 - Show all commits

View File

@@ -74,6 +74,35 @@ pub const Value = union(enum) {
}; };
} }
/// The name mongod uses for this type in a TypeMismatch message ("is the
/// wrong type 'int', expected type 'object'"). Its own names, not Zig's:
/// a driver that matches on the text is matching on these.
pub fn type_name(self: Value) []const u8 {
return switch (self) {
.double => "double",
.string => "string",
.doc => "object",
.array => "array",
.binary => "binData",
.object_id => "objectId",
.bool => "bool",
.datetime => "date",
.null => "null",
.regex => "regex",
.code => "javascript",
.symbol => "symbol",
.int32 => "int",
.timestamp => "timestamp",
.int64 => "long",
.decimal128 => "decimal",
.min_key => "minKey",
.max_key => "maxKey",
// An unparsed value keeps only its tag byte, and the tags this
// union does not name are the ones nothing here inspects.
.opaque_val => "unknown",
};
}
pub fn is_number(self: Value) bool { pub fn is_number(self: Value) bool {
return switch (self) { return switch (self) {
.double, .int32, .int64 => true, .double, .int32, .int64 => true,

View File

@@ -185,6 +185,81 @@ pub const Message = struct {
}; };
} }
/// A logical session id: a UUID, so 16 bytes of binary subtype 4.
pub const SessionId = [16]u8;
/// Every way an `lsid` can be malformed, named after what is wrong rather
/// than after the error the caller will send: the codes belong to the
/// command layer, which is where they were measured.
pub const LsidError = error{
LsidNotDocument,
LsidUnknownField,
LsidIdMissing,
LsidIdNotBinary,
LsidIdNotUuid,
LsidIdWrongLength,
LsidInternalSession,
LsidTxnNumberWithoutTxnUuid,
};
/// The logical session id, or null when the command carries no `lsid`.
///
/// An accessor over the command envelope, like `db_name`: called from
/// dispatch, never from `parse`, because a malformed session id is a
/// command that gets an error reply and not a connection that gets torn
/// down.
///
/// The fields it tolerates were measured against mongod 8.3.7, not
/// recalled, and the measurement contradicted the assumption it was
/// written on. mongod does *not* ignore unknown fields inside `lsid` --
/// it answers IDLUnknownField -- and it does accept `uid`, the hash of
/// the credentials that own the session, which a driver sends as soon as
/// authentication is on. Both matter to us: the first because tolerating
/// what the server rejects is the kind of divergence that only shows up
/// under a driver nobody tested, the second because M7 would otherwise
/// break every command.
pub fn lsid(self: *const Message) LsidError!?SessionId {
const v = bson.get_pair(self.body.pairs, "lsid") orelse return null;
const doc = switch (v) {
.doc => |d| d,
else => return error.LsidNotDocument,
};
var id: ?bson.Binary = null;
var has_txn_number = false;
var has_txn_uuid = false;
for (doc) |pair| {
if (std.mem.eql(u8, pair.key, "id")) {
id = switch (pair.value) {
.binary => |b| b,
else => return error.LsidIdNotBinary,
};
} else if (std.mem.eql(u8, pair.key, "uid")) {
// Accepted and ignored: it identifies the user a session
// belongs to, and this server has exactly one.
} else if (std.mem.eql(u8, pair.key, "txnNumber")) {
has_txn_number = true;
} else if (std.mem.eql(u8, pair.key, "txnUUID")) {
has_txn_uuid = true;
} else {
return error.LsidUnknownField;
}
}
// A `txnNumber` inside the session id is not the retryable-write one
// outside it: together with `txnUUID` the two name an *internal*
// session, which only exists to run a transaction on another
// session's behalf. Neither can mean anything here, and mongod
// refuses them on a standalone too.
if (has_txn_number and !has_txn_uuid) return error.LsidTxnNumberWithoutTxnUuid;
if (has_txn_uuid) return error.LsidInternalSession;
const bin = id orelse return error.LsidIdMissing;
if (bin.subtype != 4) return error.LsidIdNotUuid;
if (bin.data.len != 16) return error.LsidIdWrongLength;
return bin.data[0..16].*;
}
/// Documents of a batch argument (`documents`, `updates`, `deletes`). /// Documents of a batch argument (`documents`, `updates`, `deletes`).
/// Drivers send them either as an OP_MSG document sequence or as an array /// Drivers send them either as an OP_MSG document sequence or as an array
/// inside the command body; callers should not have to care which. The /// inside the command body; callers should not have to care which. The
@@ -378,6 +453,94 @@ test "reply serializes to a parseable message" {
try testing.expectEqual(@as(usize, 0), msg.seqs.len); try testing.expectEqual(@as(usize, 0), msg.seqs.len);
} }
/// An OP_MSG carrying one body section, for tests that care about the command
/// envelope rather than about framing.
fn fake_op_msg(gpa: std.mem.Allocator, pairs: []const bson.Pair) !Message {
var doc: std.ArrayListUnmanaged(u8) = .empty;
defer doc.deinit(gpa);
try bson.write_doc(pairs, gpa, &doc);
var buf: std.ArrayListUnmanaged(u8) = .empty;
defer buf.deinit(gpa);
try buf.appendSlice(gpa, &[_]u8{ 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0xDD, 0x07, 0, 0, 0, 0, 0, 0 });
try buf.append(gpa, 0x00);
try buf.appendSlice(gpa, doc.items);
std.mem.writeInt(u32, buf.items[0..4], @intCast(buf.items.len), .little);
return Message.parse(gpa, buf.items);
}
test "a session id is read out of a command" {
const uuid = [_]u8{0xAB} ** 16;
var msg = try fake_op_msg(testing.allocator, &.{
.{ .key = "ping", .value = .{ .int32 = 1 } },
.{ .key = "lsid", .value = .{ .doc = &.{
.{ .key = "id", .value = .{ .binary = .{ .subtype = 4, .data = &uuid } } },
} } },
});
defer msg.deinit();
try testing.expectEqual(uuid, (try msg.lsid()).?);
}
test "a command with no lsid has no session" {
var msg = try fake_op_msg(testing.allocator, &.{.{ .key = "ping", .value = .{ .int32 = 1 } }});
defer msg.deinit();
try testing.expect((try msg.lsid()) == null);
}
test "a session id carrying a user hash is still a session id" {
// `uid` arrives as soon as authentication is on (M7). Rejecting it as an
// unknown field would break every command the moment that lands, which is
// exactly the kind of divergence a measurement against a real server is
// for -- mongod 8.3.7 answers ok:1 to this.
const uuid = [_]u8{0x11} ** 16;
var msg = try fake_op_msg(testing.allocator, &.{
.{ .key = "ping", .value = .{ .int32 = 1 } },
.{ .key = "lsid", .value = .{ .doc = &.{
.{ .key = "id", .value = .{ .binary = .{ .subtype = 4, .data = &uuid } } },
.{ .key = "uid", .value = .{ .binary = .{ .subtype = 0, .data = &[_]u8{0} ** 32 } } },
} } },
});
defer msg.deinit();
try testing.expectEqual(uuid, (try msg.lsid()).?);
}
test "every malformed session id is named" {
const uuid = [_]u8{0x22} ** 16;
const cases = [_]struct { want: anyerror, lsid: bson.Value }{
.{ .want = error.LsidNotDocument, .lsid = .{ .int32 = 5 } },
.{ .want = error.LsidIdMissing, .lsid = .{ .doc = &.{} } },
.{ .want = error.LsidIdNotBinary, .lsid = .{ .doc = &.{
.{ .key = "id", .value = .{ .string = "nope" } },
} } },
.{ .want = error.LsidIdNotUuid, .lsid = .{ .doc = &.{
.{ .key = "id", .value = .{ .binary = .{ .subtype = 0, .data = &uuid } } },
} } },
.{ .want = error.LsidIdWrongLength, .lsid = .{ .doc = &.{
.{ .key = "id", .value = .{ .binary = .{ .subtype = 4, .data = uuid[0..15] } } },
} } },
.{ .want = error.LsidUnknownField, .lsid = .{ .doc = &.{
.{ .key = "id", .value = .{ .binary = .{ .subtype = 4, .data = &uuid } } },
.{ .key = "bogus", .value = .{ .int32 = 1 } },
} } },
.{ .want = error.LsidTxnNumberWithoutTxnUuid, .lsid = .{ .doc = &.{
.{ .key = "id", .value = .{ .binary = .{ .subtype = 4, .data = &uuid } } },
.{ .key = "txnNumber", .value = .{ .int64 = 1 } },
} } },
.{ .want = error.LsidInternalSession, .lsid = .{ .doc = &.{
.{ .key = "id", .value = .{ .binary = .{ .subtype = 4, .data = &uuid } } },
.{ .key = "txnUUID", .value = .{ .binary = .{ .subtype = 4, .data = &uuid } } },
} } },
};
for (cases) |c| {
var msg = try fake_op_msg(testing.allocator, &.{
.{ .key = "ping", .value = .{ .int32 = 1 } },
.{ .key = "lsid", .value = c.lsid },
});
defer msg.deinit();
try testing.expectError(c.want, msg.lsid());
}
}
test "reject non-OP_MSG non-OP_QUERY opcodes" { test "reject non-OP_MSG non-OP_QUERY opcodes" {
var buf: [20]u8 = undefined; var buf: [20]u8 = undefined;
std.mem.writeInt(u32, buf[0..4], 20, .little); std.mem.writeInt(u32, buf[0..4], 20, .little);