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

catalog: Improve fencing #29224

Merged
merged 3 commits into from
Aug 27, 2024
Merged

catalog: Improve fencing #29224

merged 3 commits into from
Aug 27, 2024

Conversation

jkosh44
Copy link
Contributor

@jkosh44 jkosh44 commented Aug 26, 2024

Previously, the durable catalog could be fenced by one of the
following ways:

  • It tried to listen to the durable catalog and saw a higher epoch.
  • It tried to listen to the durable catalog and saw a higher binary
    version.
  • It tried to write to the durable catalog and had an upper
    mismatch.
  • It tried to write to the catalog migration shard and had an upper
    mismatch.

These would either result in a non-descriptive
DurableCatalogError::Fence error or a
DurableCatalogError::IncompatiblePersistVersion error. Additionally,
if multiple of these conditions happened at the same time, it was
non-deterministic which error would be returned.

This commit modifies the conditions for when the durable catalog can be
fenced to the following conditions:

  • It tried to listen to the durable catalog and saw a higher deploy
    generation.
  • It tried to listen to the durable catalog and saw a higher epoch.
  • It tried to write to the durable catalog shard and had an upper
    mismatch.
  • It tried to write to the catalog migration shard and had an upper
    mismatch.

When initializing the catalog, we still check that the current binary
version is not less than the catalog binary version, but we no longer
check during every update. After the catalog has been initialized,
another catalog can only increase the binary version by also fencing
the current catalog by another method.

This commit also creates a new error enum to explicitly codify these
different types of fences. When multiple of these conditions happen at
the same time, then we deterministically will return a fence error with
the priority of how the conditions are ordered above. The context of
the fence is useful to higher layers in determining what action to
take. For example if the deploy generation has increased, then we'd
like to gracefully exit with exit code 0 and let the new instance take
over.

Works towards resolving MaterializeInc/database-issues#8474

Motivation

This PR fixes a recognized bug.

Tips for reviewer

I would recommend reviewing the files in the following order:

  1. error.rs
  2. persist.rs
  3. durable.rs
  4. environmentd/src/lib.rs
  5. preflight.rs
  6. builtin_item_migrations.rs
  7. catalog-debug/src/main.rs
  8. catalog.rs
  9. All other files (which happen to only contain tests).

Checklist

  • This PR has adequate test coverage / QA involvement has been duly considered. (trigger-ci for additional test/nightly runs)
  • This PR has an associated up-to-date design doc, is a design doc (template), or is sufficiently small to not require a design.
  • If this PR evolves an existing $T ⇔ Proto$T mapping (possibly in a backwards-incompatible way), then it is tagged with a T-proto label.
  • If this PR will require changes to cloud orchestration or tests, there is a companion cloud PR to account for those changes that is tagged with the release-blocker label (example).
  • If this PR includes major user-facing behavior changes, I have pinged the relevant PM to schedule a changelog post.

@jkosh44 jkosh44 force-pushed the catalog-fencing branch 13 times, most recently from 8a7da69 to 5d550c9 Compare August 27, 2024 00:27
Previously, the durable catalog could be fenced by one of the
following ways:

  - It tried to listen to the durable catalog and saw a higher epoch.
  - It tried to listen to the durable catalog and saw a higher binary
    version.
  - It tried to write to the durable catalog and had an upper
    mismatch.
  - It tried to write to the catalog migration shard and had an upper
    mismatch.

These would either result in a non-descriptive
`DurableCatalogError::Fence` error or a
`DurableCatalogError::IncompatiblePersistVersion` error. Additionally,
if multiple of these conditions happened at the same time, it was
non-deterministic which error would be returned.

This commit modifies the conditions for when the durable catalog can be
fenced to the following conditions:

  - It tried to listen to the durable catalog and saw a higher deploy
    generation.
  - It tried to listen to the durable catalog and saw a higher epoch.
  - It tried to write to the durable catalog shard and had an upper
    mismatch.
  - It tried to write to the catalog migration shard and had an upper
    mismatch.

When initializing the catalog, we still check that the current binary
version is not less than the catalog binary version, but we no longer
check during every update. After the catalog has been initialized,
another catalog can only increase the binary version by also fencing
the current catalog by another method.

This commit also creates a new error enum to explicitly codify these
different types of fences. When multiple of these conditions happen at
the same time, then we deterministically will return a fence error with
the priority of how the conditions are ordered above. The context of
the fence is useful to higher layers in determining what action to
take. For example if the deploy generation has increased, then we'd
like to gracefully exit with exit code 0 and let the new instance take
over.

Works towards resolving #29199
@jkosh44 jkosh44 changed the title catalog: Improve fence error reporting catalog: Improve fencing Aug 27, 2024
Comment on lines +154 to +160
// It's very likely that if we were to read the latest updates after this error, then we'd see
// a higher epoch/deploy generation from the other writer and get fenced through that. We could
// potentially always do that and eliminate this fence variant. However, the catalog debug tool
// is able to write to the catalog without incrementing the epoch and therefore would not be
// able to fence this instance. It's technically possible for this instance to survive and
// respond to a write from the catalog debug tool, but until more thought is put into it, the
// safest thing to do is crash and reboot.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've realized that this isn't actually true. It's possible for the following to happen:

  1. Catalog opens.
  2. Catalog debug tool writes something.
  3. Catalog performs a read, which first involves listening to all changes in the shard, including the changes written by the debug tool. This also updates the internal upper.
  4. Catalog goes to make a write, it's already updated it's internal upper so no upper mismatch happens.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is an existing issue though so I'll address it in a follow up.

@jkosh44 jkosh44 requested a review from aljoscha August 27, 2024 01:48
@@ -777,6 +792,7 @@ impl UnopenedPersistCatalogState {
persist_client: PersistClient,
organization_id: Uuid,
version: semver::Version,
deploy_generation: Option<u64>,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This one line is the reason the LoC is so high on this PR. We used to pass in the deploy generation when opening the durable catalog. Now we pass it in even earlier, before opening the catalog when we initialize it, which is handled by this function. This changed the API of a handful of heavily used methods.

The reason is so that we can fence the catalog by deploy version, before we even open the catalog. Maybe that was unnecessary and we should have kept the API as it was. This feels a bit safer for some reason.

@jkosh44 jkosh44 marked this pull request as ready for review August 27, 2024 01:53
@jkosh44 jkosh44 requested a review from a team as a code owner August 27, 2024 01:53
@jkosh44 jkosh44 requested a review from ParkMyCar August 27, 2024 01:53
src/catalog/src/durable/error.rs Outdated Show resolved Hide resolved
#[error(
"builtin table migration shard upper {expected_upper:?} fenced by new builtin table migration shard upper {actual_upper:?}"
)]
Migration {
Copy link
Contributor

Choose a reason for hiding this comment

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

ignore if you got better things to do, but this could be MigrationUpper to match Upper

jkosh44 and others added 2 commits August 27, 2024 09:02
Co-authored-by: Aljoscha Krettek <mail@aljoscha.dev>
Copy link
Member

@ParkMyCar ParkMyCar left a comment

Choose a reason for hiding this comment

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

Nice!

@jkosh44 jkosh44 merged commit da7c2af into MaterializeInc:main Aug 27, 2024
82 checks passed
@jkosh44 jkosh44 deleted the catalog-fencing branch August 27, 2024 14:30
@github-actions github-actions bot locked and limited conversation to collaborators Aug 27, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants