[PATCH 4/4] Add cfg cache-lock-fail behavior if failing to acquire cache-file, lock
Sven Göthel
sgothel at jausoft.com
Sun Jul 19 17:07:33 UTC 2026
>From 23472c578356f8ac5a2858ebaa7287bc24c3a5b0 Mon Sep 17 00:00:00 2001
From: Sven Göthel <sgothel at jausoft.com>
Date: Sun, 19 Jul 2026 16:47:26 +0200
Subject: Add cfg cache-lock-fail behavior if failing to acquire cache-file
lock
New default is http-response 503 and simply serve an error page
to avoid server lockup.
cache-lock-fail can be configured to 200 for old behavior,
i.e. producing an off-cache content page.
diff --git a/cache.c b/cache.c
index ca22194..10e5db5 100644
--- a/cache.c
+++ b/cache.c
@@ -16,6 +16,7 @@
#include "cgit.h"
#include "cache.h"
#include "html.h"
+#include "ui-shared.h"
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
@@ -290,10 +291,20 @@ static int lock_slot(struct cache_slot *slot, time_t tStart)
};
size_t wait_count = 0;
- slot->lock_fd = open(slot->lock_name, O_RDWR | O_CREAT,
- S_IRUSR | S_IWUSR);
- if (slot->lock_fd == -1)
- return errno;
+ if (0 == strncmp("cgit_test_key_no_lock", slot->key, 21)) {
+ cache_log("[cgit] Lock (%ds): Test-Key: %s -> forced fail\n",
+ (int)(time(NULL) - tStart), slot->key);
+ return ENOENT;
+ }
+ slot->lock_fd =
+ open(slot->lock_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ if (slot->lock_fd == -1) {
+ int saved_errno = errno;
+ cache_log("[cgit] Lock (%ds): Unable to open/create lock slot %s (%s): %s (%d)\n",
+ (int)(time(NULL) - tStart), slot->lock_name,
+ slot->key, strerror(saved_errno), saved_errno);
+ return saved_errno;
+ }
while (fcntl(slot->lock_fd, F_SETLK, &lock) < 0) {
int saved_errno = errno;
time_t tNow = time(NULL);
@@ -489,20 +500,34 @@ static int process_slot(struct cache_slot *slot)
close_slot(slot);
if ((err = lock_slot(slot, tStart)) != 0) {
time_t tNow1 = time(NULL);
- slot->fn();
- cache_log("[cgit] Unable to lock new slot (%ds, %ds) %s (%s): %s (%d)\n",
- (int)(tNow1 - tStart), (int)(time(NULL) - tStart),
- slot->lock_name, slot->key, strerror(err), err);
+ if (ctx.cfg.cache_lock_fail != 200) {
+ ctx.page.title = "cgit error";
+ cgit_print_error_page(503, "Service Unavailable",
+ "Service Unavailable. Cache: Could not lock new-slot within %ds.",
+ (int)(tNow1 - tStart));
+ } else {
+ slot->fn();
+ }
+ cache_log("[cgit] Unable to lock new slot (%ds, %ds) %s (%s): %d: %s (%d)\n",
+ (int)(tNow1 - tStart), (int)(time(NULL) - tStart),
+ slot->lock_name, slot->key, ctx.cfg.cache_lock_fail, strerror(err), err);
return 0;
}
if (slot->cache_fd <= 0) {
// first concurrent process lock
if ((err = fill_slot(slot)) != 0) {
- cache_log("[cgit] Unable to fill slot %s (%s): %s (%d)\n",
- slot->lock_name, slot->key, strerror(err), err);
+ cache_log("[cgit] Unable to fill slot %s (%s): %d: %s (%d)\n",
+ slot->lock_name, slot->key, ctx.cfg.cache_lock_fail, strerror(err), err);
unlock_slot(slot, 0);
close_lock(slot);
- slot->fn();
+ if (ctx.cfg.cache_lock_fail != 200) {
+ ctx.page.title = "cgit error";
+ cgit_print_error_page(503, "Service Unavailable",
+ "Service Unavailable. Cache: Could not fill slot within %ds.",
+ (int)(time(NULL) - tStart));
+ } else {
+ slot->fn();
+ }
return 0;
}
// We've got a valid cache slot in the lock file, which
diff --git a/cgit.c b/cgit.c
index 25e29f9..83665ca 100644
--- a/cgit.c
+++ b/cgit.c
@@ -217,6 +217,8 @@ static void config_cb(const char *name, const char *value)
ctx.cfg.cache_scanrc_ttl = atoi(value);
else if (!strcmp(name, "cache-static-ttl"))
ctx.cfg.cache_static_ttl = atoi(value);
+ else if (!strcmp(name, "cache-lock-fail"))
+ ctx.cfg.cache_lock_fail = atoi(value);
else if (!strcmp(name, "cache-lock-timeout"))
ctx.cfg.cache_lock_timeout = atoi(value);
else if (!strcmp(name, "client-io-idle-timeout"))
@@ -390,6 +392,7 @@ static void prepare_context(void)
ctx.cfg.cache_scanrc_ttl = 15;
ctx.cfg.cache_dynamic_ttl = 5;
ctx.cfg.cache_static_ttl = -1;
+ ctx.cfg.cache_lock_fail = 503;
ctx.cfg.cache_lock_timeout = 10;
ctx.cfg.client_io_idle_timeout = 20;
ctx.cfg.client_io_min_rate = 500;
@@ -890,6 +893,7 @@ static void print_config(FILE *f, const char *prefix)
fprintf(f, "%slog-level=%d\n", prefix, ctx.cfg.log_level);
fprintf(f, "%sproject-list=%s\n", prefix, ctx.cfg.project_list);
fprintf(f, "%sscan-path=%s\n", prefix, ctx.cfg.scan_path);
+ fprintf(f, "%scache-lock-fail=%d\n", prefix, ctx.cfg.cache_lock_fail);
fprintf(f, "%scache-lock-timeout=%d\n", prefix, ctx.cfg.cache_lock_timeout);
fprintf(f, "%sclient-io-idle-timeout=%d\n", prefix, ctx.cfg.client_io_idle_timeout);
fprintf(f, "%sclient-io-min-rate=%ld\n", prefix, ctx.cfg.client_io_min_rate);
diff --git a/cgit.h b/cgit.h
index 4aed96c..df6a6c1 100644
--- a/cgit.h
+++ b/cgit.h
@@ -229,6 +229,8 @@ struct cgit_config {
int cache_static_ttl;
int cache_about_ttl;
int cache_snapshot_ttl;
+ /* cache lock fail action as http-response code. 200 returns un-cached processed content (old behavior), otherwise an error page is served (new default). Defaults to 503. */
+ int cache_lock_fail;
/* cache lock timeout in seconds to acquire the cache lock-file against concurrent processes. Defaults to 10s. */
int cache_lock_timeout;
/* idle timeout in seconds between sending/receiving chunks of the cached body to/from the client. Defaults to 20s. */
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index d3a8ae8..de25244 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -100,6 +100,11 @@ cache-static-ttl::
version of repository pages accessed with a fixed SHA1. See also:
"CACHE". Default value: -1".
+cache-lock-fail::
+ Cache lock fail action as http-response code.
+ 200 returns un-cached processed content (old behavior),
+ otherwise an error page is served (new default). Default value: "503".
+
cache-lock-timeout::
Timeout in seconds to acquire the cache lock-file
against concurrent processes. Default value: "10".
More information about the CGit
mailing list