diff options
context:
space:
mode:
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Keep clone files and oversized responses out of the cache
The dumb transport reads files that already sit on the disk, so a pack copied into a slot cost that disk twice and the request a second write of every byte. A snapshot took a slot whatever its size, so a visitor naming distinct refs and ids could fill the cache root with archives. `cache-max-slot-size`, 64 MB unless set, now serves a larger response from the lock file and drops it, along with any expired copy it would have replaced.
Diffstat (limited to 'source/cache.c')
-rw-r--r--source/cache.c25
1 file changed, 22 insertions, 3 deletions
diff --git a/source/cache.c b/source/cache.c
index 091c078..8c81f5c 100644
--- a/source/cache.c
+++ b/source/cache.c
@@ -60,6 +60,11 @@ struct cache_slot {
// page has already reached the visitor and nothing more may be served
// after it, not the lock file and not the stale copy still open.
int abandoned;
+ // The largest page a slot may keep, with zero for no bound, and whether
+ // the fill went past it, in which case the lock file is served and then
+ // dropped instead of published.
+ size_t max_bytes;
+ int oversized;
// The slot as it was when it was opened, or the lock file once
// fill_slot has written a page into it.
struct stat st;
@@ -374,6 +379,7 @@ static int fill_slot(struct cache_slot *slot)
// it copies after a fill is the lock file rather than the old slot.
if (fstat(slot->lock_fd, &slot->st))
return errno;
+ slot->oversized = slot->max_bytes && (size_t)slot->st.st_size > slot->max_bytes;
return 0;
}
@@ -398,7 +404,14 @@ static void refresh_slot(struct cache_slot *slot)
close_lock(slot);
} else {
close_slot(slot);
- publish_slot(slot);
+ if (slot->oversized) {
+ // The expired copy goes too, or every later request
+ // would refill it and drop the result again.
+ unlink(slot->path);
+ unlock_slot(slot, 0);
+ } else {
+ publish_slot(slot);
+ }
slot->cache_fd = slot->lock_fd;
}
}
@@ -460,7 +473,10 @@ static int process_slot(struct cache_slot *slot)
// concurrent writer put there for a different key, so what gets
// printed is the descriptor still open on the lock file.
slot->cache_fd = slot->lock_fd;
- publish_slot(slot);
+ if (slot->oversized)
+ unlock_slot(slot, 0);
+ else
+ publish_slot(slot);
err = serve_slot(slot);
close_slot(slot);
return err;
@@ -500,7 +516,8 @@ unsigned long cache_hash_str(const char *str)
return h;
}
-int cache_process(int size, const char *path, const char *key, int ttl, cache_fill_fn fn)
+int cache_process(int size, const char *path, const char *key, int ttl, size_t max_bytes,
+ cache_fill_fn fn)
{
unsigned long hash;
int i;
@@ -539,6 +556,8 @@ int cache_process(int size, const char *path, const char *key, int ttl, cache_fi
slot.ttl = ttl;
slot.saved_stdout = -1;
slot.abandoned = 0;
+ slot.max_bytes = max_bytes;
+ slot.oversized = 0;
slot.path = slot_path.buf;
slot.lock_path = lock_path.buf;
slot.key = key;