WIP - Start work on reconnect logic

Max Brunsfeld created

Change summary

zed/src/channel.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++
zed/src/rpc.rs     | 21 ++++++++++++--
2 files changed, 82 insertions(+), 4 deletions(-)

Detailed changes

zed/src/channel.rs 🔗

@@ -653,4 +653,69 @@ mod tests {
             );
         });
     }
+
+    #[gpui::test]
+    async fn test_channel_reconnect(mut cx: TestAppContext) {
+        let user_id = 5;
+        let mut client = Client::new();
+        let server = FakeServer::for_client(user_id, &mut client, &cx).await;
+
+        let user_store = Arc::new(UserStore::new(client.clone()));
+
+        let channel = cx.add_model(|cx| {
+            Channel::new(
+                ChannelDetails {
+                    id: 1,
+                    name: "general".into(),
+                },
+                user_store,
+                client.clone(),
+                cx,
+            )
+        });
+
+        let join_channel = server.receive::<proto::JoinChannel>().await.unwrap();
+        server
+            .respond(
+                join_channel.receipt(),
+                proto::JoinChannelResponse {
+                    messages: vec![
+                        proto::ChannelMessage {
+                            id: 10,
+                            body: "a".into(),
+                            timestamp: 1000,
+                            sender_id: 5,
+                        },
+                        proto::ChannelMessage {
+                            id: 11,
+                            body: "b".into(),
+                            timestamp: 1001,
+                            sender_id: 5,
+                        },
+                    ],
+                    done: false,
+                },
+            )
+            .await;
+
+        let get_users = server.receive::<proto::GetUsers>().await.unwrap();
+        assert_eq!(get_users.payload.user_ids, vec![5]);
+        server
+            .respond(
+                get_users.receipt(),
+                proto::GetUsersResponse {
+                    users: vec![proto::User {
+                        id: 5,
+                        github_login: "nathansobo".into(),
+                        avatar_url: "http://avatar.com/nathansobo".into(),
+                    }],
+                },
+            )
+            .await;
+
+        // Disconnect, wait for the client to reconnect.
+        server.disconnect().await;
+        cx.foreground().advance_clock(Duration::from_secs(10));
+        let get_messages = server.receive::<proto::GetChannelMessages>().await.unwrap();
+    }
 }

zed/src/rpc.rs 🔗

@@ -146,14 +146,27 @@ impl Client {
                 }));
             }
             Status::ConnectionLost => {
-                state._maintain_connection = Some(cx.foreground().spawn(async move {
-                    // TODO: try to reconnect
+                let this = self.clone();
+                let foreground = cx.foreground();
+                state._maintain_connection = Some(cx.spawn(|cx| async move {
+                    let mut delay_seconds = 5;
+                    while let Err(error) = this.authenticate_and_connect(&cx).await {
+                        log::error!("failed to connect {}", error);
+                        let delay = Duration::from_secs(delay_seconds);
+                        this.set_status(
+                            Status::ReconnectionError {
+                                next_reconnection: Instant::now() + delay,
+                            },
+                            &cx,
+                        );
+                        foreground.sleep(delay).await;
+                        delay_seconds = (delay_seconds * 2).min(300);
+                    }
                 }));
             }
-            Status::Disconnected => {
+            _ => {
                 state._maintain_connection.take();
             }
-            _ => {}
         }
     }