Repository navigation
Prevent retention from clearing recently served artifacts #687
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,12 +77,21 @@ func (db *DB) GetRetentionCandidates(fromID, toID int64, cutoff time.Time) ([]Re | |
| } | ||
|
|
||
| // ClearExpiredArtifact marks an artifact uncached like ClearArtifactCache, | ||
| // but only while it is still expired before cutoff, so an artifact served | ||
| // between the scan and the clear stays cached. It queues storagePath for | ||
| // deletion in the same transaction: if queueing fails, the record keeps its | ||
| // path and a later sweep tries again, instead of the object staying in | ||
| // storage with nothing left pointing at it. | ||
| // but only while it is still expired before cutoff. It checks pending hits as | ||
| // well as persisted access times, so a hit buffered after the scan keeps the | ||
| // artifact cached. It queues storagePath for deletion in the same transaction: | ||
| // if queueing fails, the record keeps its path and a later sweep retries, | ||
| // instead of leaving an object in storage with no record pointing to it. | ||
| func (db *DB) ClearExpiredArtifact(versionPURL, filename, storagePath string, cutoff time.Time) (bool, error) { | ||
| if db.hits != nil { | ||
| db.hits.mu.Lock() | ||
| pending, ok := db.hits.pending[hitKey{versionPURL, filename}] | ||
| db.hits.mu.Unlock() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep hit recording excluded until the clear completes. If the lookup finds no recent hit, this unlock lets Keep the pending-hit check and the clear in one critical section through transaction completion. Coordinate the lock order with batch flushing.
|
||
| if ok && !pending.last.Before(cutoff) { | ||
| return false, nil | ||
| } | ||
| } | ||
|
|
||
| tx, err := db.Beginx() | ||
| if err != nil { | ||
| return false, err | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Serialize the clear with in-flight batch flushes.
If
flushHitscallstake()before this lookup, the recent hit is no longer indb.hits.pending. Pause the flush beforewriteHits, then run this clear. The lookup finds no hit, and the UPDATE can clear the artifact and queue its object for deletion. When the flush resumes, it updates the access timestamp but does not restore the artifact.Use shared serialization across batch removal, persistence, and the clear. If persistence fails, restore the batch and abort the clear.
Based on learnings, coordinate all operations that move or consume the shared pending state.
Source: Learnings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Declining this one: closing the remaining window needs the clear to hold
hits.mu(andflushMu) across the transaction, which is what the first version did and what made every cached download wait on retention writes (~100 ns → ~1.1 ms per hit in my measurement). What the window costs is one refetch: the delete queue's grace period keeps a download that races the clear working (seesweepRetention). The lookup narrows the window enough for that.