Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f35f1e8d98 |
No files matched your search
@@ -22,14 +22,6 @@ the tag exists and is exercised; what is left is merging `next` to
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
- 2026-10-08: Made a second `snapshot create` of one name succeed when
|
|
||||||
it starts in the same second as the first
|
|
||||||
([issue #270](https://git.eeqj.de/sneak/vaultik/issues/270)). The
|
|
||||||
timestamp in a snapshot ID is in whole seconds, so the second run got
|
|
||||||
the first run's ID and failed with `UNIQUE constraint failed:
|
|
||||||
snapshots.id`. When the local index already has a snapshot with the
|
|
||||||
ID, the create now waits a second and takes a new timestamp.
|
|
||||||
|
|
||||||
- 2026-10-07: Documented that `--json` skips the confirmation prompt of
|
- 2026-10-07: Documented that `--json` skips the confirmation prompt of
|
||||||
`snapshot remove` and `prune`
|
`snapshot remove` and `prune`
|
||||||
([issue #268](https://git.eeqj.de/sneak/vaultik/issues/268)). Both
|
([issue #268](https://git.eeqj.de/sneak/vaultik/issues/268)). Both
|
||||||
|
|||||||
@@ -115,37 +115,20 @@ func ShortHostname(hostname string) string {
|
|||||||
|
|
||||||
// CreateSnapshotWithName creates a new snapshot record with an optional
|
// CreateSnapshotWithName creates a new snapshot record with an optional
|
||||||
// snapshot name. The snapshot ID format is: hostname_name_timestamp or
|
// snapshot name. The snapshot ID format is: hostname_name_timestamp or
|
||||||
// hostname_timestamp if name is empty. The timestamp is in whole seconds.
|
// hostname_timestamp if name is empty.
|
||||||
// If the local index already has a snapshot with that ID, from a run of the
|
|
||||||
// same name that started in the same second, it waits a second and takes a
|
|
||||||
// new timestamp.
|
|
||||||
func (sm *SnapshotManager) CreateSnapshotWithName(
|
func (sm *SnapshotManager) CreateSnapshotWithName(
|
||||||
ctx context.Context, hostname, name, version, gitRevision string,
|
ctx context.Context, hostname, name, version, gitRevision string,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
shortHostname := ShortHostname(hostname)
|
shortHostname := ShortHostname(hostname)
|
||||||
|
|
||||||
|
// Build snapshot ID with optional name
|
||||||
|
timestamp := time.Now().UTC().Format("2006-01-02T15:04:05Z")
|
||||||
|
|
||||||
var snapshotID string
|
var snapshotID string
|
||||||
|
if name != "" {
|
||||||
for {
|
snapshotID = fmt.Sprintf("%s_%s_%s", shortHostname, name, timestamp)
|
||||||
// Build snapshot ID with optional name
|
} else {
|
||||||
timestamp := time.Now().UTC().Format("2006-01-02T15:04:05Z")
|
snapshotID = fmt.Sprintf("%s_%s", shortHostname, timestamp)
|
||||||
|
|
||||||
if name != "" {
|
|
||||||
snapshotID = fmt.Sprintf("%s_%s_%s", shortHostname, name, timestamp)
|
|
||||||
} else {
|
|
||||||
snapshotID = fmt.Sprintf("%s_%s", shortHostname, timestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
existing, err := sm.repos.Snapshots.GetByID(ctx, snapshotID)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("looking up snapshot %s: %w", snapshotID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if existing == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot := &database.Snapshot{
|
snapshot := &database.Snapshot{
|
||||||
|
|||||||
@@ -386,36 +386,3 @@ func TestCleanSnapshotDBNonExistentSnapshot(t *testing.T) {
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Two creates of one snapshot name back to back start within one second,
|
|
||||||
// the resolution of the timestamp in a snapshot ID. See
|
|
||||||
// https://git.eeqj.de/sneak/vaultik/issues/270.
|
|
||||||
func TestCreateSnapshotWithNameTwiceBackToBack(t *testing.T) {
|
|
||||||
log.Initialize(log.Config{})
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
db, err := database.New(ctx, filepath.Join(t.TempDir(), "index.sqlite"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create database: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() { _ = db.Close() }()
|
|
||||||
|
|
||||||
sm := &SnapshotManager{repos: database.NewRepositories(db)}
|
|
||||||
|
|
||||||
first, err := sm.CreateSnapshotWithName(ctx, "test-host", "data", "v", "g")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("first create failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
second, err := sm.CreateSnapshotWithName(ctx, "test-host", "data", "v", "g")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("second create failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if first == second {
|
|
||||||
t.Fatalf("both creates returned snapshot ID %s", first)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user