Skip to content

internal/db: Remove node from dqlite cluster on failed join#774

Draft
claudiubelu wants to merge 1 commit into
canonical:v4from
claudiubelu:v4-cleanup-dqlite
Draft

internal/db: Remove node from dqlite cluster on failed join#774
claudiubelu wants to merge 1 commit into
canonical:v4from
claudiubelu:v4-cleanup-dqlite

Conversation

@claudiubelu

Copy link
Copy Markdown
Contributor

dqlite.Close() tears down Raft connections but does not remove the node from the dqlite cluster membership. When db.Join() fails after the node has already been added to the cluster (via cli.Add in app.run), the reverter only called Close(), leaving a ghost member in dqlite.

This ghost node causes CheckMembershipConsistency to fail (the node exists in dqlite but not in core_cluster_members), which blocks token generation and all future cluster joins.

Fix the Join reverter to explicitly ask the dqlite leader to remove us before closing. Also correct a misleading comment in the Bootstrap reverter.

dqlite.Close() tears down Raft connections but does not remove the node
from the dqlite cluster membership. When db.Join() fails after the node
has already been added to the cluster (via cli.Add in app.run), the
reverter only called Close(), leaving a ghost member in dqlite.

This ghost node causes CheckMembershipConsistency to fail (the node
exists in dqlite but not in core_cluster_members), which blocks token
generation and all future cluster joins.

Fix the Join reverter to explicitly ask the dqlite leader to remove us
before closing. Also correct a misleading comment in the Bootstrap
reverter.

Signed-off-by: Claudiu Belu <cbelu@cloudbasesolutions.com>

@roosterfish roosterfish left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey, can you please share some reproducer steps how you could end up in this state?
I see we have an exception to continue starting up the database in case we hit a update.ErrGracefulAbort error. But I would be very interested to see what else could go wrong.

Also could you please add the reproducer as an additional check to test_membership_consistency ?

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes a cluster-join failure mode where a node that was added to dqlite membership but failed later in db.Join() could be left behind as a “ghost” dqlite member, breaking membership consistency checks and blocking future operations like token generation.

Changes:

  • Corrected comments clarifying that dqlite.Close() tears down Raft connections but does not remove a node from dqlite membership.
  • Updated the Join() reverter to attempt removal of the local node from the dqlite cluster before closing, to avoid leaving a ghost member behind.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/db/dqlite.go
Comment on lines +247 to +251
cli, err := db.dqlite.Leader(removeCtx, dqliteClient.WithConcurrentLeaderConns(1))
if err != nil {
db.log().Warn("Failed to connect to dqlite leader to remove ourselves from the cluster", slog.String("address", db.listenAddr.String()), slog.String("error", err.Error()))
} else {
err = cli.Remove(removeCtx, db.dqlite.ID())
Comment thread internal/db/dqlite.go
Comment on lines +244 to +248
removeCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

cli, err := db.dqlite.Leader(removeCtx, dqliteClient.WithConcurrentLeaderConns(1))
if err != nil {
@roosterfish

Copy link
Copy Markdown
Contributor

Marking as draft until you get a chance to get back to it @claudiubelu.

@roosterfish roosterfish marked this pull request as draft July 8, 2026 12:40
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