From 8fe593bb6fc68b16ffaf18ab8fca1cbdf2a98bb3 Mon Sep 17 00:00:00 2001 From: Alexander Lazarenko Date: Mon, 7 Jul 2025 21:04:34 +0300 Subject: [PATCH] Refactor snapshot parent updates: replace full metadata reload with UpdateSnapshotParentID method, enhance functional test logging, and add CleanOnRestore option. --- functional_test.go | 35 +++++++++++++++++++++++++++++++---- manager.go | 14 ++------------ store/sqlite/sqlite.go | 10 ++++++++++ store/store.go | 3 +++ 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/functional_test.go b/functional_test.go index e842694..3efc5c0 100644 --- a/functional_test.go +++ b/functional_test.go @@ -20,7 +20,8 @@ func TestFullWorkflow(t *testing.T) { // Create Agate options options := AgateOptions{ - WorkDir: tempDir, + WorkDir: tempDir, + CleanOnRestore: true, } // Create Agate instance @@ -123,12 +124,20 @@ func TestFullWorkflow(t *testing.T) { } if string(content) != expectedContent { t.Errorf("Restored file %s has wrong content: got %s, want %s", path, string(content), expectedContent) + } else { + t.Logf("SUCCESS: Restored file %s has correct content after restoring first snapshot", path) } } // Check that file4.txt doesn't exist - if _, err := os.Stat(filepath.Join(dataDir, "file4.txt")); !os.IsNotExist(err) { + file4Path := filepath.Join(dataDir, "file4.txt") + _, err = os.Stat(file4Path) + if err == nil { t.Errorf("File4.txt should not exist after restoring first snapshot") + } else if !os.IsNotExist(err) { + t.Errorf("Unexpected error checking if File4.txt exists: %v", err) + } else { + t.Logf("SUCCESS: File4.txt correctly does not exist after restoring first snapshot") } // Step 9: Restore the third snapshot @@ -152,12 +161,20 @@ func TestFullWorkflow(t *testing.T) { } if string(content) != expectedContent { t.Errorf("Restored file %s has wrong content: got %s, want %s", path, string(content), expectedContent) + } else { + t.Logf("SUCCESS: Restored file %s has correct content after restoring third snapshot", path) } } // Check that file2.txt doesn't exist - if _, err := os.Stat(filepath.Join(dataDir, "file2.txt")); !os.IsNotExist(err) { + file2Path := filepath.Join(dataDir, "file2.txt") + _, err = os.Stat(file2Path) + if err == nil { t.Errorf("File2.txt should not exist after restoring third snapshot") + } else if !os.IsNotExist(err) { + t.Errorf("Unexpected error checking if File2.txt exists: %v", err) + } else { + t.Logf("SUCCESS: File2.txt correctly does not exist after restoring third snapshot") } // Step 11: Delete a snapshot @@ -190,17 +207,26 @@ func TestFullWorkflow(t *testing.T) { // Verify that snapshot 3's parent ID has been updated to point to snapshot 1 if snapshot3 != nil && snapshot3.ParentID != snapshot1ID { t.Errorf("Snapshot 3's parent ID should be updated to point to Snapshot 1 after Snapshot 2 is deleted. Got ParentID=%s, want ParentID=%s", snapshot3.ParentID, snapshot1ID) + } else { + t.Logf("SUCCESS: Snapshot 3's parent ID has been correctly updated to point to Snapshot 1: %s", snapshot3.ParentID) } if len(snapshots) != 2 { t.Errorf("Expected 2 snapshots after deletion, got %d", len(snapshots)) + } else { + t.Logf("SUCCESS: Found correct number of snapshots after deletion: %d", len(snapshots)) } + foundDeletedSnapshot := false for _, snap := range snapshots { if snap.ID == snapshot2ID { + foundDeletedSnapshot = true t.Errorf("Snapshot 2 (ID=%s) should have been deleted", snapshot2ID) } } + if !foundDeletedSnapshot { + t.Logf("SUCCESS: Snapshot 2 (ID=%s) was correctly deleted", snapshot2ID) + } } // TestLargeFiles tests creating and restoring snapshots with large files @@ -219,7 +245,8 @@ func TestLargeFiles(t *testing.T) { // Create Agate options options := AgateOptions{ - WorkDir: tempDir, + WorkDir: tempDir, + CleanOnRestore: true, OpenFunc: func(dir string) error { return nil }, diff --git a/manager.go b/manager.go index fc8df9a..6b0e14d 100644 --- a/manager.go +++ b/manager.go @@ -207,18 +207,8 @@ func (data *SnapshotManagerData) DeleteSnapshot(ctx context.Context, snapshotID // Update parent references for any snapshots that have this one as a parent for _, info := range allSnapshots { if info.ParentID == snapshotID { - // Get the full snapshot details - childSnapshot, err := data.metadataStore.GetSnapshotMetadata(ctx, info.ID) - if err != nil { - data.logger.Printf("WARNING: failed to get child snapshot %s details: %v", info.ID, err) - continue - } - - // Update the parent ID to point to the deleted snapshot's parent - childSnapshot.ParentID = parentID - - // Save the updated snapshot - if err := data.metadataStore.SaveSnapshotMetadata(ctx, *childSnapshot); err != nil { + // Используем новый, более надежный метод для обновления только parent_id + if err := data.metadataStore.UpdateSnapshotParentID(ctx, info.ID, parentID); err != nil { data.logger.Printf("WARNING: failed to update parent reference for snapshot %s: %v", info.ID, err) } else { data.logger.Printf("Updated parent reference for snapshot %s from %s to %s", info.ID, snapshotID, parentID) diff --git a/store/sqlite/sqlite.go b/store/sqlite/sqlite.go index bd20a00..e3f7167 100644 --- a/store/sqlite/sqlite.go +++ b/store/sqlite/sqlite.go @@ -265,3 +265,13 @@ func (s *sqliteStore) DeleteSnapshotMetadata(ctx context.Context, snapshotID str return nil // Не возвращаем ошибку, если запись не найдена } + +// UpdateSnapshotParentID обновляет ParentID для указанного снапшота. +func (s *sqliteStore) UpdateSnapshotParentID(ctx context.Context, snapshotID, newParentID string) error { + query := `UPDATE snapshots SET parent_id = ? WHERE id = ?;` + _, err := s.db.ExecContext(ctx, query, newParentID, snapshotID) + if err != nil { + return fmt.Errorf("failed to update parent ID for snapshot %s: %w", snapshotID, err) + } + return nil +} diff --git a/store/store.go b/store/store.go index d181b33..a96ad54 100644 --- a/store/store.go +++ b/store/store.go @@ -55,6 +55,9 @@ type MetadataStore interface { // Не должен возвращать ошибку, если снапшот не найден. DeleteSnapshotMetadata(ctx context.Context, snapshotID string) error + // UpdateSnapshotParentID обновляет ParentID для указанного снапшота. + UpdateSnapshotParentID(ctx context.Context, snapshotID, newParentID string) error + // Close закрывает соединение с хранилищем метаданных. Close() error }