Skip to content

kvstorage: add WAG truncation infrastructure#167533

Merged
trunk-io[bot] merged 2 commits into
cockroachdb:masterfrom
iskettaneh:rse_truncate_2
Apr 9, 2026
Merged

kvstorage: add WAG truncation infrastructure#167533
trunk-io[bot] merged 2 commits into
cockroachdb:masterfrom
iskettaneh:rse_truncate_2

Conversation

@iskettaneh

@iskettaneh iskettaneh commented Apr 4, 2026

Copy link
Copy Markdown
Contributor

This PR adds the infrastructure that will be used to truncating WAG
nodes. It will be used in two places: 1) Truncating all WAG nodes on
startup, and 2) Periodically truncating durably applied WAG nodes.

Release note: None

References: #167607

@trunk-io

trunk-io Bot commented Apr 4, 2026

Copy link
Copy Markdown
Contributor

😎 Merged successfully - details.

@cockroach-teamcity

Copy link
Copy Markdown
Member

This change is Reviewable

@iskettaneh iskettaneh changed the title kvstorage/wag: prepare for WAG truncation kvstorage: add WAG truncation infrastructure Apr 5, 2026
@iskettaneh iskettaneh marked this pull request as ready for review April 7, 2026 21:03
@iskettaneh iskettaneh requested a review from a team as a code owner April 7, 2026 21:03
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated

@pav-kv pav-kv 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.

A few quick comments. Will take a closer look tomorrow.

Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated
return truncationResult{}, err
}
if deleted == 0 {
return truncationResult{outcome: TruncatedNone}, nil

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.

Is there a difference between TruncatedNone with seen == deleted == 0 and seen == 1 && deleted == 0?

Could you outline for my information how this enum result is going to be used? Alternatively, don't introduce this for now, add later in the PR that would be using it. I'm assuming this would be mainly for the online truncation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was assuming that we will be able to remove all WAG nodes in one shot. My idea was that we could use to perform some assertions. For example, at engine startup, we expect ALL wag nodes to be deleted. But now that we are going to limit the number of WAG nodes that can be deleted in one go, I don't think this field helps much.

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 see. For this assertion, seems like seen == deleted == 0 should be counted as "all" rather than "none", correct? Maybe move the if deleted == seen check up? Meaning an empty WAG is fully applied by definition.

Possibly, the None case is not interesting. We just want to know if the WAG is fully applied or not?

  • the startup code cares about WAG being fully applied (not necessarily truncated, btw; maybe in the future we may want to keep it around for a bit, for debuggability)
  • the runtime truncation might use this as an optimization to know if it should ignore flushes. Though it's quite possible that it will be implemented in some other way in the end, due to concurrency/gaps/etc. E.g. WAG can be empty but some writes already queued up via the Seq. More likely we would be watching the last deleted WAG index vs Seq index for that sort of signal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I removed that logic and I am just returning a boolean to say whether a deletion happened or not. The startup truncation assertion can be done without this piece.
And for the periodic truncations, I am thinking of this signal:

  1. Have a variable that contains the last WAG index that was successfully deleted.
  2. When we recieve a signal from the state engine that there was a flush, check if the last given seq number is higher than the last deleted WAG node. If not (they are equal), there is no way that there is any WAG node to even check.
  3. When we run a periodic truncation, we can start from the index right after the last successfully truncated WAG node index. This way we don't need to go through previously deleted WAG nodes.

Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go

@iskettaneh iskettaneh left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@iskettaneh made 1 comment.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on pav-kv).

return truncationResult{}, err
}
if deleted == 0 {
return truncationResult{outcome: TruncatedNone}, nil

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was assuming that we will be able to remove all WAG nodes in one shot. My idea was that we could use to perform some assertions. For example, at engine startup, we expect ALL wag nodes to be deleted. But now that we are going to limit the number of WAG nodes that can be deleted in one go, I don't think this field helps much.

@iskettaneh iskettaneh left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@iskettaneh made 2 comments.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on pav-kv).

Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated
return truncationResult{}, err
}
if deleted == 0 {
return truncationResult{outcome: TruncatedNone}, nil

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 see. For this assertion, seems like seen == deleted == 0 should be counted as "all" rather than "none", correct? Maybe move the if deleted == seen check up? Meaning an empty WAG is fully applied by definition.

Possibly, the None case is not interesting. We just want to know if the WAG is fully applied or not?

  • the startup code cares about WAG being fully applied (not necessarily truncated, btw; maybe in the future we may want to keep it around for a bit, for debuggability)
  • the runtime truncation might use this as an optimization to know if it should ignore flushes. Though it's quite possible that it will be implemented in some other way in the end, due to concurrency/gaps/etc. E.g. WAG can be empty but some writes already queued up via the Seq. More likely we would be watching the last deleted WAG index vs Seq index for that sort of signal.

Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated

@iskettaneh iskettaneh left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@iskettaneh made 16 comments and resolved 2 discussions.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on pav-kv).

return truncationResult{}, err
}
if deleted == 0 {
return truncationResult{outcome: TruncatedNone}, nil

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I removed that logic and I am just returning a boolean to say whether a deletion happened or not. The startup truncation assertion can be done without this piece.
And for the periodic truncations, I am thinking of this signal:

  1. Have a variable that contains the last WAG index that was successfully deleted.
  2. When we recieve a signal from the state engine that there was a flush, check if the last given seq number is higher than the last deleted WAG node. If not (they are equal), there is no way that there is any WAG node to even check.
  3. When we run a periodic truncation, we can start from the index right after the last successfully truncated WAG node index. This way we don't need to go through previously deleted WAG nodes.

Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
@iskettaneh iskettaneh requested a review from pav-kv April 8, 2026 17:53

@pav-kv pav-kv 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.

Looks great, thanks for the cleanups. Probably good to merge after rebasing on Mira's PR, I'll review it soon.

Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go
Comment on lines +68 to +69
sl := MakeStateLoader(rangeID)
prefix := sl.RaftLogPrefix()

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.

nit: sl is used only once. Simply use keys.RaftLogPrefix(rangeID) instead?

Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated

@iskettaneh iskettaneh left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@iskettaneh made 4 comments and resolved 5 discussions.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on pav-kv).

Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator.go
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go Outdated
Comment thread pkg/kv/kvserver/kvstorage/wag_truncator_test.go
@iskettaneh iskettaneh requested a review from pav-kv April 8, 2026 21:03
iskettaneh and others added 2 commits April 8, 2026 20:20
This PR adds the infrastructure that will be used to truncating WAG
nodes. It will be used in two places: 1) Truncating all WAG nodes on
startup, and 2) Periodically truncating durably applied WAG nodes.

A lot of functions are similar to WAG replay, and there are TODOs to
reconcile the functions between WAG replay and truncation.

There is also some work left for clearing the Raft state, and deleting
sideloaded files.

Release note: None

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
When deleting a WAG node that has the events destroy or subsume, we also
need to delete the raft log (up until and including the addr.index), and
any sideloaded files that are part of that raft log prefix.

We do this because when a replica delete or subsume is applied, we cannot
just yet delete the replica state (log and sideloaded files) because we
might crash before the state engine has flushed, and we might need the
raft log to replay the event.

Release note: None

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
@iskettaneh

Copy link
Copy Markdown
Contributor Author

TFTR!

/trunk merge

@trunk-io trunk-io Bot merged commit 943fc6b into cockroachdb:master Apr 9, 2026
37 of 38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants