Skip to content

Commit 1b5612b

Browse files
committed
perf(fast-inbox): roll back inbox messages by bucket, not by message (A-1388)
Finding the cut point for an L1-block rollback walked the messages backwards from the tip, deserializing every message being removed just to learn where the removal starts. A bucket lives entirely within one L1 block and its snapshot now records that block plus the bucket's first message index, so scan the bucket snapshots instead — a few records per L1 block rather than one per message.
1 parent 825cb9b commit 1b5612b

2 files changed

Lines changed: 32 additions & 4 deletions

File tree

yarn-project/archiver/src/store/message_store.test.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,11 +225,14 @@ describe('MessageStore', () => {
225225
describe('Inbox buckets', () => {
226226
// Builds `count` consecutive valid messages, then reassigns their bucket sequence and timestamp per the given
227227
// per-message spec so we can exercise multi-message and rollover buckets.
228-
const makeBucketedMessages = (spec: { seq: bigint; timestamp: bigint }[]): InboxMessage[] => {
228+
const makeBucketedMessages = (
229+
spec: { seq: bigint; timestamp: bigint; l1BlockNumber?: bigint }[],
230+
): InboxMessage[] => {
229231
const msgs = makeInboxMessages(spec.length);
230232
msgs.forEach((msg, i) => {
231233
msg.bucketSeq = spec[i].seq;
232234
msg.bucketTimestamp = spec[i].timestamp;
235+
msg.l1BlockNumber = spec[i].l1BlockNumber ?? msg.l1BlockNumber;
233236
});
234237
return msgs;
235238
};
@@ -505,6 +508,27 @@ describe('MessageStore', () => {
505508
expect((await messageStore.getLatestInboxBucketAtOrBefore(150n))!.seq).toEqual(1n);
506509
});
507510

511+
it('rolls back whole buckets past an L1 block', async () => {
512+
// Bucket 1 fills L1 block 100, bucket 2 fills L1 block 101 and bucket 3 fills L1 block 102.
513+
const msgs = makeBucketedMessages([
514+
{ seq: 1n, timestamp: 100n, l1BlockNumber: 100n },
515+
{ seq: 1n, timestamp: 100n, l1BlockNumber: 100n },
516+
{ seq: 1n, timestamp: 100n, l1BlockNumber: 100n },
517+
{ seq: 2n, timestamp: 200n, l1BlockNumber: 101n },
518+
{ seq: 2n, timestamp: 200n, l1BlockNumber: 101n },
519+
{ seq: 3n, timestamp: 300n, l1BlockNumber: 102n },
520+
]);
521+
await messageStore.addL1ToL2MessageBuckets(msgs);
522+
523+
await messageStore.rollbackL1ToL2MessagesAfterL1Block(100n);
524+
525+
expect(await toArray(messageStore.iterateL1ToL2Messages())).toEqual(msgs.slice(0, 3));
526+
expect(await messageStore.getInboxBucket(2n)).toBeUndefined();
527+
expect(await messageStore.getInboxBucket(3n)).toBeUndefined();
528+
expect(await messageStore.getInboxBucket(1n)).toMatchObject({ msgCount: 3, totalMsgCount: 3n });
529+
expect((await messageStore.getLatestInboxBucketAtOrBefore(300n))!.seq).toEqual(1n);
530+
});
531+
508532
it('clears all buckets when every message is removed', async () => {
509533
const msgs = makeBucketedMessages(threeBucketSpec);
510534
await messageStore.addL1ToL2MessageBuckets(msgs);

yarn-project/archiver/src/store/message_store.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -582,15 +582,19 @@ export class MessageStore {
582582
/**
583583
* Removes every L1 to L2 message inserted after the given L1 block, so the message store matches the L1 Inbox state
584584
* as of that block. Used when rolling the archiver back to an earlier checkpoint, whose L1 block is passed here.
585+
*
586+
* A bucket lives entirely within one L1 block, so the cut always falls on a bucket boundary and can be found from
587+
* the bucket snapshots alone, without reading the messages being removed.
585588
*/
586589
public async rollbackL1ToL2MessagesAfterL1Block(l1BlockNumber: bigint): Promise<void> {
587590
this.#log.debug(`Deleting L1 to L2 messages inserted after L1 block ${l1BlockNumber}`);
588591
let removeFromIndex: bigint | undefined;
589-
for await (const message of this.iterateL1ToL2Messages({ reverse: true })) {
590-
if (message.l1BlockNumber <= l1BlockNumber) {
592+
for await (const snapBuffer of this.#inboxBuckets.valuesAsync({ reverse: true })) {
593+
const snapshot = deserializeBucketSnapshot(snapBuffer);
594+
if (snapshot.l1BlockNumber <= l1BlockNumber) {
591595
break;
592596
}
593-
removeFromIndex = message.index;
597+
removeFromIndex = snapshot.firstMessageIndex;
594598
}
595599
if (removeFromIndex !== undefined) {
596600
await this.removeL1ToL2Messages(removeFromIndex);

0 commit comments

Comments
 (0)