PranitInitial commit439f83c2 months agoHistory
596 lines · 20.3 KB · Zig
RawDownload
const std = @import("std");const io_mod = @import("../shared/io.zig");const session_codec = @import("session_codec.zig");const session_log = @import("session_log.zig");const Allocator = std.mem.Allocator; const discovery = @import("session_discovery.zig");const paths = @import("session_store_paths.zig");const summary_codec = @import("session_summary_codec.zig");const store_types = @import("session_store_types.zig"); const validateSessionId = paths.validateSessionId;const SessionSummary = store_types.SessionSummary;const summaryFromState = discovery.summaryFromState;const freeSummaries = summary_codec.freeSummaries;const readSessionIndex = summary_codec.readSessionIndex;const removeSessionIndexMarker = summary_codec.removeSessionIndexMarker;const replaceIndexedSummary = summary_codec.replaceIndexedSummary;const writeSessionIndex = summary_codec.writeSessionIndex;const writeSessionIndexMarker = summary_codec.writeSessionIndexMarker; /// Two-phase state of a workspace latest pointer on disk: `pending` is written/// under lock before commit, `ready` after the session is durable.pub const PointerStatus = enum { pending, ready }; pub const LatestPointer = struct {    session_id: []u8,    updated_at_ms: i64,     pub fn deinit(self: *LatestPointer, alloc: Allocator) void {        alloc.free(self.session_id);        self.* = undefined;    }}; pub const InitialIndexEffect = union(enum) {    maintain,    preserve,    replace_latest_if_current: ConditionalLatestReplacement,}; pub const LatestCandidate = struct {    session_id: []const u8,    updated_at_ms: i64,}; pub const ConditionalLatestReplacement = struct {    expected_current_id: []const u8,    discovered_latest: ?LatestCandidate,    observed_snapshot: LatestSnapshotToken,}; pub const LatestSnapshotToken = union(enum) {    missing,    file: struct {        inode: u128,        size: u64,        mtime_ns: i96,        ctime_ns: i96,    },}; const LatestPointerJson = struct {    schema_version: i64,    status: []const u8,    workspace_root: []const u8,    session_id: ?[]const u8 = null,    updated_at_ms: ?i64 = null,}; pub const LatestCache = struct {    sessions: io_mod.VerifiedDir,    test_controls: session_log.TestControls,    cache_lock: ?io_mod.TimedAdvisoryLock = null,    pending_workspace_roots: std.ArrayList([]u8) = .empty,    prior_ready: ?LatestPointer = null,    prior_index: ?std.ArrayList(SessionSummary) = null,    next_index_effect: InitialIndexEffect,     pub fn init(        alloc: Allocator,        sessions: *const io_mod.VerifiedDir,        test_controls: session_log.TestControls,        initial_index_effect: InitialIndexEffect,    ) !*LatestCache {        const owned_sessions = try sessions.dir.openDir(io_mod.getIo(), ".", .{            .iterate = true,            .follow_symlinks = false,        });        errdefer owned_sessions.close(io_mod.getIo());        const result = try alloc.create(LatestCache);        result.* = .{            .sessions = .{ .dir = owned_sessions },            .test_controls = test_controls,            .next_index_effect = initial_index_effect,        };        return result;    }     pub fn deinit(self: *LatestCache, alloc: Allocator) void {        self.abort();        self.clearPreparedState(alloc);        self.sessions.close();        alloc.destroy(self);    }     fn clearPreparedState(self: *LatestCache, alloc: Allocator) void {        for (self.pending_workspace_roots.items) |workspace_root| alloc.free(workspace_root);        self.pending_workspace_roots.deinit(alloc);        self.pending_workspace_roots = .empty;        if (self.prior_ready) |*pointer| pointer.deinit(alloc);        self.prior_ready = null;        if (self.prior_index) |*index| freeSummaries(alloc, index);        self.prior_index = null;    }     pub fn begin(        self: *LatestCache,        alloc: Allocator,        session_id: []const u8,        previous_workspace_root: []const u8,        next_workspace_root: []const u8,        commit_lock_deadline_ms: u64,    ) !void {        if (self.cache_lock == null) {            self.cache_lock = io_mod.acquireTimedAdvisoryLock(                &self.sessions,                latest_sessions_lock_file,                commit_lock_deadline_ms,            ) catch |err| return switch (err) {                error.LockBusy, error.LockUnsupported => error.SessionCommitBoundaryUnavailable,                else => err,            };        }        errdefer {            self.abort();            self.clearPreparedState(alloc);        }        switch (self.next_index_effect) {            .replace_latest_if_current => |replacement| {                const current_snapshot = try readLatestSnapshotToken(                    &self.sessions,                    next_workspace_root,                );                if (!std.meta.eql(                    current_snapshot,                    replacement.observed_snapshot,                )) {                    return error.SessionLatestBaselineChanged;                }            },            .maintain, .preserve => {},        }        if (self.prior_ready == null) {            self.prior_ready = readLatestPointerFromSessions(                &self.sessions,                alloc,                next_workspace_root,            ) catch |err| switch (err) {                error.OutOfMemory => return error.OutOfMemory,                else => null,            };        }        switch (self.next_index_effect) {            .preserve => {},            .maintain, .replace_latest_if_current => {                if (self.prior_index == null) {                    self.prior_index = readSessionIndex(alloc, &self.sessions) catch |err| switch (err) {                        error.OutOfMemory => return error.OutOfMemory,                        else => null,                    };                }                try writeSessionIndexMarker(alloc, &self.sessions);            },        }        try self.rememberPendingWorkspace(alloc, previous_workspace_root);        try self.rememberPendingWorkspace(alloc, next_workspace_root);        for (self.pending_workspace_roots.items) |workspace_root| {            try self.writePointer(alloc, workspace_root, .pending, session_id, null);        }        try self.test_controls.boundary(.after_latest_cache_pending);    }     pub fn publish(        self: *LatestCache,        alloc: Allocator,        state: session_codec.DurableSessionState,    ) !void {        defer self.releaseLock();        defer self.clearPreparedState(alloc);        const index_effect = self.next_index_effect;        self.next_index_effect = .maintain;        try self.test_controls.boundary(.before_latest_cache_ready);        var prior_candidate: ?LatestCandidate = if (self.prior_ready) |prior|            .{                .session_id = prior.session_id,                .updated_at_ms = prior.updated_at_ms,            }        else            null;        switch (index_effect) {            .replace_latest_if_current => |replacement| {                if (replacement.discovered_latest) |discovered| {                    if (prior_candidate == null or latestCandidateNewer(                        discovered,                        prior_candidate.?,                    )) {                        prior_candidate = discovered;                    }                }            },            .maintain, .preserve => {},        }        if (prior_candidate) |prior| {            const replace_current = switch (index_effect) {                .replace_latest_if_current => |replacement| std.mem.eql(                    u8,                    prior.session_id,                    replacement.expected_current_id,                ) or                    std.mem.eql(u8, prior.session_id, state.id),                .maintain, .preserve => false,            };            if (!replace_current and latestCandidateNewerThanState(prior, state)) {                try self.writePointer(                    alloc,                    state.workspace_root,                    .ready,                    prior.session_id,                    prior.updated_at_ms,                );            } else {                try self.writePointer(                    alloc,                    state.workspace_root,                    .ready,                    state.id,                    state.updated_at_ms,                );            }        } else {            try self.writePointer(                alloc,                state.workspace_root,                .ready,                state.id,                state.updated_at_ms,            );        }        switch (index_effect) {            .preserve => return,            .maintain, .replace_latest_if_current => {},        }        if (self.prior_index) |*index| {            var summary = try summaryFromState(alloc, state);            var summary_owned = true;            errdefer if (summary_owned) summary.deinit(alloc);            try summary_codec.preserveIndexedDisplayMetadata(alloc, index.items, &summary);            try replaceIndexedSummary(alloc, index, &summary);            summary_owned = false;            try writeSessionIndex(alloc, &self.sessions, index.items);            try removeSessionIndexMarker(&self.sessions);        }    }     fn abort(self: *LatestCache) void {        self.releaseLock();    }     fn releaseLock(self: *LatestCache) void {        if (self.cache_lock) |*lock| lock.release();        self.cache_lock = null;    }     fn rememberPendingWorkspace(        self: *LatestCache,        alloc: Allocator,        workspace_root: []const u8,    ) !void {        for (self.pending_workspace_roots.items) |existing| {            if (std.mem.eql(u8, existing, workspace_root)) return;        }        const owned_workspace_root = try alloc.dupe(u8, workspace_root);        errdefer alloc.free(owned_workspace_root);        try self.pending_workspace_roots.append(alloc, owned_workspace_root);    }     fn writePointer(        self: *LatestCache,        alloc: Allocator,        workspace_root: []const u8,        status: PointerStatus,        session_id: []const u8,        updated_at_ms: ?i64,    ) !void {        var latest = try io_mod.openOrCreateVerifiedPrivateDir(            &self.sessions,            latest_sessions_dir,        );        defer latest.close();        var name: [69]u8 = undefined;        const filename = latestPointerFilename(workspace_root, &name);        const text = try encodeLatestPointer(            alloc,            workspace_root,            status,            session_id,            updated_at_ms,        );        defer alloc.free(text);        try io_mod.durableReplaceVerified(alloc, &latest, filename, text);    }}; pub const latest_sessions_dir = "latest"; pub const latest_sessions_lock_file = "latest.lock"; const latest_pointer_schema_version: i64 = 2; const max_latest_pointer_bytes: usize = 8 * 1024; pub fn readLatestSnapshotToken(    sessions: *const io_mod.VerifiedDir,    workspace_root: []const u8,) !LatestSnapshotToken {    var latest = sessions.dir.openDir(io_mod.getIo(), latest_sessions_dir, .{        .iterate = true,        .follow_symlinks = false,    }) catch |err| switch (err) {        error.FileNotFound => return .missing,        error.NotDir, error.SymLinkLoop => return error.SessionPathUnsafe,        else => return err,    };    defer latest.close(io_mod.getIo());     var name: [69]u8 = undefined;    var file = latest.openFile(        io_mod.getIo(),        latestPointerFilename(workspace_root, &name),        .{            .mode = .read_only,            .allow_directory = false,            .follow_symlinks = false,            .resolve_beneath = true,        },    ) catch |err| switch (err) {        error.FileNotFound => return .missing,        error.NotDir, error.SymLinkLoop, error.IsDir => {            return error.InvalidSessionIndex;        },        else => return err,    };    defer file.close(io_mod.getIo());    const stat = try file.stat(io_mod.getIo());    if (stat.kind != .file or stat.nlink != 1) {        return error.InvalidSessionIndex;    }    return .{ .file = .{        .inode = @intCast(stat.inode),        .size = stat.size,        .mtime_ns = stat.mtime.toNanoseconds(),        .ctime_ns = stat.ctime.toNanoseconds(),    } };} fn latestCandidateNewerThanState(    candidate: LatestCandidate,    state: session_codec.DurableSessionState,) bool {    return latestCandidateNewer(candidate, .{        .session_id = state.id,        .updated_at_ms = state.updated_at_ms,    });} fn latestCandidateNewer(    left: LatestCandidate,    right: LatestCandidate,) bool {    if (left.updated_at_ms != right.updated_at_ms) {        return left.updated_at_ms > right.updated_at_ms;    }    return std.mem.order(u8, left.session_id, right.session_id) == .gt;} /// Commit-lifecycle `prepare` thunk: recovers the `LatestCache` from the opaque/// context and begins the pending phase for the previous/next workspace roots.pub fn latestCachePrepare(    raw: ?*anyopaque,    alloc: Allocator,    session_id: []const u8,    previous_workspace_root: []const u8,    next_workspace_root: []const u8,    commit_lock_deadline_ms: u64,) !void {    const cache: *LatestCache = @ptrCast(@alignCast(raw orelse return error.SessionStoreUnavailable));    try cache.begin(        alloc,        session_id,        previous_workspace_root,        next_workspace_root,        commit_lock_deadline_ms,    );} /// Commit-lifecycle `publish` thunk: writes the ready pointer for the committed state.pub fn latestCachePublish(    raw: ?*anyopaque,    alloc: Allocator,    state: session_codec.DurableSessionState,) !void {    const cache: *LatestCache = @ptrCast(@alignCast(raw orelse return error.SessionStoreUnavailable));    try cache.publish(alloc, state);} /// Commit-lifecycle `abort` thunk: releases the cache lock without publishing.pub fn latestCacheAbort(raw: ?*anyopaque) void {    const cache: *LatestCache = @ptrCast(@alignCast(raw orelse return));    cache.abort();} /// Commit-lifecycle `deinit` thunk: aborts if needed and frees the cache.pub fn latestCacheDeinit(raw: ?*anyopaque, alloc: Allocator) void {    const cache: *LatestCache = @ptrCast(@alignCast(raw orelse return));    cache.deinit(alloc);} /// Writes the deterministic pointer filename (sha256(workspace_root) hex +/// ".json") into `output` and returns the slice.fn latestPointerFilename(workspace_root: []const u8, output: *[69]u8) []const u8 {    var digest: [32]u8 = undefined;    std.crypto.hash.sha2.Sha256.hash(workspace_root, &digest, .{});    const hex = std.fmt.bytesToHex(digest, .lower);    @memcpy(output[0..64], &hex);    @memcpy(output[64..], ".json");    return output[0..];} fn encodeLatestPointer(    alloc: Allocator,    workspace_root: []const u8,    status: PointerStatus,    session_id: []const u8,    updated_at_ms: ?i64,) ![]u8 {    var out: std.Io.Writer.Allocating = .init(alloc);    defer out.deinit();    try out.writer.print("{{\"schema_version\":{d},\"status\":", .{latest_pointer_schema_version});    try std.json.Stringify.value(@tagName(status), .{}, &out.writer);    try out.writer.writeAll(",\"workspace_root\":");    try std.json.Stringify.value(workspace_root, .{}, &out.writer);    try out.writer.writeAll(",\"session_id\":");    try std.json.Stringify.value(session_id, .{}, &out.writer);    try out.writer.writeAll(",\"updated_at_ms\":");    if (updated_at_ms) |value| {        try out.writer.print("{d}", .{value});    } else {        try out.writer.writeAll("null");    }    try out.writer.writeByte('}');    return try out.toOwnedSlice();} /// Reads the ready pointer for `workspace_root` from the `latest/` directory./// Returns null when no pointer exists; `error.InvalidSessionIndex` on a/// malformed or non-ready file. Caller owns the returned pointer.pub fn readLatestPointerFromSessions(    sessions: *const io_mod.VerifiedDir,    alloc: Allocator,    workspace_root: []const u8,) !?LatestPointer {    var parsed = (try readLatestPointerJsonFromSessions(        sessions,        alloc,        workspace_root,    )) orelse return null;    defer parsed.deinit();    if (!std.mem.eql(u8, parsed.value.status, "ready")) {        return error.InvalidSessionIndex;    }    const session_id = parsed.value.session_id orelse return error.InvalidSessionIndex;    const updated_at_ms = parsed.value.updated_at_ms orelse return error.InvalidSessionIndex;    try validateSessionId(session_id);    return .{        .session_id = try alloc.dupe(u8, session_id),        .updated_at_ms = updated_at_ms,    };} /// Reads an owned pending session ID for `workspace_root`. Returns null when/// no pointer exists or the pointer is ready.pub fn readPendingLatestSessionIdFromSessions(    sessions: *const io_mod.VerifiedDir,    alloc: Allocator,    workspace_root: []const u8,) !?[]u8 {    var parsed = (try readLatestPointerJsonFromSessions(        sessions,        alloc,

Lines 1–500 of 596.