diff --git a/cmd/bd/create.go b/cmd/bd/create.go index 545c0b7a..00ba9916 100644 --- a/cmd/bd/create.go +++ b/cmd/bd/create.go @@ -361,11 +361,14 @@ var createCmd = &cobra.Command{ if err != nil { FatalError("failed to open target store: %v", err) } - defer func() { - if err := targetStore.Close(); err != nil { - fmt.Fprintf(os.Stderr, "warning: failed to close target store: %v\n", err) - } - }() + + // Close the original store before replacing it (it won't be used anymore) + // Note: We don't defer-close targetStore here because PersistentPostRun + // will close whatever store is assigned to the global `store` variable. + // This fixes the "database is closed" error during auto-flush (GH#routing-close-bug). + if store != nil { + _ = store.Close() + } // Replace store for remainder of create operation // This also bypasses daemon mode since daemon owns the current repo's store diff --git a/cmd/bd/main_test.go b/cmd/bd/main_test.go index b94291b0..e0ce4744 100644 --- a/cmd/bd/main_test.go +++ b/cmd/bd/main_test.go @@ -76,6 +76,12 @@ func TestAutoFlushOnExit(t *testing.T) { // Mark dirty (simulating CRUD operation) markDirtyAndScheduleFlush() + // Allow time for the FlushManager's background goroutine to process the markDirty event. + // Without this, there's a race condition: if Shutdown() is processed before the markDirty + // event, isDirty remains false and no flush occurs. This mimics real-world behavior where + // there's always some time between CRUD operations and process exit. + time.Sleep(10 * time.Millisecond) + // Simulate PersistentPostRun exit behavior - shutdown FlushManager // This performs the final flush before exit if err := flushManager.Shutdown(); err != nil {