Skip to content

Decentralized controller consistency - #1273

Open
leonard-amsler wants to merge 7 commits into
mainfrom
decentralized-controller-consistency
Open

leonard-amsler wants to merge 7 commits into
mainfrom
decentralized-controller-consistency

Conversation

@leonard-amsler

Copy link
Copy Markdown
Collaborator

Context

#1227 reworked the federated setting. This PR moves the shared parts to the abstract classes and uses them also in the decentralized setting.

Server

  • Moved send, crashClient, disconnect, the client ids, the ClientConnected counter and the connect/disconnect cleanup from the federated controller to TrainingController, so both controllers share them.
  • The decentralized controller now crashes a peer that:
    • sends a message before ClientConnected
    • sends more than 5 ClientConnected
    • sends an invalid message, or a message the server fails to handle
  • A repeated ClientConnected gets the same answer again, without counting the peer twice or notifying the other peers.
  • Every message goes through the send helper, so a failed send disconnects the peer.
  • A peer leaving is now also removed from #syncingNodes.
  • Documented the sets of the controller and moved to # methods.
  • Fixed the connection timer: after a retry, the old timer kept running in the next rounds and could send RetryPeerConnections, and then ConnectionFail, to peers that were fine. There is now one timer per PeersForRound, and the retry count restarts at each round.

Client

  • disconnect() is now shared in Client. It closes the server connection if there is one (local training) and stops waiting for participants, so a client no longer keeps waiting after disconnecting. It also works when called multiple times.
  • The decentralized client retries its join with sendAndWaitWithRetry.
  • It stops with a ClientCrashError when the server crashes it, at the start of a round and while waiting for the server.
  • CrashClient is now part of the decentralized messages.

Tests

  • Decentralized controller: join retry, crashing or disconnecting a peer
  • Decentralized client: crashed and disconnected by the server after too many joins

Fixes #1250

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

All good! Only minor comments. Fyi, I will follow up this PR with another one for these points:

  • create an abort signal when the participant disconnects manually to stop any promise it was waiting on
  • disconnect server-side any peer that timeouts on PeerIsReady
  • check that ConnectionsReady only come from peers that sent PeerIsReady first (an attacker could send a ConnectionsReady during training and currently it would increment the round)

this.#connectionRetry = 0;
// Restart the timeout after sending retry messages
this.startTimeout();

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.

Let's make sure we add a timeout back here. If we are sending a RetryPeerConnections it means one peer failed to connect in time so it is likely that it will fail again. We need a timeout to ensure the server doesn't get stuck waiting and can exclude the peer.

Comment on lines 222 to 227
const providerInfo = await waitMessageWithTimeout(
this.server,
MType.ModelProviderInfo,
this.task.trainingInformation.maxModelSyncTime,
"Timeout while waiting for the latest model provider",
);

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 think we should also check for an abort signal here. There is still the timeout as fallback but it would be cleaner to fail with the proper cause I think

Comment on lines 296 to 299
if (this.#timeout !== undefined) {
clearTimeout(this.#timeout);
this.#timeout = undefined;
}

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.

you can use this.#clearTimeout() now

* Complete list of client ids that have connected their websocket
* Make sure two clients don't share the same ID.
*/
protected clientIds = new Set<NodeID>();

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 import Set from immutable rather than using the native JS Set? To be consistent with Map

protected clientIds = new Set<NodeID>();
/**
* Number of ClientConnected messages each client's socket sent.
* We answer them with the global weights, so a client resending it

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.

Suggested change
* We answer them with the global weights, so a client resending it
* In FL we answer with the global weights, so a client resending it

* - `connections` (inherited): map between the ids of clients
* that completed the connection handshake and their socket.
* - `#roundPeers`: the nodes that want to join the current round and their readiness status.
* - `#connectFinishedNodes`: the nodes that have completed the connection handshake and are ready for the current round.

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.

Suggested change
* - `#connectFinishedNodes`: the nodes that have completed the connection handshake and are ready for the current round.
* - `#connectFinishedNodes`: the nodes that have completed their peer-to-peer webRTC connections and are ready for the current round.

To make sure it's not confused with the connection handshake with the server

);
}

protected override connectClient(nodeId: string, ws: WebSocket): void {

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.

Suggested change
protected override connectClient(nodeId: string, ws: WebSocket): void {
protected override connectClient(nodeId: NodeID, ws: WebSocket): void {

}
}

protected override disconnectClient(nodeId: string): void {

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.

Suggested change
protected override disconnectClient(nodeId: string): void {
protected override disconnectClient(nodeId: NodeID): void {

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.

Decentralized and federated controller consistency

2 participants