╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │79 ) !void { │ │80 const Ctx = struct { │ │81 repo_events: []RepoEvent(hash_kind), │ │82 - history_count: u64, │ │82 │ │83 pub fn run(ctx: @This(), cursor: *DB.Cursor(.read_write)) !void {│ │84 const moment = try DB.HashMap(.read_write).init(cursor.*); │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │86 - │ │87 - // the map with all of haxy's state and materialized views │ │88 - const haxy_cursor = try moment.putCursor(hash.hashInt(hash_kind, "haxy")); │ │89 - const haxy = try DB.HashMap(.read_write).init(haxy_cursor); │ │85 │ │91 - // try reading the last object id that was consumed │ │86 + // the last_object_id represents the object id that was last consumed │ │87 var last_object_id_maybe: ?[hash.byteLen(hash_kind)]u8 = null; │ │93 - if (try haxy.getCursor(hash.hashInt(hash_kind, "last-object-id"))) |last_object_id_cursor| { │ │88 + if (try moment.getCursor(hash.hashInt(hash_kind, "haxy-last-object-id"))) |last_object_id_cursor| {│ │89 var last_object_id_buffer: [hash.byteLen(hash_kind)]u8 = undefined; │ │90 _ = try last_object_id_cursor.readBytes(&last_object_id_buffer); │ │91 last_object_id_maybe = last_object_id_buffer; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 92 } │ │ 93 │ │ 99 - // for each event we want to consume... │ │100 - for (ctx.repo_events) |repo_event| { │ │101 - // map that associates this oid with the current transaction id. │ │102 - // this will be important for reverting things when a rebase occurrs. │ │103 - var object_id_to_tx_id_cursor = try haxy.putCursor(hash.hashInt(hash_kind, "object-id->tx-id")); │ │ 94 + // the list with all of haxy's state, including materialized views. │ │ 95 + // the reason it is a list is so we can keep every previous haxy │ │ 96 + // state, making it easy to revert to an older state if the user │ │ 97 + // rebases some of the past commits. │ │ 98 + const haxy_cursor = try moment.putCursor(hash.hashInt(hash_kind, "haxy")); │ │ 99 + const haxy = try DB.ArrayList(.read_write).init(haxy_cursor); │ │100 │ │105 - // map with the views as they appeared when each event was consumed. │ │106 - // we can use this to see (and revert) the views to any previous state. │ │107 - var object_id_to_views_cursor = try haxy.putCursor(hash.hashInt(hash_kind, "object-id->views")); │ │101 + // add a new item to the haxy list created above. │ │102 + // we call the item haxy_moments. it is a map of object id to haxy moment. │ │103 + // in other words, it maps each object id to a hash map containing the │ │104 + // state that the database was in when that object id was consumed. │ │105 + var haxy_moments_cursor = try haxy.appendCursor(); │ │106 + // use the previous haxy_moments as the basis for this one if it exists │ │107 + if (try haxy.getCursor(-2)) |last_haxy_moments_cursor| { │ │108 + try haxy_moments_cursor.write(.{ .slot = last_haxy_moments_cursor.slot() }); │ │109 + } │ │110 + var haxy_moments = try DB.HashMap(.read_write).init(haxy_moments_cursor); │ │111 │ │112 + // for each event we want to consume... │ │113 + for (ctx.repo_events) |repo_event| { │ │114 // if this object id has already been consumed, skip it │ │110 - { │ │111 - const object_id_to_views = try DB.HashMap(.read_only).init(object_id_to_views_cursor.readOnly());│ │112 - if (null != try object_id_to_views.getCursor(hash.bytesToInt(hash_kind, &repo_event.oid))) { │ │113 - continue; │ │114 - } │ │115 + if (null != try haxy_moments.getCursor(hash.bytesToInt(hash_kind, &repo_event.oid))) { │ │116 + continue; │ │117 } │ │118 │ │117 - // compare last-object-id to the event's parent. this is important │ │119 + // compare last_object_id to the event's parent. this is important │ │120 // in situations where the branch was rebased, because in that case │ │119 - // the last-object-id may no longer be valid. │ │121 + // the last_object_id may no longer be valid. │ │122 if (last_object_id_maybe) |*last_object_id| { │ │123 if (repo_event.parent_oid) |*parent_oid| { │ │124 if (!std.mem.eql(u8, last_object_id, parent_oid)) { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │123 - // the last-object-id does not match the current event's parent id. │ │125 + // the last_object_id does not match the current event's parent id. │ │126 // this means that a rebase occurred. we just need to revert the │ │125 - // object-id maps to the state they were in when parent-oid was consumed. │ │127 + // haxy list to the state it was in what parent_oid was consumed, │ │128 + // and then create a new haxy_moments map to work with. │ │129 │ │127 - const object_id_to_tx_id = try DB.HashMap(.read_only).init(object_id_to_tx_id_cursor.readOnly()); │ │128 - const tx_id_cursor = try object_id_to_tx_id.getCursor(hash.bytesToInt(hash_kind, parent_oid)) orelse return error.ObjectNotFound; │ │129 - const tx_id = try tx_id_cursor.readUint(); │ │130 - const history = try DB.ArrayList(.read_only).init(cursor.db.rootCursor().readOnly()); │ │131 - │ │132 - const old_moment_cursor = try history.getCursor(tx_id) orelse return error.TransactionNotFound; │ │130 + const old_moment_cursor = try haxy_moments.getCursor(hash.bytesToInt(hash_kind, parent_oid)) orelse return error.CursorNotFound; │ │131 const old_moment = try DB.HashMap(.read_only).init(old_moment_cursor); │ │132 │ │135 - const old_haxy_cursor = try old_moment.getCursor(hash.hashInt(hash_kind, "haxy")) orelse return error.CursorNotFound; │ │136 - const old_haxy = try DB.HashMap(.read_only).init(old_haxy_cursor); │ │133 + const old_moment_index_cursor = try old_moment.getCursor(hash.hashInt(hash_kind, "index")) orelse return error.CursorNotFound; │ │134 + const old_moment_index = try old_moment_index_cursor.readUint(); │ │135 │ │138 - const old_object_id_to_tx_id_cursor = try old_haxy.getCursor(hash.hashInt(hash_kind, "object-id->tx-id")) orelse return error.CursorNotFound;│ │139 - const old_object_id_to_views_cursor = try old_haxy.getCursor(hash.hashInt(hash_kind, "object-id->views")) orelse return error.CursorNotFound;│ │136 + // resize the haxy list so we truncate all the moments that were │ │137 + // created after the parent_oid was consumed │ │138 + try haxy.slice(old_moment_index + 1); │ │139 │ │141 - try object_id_to_tx_id_cursor.write(.{ .slot = old_object_id_to_tx_id_cursor.slot() }); │ │142 - try object_id_to_views_cursor.write(.{ .slot = old_object_id_to_views_cursor.slot() }); │ │140 + // make a new haxy moment and set its initial value to the last haxy moment │ │141 + haxy_moments_cursor = try haxy.appendCursor(); │ │142 + const old_haxy_moments_cursor = try haxy.getCursor(old_moment_index) orelse return error.CursorNotFound; │ │143 + try haxy_moments_cursor.write(.{ .slot = old_haxy_moments_cursor.slot() }); │ │144 + haxy_moments = try DB.HashMap(.read_write).init(haxy_moments_cursor); │ │145 │ │146 last_object_id_maybe = parent_oid.*; │ │147 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │148 } else { │ │149 // the branch was rebased all the way to the very beginning. │ │150 // we have a repo event with no parent, which means it is now │ │149 - // the very first event. all we need to do is set the │ │150 - // object-id maps to be empty. │ │151 - try object_id_to_tx_id_cursor.write(.{ .slot = null }); │ │152 - try object_id_to_views_cursor.write(.{ .slot = null }); │ │151 + // the very first event. all we need to do is set the haxy list │ │152 + // to be empty and make a new haxy_moments map to work with. │ │153 + │ │154 + try haxy.slice(0); │ │155 + haxy_moments_cursor = try haxy.appendCursor(); │ │156 + haxy_moments = try DB.HashMap(.read_write).init(haxy_moments_cursor);│ │157 + │ │158 + last_object_id_maybe = null; │ │159 } │ │160 } else { │ │161 if (repo_event.parent_oid) |_| { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────╮ │156 - // there is no last-object-id, but this event has a parent. │ │162 + // there is no last_object_id, but this event has a parent. │ │163 // this is an invalid state. if the event has a parent, that│ │164 // implies that an event has already been processed, but │ │159 - // if that's true then there would be a last-object-id. │ │165 + // if that's true then there would be a last_object_id. │ │166 return error.UnexpectedParent; │ │167 } │ │168 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │169 │ │164 - // associate this object id with this transaction id │ │165 - const object_id_to_tx_id = try DB.HashMap(.read_write).init(object_id_to_tx_id_cursor); │ │166 - try object_id_to_tx_id.put(hash.bytesToInt(hash_kind, &repo_event.oid), .{ .uint = ctx.history_count }); │ │167 - │ │168 - // init the object-id->views map │ │169 - const object_id_to_views = try DB.HashMap(.read_write).init(object_id_to_views_cursor); │ │170 + // create a moment for this object id │ │171 + var haxy_moment_cursor = try haxy_moments.putCursor(hash.bytesToInt(hash_kind, &repo_event.oid)); │ │172 │ │171 - // create a new views map for the current event we are consuming │ │172 - var views_cursor = try object_id_to_views.putCursor(hash.bytesToInt(hash_kind, &repo_event.oid)); │ │173 - │ │174 - // if there was a previous event, set the views map to have the same value as it. │ │173 + // if there was a previous object id, make this haxy moment's initial value to it. │ │174 // this efficiently "clones" the map so we make further modifications based on it. │ │175 if (last_object_id_maybe) |*last_object_id| { │ │177 - if (try object_id_to_views.getCursor(hash.bytesToInt(hash_kind, last_object_id))) |last_view_cursor| { │ │178 - try views_cursor.write(.{ .slot = last_view_cursor.slot() }); │ │176 + if (try haxy_moments.getCursor(hash.bytesToInt(hash_kind, last_object_id))) |last_haxy_moment_cursor| {│ │177 + try haxy_moment_cursor.write(.{ .slot = last_haxy_moment_cursor.slot() }); │ │178 } │ │179 } │ │180 + │ │181 + const haxy_moment = try DB.HashMap(.read_write).init(haxy_moment_cursor); │ │182 + │ │183 + // associate this moment with the index it will first appear at in the haxy list. │ │184 + // this will be important later so we can truncate that list if the user ever │ │185 + // rebases starting at this object id. │ │186 + try haxy_moment.put(hash.hashInt(hash_kind, "index"), .{ .uint = try haxy.count() - 1 }); │ │187 │ │188 // consume the event into the views map │ │189 { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │193 var current_event_id: [event_id_size]u8 = undefined; │ │194 _ = try std.fmt.hexToBytes(&current_event_id, &event.id); │ │195 │ │196 + // create a new views map for the current event we are consuming │ │197 + const views_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, "views"));│ │198 const views = try DB.HashMap(.read_write).init(views_cursor); │ │199 │ │200 switch (event.data) { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │218 } │ │219 │ │220 if (last_object_id_maybe) |*last_object_id| { │ │213 - try haxy.put(hash.hashInt(hash_kind, "last-object-id"), .{ .bytes = last_object_id }); │ │221 + try moment.put(hash.hashInt(hash_kind, "haxy-last-object-id"), .{ .bytes = last_object_id });│ │222 } │ │223 } │ │224 }; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │228 │ │229 // create a new transaction in the database that runs the above-defined Ctx function │ │230 const history = try DB.ArrayList(.read_write).init(db.rootCursor()); │ │223 - try history.appendContext(.{ .slot = try history.getSlot(-1) }, Ctx{ │ │224 - .repo_events = repo_events, │ │225 - .history_count = try history.count(), │ │226 - }); │ │231 + try history.appendContext(.{ .slot = try history.getSlot(-1) }, Ctx{ .repo_events = repo_events });│ │232 } │ │233 │ │234 fn upsert( │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯