diff --git a/cache.c b/cache.c index 860083dc..3658bb77 100644 --- a/cache.c +++ b/cache.c @@ -111,13 +111,14 @@ inline uint64_t uwsgi_cache_get_index(char *key, uint16_t keylen) { uint64_t slot = uwsgi.cache_hashtable[hash_key]; struct uwsgi_cache_item *uci; + uint64_t rounds = 0; //uwsgi_log("found slot %d for key %d\n", slot, hash_key); uci = &uwsgi.cache_items[slot]; // first round - if (uci->djbhash != hash) goto cycle; + if (uci->djbhash != hash) return 0; if (uci->keysize != keylen) goto cycle; if (memcmp(uci->key, key, keylen)) goto cycle; @@ -127,7 +128,20 @@ cycle: while(uci->next) { slot = uci->next; uci = &uwsgi.cache_items[slot]; - if (uci->djbhash != hash) continue; + rounds++; + if (rounds > uwsgi.cache_max_items) { + uwsgi_log("ALARM !!! cache-loop (and potential deadlock) detected slot = %llu prev = %llu next = %llu\n", uci->next, slot, uci->prev, uci->next); + // terrible case: the whole uWSGI stack can deadlock, leaving only the master alive + // if the master is avalable, trigger a brutal reload + if (uwsgi.master_process) { + kill(uwsgi.workers[0].pid, SIGTERM); + } + // otherwise kill the current worker (could be pretty useless...) + else { + exit(1); + } + } + if (uci->djbhash != hash) return 0; if (uci->keysize != keylen) continue; if (!memcmp(uci->key, key, keylen)) return slot; } @@ -154,13 +168,14 @@ char *uwsgi_cache_get(char *key, uint16_t keylen, uint64_t *valsize) { return NULL; } -int uwsgi_cache_del(char *key, uint16_t keylen) { +int uwsgi_cache_del(char *key, uint16_t keylen, uint64_t index) { - uint64_t index = 0; struct uwsgi_cache_item *uci; int ret = -1; - index = uwsgi_cache_get_index(key, keylen); + if (!index) + index = uwsgi_cache_get_index(key, keylen); + if (index) { uci = &uwsgi.cache_items[index] ; uci->keysize = 0; @@ -177,9 +192,15 @@ int uwsgi_cache_del(char *key, uint16_t keylen) { if (uci->prev) { uwsgi.cache_items[uci->prev].next = uci->next; } + else { + // set next as the new entry point (could be 0) + uwsgi.cache_hashtable[uci->djbhash % 0xffff] = uci->next; + } + if (uci->next) { uwsgi.cache_items[uci->next].prev = uci->prev; } + if (!uci->prev && !uci->next) { // reset hashtable entry //uwsgi_log("!!! resetted hashtable entry !!!\n"); @@ -188,6 +209,7 @@ int uwsgi_cache_del(char *key, uint16_t keylen) { uci->djbhash = 0; uci->prev = 0; uci->next = 0; + uci->expires = 0; } return ret; @@ -262,6 +284,9 @@ int uwsgi_cache_set(char *key, uint16_t keylen, char *val, uint64_t vallen, uint ret = 0; // now put the value in the 16bit hashtable slot = uci->djbhash % 0xffff; + // reset values + uci->prev = 0; + uci->next = 0; if (uwsgi.cache_hashtable[slot] == 0) { uwsgi.cache_hashtable[slot] = index; diff --git a/master.c b/master.c index 033571cf..ae59cc33 100644 --- a/master.c +++ b/master.c @@ -123,15 +123,21 @@ void *cache_sweeper_loop(void *noarg) { // remove expired cache items TODO use rb_tree timeouts for(;;) { sleep(uwsgi.cache_expire_freq); - for (i = 0; i < (int) uwsgi.cache_max_items; i++) { + uint64_t freed_items = 0; + // skip the first slot + for (i = 1; i < (int) uwsgi.cache_max_items; i++) { uwsgi_wlock(uwsgi.cache_lock); if (uwsgi.cache_items[i].expires) { if (uwsgi.cache_items[i].expires < (uint64_t) uwsgi.current_time) { - uwsgi_cache_del(uwsgi.cache_items[i].key, uwsgi.cache_items[i].keysize); + uwsgi_cache_del(NULL, 0, i); + freed_items++; } } uwsgi_rwunlock(uwsgi.cache_lock); } + if (uwsgi.cache_report_freed_items && freed_items > 0) { + uwsgi_log("freed %llu cache items\n", (unsigned long long) freed_items); + } }; } diff --git a/plugins/cache/cache.c b/plugins/cache/cache.c index 22ec375d..a160288f 100644 --- a/plugins/cache/cache.c +++ b/plugins/cache/cache.c @@ -63,7 +63,7 @@ int uwsgi_cache_request(struct wsgi_request *wsgi_req) { case 2: // del if (wsgi_req->uh.pktsize > 0) { - uwsgi_cache_del(wsgi_req->buffer, wsgi_req->uh.pktsize); + uwsgi_cache_del(wsgi_req->buffer, wsgi_req->uh.pktsize, 0); } break; case 3: diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index 3f5bd4e9..f6748810 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -351,7 +351,7 @@ PHP_FUNCTION(uwsgi_cache_del) { } uwsgi_wlock(uwsgi.cache_lock); - if (uwsgi_cache_del(key, keylen)) { + if (uwsgi_cache_del(key, keylen, 0)) { uwsgi_rwunlock(uwsgi.cache_lock); RETURN_TRUE; } diff --git a/plugins/python/uwsgi_pymodule.c b/plugins/python/uwsgi_pymodule.c index be20e5fd..955c3b0c 100644 --- a/plugins/python/uwsgi_pymodule.c +++ b/plugins/python/uwsgi_pymodule.c @@ -3138,7 +3138,7 @@ PyObject *py_uwsgi_cache_del(PyObject * self, PyObject * args) { else if (uwsgi.cache_max_items) { UWSGI_RELEASE_GIL uwsgi_wlock(uwsgi.cache_lock); - if (uwsgi_cache_del(key, keylen)) { + if (uwsgi_cache_del(key, keylen, 0)) { uwsgi_rwunlock(uwsgi.cache_lock); UWSGI_GET_GIL Py_INCREF(Py_None); diff --git a/plugins/rack/rack_api.c b/plugins/rack/rack_api.c index 17328723..e98cdf65 100644 --- a/plugins/rack/rack_api.c +++ b/plugins/rack/rack_api.c @@ -347,7 +347,7 @@ VALUE rack_uwsgi_cache_del(VALUE *class, VALUE rbkey) { size_t keylen = RSTRING_LEN(rbkey); uwsgi_wlock(uwsgi.cache_lock); - if (uwsgi_cache_del(key, keylen)) { + if (uwsgi_cache_del(key, keylen, 0)) { uwsgi_rwunlock(uwsgi.cache_lock); return Qfalse; } diff --git a/uwsgi.c b/uwsgi.c index 6aa2c549..126d4611 100644 --- a/uwsgi.c +++ b/uwsgi.c @@ -160,6 +160,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"cache-server-threads", required_argument, 0, "set the number of threads for the cache server", uwsgi_opt_set_int, &uwsgi.cache_server_threads,0}, {"cache-no-expire", required_argument, 0, "disable auto sweep of expired items", uwsgi_opt_true, &uwsgi.cache_no_expire,0}, {"cache-expire-freq", required_argument, 0, "set the frequency of cache sweeper scans (default 3 seconds)", uwsgi_opt_set_int, &uwsgi.cache_expire_freq,0}, + {"cache-report-freed-items", required_argument, 0, "constantly report the cache item freed by the sweeper (use only for debug)", uwsgi_opt_true, &uwsgi.cache_report_freed_items,0}, {"queue", required_argument, 0, "enable shared queue", uwsgi_opt_set_int, &uwsgi.queue_size, 0}, {"queue-blocksize", required_argument, 0, "set queue blocksize", uwsgi_opt_set_int, &uwsgi.queue_store_sync, 0}, diff --git a/uwsgi.h b/uwsgi.h index 29a2f6c1..23189af5 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -1574,6 +1574,7 @@ struct uwsgi_server { int cache_store_sync; int cache_no_expire; int cache_expire_freq; + int cache_report_freed_items; char *cache_server; int cache_server_threads; @@ -2118,7 +2119,7 @@ ssize_t uwsgi_send_message(int, uint8_t, uint8_t, char *, uint16_t, int, ssize_t char *uwsgi_cluster_best_node(void); int uwsgi_cache_set(char *, uint16_t, char *, uint64_t, uint64_t, uint16_t); -int uwsgi_cache_del(char *, uint16_t); +int uwsgi_cache_del(char *, uint16_t, uint64_t); char *uwsgi_cache_get(char *, uint16_t, uint64_t *); uint32_t uwsgi_cache_exists(char *, uint16_t);