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