Skip to content

Count cached metadata against max_size and evict it LRU - #507

Merged
andrew merged 6 commits into
git-pkgs:mainfrom
pinguinfuss:issue-461-metadata-eviction
Oct 20, 2026
Merged

andrew merged 6 commits into
git-pkgs:mainfrom
pinguinfuss:issue-461-metadata-eviction

Conversation

@pinguinfuss

Copy link
Copy Markdown
Contributor

Fixes #461.

max_size now counts cached metadata as well as artifacts, and eviction drops whatever was served least recently.

  1. metadata_cache gets last_accessed_at (migration 014, backfilled from updated_at), and every cached read records a hit, batched like artifact hits.
  2. Eviction merges the oldest artifacts and metadata rows. An artifact that was never served counts from its fetch time. Both orders are indexed.
  3. Arch writes a database blob again if eviction removed it while a refresh was re-creating the row.
  4. /stats is unchanged. New metric: proxy_metadata_evicted_total.

Tested live with max_size 5MB on SQLite and Postgres: floods of sumdb hash tiles and large npm packuments always went back to 5 MB after the next eviction pass. On main the same setup stayed over the limit.

Not covered: storage can exceed the limit until the next pass (once a minute), and a metadata flood now evicts artifacts too.

…n drops whichever was used least recently, so metadata no longer piles up for good (git-pkgs#461). Metadata rows record when they were last served, an artifact not yet served counts from its download, and Arch stores a database blob again if eviction removed it under a refreshed row.

@andrew andrew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address durable metadata cleanup, move the data backfill out of the schema migration, and surface the new eviction metric in analytics.


// Metadata goes now too: readers hold the whole document in memory,
// and a row refetched meanwhile refetches again on a missing blob.
if err := store.Delete(ctx, path); err != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new metadata path deletes the metadata_cache row before deleting the blob, and queues cleanup only after a storage error. A process crash between the row deletion and this call leaves a blob with neither a cache row nor a pending-delete record, so it disappears from max_size accounting and cannot be reclaimed by the queue. Delete the metadata row and enqueue its storage path in one database transaction, then let cleanup remove the blob. Please cover interruption and retry behavior.

Comment thread internal/database/schema.go Outdated
return fmt.Errorf("adding column last_accessed_at to metadata_cache: %w", err)
}
}
if err := backfillMetadataLastAccessed(db); err != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep this data backfill outside the schema migration. backfillMetadataLastAccessed loads every existing row into memory, then performs one UPDATE per row in a single transaction before startup can finish. Move it into bounded, resumable work and keep eviction safe while last_accessed_at is still NULL. Cover an upgrade with existing rows and an interrupted backfill.


MetadataEvicted = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "proxy_metadata_evicted_total",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new proxy_metadata_evicted_total metric is absent from /ui/analytics and metricSurface, contrary to CONTRIBUTING.md. TestEveryMetricIsSurfaced also does not instantiate this counter, so the normal test order hides the omission. Reproduced with go test ./internal/server -run 'TestEveryMetricIsSurfaced|TestEvictLRU_CountsAndEvictsMetadata' -count=2. Add the analytics display, the metricSurface entry, and a RecordMetadataEvicted call in the coverage test setup.

pinguinfuss and others added 4 commits October 19, 2026 19:39
…, and cover the new counter in the metric surface test.
…ction, so a crash before the blob is deleted leaves it to reclaim instead of orphaning it.
…0 instead of inside the migration, resuming after a restart, and until a row is backfilled eviction orders it by its write time.
@andrew
andrew merged commit d56d436 into git-pkgs:main Oct 20, 2026
6 checks passed
@pinguinfuss
pinguinfuss deleted the issue-461-metadata-eviction branch October 26, 2026 06:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Metadata cache never shrinks and isn't counted against max_size

2 participants