Allow nulls in `projects.host_connection_{id,server_id}`

Antonio Scandurra created

The server version on stable won't be able to fill values for those
columns when we deploy the migration to preview.

With this commit we're also dropping the unused `worktree_extensions`
and `project_activity_periods` tables. The last version of the server
on stable (0.2.6) doesn't contain any code that accesses those tables.

Change summary

crates/collab/migrations.sqlite/20221109000000_test_schema.sql                |  7 
crates/collab/migrations/20221214144346_change_epoch_from_uuid_to_integer.sql |  8 
crates/collab/src/db.rs                                                       | 39 
crates/collab/src/db/project.rs                                               | 23 
crates/collab/src/rpc.rs                                                      |  1 
5 files changed, 40 insertions(+), 38 deletions(-)

Detailed changes

crates/collab/migrations.sqlite/20221109000000_test_schema.sql 🔗

@@ -43,11 +43,12 @@ CREATE TABLE "projects" (
     "id" INTEGER PRIMARY KEY AUTOINCREMENT,
     "room_id" INTEGER REFERENCES rooms (id) NOT NULL,
     "host_user_id" INTEGER REFERENCES users (id) NOT NULL,
-    "host_connection_id" INTEGER NOT NULL,
-    "host_connection_server_id" INTEGER NOT NULL REFERENCES servers (id) ON DELETE CASCADE,
+    "host_connection_id" INTEGER,
+    "host_connection_server_id" INTEGER REFERENCES servers (id) ON DELETE CASCADE,
     "unregistered" BOOLEAN NOT NULL DEFAULT FALSE
 );
-CREATE INDEX "index_projects_on_host_connection_epoch" ON "projects" ("host_connection_epoch");
+CREATE INDEX "index_projects_on_host_connection_server_id" ON "projects" ("host_connection_server_id");
+CREATE INDEX "index_projects_on_host_connection_id_and_host_connection_server_id" ON "projects" ("host_connection_id", "host_connection_server_id");
 
 CREATE TABLE "worktrees" (
     "project_id" INTEGER NOT NULL REFERENCES projects (id) ON DELETE CASCADE,

crates/collab/migrations/20221214144346_change_epoch_from_uuid_to_integer.sql 🔗

@@ -3,10 +3,14 @@ CREATE TABLE servers (
     environment VARCHAR NOT NULL
 );
 
-DELETE FROM projects;
+DROP TABLE worktree_extensions;
+DROP TABLE project_activity_periods;
+DELETE from projects;
 ALTER TABLE projects
     DROP COLUMN host_connection_epoch,
-    ADD COLUMN host_connection_server_id INTEGER NOT NULL REFERENCES servers (id) ON DELETE CASCADE;
+    ADD COLUMN host_connection_server_id INTEGER REFERENCES servers (id) ON DELETE CASCADE;
+CREATE INDEX "index_projects_on_host_connection_server_id" ON "projects" ("host_connection_server_id");
+CREATE INDEX "index_projects_on_host_connection_id_and_host_connection_server_id" ON "projects" ("host_connection_id", "host_connection_server_id");
 
 DELETE FROM project_collaborators;
 ALTER TABLE project_collaborators

crates/collab/src/db.rs 🔗

@@ -1643,10 +1643,7 @@ impl Database {
                     left_projects.push(LeftProject {
                         id: project.id,
                         host_user_id: project.host_user_id,
-                        host_connection_id: ConnectionId {
-                            id: project.host_connection_id as u32,
-                            owner_id: project.host_connection_server_id.0 as u32,
-                        },
+                        host_connection_id: project.host_connection()?,
                         connection_ids,
                     });
                 }
@@ -1764,10 +1761,7 @@ impl Database {
 
         while let Some(row) = db_projects.next().await {
             let (db_project, db_worktree) = row?;
-            let host_connection = ConnectionId {
-                owner_id: db_project.host_connection_server_id.0 as u32,
-                id: db_project.host_connection_id as u32,
-            };
+            let host_connection = db_project.host_connection()?;
             if let Some(participant) = participants.get_mut(&host_connection) {
                 let project = if let Some(project) = participant
                     .projects
@@ -1848,8 +1842,10 @@ impl Database {
             let project = project::ActiveModel {
                 room_id: ActiveValue::set(participant.room_id),
                 host_user_id: ActiveValue::set(participant.user_id),
-                host_connection_id: ActiveValue::set(connection.id as i32),
-                host_connection_server_id: ActiveValue::set(ServerId(connection.owner_id as i32)),
+                host_connection_id: ActiveValue::set(Some(connection.id as i32)),
+                host_connection_server_id: ActiveValue::set(Some(ServerId(
+                    connection.owner_id as i32,
+                ))),
                 ..Default::default()
             }
             .insert(&*tx)
@@ -1901,11 +1897,7 @@ impl Database {
                 .one(&*tx)
                 .await?
                 .ok_or_else(|| anyhow!("project not found"))?;
-            let host_connection = ConnectionId {
-                owner_id: project.host_connection_server_id.0 as u32,
-                id: project.host_connection_id as u32,
-            };
-            if host_connection == connection {
+            if project.host_connection()? == connection {
                 let room_id = project.room_id;
                 project::Entity::delete(project.into_active_model())
                     .exec(&*tx)
@@ -2088,11 +2080,7 @@ impl Database {
                 .one(&*tx)
                 .await?
                 .ok_or_else(|| anyhow!("no such project"))?;
-            let host_connection = ConnectionId {
-                owner_id: project.host_connection_server_id.0 as u32,
-                id: project.host_connection_id as u32,
-            };
-            if host_connection != connection {
+            if project.host_connection()? != connection {
                 return Err(anyhow!("can't update a project hosted by someone else"))?;
             }
 
@@ -2145,11 +2133,7 @@ impl Database {
                 .one(&*tx)
                 .await?
                 .ok_or_else(|| anyhow!("no such project"))?;
-            let host_connection = ConnectionId {
-                owner_id: project.host_connection_server_id.0 as u32,
-                id: project.host_connection_id as u32,
-            };
-            if host_connection != connection {
+            if project.host_connection()? != connection {
                 return Err(anyhow!("can't update a project hosted by someone else"))?;
             }
 
@@ -2362,10 +2346,7 @@ impl Database {
             let left_project = LeftProject {
                 id: project_id,
                 host_user_id: project.host_user_id,
-                host_connection_id: ConnectionId {
-                    owner_id: project.host_connection_server_id.0 as u32,
-                    id: project.host_connection_id as u32,
-                },
+                host_connection_id: project.host_connection()?,
                 connection_ids,
             };
             Ok((project.room_id, left_project))

crates/collab/src/db/project.rs 🔗

@@ -1,4 +1,6 @@
-use super::{ProjectId, RoomId, ServerId, UserId};
+use super::{ProjectId, Result, RoomId, ServerId, UserId};
+use anyhow::anyhow;
+use rpc::ConnectionId;
 use sea_orm::entity::prelude::*;
 
 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
@@ -8,8 +10,23 @@ pub struct Model {
     pub id: ProjectId,
     pub room_id: RoomId,
     pub host_user_id: UserId,
-    pub host_connection_id: i32,
-    pub host_connection_server_id: ServerId,
+    pub host_connection_id: Option<i32>,
+    pub host_connection_server_id: Option<ServerId>,
+}
+
+impl Model {
+    pub fn host_connection(&self) -> Result<ConnectionId> {
+        let host_connection_server_id = self
+            .host_connection_server_id
+            .ok_or_else(|| anyhow!("empty host_connection_server_id"))?;
+        let host_connection_id = self
+            .host_connection_id
+            .ok_or_else(|| anyhow!("empty host_connection_id"))?;
+        Ok(ConnectionId {
+            owner_id: host_connection_server_id.0 as u32,
+            id: host_connection_id as u32,
+        })
+    }
 }
 
 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

crates/collab/src/rpc.rs 🔗

@@ -1506,7 +1506,6 @@ async fn update_buffer(
         .project_connection_ids(project_id, session.connection_id)
         .await?;
 
-    dbg!(session.connection_id, &*project_connection_ids);
     broadcast(
         session.connection_id,
         project_connection_ids.iter().copied(),