Skip to content

Decentralized: Replace ConnectionFail with CrashClient - #1276

Open
leonard-amsler wants to merge 7 commits into
mainfrom
duplicate-termination-signal
Open

leonard-amsler wants to merge 7 commits into
mainfrom
duplicate-termination-signal

Conversation

@leonard-amsler

Copy link
Copy Markdown
Collaborator

Decentralized training had two different messages for the server to remove a client: CrashClient and ConnectionFail. This PR removes ConnectionFail and uses CrashClient for both.

Changes

  • Server: Instead of sending a ConnectionFail, it now uses the crashClient() helper to notify the clients about the crash and also closes their socket.
  • Client: The client now stops waiting for getPeers when it is told to crash, so a client stuck connecting to peers fails with a ClientCrashError instead of waiting forever.
  • Cleanup: Removed MType.ConnectionFail and the webapp error branch for that error. Updated the documentation to match the implementation.
  • Tests: The controller spec now checks that excluded peers receive the CrashClient message and that their WebSocket is closed.

@JulienVig JulienVig 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.

Thanks for the follow-up!
I think that the current way to abort waiting for getPeers could be too superficial. Even with the abort signal the method can continue trying to establish webRTC connections

I have published a PR improving the robustness of decentralized learning, notably by creating a stopSignal that combines crash signal and disconnected signal (new one). Can you rebase your PR onto it so that you can replace this.crashSignal by this.stopSignal?

const ws = this.connections.get(nodeId);
// Cannot happen because a node leaving the session
// is also removed from #connectFinishedNodes
if (ws === undefined) return;

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.

If something that can't happen happens then should we either fail with an error or at least log that it happened

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.

Makes sense, let's make it crash then.

Comment on lines -283 to -289
} else if (
e instanceof Error &&
e.message.includes("Client disconnected after connection failure")
) {
toaster.error(
"Client disconnected after multiple peer connection failure. Please rejoin the training.",
);

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 not keep this toaster error and just update the string to match?

// weight update and adds it to our aggregator upon reception
(conn) => this.receivePayloads(conn),
),
this.crashSignal,

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.

Could you rebase this branch on mine (#1277) and use this.stopSignal instead here?

(conn) => this.receivePayloads(conn),
// Making it abortable with the crash signal,
// otherwise the client may be stuck waiting for connections indefinitely
const connections = await abortable(

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.

Can you add some tests for this?

Comment on lines +387 to +393
this.#pool.getPeers(
peers,
this.server,
// Init receipt of peers weights. this awaits the peer's
// weight update and adds it to our aggregator upon reception
(conn) => this.receivePayloads(conn),
),

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.

If an abort signal comes up, we only stop waiting for the promise but the peer connection loop keeps running in this.#pool.getPeers(). You could pass the signal as an argument to getPeers so that it can interrupt its loop

@leonard-amsler
leonard-amsler force-pushed the duplicate-termination-signal branch from 52570e5 to 4701566 Compare November 4, 2026 12:47

This branch has not been deployed

No deployments
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