╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1275 outcome = @splat(.kept); │ │1276 merged = target_record; │ │1277 merged.event = mergeFields(T, if (baseline_record) |baseline| baseline.event else null, target_record.event, parent_record.event, &outcome);│ │1278 + merged.updated_order = @max(target_record.updated_order, parent_record.updated_order); │ │1279 if (comptime @hasDecl(T, "resolveMerge")) { │ │1280 T.resolveMerge(target_record.event, parent_record.event, &merged.event, &outcome); │ │1281 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1463 return orderKey(std.math.maxInt(u64) - order, event_id); │ │1464 } │ │1465 │ │1459 -// add or refresh one entry in the global logical-event index. updates and │ │1460 -// removed records preserve created_order, so the key never moves. │ │1466 +pub fn removedRecord( │ │1467 + comptime T: type, │ │1468 + comptime DB: type, │ │1469 + comptime hash_kind: hash.HashKind, │ │1470 + haxy_moment: DB.HashMap(.read_only), │ │1471 + existing_maybe: ?T, │ │1472 +) !T { │ │1473 + var record = existing_maybe orelse return error.EventNotFound; │ │1474 + record.removed = true; │ │1475 + const order_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, event_order_key)) orelse return error.CursorNotFound;│ │1476 + record.updated_order = try order_cursor.readUint(); │ │1477 + return record; │ │1478 +} │ │1479 + │ │1480 +// add or refresh one entry in the global logical-event index │ │1481 pub fn indexEvent( │ │1482 comptime DB: type, │ │1483 comptime hash_kind: hash.HashKind, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1484 haxy_moment: DB.HashMap(.read_write), │ │1485 event_id: *const [event_id_size]u8, │ │1486 kind: EventKind, │ │1467 - created_order: u64, │ │1468 - removed: bool, │ │1487 + existing_maybe: anytype, │ │1488 + record: anytype, │ │1489 ) !void { │ │1490 + const event_key = hash.hashInt(hash_kind, event_id); │ │1491 const kind_map_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, event_index_key)); │ │1492 const kind_map = try DB.HashMap(.read_write).init(kind_map_cursor); │ │1472 - try kind_map.put(hash.hashInt(hash_kind, event_id), .{ .bytes = @tagName(kind) }); │ │1493 + try kind_map.put(event_key, .{ .bytes = @tagName(kind) }); │ │1494 │ │1495 const active_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, active_event_id_set_key));│ │1496 const active = try DB.SortedSet(.read_write).init(active_cursor); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1497 const removed_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, removed_event_id_set_key));│ │1498 const removed_set = try DB.SortedSet(.read_write).init(removed_cursor); │ │1478 - const order_key = orderKeyDesc(created_order, event_id); │ │1479 - if (removed) { │ │1480 - _ = try active.remove(&order_key); │ │1499 + │ │1500 + if (existing_maybe) |existing| { │ │1501 + const old_key = orderKeyDesc(existing.updated_order, event_id); │ │1502 + _ = try active.remove(&old_key); │ │1503 + _ = try removed_set.remove(&old_key); │ │1504 + } │ │1505 + │ │1506 + const order_key = orderKeyDesc(record.updated_order, event_id); │ │1507 + if (record.removed) { │ │1508 try removed_set.put(&order_key); │ │1509 } else { │ │1483 - _ = try removed_set.remove(&order_key); │ │1510 try active.put(&order_key); │ │1511 } │ │1512 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───────────────╮ │src/event/Attachment.zig│ ╰────────────────────────╯ ╭─────────────────────────────────────────╮ │14 removed: bool = false, │ │15 author_email: ?[]const u8 = null,│ │16 created_order: u64 = 0, │ │17 + updated_order: u64 = 0, │ │18 name: []const u8, │ │19 blob_oid: []const u8, │ │20 }; │ ╰─────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │77 existing_record_maybe = try evt.read(Record, DB, hash_kind, arena, existing_attachment); │ │78 } │ │79 │ │79 - var record_to_write = if (record_maybe) |record| │ │80 - record │ │81 - else blk: { │ │82 - var record = existing_record_maybe orelse return error.EventNotFound; │ │83 - record.removed = true; │ │84 - break :blk record; │ │85 - }; │ │80 + var record_to_write = record_maybe orelse try evt.removedRecord(Record, DB, hash_kind, haxy_moment.readOnly(), existing_record_maybe);│ │81 │ │82 const id_bytes = try evt.parseEventId(&record_to_write.event.parent_id); │ │83 if (!try parentExists(DB, hash_kind, haxy_moment, &id_bytes)) return error.ParentNotFound; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 98 const attachment_cursor = try event_id_to_attachment.putCursor(attachment_key); │ │ 99 const attachment = try DB.HashMap(.read_write).init(attachment_cursor); │ │100 try evt.upsert(Record, DB, hash_kind, attachment, record_to_write); │ │106 - try evt.indexEvent(DB, hash_kind, haxy_moment, event_id, .attach, record_to_write.created_order, record_to_write.removed);│ │101 + try evt.indexEvent(DB, hash_kind, haxy_moment, event_id, .attach, existing_record_maybe, record_to_write); │ │102 │ │103 if (existing_cursor_maybe == null) { │ │104 const attachment_id_set_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, id_set_key)); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────────╮ │src/event/Comment.zig│ ╰─────────────────────╯ ╭─────────────────────────────────────────╮ │14 removed: bool = false, │ │15 author_email: ?[]const u8 = null,│ │16 created_order: u64 = 0, │ │17 + updated_order: u64 = 0, │ │18 }; │ │19 │ │20 const Self = @This(); │ ╰─────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │59 existing_record_maybe = try evt.read(Record, DB, hash_kind, arena, existing_comment); │ │60 } │ │61 │ │61 - var record_to_write = if (record_maybe) |record| │ │62 - record │ │63 - else blk: { │ │64 - var record = existing_record_maybe orelse return error.EventNotFound; │ │65 - record.removed = true; │ │66 - break :blk record; │ │67 - }; │ │62 + var record_to_write = record_maybe orelse try evt.removedRecord(Record, DB, hash_kind, haxy_moment.readOnly(), existing_record_maybe);│ │63 │ │64 const thread_id = try evt.parseEventId(&record_to_write.event.thread_id); │ │65 _ = try evt.parseEventId(&record_to_write.event.parent_id); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │77 const comment_cursor = try event_id_to_comment.putCursor(comment_key); │ │78 const comment = try DB.HashMap(.read_write).init(comment_cursor); │ │79 try evt.upsert(Record, DB, hash_kind, comment, record_to_write); │ │85 - try evt.indexEvent(DB, hash_kind, haxy_moment, event_id, .comment, record_to_write.created_order, record_to_write.removed);│ │80 + try evt.indexEvent(DB, hash_kind, haxy_moment, event_id, .comment, existing_record_maybe, record_to_write); │ │81 │ │82 if (existing_cursor_maybe == null) { │ │83 const comment_id_set_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, id_set_key)); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯