Skip to content

Commit b1f0a09

Browse files
committed
fix(snapshot): debounce LastAccessedAt touch + finalize-time stamp on Create
- lookupRecord: read first via store.With, only escalate to store.Update when prior touch is older than accessTouchDebounce (1 minute). Hot paths (clone DataDir, Export which calls DataDir twice in the gzip/tar path) used to take the exclusive flock + rewrite the entire index JSON every call. - Create: stamp LastAccessedAt at finalize via a fresh time.Now() instead of the now captured before tar extraction so freshly-finished snapshots don't look minutes old to --snapshot-age right after Create returns.
1 parent f40d3de commit b1f0a09

1 file changed

Lines changed: 26 additions & 10 deletions

File tree

snapshot/localfile/localfile.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import (
2121
"github.com/cocoonstack/cocoon/utils"
2222
)
2323

24-
const typ = "localfile"
24+
const (
25+
typ = "localfile"
26+
accessTouchDebounce = time.Minute
27+
)
2528

2629
// compile-time interface checks.
2730
var (
@@ -124,14 +127,15 @@ func (lf *LocalFile) Create(ctx context.Context, cfg *types.SnapshotConfig, stre
124127
if sizeErr != nil {
125128
return "", fmt.Errorf("compute data dir size: %w", sizeErr)
126129
}
130+
finalizedAt := time.Now()
127131
if err = lf.store.Update(ctx, func(idx *snapshot.SnapshotIndex) error {
128132
rec := idx.Snapshots[id]
129133
if rec == nil {
130134
return fmt.Errorf("snapshot %q disappeared from index", id)
131135
}
132136
rec.Pending = false
133137
rec.SizeBytes = size
134-
rec.LastAccessedAt = now
138+
rec.LastAccessedAt = finalizedAt
135139
return nil
136140
}); err != nil {
137141
return "", fmt.Errorf("finalize snapshot: %w", err)
@@ -224,10 +228,11 @@ func (lf *LocalFile) rollbackCreate(ctx context.Context, id, name string) {
224228
}
225229
}
226230

227-
// lookupRecord resolves ref to a non-pending record; touch=true also bumps LastAccessedAt under the same write lock.
231+
// lookupRecord resolves ref to a non-pending record; touch=true bumps LastAccessedAt only when the prior touch is older than accessTouchDebounce so hot paths (clone DataDir, Export) don't serialize on the index write lock.
228232
func (lf *LocalFile) lookupRecord(ctx context.Context, ref string, touch bool) (snapshot.SnapshotRecord, error) {
229233
var rec snapshot.SnapshotRecord
230-
apply := func(idx *snapshot.SnapshotIndex) error {
234+
var resolvedID string
235+
if err := lf.store.With(ctx, func(idx *snapshot.SnapshotIndex) error {
231236
id, err := idx.Resolve(ref)
232237
if err != nil {
233238
return err
@@ -236,16 +241,27 @@ func (lf *LocalFile) lookupRecord(ctx context.Context, ref string, touch bool) (
236241
if r == nil || r.Pending {
237242
return snapshot.ErrNotFound
238243
}
239-
if touch {
240-
r.LastAccessedAt = time.Now()
241-
}
244+
resolvedID = id
242245
rec = *r
243246
return nil
247+
}); err != nil {
248+
return rec, err
249+
}
250+
if !touch || time.Since(rec.LastAccessedAt) < accessTouchDebounce {
251+
return rec, nil
244252
}
245-
if touch {
246-
return rec, lf.store.Update(ctx, apply)
253+
if err := lf.store.Update(ctx, func(idx *snapshot.SnapshotIndex) error {
254+
r := idx.Snapshots[resolvedID]
255+
if r == nil || r.Pending {
256+
return nil
257+
}
258+
r.LastAccessedAt = time.Now()
259+
rec.LastAccessedAt = r.LastAccessedAt
260+
return nil
261+
}); err != nil {
262+
log.WithFunc("localfile.lookupRecord").Warnf(ctx, "touch LastAccessedAt for %s: %v", resolvedID, err)
247263
}
248-
return rec, lf.store.With(ctx, apply)
264+
return rec, nil
249265
}
250266

251267
// snapshotRecordToConfig builds a detached SnapshotConfig from a record,

0 commit comments

Comments
 (0)