Skip to content

Commit 459090a

Browse files
committed
feat: Store the cached index entry mtime in files
Use file attributes to store the cached index entry metadata persistently on the filesystem. Try to recreate the volatile metadata cache entry from the index cache file attributes. This speeds up the volatile metadata cache regeneration after restarts.
1 parent 0e584d2 commit 459090a

File tree

3 files changed

+62
-6
lines changed

3 files changed

+62
-6
lines changed

src/file_cache.rs

+34-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Index entry and crate file cache helpers
22
3-
use std::fs::{create_dir_all, read, write};
3+
use std::fs::{create_dir_all, metadata, read, write, File};
4+
use std::io::Write;
45
use std::path::Path;
56

67
use log::error;
@@ -35,11 +36,41 @@ pub fn cache_store_index_entry(dir: &Path, entry: &IndexEntry, data: &[u8]) {
3536
return;
3637
}
3738

38-
write(entry_file_path, data)
39-
.unwrap_or_else(|e| error!("cache: failed to write index entry file: {e}"));
39+
let mut file = match File::create(entry_file_path) {
40+
Ok(f) => f,
41+
Err(e) => {
42+
error!("cache: failed to create index entry file: {e}");
43+
return;
44+
}
45+
};
46+
47+
if let Err(e) = file.write_all(data) {
48+
error!("cache: failed to write index entry data: {e}");
49+
return;
50+
}
51+
52+
// Set the cache file mtime according to the Last-Modified HTTP metadata.
53+
if let Some(mtime) = entry.mtime() {
54+
file.set_modified(mtime)
55+
.unwrap_or_else(|e| error!("cache: failed to set index entry file mtime: {e}"));
56+
}
4057
}
4158

4259
/// Fetches the cached index entry file from the local filesystem, if present.
4360
pub fn cache_fetch_index_entry(dir: &Path, entry: &IndexEntry) -> Option<Vec<u8>> {
4461
read(dir.join(entry.to_file_path())).ok()
4562
}
63+
64+
/// Tries to recreate the missing index entry metadata from the cache file metadata.
65+
pub fn cache_try_find_index_entry(dir: &Path, name: &str) -> Option<IndexEntry> {
66+
let mut entry = IndexEntry::new(name);
67+
68+
let mtime = metadata(dir.join(entry.to_file_path()))
69+
.ok()?
70+
.modified()
71+
.ok()?;
72+
73+
entry.set_mtime(mtime);
74+
75+
Some(entry)
76+
}

src/index_entry.rs

+13
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,23 @@ impl IndexEntry {
7979
}
8080

8181
/// Gets the HTTP entity tag metadata.
82+
#[must_use]
8283
pub fn etag(&self) -> Option<&str> {
8384
self.etag.as_deref()
8485
}
8586

8687
/// Gets the HTTP Last-Modified metadata.
88+
#[must_use]
8789
pub fn last_modified(&self) -> Option<String> {
8890
self.mtime.map(fmt_http_date)
8991
}
9092

93+
/// Gets the file modification time metadata.
94+
#[must_use]
95+
pub fn mtime(&self) -> Option<SystemTime> {
96+
self.mtime
97+
}
98+
9199
/// Sets the HTTP entity tag metadata.
92100
pub fn set_etag(&mut self, etag: &str) {
93101
self.etag = Some(etag.to_owned());
@@ -98,6 +106,11 @@ impl IndexEntry {
98106
self.mtime = parse_http_date(last_modified).ok();
99107
}
100108

109+
/// Sets the file modification time metadata.
110+
pub fn set_mtime(&mut self, mtime: SystemTime) {
111+
self.mtime = Some(mtime);
112+
}
113+
101114
/// Updates the last upstream server access time metadata.
102115
pub fn set_last_updated(&mut self) {
103116
self.atime = Some(Instant::now());

src/main.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ use crate::config_json::{gen_config_json_file, is_config_json_url};
4747
use crate::crate_info::CrateInfo;
4848
use crate::file_cache::{
4949
cache_fetch_crate, cache_fetch_index_entry, cache_store_crate, cache_store_index_entry,
50+
cache_try_find_index_entry,
5051
};
5152
use crate::index_entry::IndexEntry;
5253
use crate::metadata_cache::{
@@ -447,12 +448,12 @@ fn handle_index_request(request: Request, index_url: &str, config: &ProxyConfig)
447448
for header in request.headers() {
448449
if header.field.equiv("If-None-Match") {
449450
let etag = header.value.as_str();
450-
debug!("proxy: checking known index entry {index_entry} with ETag {etag}");
451+
debug!("proxy: checking known index entry {index_entry} with ETag: {etag}");
451452
index_entry.set_etag(etag);
452453
}
453454
if header.field.equiv("If-Modified-Since") {
454455
let last_modified = header.value.as_str();
455-
debug!("proxy: checking known index entry {index_entry} with Last-Modified {last_modified}");
456+
debug!("proxy: checking known index entry {index_entry} with Last-Modified: {last_modified}");
456457
index_entry.set_last_modified(last_modified);
457458
}
458459
}
@@ -482,8 +483,19 @@ fn handle_index_request(request: Request, index_url: &str, config: &ProxyConfig)
482483
}
483484
}
484485

486+
// Try to recreate the index entry metadata from the cached file mtime.
487+
let mtimed_entry = cache_try_find_index_entry(&config.index_dir, index_entry.name());
488+
489+
if let Some(entry) = &mtimed_entry {
490+
let last_modified = entry.last_modified().unwrap();
491+
492+
info!(
493+
"proxy: recreated index cache metadata for {entry} with Last-Modified: {last_modified}"
494+
);
495+
}
496+
485497
// Fall back to forwarding the request to the upstream registry.
486-
forward_index_request(request, index_entry, None, config.clone());
498+
forward_index_request(request, index_entry, mtimed_entry, config.clone());
487499
}
488500

489501
/// Processes one HTTP GET request.

0 commit comments

Comments
 (0)