collab: Don't run migrations on startup (#44430)

This PR removes the step that applies migrations when Collab starts up,
as migrations are now done as part of Cloud deployments.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers
2025-12-08 21:46:52 +00:00
committed by GitHub
parent 6b2d1f153d
commit c005adb09c
10 changed files with 10 additions and 210 deletions
-4
View File
@@ -34,8 +34,4 @@ RUN apt-get update; \
linux-perf binutils
WORKDIR app
COPY --from=builder /app/collab /app/collab
COPY --from=builder /app/crates/collab/migrations /app/migrations
COPY --from=builder /app/crates/collab/migrations_llm /app/migrations_llm
ENV MIGRATIONS_PATH=/app/migrations
ENV LLM_DATABASE_MIGRATIONS_PATH=/app/migrations_llm
ENTRYPOINT ["/app/collab"]
-12
View File
@@ -63,15 +63,3 @@ Deployment is triggered by pushing to the `collab-staging` (or `collab-productio
- `./script/deploy-collab production`
You can tell what is currently deployed with `./script/what-is-deployed`.
# Database Migrations
To create a new migration:
```sh
./script/create-migration <name>
```
Migrations are run automatically on service start, so run `foreman start` again. The service will crash if the migrations fail.
When you create a new migration, you also need to update the [SQLite schema](./migrations.sqlite/20221109000000_test_schema.sql) that is used for testing.
-21
View File
@@ -1,21 +0,0 @@
apiVersion: batch/v1
kind: Job
metadata:
namespace: ${ZED_KUBE_NAMESPACE}
name: ${ZED_MIGRATE_JOB_NAME}
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrator
imagePullPolicy: Always
image: ${ZED_IMAGE_ID}
args:
- migrate
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: database
key: url
+8 -9
View File
@@ -3,22 +3,21 @@ mod channel_tests;
mod contributor_tests;
mod db_tests;
mod extension_tests;
mod migrations;
use crate::migrations::run_database_migrations;
use std::sync::Arc;
use std::sync::atomic::{AtomicI32, Ordering::SeqCst};
use std::time::Duration;
use super::*;
use gpui::BackgroundExecutor;
use parking_lot::Mutex;
use rand::prelude::*;
use sea_orm::ConnectionTrait;
use sqlx::migrate::MigrateDatabase;
use std::{
sync::{
Arc,
atomic::{AtomicI32, Ordering::SeqCst},
},
time::Duration,
};
use self::migrations::run_database_migrations;
use super::*;
pub struct TestDb {
pub db: Option<Arc<Database>>,
-2
View File
@@ -3,8 +3,6 @@ pub mod auth;
pub mod db;
pub mod env;
pub mod executor;
pub mod llm;
pub mod migrations;
pub mod rpc;
pub mod seed;
-1
View File
@@ -1 +0,0 @@
pub mod db;
-98
View File
@@ -1,98 +0,0 @@
use std::future::Future;
use std::sync::Arc;
use anyhow::Context;
pub use sea_orm::ConnectOptions;
use sea_orm::{DatabaseConnection, DatabaseTransaction, IsolationLevel, TransactionTrait};
use crate::Result;
use crate::db::TransactionHandle;
use crate::executor::Executor;
/// The database for the LLM service.
pub struct LlmDatabase {
options: ConnectOptions,
pool: DatabaseConnection,
#[allow(unused)]
executor: Executor,
#[cfg(test)]
runtime: Option<tokio::runtime::Runtime>,
}
impl LlmDatabase {
/// Connects to the database with the given options
pub async fn new(options: ConnectOptions, executor: Executor) -> Result<Self> {
sqlx::any::install_default_drivers();
Ok(Self {
options: options.clone(),
pool: sea_orm::Database::connect(options).await?,
executor,
#[cfg(test)]
runtime: None,
})
}
pub fn options(&self) -> &ConnectOptions {
&self.options
}
pub async fn transaction<F, Fut, T>(&self, f: F) -> Result<T>
where
F: Send + Fn(TransactionHandle) -> Fut,
Fut: Send + Future<Output = Result<T>>,
{
let body = async {
let (tx, result) = self.with_transaction(&f).await?;
match result {
Ok(result) => match tx.commit().await.map_err(Into::into) {
Ok(()) => Ok(result),
Err(error) => Err(error),
},
Err(error) => {
tx.rollback().await?;
Err(error)
}
}
};
self.run(body).await
}
async fn with_transaction<F, Fut, T>(&self, f: &F) -> Result<(DatabaseTransaction, Result<T>)>
where
F: Send + Fn(TransactionHandle) -> Fut,
Fut: Send + Future<Output = Result<T>>,
{
let tx = self
.pool
.begin_with_config(Some(IsolationLevel::ReadCommitted), None)
.await?;
let mut tx = Arc::new(Some(tx));
let result = f(TransactionHandle(tx.clone())).await;
let tx = Arc::get_mut(&mut tx)
.and_then(|tx| tx.take())
.context("couldn't complete transaction because it's still in use")?;
Ok((tx, result))
}
async fn run<F, T>(&self, future: F) -> Result<T>
where
F: Future<Output = Result<T>>,
{
#[cfg(test)]
{
if let Executor::Deterministic(executor) = &self.executor {
executor.simulate_random_delay().await;
}
self.runtime.as_ref().unwrap().block_on(future)
}
#[cfg(not(test))]
{
future.await
}
}
}
+2 -60
View File
@@ -1,4 +1,4 @@
use anyhow::{Context as _, anyhow};
use anyhow::anyhow;
use axum::headers::HeaderMapExt;
use axum::{
Extension, Router,
@@ -9,8 +9,6 @@ use axum::{
use collab::ServiceMode;
use collab::api::CloudflareIpCountryHeader;
use collab::llm::db::LlmDatabase;
use collab::migrations::run_database_migrations;
use collab::{
AppState, Config, Result, api::fetch_extensions_from_blob_store_periodically, db, env,
executor::Executor,
@@ -19,7 +17,6 @@ use db::Database;
use std::{
env::args,
net::{SocketAddr, TcpListener},
path::Path,
sync::Arc,
time::Duration,
};
@@ -49,10 +46,6 @@ async fn main() -> Result<()> {
Some("version") => {
println!("collab v{} ({})", VERSION, REVISION.unwrap_or("unknown"));
}
Some("migrate") => {
let config = envy::from_env::<Config>().expect("error loading config");
setup_app_database(&config).await?;
}
Some("seed") => {
let config = envy::from_env::<Config>().expect("error loading config");
let db_options = db::ConnectOptions::new(config.database_url.clone());
@@ -69,7 +62,7 @@ async fn main() -> Result<()> {
Some("all") => ServiceMode::All,
_ => {
return Err(anyhow!(
"usage: collab <version | migrate | seed | serve <api|collab|all>>"
"usage: collab <version | seed | serve <api|collab|all>>"
))?;
}
};
@@ -90,7 +83,6 @@ async fn main() -> Result<()> {
if mode.is_collab() || mode.is_api() {
setup_app_database(&config).await?;
setup_llm_database(&config).await?;
let state = AppState::new(config, Executor::Production).await?;
@@ -211,25 +203,6 @@ async fn setup_app_database(config: &Config) -> Result<()> {
let db_options = db::ConnectOptions::new(config.database_url.clone());
let mut db = Database::new(db_options).await?;
let migrations_path = config.migrations_path.as_deref().unwrap_or_else(|| {
#[cfg(feature = "sqlite")]
let default_migrations = concat!(env!("CARGO_MANIFEST_DIR"), "/migrations.sqlite");
#[cfg(not(feature = "sqlite"))]
let default_migrations = concat!(env!("CARGO_MANIFEST_DIR"), "/migrations");
Path::new(default_migrations)
});
let migrations = run_database_migrations(db.options(), migrations_path).await?;
for (migration, duration) in migrations {
log::info!(
"Migrated {} {} {:?}",
migration.version,
migration.description,
duration
);
}
db.initialize_notification_kinds().await?;
if config.seed_path.is_some() {
@@ -239,37 +212,6 @@ async fn setup_app_database(config: &Config) -> Result<()> {
Ok(())
}
async fn setup_llm_database(config: &Config) -> Result<()> {
let database_url = config
.llm_database_url
.as_ref()
.context("missing LLM_DATABASE_URL")?;
let db_options = db::ConnectOptions::new(database_url.clone());
let db = LlmDatabase::new(db_options, Executor::Production).await?;
let migrations_path = config
.llm_database_migrations_path
.as_deref()
.unwrap_or_else(|| {
let default_migrations = concat!(env!("CARGO_MANIFEST_DIR"), "/migrations_llm");
Path::new(default_migrations)
});
let migrations = run_database_migrations(db.options(), migrations_path).await?;
for (migration, duration) in migrations {
log::info!(
"Migrated {} {} {:?}",
migration.version,
migration.description,
duration
);
}
Ok(())
}
async fn handle_root(Extension(mode): Extension<ServiceMode>) -> String {
format!("zed:{mode} v{VERSION} ({})", REVISION.unwrap_or("unknown"))
}
-3
View File
@@ -1,3 +0,0 @@
zed . \
"crates/collab/migrations.sqlite/20221109000000_test_schema.sql" \
"crates/collab/migrations/$(date -u +%Y%m%d%H%M%S)_$(echo $1 | sed 's/[^a-z0-9]/_/g').sql"