Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: Move replay + error correction logic to commit log #554

Merged
merged 2 commits into from
Nov 20, 2023

Conversation

kim
Copy link
Contributor

@kim kim commented Nov 13, 2023

Description of Changes

Makes CommitLog a read-only handle and introduces CommitLogMut,
allowing to append. This better encapsulates the error detection and
trimming logic, and enforces it to be executed before being able to
mutate the log.

Stacked on top of #527
Compare

API and ABI breaking changes

Expected complexity level and risk

2

@kim kim force-pushed the kim/move-replay-to-commit-log branch from 498bda7 to f779cac Compare November 13, 2023 13:26
@kim kim mentioned this pull request Nov 14, 2023
4 tasks
@kim kim force-pushed the kim/move-replay-to-commit-log branch from f779cac to 9ce0c02 Compare November 15, 2023 10:16
@kim kim force-pushed the kim/move-replay-to-commit-log branch from 9ce0c02 to 7d57673 Compare November 15, 2023 10:22
/// Similar to [`Iter`], but performs integrity checking and maintains
/// additional state.
#[must_use = "iterators are lazy and do nothing unless consumed"]
struct Replay {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would be nice to replace Iter with this, so the error detection is available by default. However, Iter is supposed to be able to start iteration at a specified commit offset, so this is not currently possible.

crates/core/src/db/commit_log.rs Show resolved Hide resolved
crates/core/src/db/commit_log.rs Show resolved Hide resolved
crates/core/src/db/commit_log.rs Show resolved Hide resolved
crates/core/src/db/commit_log.rs Show resolved Hide resolved
// We are near the end of the log, so trim it to the known-
// good prefix.
Err(
ReplayError::OutOfOrder { last_commit_offset, .. }
Copy link
Collaborator

Choose a reason for hiding this comment

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

I understand if the last commit is corrupted, it makes sense to drop it, but it's unclear the failure scenario that would lead to an out of order commit. And so I would be hesitant to just ignore it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Now I understand the failure scenarios, however I think I'm even more of the opinion that we should not ignore an OutOfOrder error here, as it potentially points to a legitimate bug within the database.

// good prefix.
Err(
ReplayError::OutOfOrder { last_commit_offset, .. }
| ReplayError::CorruptedData { last_commit_offset, .. },
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is the last segment, but not necessarily the last commit right? Is there any reason not to require that corruption be confined to the last commit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can't know if it is the last commit exactly -- MessageLog::open_segment_max_offset (meaning: total number of entries) is calculated without decoding the payload or checking integrity, it only means that the number of bytes is aligned.

Since commits are variable-length, we could only apply a heuristic such as "not more than ~50KiB remaining".

A viable approach for a more robust solution might be to calculate a CRC32 for every record, and allow to skip over out-of-order commits iff the next commit has the right sequence number (although I don't know how this could arise).

crates/core/src/db/commit_log.rs Outdated Show resolved Hide resolved
crates/core/src/db/commit_log.rs Outdated Show resolved Hide resolved
crates/core/src/db/relational_db.rs Show resolved Hide resolved
@kim kim force-pushed the kim/move-replay-to-commit-log branch from 03005dc to 80b6341 Compare November 16, 2023 12:10
Makes `CommitLog` a read-only handle and introduces `CommitLogMut`,
allowing to append. This better encapsulates the error detection and
trimming logic, and enforces it to be executed before being able to
mutate the log.
@kim kim force-pushed the kim/move-replay-to-commit-log branch from 80b6341 to c38cceb Compare November 16, 2023 15:40
Copy link
Collaborator

@joshua-spacetime joshua-spacetime left a comment

Choose a reason for hiding this comment

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

This looks good to me @kim, aside from my comment about ignoring OutOfOrder errors. But I'll let @kulakowski have the final approval.

Copy link
Contributor

@kulakowski kulakowski left a comment

Choose a reason for hiding this comment

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

Modulo Josh's outstanding comment

/// We may in the future verify the commit hash, and include expected and
/// actual value in this variant.
///
/// [465]: https://github.com/clockworklabs/SpacetimeDB/pull/465
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bag fan of leaving these kind of break crumbs!

}

impl CommitLogMut {
pub fn set_fsync(&mut self, fsync: bool) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this get a doc comment describing its postcondition? Just from the name, I could imagine two useful ones:

  • All future writes will happen according to fsync, but any prior writes may be left up to the OS to actually flush (if self.fsync was false)
  • All future writes will happen according to fsync. Additionally, flush now.

This looks unused, so it's hard to guess which is more useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh ok, I'll fix the TODO then locally (use FsyncPolicy instead of bool)

@kim
Copy link
Contributor Author

kim commented Nov 16, 2023

Modulo Josh's outstanding comment

So what’s your opinion on that?

I’ll point out that if we don’t trim the log for any reason, the database will be left unusable. So what we’d gain is to preserve corrupt data for forensics or hypothetical future tooling to repair it offline. For that purpose, we could also just make a copy of the corrupt segment before truncating. Or, we could start the database with the prefix but prevent any writes.

@kulakowski
Copy link
Contributor

It's a little hard to tell what Joshua meant because his commentary came in a couple comments and its hard for me to tell which comment of his is responding to which.

I agree that right now, trimming is the correct thing. And later if we want we can make it fancier, like you say.

What I meant, though, was for you, Kim, to either agree or disagree with "as it potentially points to a legitimate bug within the database."

@kim
Copy link
Contributor Author

kim commented Nov 20, 2023

Ok, so I think the ambiguity of the out-of-order case is an artifact of the on-disk format, and should inform future amendments to it (the format).

Meanwhile, we're maintaining the ordering invariant in the same file, so I added another heads-up comment. It could be made more robust by transferring ownership of MessageLog and ObjectDB to CommitLog -- which, however, is a larger chunk of work in order to get the API right. I'm slowly moving towards it in my current line of work...

@kim kim enabled auto-merge (squash) November 20, 2023 08:48
@kim kim merged commit a376088 into master Nov 20, 2023
5 checks passed
@kim kim deleted the kim/move-replay-to-commit-log branch November 20, 2023 08:53
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.

3 participants