Compare commits

...
Author SHA1 Message Date
mackabyandZaki 5677e5e955 fix(db): invoke shutdown during runtime teardown
Call Database::shutdown() from async_main graceful shutdown after webhook/tunnel stop.

Add debug logs for libSQL no-op shutdown paths (no replicator and sync-not-supported).
2026-03-12 15:08:08 -07:00
mackabyandZaki 91e0c2ee62 feat(db): add backend shutdown hook for graceful runtime shutdown
Add Database::shutdown() with a default no-op implementation for backward compatibility.

Implement libSQL shutdown via flush_replicator(), treating SyncNotSupported as non-fatal.

Implement Postgres shutdown by closing the pool.
2026-03-12 15:06:12 -07:00
4 changed files with 38 additions and 0 deletions
+18
View File
@@ -326,6 +326,24 @@ impl Database for LibSqlBackend {
libsql_migrations::run_incremental(&conn).await?;
Ok(())
}
async fn shutdown(&self) -> Result<(), DatabaseError> {
match self.db.flush_replicator().await {
Ok(Some(frame_no)) => {
tracing::debug!("libSQL replicator flushed at frame {}", frame_no);
Ok(())
}
Ok(None) => {
tracing::debug!("No libSQL replicator to flush, skipping shutdown sync");
Ok(())
}
Err(libsql::Error::SyncNotSupported(_)) => {
tracing::debug!("libSQL sync not supported, skipping flush on shutdown");
Ok(())
}
Err(error) => Err(DatabaseError::from(error)),
}
}
}
// ==================== Row conversion helpers ====================
+7
View File
@@ -523,6 +523,13 @@ pub trait Database:
{
/// Run schema migrations for this backend.
async fn run_migrations(&self) -> Result<(), DatabaseError>;
/// Shutdown hook for backend-specific drain/flush behavior.
///
/// Default implementation is a no-op so existing backends remain compatible.
async fn shutdown(&self) -> Result<(), DatabaseError> {
Ok(())
}
}
#[cfg(test)]
+5
View File
@@ -61,6 +61,11 @@ impl Database for PgBackend {
async fn run_migrations(&self) -> Result<(), DatabaseError> {
self.store.run_migrations().await
}
async fn shutdown(&self) -> Result<(), DatabaseError> {
self.store.pool().close();
Ok(())
}
}
// ==================== ConversationStore ====================
+8
View File
@@ -672,6 +672,8 @@ async fn async_main() -> anyhow::Result<()> {
.as_ref()
.map(|db| Arc::clone(db) as Arc<dyn ironclaw::db::SettingsStore>);
let db_for_shutdown = components.db.clone();
let deps = AgentDeps {
store: components.db,
llm: components.llm,
@@ -930,6 +932,12 @@ async fn async_main() -> anyhow::Result<()> {
}
}
if let Some(db) = db_for_shutdown {
if let Err(e) = db.shutdown().await {
tracing::warn!("Failed to shutdown database cleanly: {}", e);
}
}
tracing::debug!("Agent shutdown complete");
Ok(())