revocation: Block only one thread per URL after a previous CRL fetch failed

If a CRL server is unresponsive, all threads trying to fetch this CRL will
block execution. If a recent previous attempt to fetch the CRL failed, it
is likely that it will fail again. While it makes sense to retry fetching
the CRL on demand with one thread, it hardly does to block additional
threads while the first one is blocked during the fetch.

So remember the timestamp of the last CRL fetch failure per URL, and do
not block more than one thread in the CRL fetch for some time. This time
is a multiple of the configured fetch timeout, so that it works well for
any configured value. With the default configuration, a failing CRL fetch
will impact concurrent CRL fetches for the same URL for 30s.
This commit is contained in:
Martin Willi
2025-11-13 18:15:15 +01:00
committed by Tobias Brunner
parent 330a7d1963
commit df6977d4cf
@@ -26,6 +26,9 @@
#include <credentials/certificates/ocsp_request.h>
#include <credentials/certificates/ocsp_response.h>
/* number of fetch timeouts to degrade a CRL fetch after a failure */
#define CRL_DEGRADATION_TIMES 3
typedef struct private_revocation_fetcher_t private_revocation_fetcher_t;
/**
@@ -71,6 +74,11 @@ struct crl_fetch_t {
*/
u_int fetchers;
/**
* Has the previous fetch failed, until when is this URL degraded?
*/
time_t failing;
/**
* CRL received in the currently active fetch.
*/
@@ -119,6 +127,14 @@ static certificate_t *start_crl_fetch(private_revocation_fetcher_t *this,
crl = do_crl_fetch(this, fetch->url, timeout);
this->mutex->lock(this->mutex);
fetch->crl = crl;
if (crl)
{
fetch->failing = 0;
}
else
{
fetch->failing = time_monotonic(NULL) + timeout * CRL_DEGRADATION_TIMES;
}
while (fetch->fetchers > 1)
{
fetch->condvar->signal(fetch->condvar);
@@ -137,6 +153,12 @@ static certificate_t *wait_for_crl(private_revocation_fetcher_t *this,
{
certificate_t *crl = NULL;
if (fetch->failing && fetch->failing > time_monotonic(NULL))
{
DBG1(DBG_CFG, " crl fetch from '%s' recently failed, skipping",
fetch->url);
return NULL;
}
DBG1(DBG_CFG, " waiting for crl fetch from '%s' ...", fetch->url);
if (fetch->crl)
{