skip servers with too many errors
Keep track of errors from servers so that bad ones can be skipped once a threshold is reached. Key the error tracking off the hostname because hosts may serve multiple repos under different url's and errors are likely to be host-wide. Implements: FS#29293. Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> Signed-off-by: Allan McRae <allan@archlinux.org>
This commit is contained in:
parent
bdf6aa3fb7
commit
4bf7aa119d
3 changed files with 87 additions and 4 deletions
|
@ -114,6 +114,7 @@ int SYMEXPORT alpm_release(alpm_handle_t *myhandle)
|
||||||
#ifdef HAVE_LIBCURL
|
#ifdef HAVE_LIBCURL
|
||||||
curl_multi_cleanup(myhandle->curlm);
|
curl_multi_cleanup(myhandle->curlm);
|
||||||
curl_global_cleanup();
|
curl_global_cleanup();
|
||||||
|
FREELIST(myhandle->server_errors);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_alpm_handle_unlock(myhandle);
|
_alpm_handle_unlock(myhandle);
|
||||||
|
|
|
@ -51,8 +51,83 @@
|
||||||
|
|
||||||
#ifdef HAVE_LIBCURL
|
#ifdef HAVE_LIBCURL
|
||||||
|
|
||||||
|
/* RFC1123 states applications should support this length */
|
||||||
|
#define HOSTNAME_SIZE 256
|
||||||
|
|
||||||
static int curl_add_payload(alpm_handle_t *handle, CURLM *curlm,
|
static int curl_add_payload(alpm_handle_t *handle, CURLM *curlm,
|
||||||
struct dload_payload *payload, const char *localpath);
|
struct dload_payload *payload, const char *localpath);
|
||||||
|
static int curl_gethost(const char *url, char *buffer, size_t buf_len);
|
||||||
|
|
||||||
|
/* number of "soft" errors required to blacklist a server, set to 0 to disable
|
||||||
|
* server blacklisting */
|
||||||
|
const unsigned int server_error_limit = 3;
|
||||||
|
|
||||||
|
struct server_error_count {
|
||||||
|
char server[HOSTNAME_SIZE];
|
||||||
|
unsigned int errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct server_error_count *find_server_errors(alpm_handle_t *handle, const char *server)
|
||||||
|
{
|
||||||
|
alpm_list_t *i;
|
||||||
|
struct server_error_count *h;
|
||||||
|
char hostname[HOSTNAME_SIZE];
|
||||||
|
/* key off the hostname because a host may serve multiple repos under
|
||||||
|
* different url's and errors are likely to be host-wide */
|
||||||
|
if(curl_gethost(server, hostname, sizeof(hostname)) != 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for(i = handle->server_errors; i; i = i->next) {
|
||||||
|
h = i->data;
|
||||||
|
if(strcmp(hostname, h->server) == 0) {
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if((h = calloc(sizeof(struct server_error_count), 1))
|
||||||
|
&& alpm_list_append(&handle->server_errors, h)) {
|
||||||
|
strcpy(h->server, hostname);
|
||||||
|
h->errors = 0;
|
||||||
|
return h;
|
||||||
|
} else {
|
||||||
|
free(h);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int should_skip_server(alpm_handle_t *handle, const char *server)
|
||||||
|
{
|
||||||
|
struct server_error_count *h;
|
||||||
|
if(server_error_limit && (h = find_server_errors(handle, server)) ) {
|
||||||
|
return h->errors >= server_error_limit;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void server_increment_error(alpm_handle_t *handle, const char *server, int count)
|
||||||
|
{
|
||||||
|
struct server_error_count *h;
|
||||||
|
if(server_error_limit
|
||||||
|
&& (h = find_server_errors(handle, server))
|
||||||
|
&& !should_skip_server(handle, server) ) {
|
||||||
|
h->errors += count;
|
||||||
|
|
||||||
|
if(should_skip_server(handle, server)) {
|
||||||
|
_alpm_log(handle, ALPM_LOG_WARNING,
|
||||||
|
_("too many errors from %s, skipping for the remainder of this transaction\n"),
|
||||||
|
h->server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void server_soft_error(alpm_handle_t *handle, const char *server)
|
||||||
|
{
|
||||||
|
server_increment_error(handle, server, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void server_hard_error(alpm_handle_t *handle, const char *server)
|
||||||
|
{
|
||||||
|
server_increment_error(handle, server, server_error_limit);
|
||||||
|
}
|
||||||
|
|
||||||
static const char *get_filename(const char *url)
|
static const char *get_filename(const char *url)
|
||||||
{
|
{
|
||||||
|
@ -332,9 +407,6 @@ static FILE *create_tempfile(struct dload_payload *payload, const char *localpat
|
||||||
return fp;
|
return fp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RFC1123 states applications should support this length */
|
|
||||||
#define HOSTNAME_SIZE 256
|
|
||||||
|
|
||||||
/* Return 0 if retry was successful, -1 otherwise */
|
/* Return 0 if retry was successful, -1 otherwise */
|
||||||
static int curl_retry_next_server(CURLM *curlm, CURL *curl, struct dload_payload *payload)
|
static int curl_retry_next_server(CURLM *curlm, CURL *curl, struct dload_payload *payload)
|
||||||
{
|
{
|
||||||
|
@ -342,7 +414,7 @@ static int curl_retry_next_server(CURLM *curlm, CURL *curl, struct dload_payload
|
||||||
size_t len;
|
size_t len;
|
||||||
alpm_handle_t *handle = payload->handle;
|
alpm_handle_t *handle = payload->handle;
|
||||||
|
|
||||||
if(payload->servers) {
|
while(payload->servers && should_skip_server(handle, payload->servers->data)) {
|
||||||
payload->servers = payload->servers->next;
|
payload->servers = payload->servers->next;
|
||||||
}
|
}
|
||||||
if(!payload->servers) {
|
if(!payload->servers) {
|
||||||
|
@ -351,6 +423,7 @@ static int curl_retry_next_server(CURLM *curlm, CURL *curl, struct dload_payload
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
server = payload->servers->data;
|
server = payload->servers->data;
|
||||||
|
payload->servers = payload->servers->next;
|
||||||
|
|
||||||
/* regenerate a new fileurl */
|
/* regenerate a new fileurl */
|
||||||
FREE(payload->fileurl);
|
FREE(payload->fileurl);
|
||||||
|
@ -423,6 +496,7 @@ static int curl_check_finished_download(CURLM *curlm, CURLMsg *msg,
|
||||||
_alpm_log(handle, ALPM_LOG_ERROR,
|
_alpm_log(handle, ALPM_LOG_ERROR,
|
||||||
_("failed retrieving file '%s' from %s : %s\n"),
|
_("failed retrieving file '%s' from %s : %s\n"),
|
||||||
payload->remote_name, hostname, payload->error_buffer);
|
payload->remote_name, hostname, payload->error_buffer);
|
||||||
|
server_soft_error(handle, payload->fileurl);
|
||||||
}
|
}
|
||||||
if(curl_retry_next_server(curlm, curl, payload) == 0) {
|
if(curl_retry_next_server(curlm, curl, payload) == 0) {
|
||||||
return 2;
|
return 2;
|
||||||
|
@ -440,6 +514,7 @@ static int curl_check_finished_download(CURLM *curlm, CURLMsg *msg,
|
||||||
_alpm_log(handle, ALPM_LOG_ERROR,
|
_alpm_log(handle, ALPM_LOG_ERROR,
|
||||||
_("failed retrieving file '%s' from %s : expected download size exceeded\n"),
|
_("failed retrieving file '%s' from %s : expected download size exceeded\n"),
|
||||||
payload->remote_name, hostname);
|
payload->remote_name, hostname);
|
||||||
|
server_soft_error(handle, payload->fileurl);
|
||||||
}
|
}
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
case CURLE_COULDNT_RESOLVE_HOST:
|
case CURLE_COULDNT_RESOLVE_HOST:
|
||||||
|
@ -448,6 +523,7 @@ static int curl_check_finished_download(CURLM *curlm, CURLMsg *msg,
|
||||||
_alpm_log(handle, ALPM_LOG_ERROR,
|
_alpm_log(handle, ALPM_LOG_ERROR,
|
||||||
_("failed retrieving file '%s' from %s : %s\n"),
|
_("failed retrieving file '%s' from %s : %s\n"),
|
||||||
payload->remote_name, hostname, payload->error_buffer);
|
payload->remote_name, hostname, payload->error_buffer);
|
||||||
|
server_hard_error(handle, payload->fileurl);
|
||||||
if(curl_retry_next_server(curlm, curl, payload) == 0) {
|
if(curl_retry_next_server(curlm, curl, payload) == 0) {
|
||||||
return 2;
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
|
@ -463,6 +539,7 @@ static int curl_check_finished_download(CURLM *curlm, CURLMsg *msg,
|
||||||
_alpm_log(handle, ALPM_LOG_ERROR,
|
_alpm_log(handle, ALPM_LOG_ERROR,
|
||||||
_("failed retrieving file '%s' from %s : %s\n"),
|
_("failed retrieving file '%s' from %s : %s\n"),
|
||||||
payload->remote_name, hostname, payload->error_buffer);
|
payload->remote_name, hostname, payload->error_buffer);
|
||||||
|
server_soft_error(handle, payload->fileurl);
|
||||||
} else {
|
} else {
|
||||||
_alpm_log(handle, ALPM_LOG_DEBUG,
|
_alpm_log(handle, ALPM_LOG_DEBUG,
|
||||||
"failed retrieving file '%s' from %s : %s\n",
|
"failed retrieving file '%s' from %s : %s\n",
|
||||||
|
@ -632,11 +709,15 @@ static int curl_add_payload(alpm_handle_t *handle, CURLM *curlm,
|
||||||
ASSERT(!payload->filepath, GOTO_ERR(handle, ALPM_ERR_WRONG_ARGS, cleanup));
|
ASSERT(!payload->filepath, GOTO_ERR(handle, ALPM_ERR_WRONG_ARGS, cleanup));
|
||||||
} else {
|
} else {
|
||||||
const char *server;
|
const char *server;
|
||||||
|
while(payload->servers && should_skip_server(handle, payload->servers->data)) {
|
||||||
|
payload->servers = payload->servers->next;
|
||||||
|
}
|
||||||
|
|
||||||
ASSERT(payload->servers, GOTO_ERR(handle, ALPM_ERR_SERVER_NONE, cleanup));
|
ASSERT(payload->servers, GOTO_ERR(handle, ALPM_ERR_SERVER_NONE, cleanup));
|
||||||
ASSERT(payload->filepath, GOTO_ERR(handle, ALPM_ERR_WRONG_ARGS, cleanup));
|
ASSERT(payload->filepath, GOTO_ERR(handle, ALPM_ERR_WRONG_ARGS, cleanup));
|
||||||
|
|
||||||
server = payload->servers->data;
|
server = payload->servers->data;
|
||||||
|
payload->servers = payload->servers->next;
|
||||||
|
|
||||||
len = strlen(server) + strlen(payload->filepath) + 2;
|
len = strlen(server) + strlen(payload->filepath) + 2;
|
||||||
MALLOC(payload->fileurl, len, GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
|
MALLOC(payload->fileurl, len, GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
|
||||||
|
|
|
@ -60,6 +60,7 @@ struct __alpm_handle_t {
|
||||||
#ifdef HAVE_LIBCURL
|
#ifdef HAVE_LIBCURL
|
||||||
/* libcurl handle */
|
/* libcurl handle */
|
||||||
CURLM *curlm;
|
CURLM *curlm;
|
||||||
|
alpm_list_t *server_errors;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
unsigned short disable_dl_timeout;
|
unsigned short disable_dl_timeout;
|
||||||
|
|
Loading…
Add table
Reference in a new issue