From ca07137d3215bb1d9d62c4498083bd0274a6ad45 Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Thu, 14 Apr 2022 19:17:44 -0600 Subject: [PATCH] Re-add point-in-time restore --- cmd/litestream/restore.go | 45 +++-- replica_client.go | 82 +++++++++ replica_client_test.go | 161 ++++++++++++++++++ testdata/Makefile | 5 + .../generations/0000000000000000/.gitignore | 0 testdata/index-by-timestamp/no-wal/Makefile | 6 + .../snapshots/0000000000000000.snapshot.lz4 | Bin 0 -> 93 bytes .../snapshots/0000000000000001.snapshot.lz4 | Bin 0 -> 93 bytes .../snapshots/0000000000000002.snapshot.lz4 | Bin 0 -> 93 bytes testdata/index-by-timestamp/ok/Makefile | 11 ++ .../snapshots/0000000000000000.snapshot.lz4 | Bin 0 -> 93 bytes .../snapshots/0000000000000001.snapshot.lz4 | Bin 0 -> 93 bytes .../0000000000000000/0000000000000000.wal.lz4 | Bin 0 -> 93 bytes .../0000000000000000/0000000000001234.wal.lz4 | Bin 0 -> 93 bytes .../0000000000000001/0000000000000000.wal.lz4 | Bin 0 -> 93 bytes .../0000000000000002/0000000000000000.wal.lz4 | Bin 0 -> 93 bytes .../0000000000000003/0000000000000000.wal.lz4 | Bin 0 -> 93 bytes .../snapshot-later-than-wal/Makefile | 7 + .../snapshots/0000000000000000.snapshot.lz4 | Bin 0 -> 93 bytes .../snapshots/0000000000000001.snapshot.lz4 | Bin 0 -> 93 bytes .../0000000000000000/0000000000000000.wal.lz4 | Bin 0 -> 93 bytes .../0000000000000000/0000000000001234.wal.lz4 | Bin 0 -> 93 bytes .../generations/0000000000000000/.gitignore | 0 .../snapshot-index-by-timestamp/ok/Makefile | 5 + .../snapshots/0000000000000000.snapshot.lz4 | Bin 0 -> 93 bytes .../snapshots/00000000000003e8.snapshot.lz4 | Bin 0 -> 93 bytes .../snapshots/00000000000007d0.snapshot.lz4 | Bin 0 -> 93 bytes testdata/wal-index-by-timestamp/ok/Makefile | 6 + .../0000000000000000/0000000000000000.wal.lz4 | Bin 0 -> 93 bytes .../0000000000000000/0000000000001234.wal.lz4 | Bin 0 -> 93 bytes .../0000000000000001/0000000000000000.wal.lz4 | Bin 0 -> 93 bytes 31 files changed, 318 insertions(+), 10 deletions(-) create mode 100644 testdata/index-by-timestamp/no-snapshots/generations/0000000000000000/.gitignore create mode 100644 testdata/index-by-timestamp/no-wal/Makefile create mode 100644 testdata/index-by-timestamp/no-wal/generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 create mode 100644 testdata/index-by-timestamp/no-wal/generations/0000000000000000/snapshots/0000000000000001.snapshot.lz4 create mode 100644 testdata/index-by-timestamp/no-wal/generations/0000000000000000/snapshots/0000000000000002.snapshot.lz4 create mode 100644 testdata/index-by-timestamp/ok/Makefile create mode 100644 testdata/index-by-timestamp/ok/generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 create mode 100644 testdata/index-by-timestamp/ok/generations/0000000000000000/snapshots/0000000000000001.snapshot.lz4 create mode 100644 testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000000/0000000000000000.wal.lz4 create mode 100644 testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000000/0000000000001234.wal.lz4 create mode 100644 testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000001/0000000000000000.wal.lz4 create mode 100644 testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000002/0000000000000000.wal.lz4 create mode 100644 testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000003/0000000000000000.wal.lz4 create mode 100644 testdata/index-by-timestamp/snapshot-later-than-wal/Makefile create mode 100644 testdata/index-by-timestamp/snapshot-later-than-wal/generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 create mode 100644 testdata/index-by-timestamp/snapshot-later-than-wal/generations/0000000000000000/snapshots/0000000000000001.snapshot.lz4 create mode 100644 testdata/index-by-timestamp/snapshot-later-than-wal/generations/0000000000000000/wal/0000000000000000/0000000000000000.wal.lz4 create mode 100644 testdata/index-by-timestamp/snapshot-later-than-wal/generations/0000000000000000/wal/0000000000000000/0000000000001234.wal.lz4 create mode 100644 testdata/snapshot-index-by-timestamp/no-snapshots/generations/0000000000000000/.gitignore create mode 100644 testdata/snapshot-index-by-timestamp/ok/Makefile create mode 100644 testdata/snapshot-index-by-timestamp/ok/generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 create mode 100644 testdata/snapshot-index-by-timestamp/ok/generations/0000000000000000/snapshots/00000000000003e8.snapshot.lz4 create mode 100644 testdata/snapshot-index-by-timestamp/ok/generations/0000000000000000/snapshots/00000000000007d0.snapshot.lz4 create mode 100644 testdata/wal-index-by-timestamp/ok/Makefile create mode 100644 testdata/wal-index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000000/0000000000000000.wal.lz4 create mode 100644 testdata/wal-index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000000/0000000000001234.wal.lz4 create mode 100644 testdata/wal-index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000001/0000000000000000.wal.lz4 diff --git a/cmd/litestream/restore.go b/cmd/litestream/restore.go index b746b1e..6134c0f 100644 --- a/cmd/litestream/restore.go +++ b/cmd/litestream/restore.go @@ -9,6 +9,7 @@ import ( "os" "path/filepath" "strconv" + "time" "github.com/benbjohnson/litestream" ) @@ -22,14 +23,15 @@ type RestoreCommand struct { snapshotIndex int // index of snapshot to start from // CLI options - configPath string // path to config file - noExpandEnv bool // if true, do not expand env variables in config - outputPath string // path to restore database to - replicaName string // optional, name of replica to restore from - generation string // optional, generation to restore - targetIndex int // optional, last WAL index to replay - ifDBNotExists bool // if true, skips restore if output path already exists - ifReplicaExists bool // if true, skips if no backups exist + configPath string // path to config file + noExpandEnv bool // if true, do not expand env variables in config + outputPath string // path to restore database to + replicaName string // optional, name of replica to restore from + generation string // optional, generation to restore + targetIndex int // optional, last WAL index to replay + timestamp time.Time // optional, restore to point-in-time (ISO 8601) + ifDBNotExists bool // if true, skips restore if output path already exists + ifReplicaExists bool // if true, skips if no backups exist opt litestream.RestoreOptions } @@ -53,6 +55,7 @@ func (c *RestoreCommand) Run(ctx context.Context, args []string) (err error) { fs.StringVar(&c.replicaName, "replica", "", "replica name") fs.StringVar(&c.generation, "generation", "", "generation name") fs.Var((*indexVar)(&c.targetIndex), "index", "wal index") + timestampStr := fs.String("timestamp", "", "point-in-time restore (ISO 8601)") fs.IntVar(&c.opt.Parallelism, "parallelism", c.opt.Parallelism, "parallelism") fs.BoolVar(&c.ifDBNotExists, "if-db-not-exists", false, "") fs.BoolVar(&c.ifReplicaExists, "if-replica-exists", false, "") @@ -66,9 +69,20 @@ func (c *RestoreCommand) Run(ctx context.Context, args []string) (err error) { } pathOrURL := fs.Arg(0) + // Parse timestamp. + if *timestampStr != "" { + if c.timestamp, err = time.Parse(time.RFC3339Nano, *timestampStr); err != nil { + return fmt.Errorf("invalid -timestamp, expected ISO 8601: %w", err) + } + } + // Ensure a generation is specified if target index is specified. - if c.targetIndex != -1 && c.generation == "" { + if c.targetIndex != -1 && !c.timestamp.IsZero() { + return fmt.Errorf("cannot specify both -index flag and -timestamp flag") + } else if c.targetIndex != -1 && c.generation == "" { return fmt.Errorf("must specify -generation flag when using -index flag") + } else if !c.timestamp.IsZero() && c.generation == "" { + return fmt.Errorf("must specify -generation flag when using -timestamp flag") } // Default to original database path if output path not specified. @@ -117,7 +131,11 @@ func (c *RestoreCommand) Run(ctx context.Context, args []string) (err error) { } // Determine the maximum available index for the generation if one is not specified. - if c.targetIndex == -1 { + if !c.timestamp.IsZero() { + if c.targetIndex, err = litestream.FindIndexByTimestamp(ctx, r.Client(), c.generation, c.timestamp); err != nil { + return fmt.Errorf("cannot find index for timestamp in generation %q: %w", c.generation, err) + } + } else if c.targetIndex == -1 { if c.targetIndex, err = litestream.FindMaxIndexByGeneration(ctx, r.Client(), c.generation); err != nil { return fmt.Errorf("cannot determine latest index in generation %q: %w", c.generation, err) } @@ -239,6 +257,10 @@ Arguments: Restore up to a specific hex-encoded WAL index (inclusive). Defaults to use the highest available index. + -timestamp DATETIME + Restore up to a specific point-in-time. Must be ISO 8601. + Cannot be specified with -index flag. + -o PATH Output path of the restored database. Defaults to original DB path. @@ -271,6 +293,9 @@ Examples: # Restore database from specific generation on S3. $ litestream restore -replica s3 -generation xxxxxxxx /path/to/db + # Restore database to a specific point in time. + $ litestream restore -generation xxxxxxxx -timestamp 2000-01-01T00:00:00Z /path/to/db + `[1:], DefaultConfigPath(), ) diff --git a/replica_client.go b/replica_client.go index ba6d448..46af850 100644 --- a/replica_client.go +++ b/replica_client.go @@ -234,6 +234,88 @@ func ReplicaClientTimeBounds(ctx context.Context, client ReplicaClient) (min, ma return min, max, nil } +// FindIndexByTimestamp returns the highest index before a given point-in-time +// within a generation. Returns ErrNoSnapshots if no index exists on the replica +// for the generation. +func FindIndexByTimestamp(ctx context.Context, client ReplicaClient, generation string, timestamp time.Time) (index int, err error) { + snapshotIndex, err := FindSnapshotIndexByTimestamp(ctx, client, generation, timestamp) + if err == ErrNoSnapshots { + return 0, err + } else if err != nil { + return 0, fmt.Errorf("max snapshot index: %w", err) + } + + // Determine the highest available WAL index. + walIndex, err := FindWALIndexByTimestamp(ctx, client, generation, timestamp) + if err != nil && err != ErrNoWALSegments { + return 0, fmt.Errorf("max wal index: %w", err) + } + + // Use snapshot index if it's after the last WAL index. + if snapshotIndex > walIndex { + return snapshotIndex, nil + } + return walIndex, nil +} + +// FindSnapshotIndexByTimestamp returns the highest snapshot index before timestamp. +// Returns ErrNoSnapshots if no snapshots exist for the generation on the replica. +func FindSnapshotIndexByTimestamp(ctx context.Context, client ReplicaClient, generation string, timestamp time.Time) (index int, err error) { + itr, err := client.Snapshots(ctx, generation) + if err != nil { + return 0, fmt.Errorf("snapshots: %w", err) + } + defer func() { _ = itr.Close() }() + + // Iterate over snapshots to find the highest index. + var n int + for ; itr.Next(); n++ { + if info := itr.Snapshot(); info.CreatedAt.After(timestamp) { + continue + } else if info.Index > index { + index = info.Index + } + } + if err := itr.Close(); err != nil { + return 0, fmt.Errorf("snapshot iteration: %w", err) + } + + // Return an error if no snapshots were found. + if n == 0 { + return 0, ErrNoSnapshots + } + return index, nil +} + +// FindWALIndexByTimestamp returns the highest WAL index before timestamp. +// Returns ErrNoWALSegments if no segments exist for the generation on the replica. +func FindWALIndexByTimestamp(ctx context.Context, client ReplicaClient, generation string, timestamp time.Time) (index int, err error) { + itr, err := client.WALSegments(ctx, generation) + if err != nil { + return 0, fmt.Errorf("wal segments: %w", err) + } + defer func() { _ = itr.Close() }() + + // Iterate over WAL segments to find the highest index. + var n int + for ; itr.Next(); n++ { + if info := itr.WALSegment(); info.CreatedAt.After(timestamp) { + continue + } else if info.Index > index { + index = info.Index + } + } + if err := itr.Close(); err != nil { + return 0, fmt.Errorf("wal segment iteration: %w", err) + } + + // Return an error if no WAL segments were found. + if n == 0 { + return 0, ErrNoWALSegments + } + return index, nil +} + // FindMaxIndexByGeneration returns the last index within a generation. // Returns ErrNoSnapshots if no index exists on the replica for the generation. func FindMaxIndexByGeneration(ctx context.Context, client ReplicaClient, generation string) (index int, err error) { diff --git a/replica_client_test.go b/replica_client_test.go index 83117b2..37f92d2 100644 --- a/replica_client_test.go +++ b/replica_client_test.go @@ -489,6 +489,167 @@ func TestFindMaxIndexByGeneration(t *testing.T) { }) } +func TestFindSnapshotIndexByTimestamp(t *testing.T) { + t.Run("OK", func(t *testing.T) { + client := litestream.NewFileReplicaClient(filepath.Join("testdata", "snapshot-index-by-timestamp", "ok")) + if index, err := litestream.FindSnapshotIndexByTimestamp(context.Background(), client, "0000000000000000", time.Date(2000, 1, 3, 0, 0, 0, 0, time.UTC)); err != nil { + t.Fatal(err) + } else if got, want := index, 0x000007d0; got != want { + t.Fatalf("index=%d, want %d", got, want) + } + }) + + t.Run("ErrNoSnapshots", func(t *testing.T) { + client := litestream.NewFileReplicaClient(filepath.Join("testdata", "snapshot-index-by-timestamp", "no-snapshots")) + + _, err := litestream.FindSnapshotIndexByTimestamp(context.Background(), client, "0000000000000000", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) + if err != litestream.ErrNoSnapshots { + t.Fatalf("unexpected error: %s", err) + } + }) + + t.Run("ErrSnapshots", func(t *testing.T) { + var client mock.ReplicaClient + client.SnapshotsFunc = func(ctx context.Context, generation string) (litestream.SnapshotIterator, error) { + return nil, fmt.Errorf("marker") + } + + _, err := litestream.FindSnapshotIndexByTimestamp(context.Background(), &client, "0000000000000000", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) + if err == nil || err.Error() != `snapshots: marker` { + t.Fatalf("unexpected error: %s", err) + } + }) + + t.Run("ErrSnapshotIteration", func(t *testing.T) { + var itr mock.SnapshotIterator + itr.NextFunc = func() bool { return false } + itr.CloseFunc = func() error { return fmt.Errorf("marker") } + + var client mock.ReplicaClient + client.SnapshotsFunc = func(ctx context.Context, generation string) (litestream.SnapshotIterator, error) { + return &itr, nil + } + + _, err := litestream.FindSnapshotIndexByTimestamp(context.Background(), &client, "0000000000000000", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) + if err == nil || err.Error() != `snapshot iteration: marker` { + t.Fatalf("unexpected error: %s", err) + } + }) +} + +func TestFindWALIndexByTimestamp(t *testing.T) { + t.Run("OK", func(t *testing.T) { + client := litestream.NewFileReplicaClient(filepath.Join("testdata", "wal-index-by-timestamp", "ok")) + if index, err := litestream.FindWALIndexByTimestamp(context.Background(), client, "0000000000000000", time.Date(2000, 1, 3, 0, 0, 0, 0, time.UTC)); err != nil { + t.Fatal(err) + } else if got, want := index, 1; got != want { + t.Fatalf("index=%d, want %d", got, want) + } + }) + + t.Run("ErrNoWALSegments", func(t *testing.T) { + client := litestream.NewFileReplicaClient(filepath.Join("testdata", "wal-index-by-timestamp", "no-wal")) + + _, err := litestream.FindWALIndexByTimestamp(context.Background(), client, "0000000000000000", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) + if err != litestream.ErrNoWALSegments { + t.Fatalf("unexpected error: %s", err) + } + }) + + t.Run("ErrWALSegments", func(t *testing.T) { + var client mock.ReplicaClient + client.WALSegmentsFunc = func(ctx context.Context, generation string) (litestream.WALSegmentIterator, error) { + return nil, fmt.Errorf("marker") + } + + _, err := litestream.FindWALIndexByTimestamp(context.Background(), &client, "0000000000000000", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) + if err == nil || err.Error() != `wal segments: marker` { + t.Fatalf("unexpected error: %s", err) + } + }) + + t.Run("ErrWALSegmentIteration", func(t *testing.T) { + var itr mock.WALSegmentIterator + itr.NextFunc = func() bool { return false } + itr.CloseFunc = func() error { return fmt.Errorf("marker") } + + var client mock.ReplicaClient + client.WALSegmentsFunc = func(ctx context.Context, generation string) (litestream.WALSegmentIterator, error) { + return &itr, nil + } + + _, err := litestream.FindWALIndexByTimestamp(context.Background(), &client, "0000000000000000", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) + if err == nil || err.Error() != `wal segment iteration: marker` { + t.Fatalf("unexpected error: %s", err) + } + }) +} + +func TestFindIndexByTimestamp(t *testing.T) { + t.Run("OK", func(t *testing.T) { + client := litestream.NewFileReplicaClient(filepath.Join("testdata", "index-by-timestamp", "ok")) + if index, err := litestream.FindIndexByTimestamp(context.Background(), client, "0000000000000000", time.Date(2000, 1, 4, 0, 0, 0, 0, time.UTC)); err != nil { + t.Fatal(err) + } else if got, want := index, 0x00000002; got != want { + t.Fatalf("index=%d, want %d", got, want) + } + }) + + t.Run("NoWAL", func(t *testing.T) { + client := litestream.NewFileReplicaClient(filepath.Join("testdata", "index-by-timestamp", "no-wal")) + if index, err := litestream.FindIndexByTimestamp(context.Background(), client, "0000000000000000", time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC)); err != nil { + t.Fatal(err) + } else if got, want := index, 0x00000001; got != want { + t.Fatalf("index=%d, want %d", got, want) + } + }) + + t.Run("SnapshotLaterThanWAL", func(t *testing.T) { + client := litestream.NewFileReplicaClient(filepath.Join("testdata", "index-by-timestamp", "snapshot-later-than-wal")) + if index, err := litestream.FindIndexByTimestamp(context.Background(), client, "0000000000000000", time.Date(2000, 1, 3, 0, 0, 0, 0, time.UTC)); err != nil { + t.Fatal(err) + } else if got, want := index, 0x00000001; got != want { + t.Fatalf("index=%d, want %d", got, want) + } + }) + + t.Run("ErrNoSnapshots", func(t *testing.T) { + client := litestream.NewFileReplicaClient(filepath.Join("testdata", "index-by-timestamp", "no-snapshots")) + + _, err := litestream.FindIndexByTimestamp(context.Background(), client, "0000000000000000", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) + if err != litestream.ErrNoSnapshots { + t.Fatalf("unexpected error: %s", err) + } + }) + + t.Run("ErrSnapshots", func(t *testing.T) { + var client mock.ReplicaClient + client.SnapshotsFunc = func(ctx context.Context, generation string) (litestream.SnapshotIterator, error) { + return nil, fmt.Errorf("marker") + } + + _, err := litestream.FindIndexByTimestamp(context.Background(), &client, "0000000000000000", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) + if err == nil || err.Error() != `max snapshot index: snapshots: marker` { + t.Fatalf("unexpected error: %s", err) + } + }) + + t.Run("ErrWALSegments", func(t *testing.T) { + var client mock.ReplicaClient + client.SnapshotsFunc = func(ctx context.Context, generation string) (litestream.SnapshotIterator, error) { + return litestream.NewSnapshotInfoSliceIterator([]litestream.SnapshotInfo{{Index: 0x00000001}}), nil + } + client.WALSegmentsFunc = func(ctx context.Context, generation string) (litestream.WALSegmentIterator, error) { + return nil, fmt.Errorf("marker") + } + + _, err := litestream.FindIndexByTimestamp(context.Background(), &client, "0000000000000000", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) + if err == nil || err.Error() != `max wal index: wal segments: marker` { + t.Fatalf("unexpected error: %s", err) + } + }) +} + func TestRestore(t *testing.T) { t.Run("OK", func(t *testing.T) { testDir := filepath.Join("testdata", "restore", "ok") diff --git a/testdata/Makefile b/testdata/Makefile index b87ebd5..504fe25 100644 --- a/testdata/Makefile +++ b/testdata/Makefile @@ -1,8 +1,13 @@ .PHONY: default default: make -C find-latest-generation/ok + make -C index-by-timestamp/no-wal + make -C index-by-timestamp/ok + make -C index-by-timestamp/snapshot-later-than-wal make -C generation-time-bounds/ok make -C generation-time-bounds/snapshots-only make -C replica-client-time-bounds/ok make -C snapshot-time-bounds/ok + make -C snapshot-index-by-timestamp/ok make -C wal-time-bounds/ok + make -C wal-index-by-timestamp/ok diff --git a/testdata/index-by-timestamp/no-snapshots/generations/0000000000000000/.gitignore b/testdata/index-by-timestamp/no-snapshots/generations/0000000000000000/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/testdata/index-by-timestamp/no-wal/Makefile b/testdata/index-by-timestamp/no-wal/Makefile new file mode 100644 index 0000000..8775133 --- /dev/null +++ b/testdata/index-by-timestamp/no-wal/Makefile @@ -0,0 +1,6 @@ +.PHONY: default +default: + TZ=UTC touch -ct 200001010000 generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 + TZ=UTC touch -ct 200001020000 generations/0000000000000000/snapshots/0000000000000001.snapshot.lz4 + TZ=UTC touch -ct 200001030000 generations/0000000000000000/snapshots/0000000000000002.snapshot.lz4 + diff --git a/testdata/index-by-timestamp/no-wal/generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 b/testdata/index-by-timestamp/no-wal/generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/no-wal/generations/0000000000000000/snapshots/0000000000000001.snapshot.lz4 b/testdata/index-by-timestamp/no-wal/generations/0000000000000000/snapshots/0000000000000001.snapshot.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/no-wal/generations/0000000000000000/snapshots/0000000000000002.snapshot.lz4 b/testdata/index-by-timestamp/no-wal/generations/0000000000000000/snapshots/0000000000000002.snapshot.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/ok/Makefile b/testdata/index-by-timestamp/ok/Makefile new file mode 100644 index 0000000..258f1e8 --- /dev/null +++ b/testdata/index-by-timestamp/ok/Makefile @@ -0,0 +1,11 @@ +.PHONY: default +default: + TZ=UTC touch -ct 200001010000 generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 + TZ=UTC touch -ct 200001030000 generations/0000000000000000/snapshots/0000000000000001.snapshot.lz4 + + TZ=UTC touch -ct 200001010000 generations/0000000000000000/wal/0000000000000000/0000000000000000.wal.lz4 + TZ=UTC touch -ct 200001020000 generations/0000000000000000/wal/0000000000000000/0000000000001234.wal.lz4 + TZ=UTC touch -ct 200001030000 generations/0000000000000000/wal/0000000000000001/0000000000000000.wal.lz4 + TZ=UTC touch -ct 200001040000 generations/0000000000000000/wal/0000000000000002/0000000000000000.wal.lz4 + TZ=UTC touch -ct 200001050000 generations/0000000000000000/wal/0000000000000003/0000000000000000.wal.lz4 + diff --git a/testdata/index-by-timestamp/ok/generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 b/testdata/index-by-timestamp/ok/generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/ok/generations/0000000000000000/snapshots/0000000000000001.snapshot.lz4 b/testdata/index-by-timestamp/ok/generations/0000000000000000/snapshots/0000000000000001.snapshot.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000000/0000000000000000.wal.lz4 b/testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000000/0000000000000000.wal.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000000/0000000000001234.wal.lz4 b/testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000000/0000000000001234.wal.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000001/0000000000000000.wal.lz4 b/testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000001/0000000000000000.wal.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000002/0000000000000000.wal.lz4 b/testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000002/0000000000000000.wal.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000003/0000000000000000.wal.lz4 b/testdata/index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000003/0000000000000000.wal.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/snapshot-later-than-wal/Makefile b/testdata/index-by-timestamp/snapshot-later-than-wal/Makefile new file mode 100644 index 0000000..9e0d390 --- /dev/null +++ b/testdata/index-by-timestamp/snapshot-later-than-wal/Makefile @@ -0,0 +1,7 @@ +.PHONY: default +default: + TZ=UTC touch -ct 200001010000 generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 + TZ=UTC touch -ct 200001030000 generations/0000000000000000/snapshots/0000000000000001.snapshot.lz4 + + TZ=UTC touch -ct 200001020000 generations/0000000000000000/wal/0000000000000000/0000000000000000.wal.lz4 + TZ=UTC touch -ct 200001020000 generations/0000000000000000/wal/0000000000000000/0000000000001234.wal.lz4 diff --git a/testdata/index-by-timestamp/snapshot-later-than-wal/generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 b/testdata/index-by-timestamp/snapshot-later-than-wal/generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/snapshot-later-than-wal/generations/0000000000000000/snapshots/0000000000000001.snapshot.lz4 b/testdata/index-by-timestamp/snapshot-later-than-wal/generations/0000000000000000/snapshots/0000000000000001.snapshot.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/snapshot-later-than-wal/generations/0000000000000000/wal/0000000000000000/0000000000000000.wal.lz4 b/testdata/index-by-timestamp/snapshot-later-than-wal/generations/0000000000000000/wal/0000000000000000/0000000000000000.wal.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/index-by-timestamp/snapshot-later-than-wal/generations/0000000000000000/wal/0000000000000000/0000000000001234.wal.lz4 b/testdata/index-by-timestamp/snapshot-later-than-wal/generations/0000000000000000/wal/0000000000000000/0000000000001234.wal.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/snapshot-index-by-timestamp/no-snapshots/generations/0000000000000000/.gitignore b/testdata/snapshot-index-by-timestamp/no-snapshots/generations/0000000000000000/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/testdata/snapshot-index-by-timestamp/ok/Makefile b/testdata/snapshot-index-by-timestamp/ok/Makefile new file mode 100644 index 0000000..a11b6db --- /dev/null +++ b/testdata/snapshot-index-by-timestamp/ok/Makefile @@ -0,0 +1,5 @@ +.PHONY: default +default: + TZ=UTC touch -ct 200001010000 generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 + TZ=UTC touch -ct 200001020000 generations/0000000000000000/snapshots/00000000000003e8.snapshot.lz4 + TZ=UTC touch -ct 200001030000 generations/0000000000000000/snapshots/00000000000007d0.snapshot.lz4 diff --git a/testdata/snapshot-index-by-timestamp/ok/generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 b/testdata/snapshot-index-by-timestamp/ok/generations/0000000000000000/snapshots/0000000000000000.snapshot.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/snapshot-index-by-timestamp/ok/generations/0000000000000000/snapshots/00000000000003e8.snapshot.lz4 b/testdata/snapshot-index-by-timestamp/ok/generations/0000000000000000/snapshots/00000000000003e8.snapshot.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/snapshot-index-by-timestamp/ok/generations/0000000000000000/snapshots/00000000000007d0.snapshot.lz4 b/testdata/snapshot-index-by-timestamp/ok/generations/0000000000000000/snapshots/00000000000007d0.snapshot.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/wal-index-by-timestamp/ok/Makefile b/testdata/wal-index-by-timestamp/ok/Makefile new file mode 100644 index 0000000..40d692f --- /dev/null +++ b/testdata/wal-index-by-timestamp/ok/Makefile @@ -0,0 +1,6 @@ +.PHONY: default +default: + TZ=UTC touch -ct 200001010000 generations/0000000000000000/wal/0000000000000000/0000000000000000.wal.lz4 + TZ=UTC touch -ct 200001020000 generations/0000000000000000/wal/0000000000000000/0000000000001234.wal.lz4 + TZ=UTC touch -ct 200001030000 generations/0000000000000000/wal/0000000000000001/0000000000000000.wal.lz4 + diff --git a/testdata/wal-index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000000/0000000000000000.wal.lz4 b/testdata/wal-index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000000/0000000000000000.wal.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/wal-index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000000/0000000000001234.wal.lz4 b/testdata/wal-index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000000/0000000000001234.wal.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001 diff --git a/testdata/wal-index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000001/0000000000000000.wal.lz4 b/testdata/wal-index-by-timestamp/ok/generations/0000000000000000/wal/0000000000000001/0000000000000000.wal.lz4 new file mode 100644 index 0000000000000000000000000000000000000000..7536340954b2a683272e3ed74900762ffd86afcf GIT binary patch literal 93 zcmZQk@|8$&SnkEZ!0?$jIM64vBvm0TzbH4cM8TLrfPsmL!9hU*D9Omez|X{>nZU@P bXQIIC#2_HR3KIB_3OWOTN+Dp*_P@^oU0EHA literal 0 HcmV?d00001