fix(store): preserve orphan files for recovery
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
7d3b62086d
commit
e1cb275578
3 changed files with 47 additions and 3 deletions
9
store.go
9
store.go
|
|
@ -258,19 +258,22 @@ func (storeInstance *Store) Close() error {
|
|||
storeInstance.callbacksLock.Unlock()
|
||||
|
||||
storeInstance.orphanWorkspacesLock.Lock()
|
||||
var orphanCleanupErr error
|
||||
for _, orphanWorkspace := range storeInstance.orphanWorkspaces {
|
||||
orphanWorkspace.Discard()
|
||||
if err := orphanWorkspace.closeWithoutRemovingFiles(); err != nil && orphanCleanupErr == nil {
|
||||
orphanCleanupErr = err
|
||||
}
|
||||
}
|
||||
storeInstance.orphanWorkspaces = nil
|
||||
storeInstance.orphanWorkspacesLock.Unlock()
|
||||
|
||||
if storeInstance.database == nil {
|
||||
return nil
|
||||
return orphanCleanupErr
|
||||
}
|
||||
if err := storeInstance.database.Close(); err != nil {
|
||||
return core.E("store.Close", "database close", err)
|
||||
}
|
||||
return nil
|
||||
return orphanCleanupErr
|
||||
}
|
||||
|
||||
// Usage example: `colourValue, err := storeInstance.Get("config", "colour")`
|
||||
|
|
|
|||
13
workspace.go
13
workspace.go
|
|
@ -354,6 +354,16 @@ func (workspace *Workspace) aggregateFields() (map[string]any, error) {
|
|||
}
|
||||
|
||||
func (workspace *Workspace) closeAndRemoveFiles() error {
|
||||
return workspace.closeAndCleanup(true)
|
||||
}
|
||||
|
||||
// closeWithoutRemovingFiles closes the database handle but leaves the orphan
|
||||
// file on disk so a later store instance can recover it.
|
||||
func (workspace *Workspace) closeWithoutRemovingFiles() error {
|
||||
return workspace.closeAndCleanup(false)
|
||||
}
|
||||
|
||||
func (workspace *Workspace) closeAndCleanup(removeFiles bool) error {
|
||||
if workspace == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -372,6 +382,9 @@ func (workspace *Workspace) closeAndRemoveFiles() error {
|
|||
if err := workspace.database.Close(); err != nil {
|
||||
return core.E("store.Workspace.closeAndRemoveFiles", "close workspace database", err)
|
||||
}
|
||||
if !removeFiles {
|
||||
return nil
|
||||
}
|
||||
for _, path := range []string{workspace.databasePath, workspace.databasePath + "-wal", workspace.databasePath + "-shm"} {
|
||||
if result := workspace.filesystem.Delete(path); !result.OK && workspace.filesystem.Exists(path) {
|
||||
return core.E("store.Workspace.closeAndRemoveFiles", "delete workspace file", result.Value.(error))
|
||||
|
|
|
|||
|
|
@ -246,3 +246,31 @@ func TestWorkspace_New_Good_CachesOrphansDuringConstruction(t *testing.T) {
|
|||
assert.Equal(t, map[string]any{}, orphans[0].Aggregate())
|
||||
orphans[0].Discard()
|
||||
}
|
||||
|
||||
func TestWorkspace_Close_Good_PreservesOrphansForRecovery(t *testing.T) {
|
||||
stateDirectory := useWorkspaceStateDirectory(t)
|
||||
requireCoreOK(t, testFilesystem().EnsureDir(stateDirectory))
|
||||
|
||||
orphanDatabasePath := workspaceFilePath(stateDirectory, "orphan-session")
|
||||
orphanDatabase, err := openWorkspaceDatabase(orphanDatabasePath)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, orphanDatabase.Close())
|
||||
assert.True(t, testFilesystem().Exists(orphanDatabasePath))
|
||||
|
||||
storeInstance, err := New(":memory:")
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, storeInstance.Close())
|
||||
|
||||
assert.True(t, testFilesystem().Exists(orphanDatabasePath))
|
||||
|
||||
recoveryStore, err := New(":memory:")
|
||||
require.NoError(t, err)
|
||||
defer recoveryStore.Close()
|
||||
|
||||
orphans := recoveryStore.RecoverOrphans(stateDirectory)
|
||||
require.Len(t, orphans, 1)
|
||||
assert.Equal(t, "orphan-session", orphans[0].Name())
|
||||
orphans[0].Discard()
|
||||
assert.False(t, testFilesystem().Exists(orphanDatabasePath))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue