How uv-cache Implements Timestamp-Based Freshness Checking

uv uses a CacheInfo metadata structure to store creation timestamps as SystemTime wrappers, comparing them against user-provided reference timestamps to determine if cached artifacts are fresh or stale.

The uv package manager from Astral implements a robust timestamp-based freshness checking mechanism to avoid stale cache hits. This system relies on a small metadata structure called CacheInfo defined in the uv-cache-info crate, which records when a cached artifact was created and compares it against reference timestamps supplied via CLI flags like --exclude-newer or --refresh.

Core Components of Timestamp-Based Freshness Checking

CacheInfo Metadata Structure

The CacheInfo struct, defined in crates/uv-cache-info/src/cache_info.rs, serves as the primary container for cache metadata. It holds an optional Timestamp field that records when the cache entry was created:

pub struct CacheInfo {
    timestamp: Option<Timestamp>,
    // ... other fields
}

The struct provides the critical is_fresh method that performs the actual comparison:

pub fn is_fresh(&self, reference: &Timestamp) -> bool {
    self.timestamp.map_or(false, |ts| ts >= *reference)
}

This method returns true only if the cached timestamp is greater than or equal to the reference timestamp, ensuring that only entries created at or after the reference time are considered fresh.

Timestamp Wrapper Type

The Timestamp type, implemented in crates/uv-cache-info/src/timestamp.rs, provides a thin, safe wrapper around std::time::SystemTime. It exposes several key methods used throughout the freshness checking pipeline:

  • Timestamp::now() – Creates a timestamp representing the current system time when a new cache entry is created.
  • Timestamp::from_rfc3339(&str) – Parses RFC 3339 formatted strings (used by the --exclude-newer CLI argument).
  • timestamp_millis() – Returns the timestamp as milliseconds since the Unix epoch, used for comparisons in the resolver.

How Freshness Checks Work in Practice

Cache Entry Creation and Storage

When uv downloads a wheel, source distribution, or builds a distribution, it writes a cache-info JSON file alongside the artifact. For example, when caching a wheel with hash <hash>, uv creates wheel-<hash>.json containing:

{
  "timestamp": "2024-05-15T10:30:00Z"
}

The CacheInfo::from_path method reads this JSON file to reconstruct the metadata when the cache entry is later accessed:

let cache_info = CacheInfo::from_path(&artifact_path)?;

The Freshness Comparison Logic

During resolution or installation, uv loads the CacheInfo for each candidate distribution and compares its timestamp against a reference timestamp. The logic follows this pattern:

if let Some(cache) = cache_info {
    if cache.is_fresh(&exclude_newer) {
        // Use cached artifact
    } else {
        // Re-download or rebuild
    }
}

The is_fresh implementation ensures that if the cached timestamp is older than the reference (i.e., ts < reference), the entry is treated as stale and ignored.

User-Controlled Reference Timestamps

Users control the reference timestamp through two primary mechanisms defined in crates/uv/src/settings.rs:

  1. --exclude-newer <RFC3339> – Parses the provided RFC 3339 timestamp into a Timestamp instance that serves as the upper bound for freshness. Any cache entry created after this date is excluded.

  2. --refresh – Creates a virtual timestamp of "now" but instructs the resolver to ignore any existing CacheInfo, effectively treating all cache entries as stale regardless of their timestamp.

Integration with the Resolver and Distribution System

Version Map and Exclude-Newer Logic

The resolver integrates timestamp checking in crates/uv-resolver/src/version_map.rs. When evaluating available wheels for a package, the resolver checks each wheel's upload time against the exclude_newer threshold:

if upload_time >= exclude_newer.timestamp_millis() {
    // Exclude this wheel from consideration
}

This ensures that packages uploaded after the user-specified cutoff are not selected, even if they exist in the cache or index.

Distribution Database Persistence

The DistributionDatabase in crates/uv-distribution/src/distribution_database.rs handles the physical storage and retrieval of CacheInfo alongside distribution files. It ensures that:

  • CacheInfo is written to disk immediately after a successful download or build.
  • Metadata is read from disk and validated before reuse.
  • Stale entries are purged and replaced with fresh downloads when the freshness check fails.

Code Example: Checking Cache Freshness

The following Rust example demonstrates how uv determines whether a cached wheel can be reused:

use uv_cache_info::{CacheInfo, Timestamp};
use std::path::Path;

/// Determine whether a cached wheel can be reused.
fn wheel_is_fresh(wheel_path: &Path, exclude_newer: &Timestamp) -> anyhow::Result<bool> {
    // Load cache metadata stored next to the wheel.
    let cache = CacheInfo::from_path(wheel_path)?;
    
    // `is_fresh` returns true if the wheel's timestamp ≥ the user-provided timestamp.
    Ok(cache.is_fresh(exclude_newer))
}

When running the command:

uv pip install mypkg --exclude-newer 2024-05-01T00:00:00Z

uv creates a Timestamp from 2024-05-01T00:00:00Z and applies the logic above to every cached distribution. Wheels created before this date are considered fresh and reused; those created after are re-downloaded.

Summary

  • CacheInfo stores optional timestamps in JSON files alongside cached artifacts, tracking when each entry was created.
  • Timestamp wraps SystemTime to provide RFC 3339 parsing and millisecond comparisons used in freshness checks.
  • is_fresh compares the cached timestamp against a reference timestamp, returning true only when the cache entry is newer than or equal to the reference.
  • CLI integration allows users to specify reference timestamps via --exclude-newer or force invalidation with --refresh.
  • Resolver integration applies these checks during package resolution in version_map.rs and distribution retrieval in distribution_database.rs.

Frequently Asked Questions

What happens when a cache entry is considered stale?

When CacheInfo::is_fresh returns false, uv treats the cached artifact as invalid. The resolver ignores the stale entry and proceeds to download or build a fresh version from the package index. The old cache files are typically overwritten or removed during this process, ensuring that subsequent operations use the newly fetched artifact with updated metadata.

How does the --exclude-newer flag interact with cache freshness?

The --exclude-newer flag accepts an RFC 3339 timestamp that uv parses into a Timestamp instance using Timestamp::from_rfc3339. This value serves as the reference timestamp passed to CacheInfo::is_fresh. During resolution, uv excludes any package versions with an upload_time greater than or equal to this threshold, effectively treating newer cached entries as stale and preventing their use.

What is the difference between the Refresh flag and timestamp-based freshness?

The --refresh flag operates independently of timestamp comparisons. When enabled, it sets a Refresh flag in the settings that instructs the resolver to ignore all existing CacheInfo metadata, effectively treating every cache entry as stale regardless of its timestamp. In contrast, timestamp-based freshness checking uses CacheInfo::is_fresh to selectively invalidate entries based on their creation time relative to a reference timestamp, allowing older valid entries to be reused.

Where is the CacheInfo JSON stored on disk?

CacheInfo metadata is persisted in JSON files stored alongside the cached artifacts in uv's cache directory. When uv downloads a wheel or builds a distribution, it writes a JSON file (e.g., wheel-<hash>.json) containing the timestamp field to the same directory as the artifact. The DistributionDatabase in crates/uv-distribution/src/distribution_database.rs handles these read and write operations, while CacheInfo::from_path reconstructs the metadata from these JSON files during cache lookups.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →