Skip to content

Garbage collect the WAL periodically - #476

Draft
yacovm wants to merge 2 commits into
mainfrom
GC
Draft

yacovm wants to merge 2 commits into
mainfrom
GC

Conversation

@yacovm

@yacovm yacovm commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

This commit adds periodical GC to the WAL, every WALGCInterval rounds.

@yacovm
yacovm force-pushed the GC branch 2 times, most recently from bc7fe49 to c344ce8 Compare July 30, 2026 22:29
@yacovm
yacovm marked this pull request as draft October 12, 2026 22:32
@yacovm
yacovm force-pushed the GC branch 2 times, most recently from 8732708 to 74f035e Compare October 14, 2026 19:44
This commit adds periodical GC to the WAL, every WALGCInterval rounds.

Signed-off-by: Yacov Manevich <yacov.manevich@avalabs.org>
Comment thread wal/gc.go
}

// EpochAwareWAL is a GarbageCollectableWAL that ignores records with an epoch lower than its designated epoch.
type EpochAwareWAL struct {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont know why we need an entire struct just for knowing what the epoch is while reading. We already know what the epoch should be when reading the records.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it's more testable this way. Otherwise to test it, we'd have to make an epoch test with every kind of message written in the WAL in the test.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm lemme know what you think of this #629. i think the EpochAwareWal is a bit unnecessarily big.

@samliok samliok left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a pr that reduces the diff of this one by 650+ lines, i think worth doing some of them

#629

Comment thread instance.go
if walInterval <= 0 {
return nil
}
if round%uint64(walInterval) == 0 {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we do rounds and not sequences? We may end up skipping rounds(empty notarization) and thus not garbage collecting anything even though we should.

Comment thread instance_test.go
wals: []wal.DeletableWAL{staleWAL}, storage: node.storage, existingNode: true,
})

restarted.inst.lock.Lock()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using the internal lock is kinda gross here and manually setting epich

Comment thread instance_test.go
// The WAL is the node's own: the test delays the finalization of the sealing block so that the
// validators extend the sealed epoch with Telocks, and then takes the WAL as the node wiped it
// when it moved to the new epoch, which is what a crash in that window would have left behind.
func TestInstanceRestartSkipsSealedEpochWALRecords(t *testing.T) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea i think if we dont go with the epoch aware wal, then we can just remove this test all-together plus all the helpers especially these

	// hold, when set, picks the messages the network holds back instead of delivering;
	// held keeps them, in send order, until releaseHeldMessages delivers them.
	hold func(from common.NodeID, msg *common.Message) bool
	held []heldMessage

Comment thread instance_test.go
// A tiny WAL size makes the WAL rotate upon every entry, so garbage collection
// discards entries round by round rather than in large batches.
params := paramConfig
params.WALMaxSizeBytes = 10

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this means the wal will be garbage collected every time we append? if thats the case why do we iterate over all the entries below? wouldn't it be simpler we just check the length and then take the first one?

Comment thread instance_test.go
// collectRoundsFromWAL returns the rounds of the entries the node's WAL currently
// holds, across all the WAL files it rotated through. Garbage collected files are
// deleted, hence contribute no entries.
collectRoundsFromWAL := func() []uint64 {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this just be one entry?

Comment thread instance_test.go
// When we commit a sealing block we wipe out the WAL entirely, so ignore this corner case.
// We only care about the ordinary blocks because we're testing regular garbage collection of the WAL.
// We also ignore the first walGCInterval rounds because the WAL is not garbage collected until after that.
if block.SealingBlockInfo() != nil || round < walGCInterval {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would we commit a sealing block if we aren't doing an epoch change?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants