root
2 years ago
3 changed files with 221 additions and 122 deletions
-
193weed/shell/command_fs_meta_snapshots_create.go
-
86weed/shell/command_fs_meta_snapshots_create_scheduler.go
-
64weed/shell/command_fs_meta_snapshots_create_scheduler_test.go
@ -0,0 +1,86 @@ |
|||||
|
package shell |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"os" |
||||
|
"path/filepath" |
||||
|
"sort" |
||||
|
"strings" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
func computeRequirementsFromDirectory(previousSnapshots []string, homeDirectory string, snapshotPath string, count int, durationDays int, targetDate time.Time) (snapshotsToRemove []string, snapshotsToGenerate []time.Time, levelDbBootstrapPath string, err error) { |
||||
|
levelDbBootstrapPath = previousSnapshots[len(previousSnapshots)-1] |
||||
|
lastSnapshotDate, err := time.Parse(DateFormat, levelDbBootstrapPath[:len(DateFormat)]) |
||||
|
if err != nil { |
||||
|
return |
||||
|
} |
||||
|
if err != nil { |
||||
|
return |
||||
|
} |
||||
|
// the snapshots cover the last nanosecond of the current date
|
||||
|
lastSnapshotDate = lastSnapshotDate.AddDate(0, 0, 1).Add(-1 * time.Nanosecond) |
||||
|
gapDuration := targetDate.Sub(lastSnapshotDate) |
||||
|
oneSnapshotInterval := 24 * time.Hour * time.Duration(durationDays) |
||||
|
totalSnapshotsInterval := 24 * time.Hour * time.Duration(durationDays*count) |
||||
|
// gap too small no snapshot will be generated
|
||||
|
if gapDuration < oneSnapshotInterval { |
||||
|
return snapshotsToRemove, snapshotsToGenerate, levelDbBootstrapPath, errors.New(fmt.Sprintf("last snapshot was generated at %v no need to generate new snapshots", lastSnapshotDate.Format(DateFormat))) |
||||
|
} else if gapDuration > totalSnapshotsInterval { |
||||
|
// gap too large generate from targetDate
|
||||
|
// and remove all previous snapshots
|
||||
|
_, snapshotsToGenerate, _, err = computeRequirementsFromEmpty(count, durationDays, targetDate) |
||||
|
for _, file := range previousSnapshots { |
||||
|
snapshotsToRemove = append(snapshotsToRemove, filepath.Join(homeDirectory, snapshotPath, file)) |
||||
|
} |
||||
|
return |
||||
|
} |
||||
|
snapshotDate := lastSnapshotDate.AddDate(0, 0, durationDays) |
||||
|
for snapshotDate.Before(targetDate) || snapshotDate.Equal(targetDate) { |
||||
|
snapshotsToGenerate = append(snapshotsToGenerate, snapshotDate) |
||||
|
snapshotDate = snapshotDate.AddDate(0, 0, durationDays) |
||||
|
} |
||||
|
totalCount := len(previousSnapshots) + len(snapshotsToGenerate) |
||||
|
toRemoveIdx := 0 |
||||
|
for toRemoveIdx < len(previousSnapshots) && totalCount-toRemoveIdx > count { |
||||
|
snapshotsToRemove = append(snapshotsToRemove, filepath.Join(homeDirectory, snapshotPath, previousSnapshots[toRemoveIdx])) |
||||
|
toRemoveIdx += 1 |
||||
|
} |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func computeRequirementsFromEmpty(count int, durationDays int, targetDate time.Time) (snapshotsToRemove []string, snapshotsToGenerate []time.Time, levelDbBootstrapPath string, err error) { |
||||
|
snapshotDate := targetDate |
||||
|
for i := 0; i < count; i++ { |
||||
|
snapshotsToGenerate = append(snapshotsToGenerate, snapshotDate) |
||||
|
snapshotDate = snapshotDate.AddDate(0, 0, -1*durationDays) |
||||
|
} |
||||
|
return snapshotsToRemove, snapshotsToGenerate, "", nil |
||||
|
} |
||||
|
|
||||
|
// compute number of snapshot need to be generated and number of snapshots to remove from give directory.
|
||||
|
func computeRequirements(homeDirectory string, snapshotPath string, count int, durationDays int) (snapshotsToRemove []string, snapshotsToGenerate []time.Time, levelDbBootstrapPath string, err error) { |
||||
|
snapshotDirectory := filepath.Join(homeDirectory, snapshotPath) |
||||
|
files, _ := os.ReadDir(snapshotDirectory) |
||||
|
// sort files by name
|
||||
|
sort.Slice(files, func(i, j int) bool { |
||||
|
return files[i].Name() < files[j].Name() |
||||
|
}) |
||||
|
// filter for snapshots file name only
|
||||
|
var prevSnapshotFiles []string |
||||
|
for _, file := range files { |
||||
|
if strings.HasSuffix(file.Name(), SnapshotDirPostFix) { |
||||
|
prevSnapshotFiles = append(prevSnapshotFiles, file.Name()) |
||||
|
} |
||||
|
} |
||||
|
curDate := time.Now() |
||||
|
curDateStr := curDate.Format(DateFormat) |
||||
|
// ensure snapshot start at today 00:00 - 1ns
|
||||
|
today, err := time.Parse(DateFormat, curDateStr) |
||||
|
targetDate := today.Add(-1 * time.Nanosecond) |
||||
|
if len(prevSnapshotFiles) == 0 { |
||||
|
return computeRequirementsFromEmpty(count, durationDays, targetDate) |
||||
|
} |
||||
|
return computeRequirementsFromDirectory(prevSnapshotFiles, homeDirectory, snapshotPath, count, durationDays, targetDate) |
||||
|
} |
@ -0,0 +1,64 @@ |
|||||
|
package shell |
||||
|
|
||||
|
import ( |
||||
|
"github.com/stretchr/testify/assert" |
||||
|
"path/filepath" |
||||
|
"reflect" |
||||
|
"testing" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
func TestComputeRequirementsFromDirectory(t *testing.T) { |
||||
|
homeDir := "home" |
||||
|
snapDir := "test" |
||||
|
// case1: we have previous snapshots, target date is relative close to the latest snapshot date, we will use previous snapshots to generate future snapshots and remove some previous snapshots.
|
||||
|
var prevSnapshots []string |
||||
|
prevSnapshots = append(prevSnapshots, "2022-01-01") |
||||
|
prevSnapshots = append(prevSnapshots, "2022-02-01") |
||||
|
prevSnapshots = append(prevSnapshots, "2022-03-01") |
||||
|
targetDate, _ := time.Parse(DateFormat, "2022-04-01") |
||||
|
targetDate = targetDate.Add(-1 * time.Nanosecond) |
||||
|
snapshotsToRemove, snapshotsToGenerate, levelDbBootstrapPath, err := computeRequirementsFromDirectory(prevSnapshots, homeDir, snapDir, 3, 15, targetDate) |
||||
|
assert.Nil(t, err) |
||||
|
assert.Equal(t, levelDbBootstrapPath, "2022-03-01", "latest previous snapshot should be 2022-03-01") |
||||
|
expectedToRemoveSnapshots := []string{filepath.Join(homeDir, snapDir, "2022-01-01"), filepath.Join(homeDir, snapDir, "2022-02-01")} |
||||
|
assert.True(t, reflect.DeepEqual(snapshotsToRemove, expectedToRemoveSnapshots)) |
||||
|
expectedSnapshotsGenerationDate := []string{"2022-03-16", "2022-03-31"} |
||||
|
for i, date := range expectedSnapshotsGenerationDate { |
||||
|
assert.Equal(t, snapshotsToGenerate[i].Format(DateFormat), date) |
||||
|
} |
||||
|
|
||||
|
// case2: we have previous snapshots, target date is too close to the latest snapshot date, no change will happen.
|
||||
|
targetDate, _ = time.Parse(DateFormat, "2022-03-02") |
||||
|
snapshotsToRemove, snapshotsToGenerate, levelDbBootstrapPath, err = computeRequirementsFromDirectory(prevSnapshots, "home", "test", 3, 15, targetDate) |
||||
|
assert.NotNil(t, err) |
||||
|
assert.Containsf(t, err.Error(), "no need to generate new snapshots", "expected error containing %q, got %s", "no need to generate new snapshots", err) |
||||
|
assert.Empty(t, snapshotsToRemove) |
||||
|
assert.Empty(t, snapshotsToGenerate) |
||||
|
assert.Equal(t, levelDbBootstrapPath, "2022-03-01", "latest previous snapshot should be 2022-03-01") |
||||
|
|
||||
|
// case3: we have previous snapshots, target date is too far out to the latest snapshot date, all previous snapshots will be removed, snapshots will be generated going backward starting on target date.
|
||||
|
targetDate, _ = time.Parse(DateFormat, "2022-12-01") |
||||
|
targetDate = targetDate.Add(-1 * time.Nanosecond) |
||||
|
snapshotsToRemove, snapshotsToGenerate, levelDbBootstrapPath, err = computeRequirementsFromDirectory(prevSnapshots, "home", "test", 3, 15, targetDate) |
||||
|
assert.Nil(t, err) |
||||
|
expectedToRemoveSnapshots = []string{filepath.Join(homeDir, snapDir, "2022-01-01"), filepath.Join(homeDir, snapDir, "2022-02-01"), filepath.Join(homeDir, snapDir, "2022-03-01")} |
||||
|
assert.True(t, reflect.DeepEqual(snapshotsToRemove, expectedToRemoveSnapshots)) |
||||
|
// we still need to skip all logs prior to 2022-03-02
|
||||
|
assert.Equal(t, levelDbBootstrapPath, "2022-03-01", "latest previous snapshot should be 2022-03-01") |
||||
|
expectedSnapshotsGenerationDate = []string{"2022-11-30", "2022-11-15", "2022-10-31"} |
||||
|
for i, date := range expectedSnapshotsGenerationDate { |
||||
|
assert.Equal(t, snapshotsToGenerate[i].Format(DateFormat), date) |
||||
|
} |
||||
|
|
||||
|
// case4: the target date is exactly n snapshots away from previous snapshot date
|
||||
|
targetDate, _ = time.Parse(DateFormat, "2022-03-04") |
||||
|
targetDate = targetDate.Add(-1 * time.Nanosecond) |
||||
|
snapshotsToRemove, snapshotsToGenerate, levelDbBootstrapPath, err = computeRequirementsFromDirectory(prevSnapshots, "home", "test", 3, 1, targetDate) |
||||
|
expectedToRemoveSnapshots = []string{filepath.Join(homeDir, snapDir, "2022-01-01"), filepath.Join(homeDir, snapDir, "2022-02-01")} |
||||
|
assert.True(t, reflect.DeepEqual(snapshotsToRemove, expectedToRemoveSnapshots)) |
||||
|
expectedSnapshotsGenerationDate = []string{"2022-03-02", "2022-03-03"} |
||||
|
for i, date := range expectedSnapshotsGenerationDate { |
||||
|
assert.Equal(t, snapshotsToGenerate[i].Format(DateFormat), date) |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue