diff options
Diffstat (limited to 'web/server/web_client_cache.c')
-rw-r--r-- | web/server/web_client_cache.c | 309 |
1 files changed, 89 insertions, 220 deletions
diff --git a/web/server/web_client_cache.c b/web/server/web_client_cache.c index 4344209c8..b410ba7f9 100644 --- a/web/server/web_client_cache.c +++ b/web/server/web_client_cache.c @@ -6,77 +6,6 @@ // ---------------------------------------------------------------------------- // allocate and free web_clients -#ifdef ENABLE_HTTPS - -static void web_client_reuse_ssl(struct web_client *w) { - if (netdata_ssl_srv_ctx) { - if (w->ssl.conn) { - SSL_SESSION *session = SSL_get_session(w->ssl.conn); - SSL *old = w->ssl.conn; - w->ssl.conn = SSL_new(netdata_ssl_srv_ctx); - if (session) { -#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_111 - if (SSL_SESSION_is_resumable(session)) -#endif - SSL_set_session(w->ssl.conn, session); - } - SSL_free(old); - } - } -} -#endif - - -static void web_client_zero(struct web_client *w) { - // zero everything about it - but keep the buffers - - // remember the pointers to the buffers - BUFFER *b1 = w->response.data; - BUFFER *b2 = w->response.header; - BUFFER *b3 = w->response.header_output; - - // empty the buffers - buffer_flush(b1); - buffer_flush(b2); - buffer_flush(b3); - - freez(w->user_agent); - - // zero everything - memset(w, 0, sizeof(struct web_client)); - - // restore the pointers of the buffers - w->response.data = b1; - w->response.header = b2; - w->response.header_output = b3; -} - -static void web_client_free(struct web_client *w) { - buffer_free(w->response.header_output); - buffer_free(w->response.header); - buffer_free(w->response.data); - freez(w->user_agent); -#ifdef ENABLE_HTTPS - if ((!web_client_check_unix(w)) && (netdata_ssl_srv_ctx)) { - if (w->ssl.conn) { - SSL_free(w->ssl.conn); - w->ssl.conn = NULL; - } - } -#endif - freez(w); - __atomic_sub_fetch(&netdata_buffers_statistics.buffers_web, sizeof(struct web_client), __ATOMIC_RELAXED); -} - -static struct web_client *web_client_alloc(void) { - struct web_client *w = callocz(1, sizeof(struct web_client)); - __atomic_add_fetch(&netdata_buffers_statistics.buffers_web, sizeof(struct web_client), __ATOMIC_RELAXED); - w->response.data = buffer_create(NETDATA_WEB_RESPONSE_INITIAL_SIZE, &netdata_buffers_statistics.buffers_web); - w->response.header = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE, &netdata_buffers_statistics.buffers_web); - w->response.header_output = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE, &netdata_buffers_statistics.buffers_web); - return w; -} - // ---------------------------------------------------------------------------- // web clients caching @@ -87,194 +16,134 @@ static struct web_client *web_client_alloc(void) { // The size of the cache is adaptive. It caches the structures of 2x // the number of currently connected clients. -// Comments per server: -// SINGLE-THREADED : 1 cache is maintained -// MULTI-THREADED : 1 cache is maintained -// STATIC-THREADED : 1 cache for each thread of the web server - -__thread struct clients_cache web_clients_cache = { - .pid = 0, - .used = NULL, - .used_count = 0, - .avail = NULL, - .avail_count = 0, - .allocated = 0, - .reused = 0 +static struct clients_cache { + struct { + SPINLOCK spinlock; + struct web_client *head; // the structures of the currently connected clients + size_t count; // the count the currently connected clients + + size_t allocated; // the number of allocations + size_t reused; // the number of re-uses + } used; + + struct { + SPINLOCK spinlock; + struct web_client *head; // the cached structures, available for future clients + size_t count; // the number of cached structures + } avail; +} web_clients_cache = { + .used = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .head = NULL, + .count = 0, + .reused = 0, + .allocated = 0, + }, + .avail = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .head = NULL, + .count = 0, + }, }; -inline void web_client_cache_verify(int force) { -#ifdef NETDATA_INTERNAL_CHECKS - static __thread size_t count = 0; - count++; - - if(unlikely(force || count > 1000)) { - count = 0; - - struct web_client *w; - size_t used = 0, avail = 0; - for(w = web_clients_cache.used; w ; w = w->next) used++; - for(w = web_clients_cache.avail; w ; w = w->next) avail++; - - info("web_client_cache has %zu (%zu) used and %zu (%zu) available clients, allocated %zu, reused %zu (hit %zu%%)." - , used, web_clients_cache.used_count - , avail, web_clients_cache.avail_count - , web_clients_cache.allocated - , web_clients_cache.reused - , (web_clients_cache.allocated + web_clients_cache.reused)?(web_clients_cache.reused * 100 / (web_clients_cache.allocated + web_clients_cache.reused)):0 - ); - } -#else - if(unlikely(force)) { - info("web_client_cache has %zu used and %zu available clients, allocated %zu, reused %zu (hit %zu%%)." - , web_clients_cache.used_count - , web_clients_cache.avail_count - , web_clients_cache.allocated - , web_clients_cache.reused - , (web_clients_cache.allocated + web_clients_cache.reused)?(web_clients_cache.reused * 100 / (web_clients_cache.allocated + web_clients_cache.reused)):0 - ); - } -#endif -} - // destroy the cache and free all the memory it uses void web_client_cache_destroy(void) { -#ifdef NETDATA_INTERNAL_CHECKS - if(unlikely(web_clients_cache.pid != 0 && web_clients_cache.pid != gettid())) - error("Oops! wrong thread accessing the cache. Expected %d, found %d", (int)web_clients_cache.pid, (int)gettid()); - - web_client_cache_verify(1); -#endif - - netdata_thread_disable_cancelability(); + internal_error(true, "web_client_cache has %zu used and %zu available clients, allocated %zu, reused %zu (hit %zu%%)." + , web_clients_cache.used.count + , web_clients_cache.avail.count + , web_clients_cache.used.allocated + , web_clients_cache.used.reused + , (web_clients_cache.used.allocated + web_clients_cache.used.reused)?(web_clients_cache.used.reused * 100 / (web_clients_cache.used.allocated + web_clients_cache.used.reused)):0 + ); struct web_client *w, *t; - w = web_clients_cache.used; + netdata_spinlock_lock(&web_clients_cache.avail.spinlock); + w = web_clients_cache.avail.head; while(w) { t = w; - w = w->next; + w = w->cache.next; web_client_free(t); } - web_clients_cache.used = NULL; - web_clients_cache.used_count = 0; - - w = web_clients_cache.avail; - while(w) { - t = w; - w = w->next; - web_client_free(t); - } - web_clients_cache.avail = NULL; - web_clients_cache.avail_count = 0; - - netdata_thread_enable_cancelability(); + web_clients_cache.avail.head = NULL; + web_clients_cache.avail.count = 0; + netdata_spinlock_unlock(&web_clients_cache.avail.spinlock); + +// DO NOT FREE THEM IF THEY ARE USED +// netdata_spinlock_lock(&web_clients_cache.used.spinlock); +// w = web_clients_cache.used.head; +// while(w) { +// t = w; +// w = w->next; +// web_client_free(t); +// } +// web_clients_cache.used.head = NULL; +// web_clients_cache.used.count = 0; +// web_clients_cache.used.reused = 0; +// web_clients_cache.used.allocated = 0; +// netdata_spinlock_unlock(&web_clients_cache.used.spinlock); } -struct web_client *web_client_get_from_cache_or_allocate() { - -#ifdef NETDATA_INTERNAL_CHECKS - if(unlikely(web_clients_cache.pid == 0)) - web_clients_cache.pid = gettid(); - - if(unlikely(web_clients_cache.pid != 0 && web_clients_cache.pid != gettid())) - error("Oops! wrong thread accessing the cache. Expected %d, found %d", (int)web_clients_cache.pid, (int)gettid()); -#endif - - netdata_thread_disable_cancelability(); - - struct web_client *w = web_clients_cache.avail; - +struct web_client *web_client_get_from_cache(void) { + netdata_spinlock_lock(&web_clients_cache.avail.spinlock); + struct web_client *w = web_clients_cache.avail.head; if(w) { // get it from avail - if (w == web_clients_cache.avail) web_clients_cache.avail = w->next; - if(w->prev) w->prev->next = w->next; - if(w->next) w->next->prev = w->prev; - web_clients_cache.avail_count--; -#ifdef ENABLE_HTTPS - web_client_reuse_ssl(w); - SSL *ssl = w->ssl.conn; -#endif + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(web_clients_cache.avail.head, w, cache.prev, cache.next); + web_clients_cache.avail.count--; + netdata_spinlock_unlock(&web_clients_cache.avail.spinlock); + web_client_zero(w); - web_clients_cache.reused++; -#ifdef ENABLE_HTTPS - w->ssl.conn = ssl; - w->ssl.flags = NETDATA_SSL_START; - debug(D_WEB_CLIENT_ACCESS,"Reusing SSL structure with (w->ssl = NULL, w->accepted = %u)", w->ssl.flags); -#endif + + netdata_spinlock_lock(&web_clients_cache.used.spinlock); + web_clients_cache.used.reused++; } else { + netdata_spinlock_unlock(&web_clients_cache.avail.spinlock); + // allocate it - w = web_client_alloc(); + w = web_client_create(&netdata_buffers_statistics.buffers_web); + #ifdef ENABLE_HTTPS w->ssl.flags = NETDATA_SSL_START; debug(D_WEB_CLIENT_ACCESS,"Starting SSL structure with (w->ssl = NULL, w->accepted = %u)", w->ssl.flags); #endif - web_clients_cache.allocated++; + + netdata_spinlock_lock(&web_clients_cache.used.spinlock); + web_clients_cache.used.allocated++; } // link it to used web clients - if (web_clients_cache.used) web_clients_cache.used->prev = w; - w->next = web_clients_cache.used; - w->prev = NULL; - web_clients_cache.used = w; - web_clients_cache.used_count++; + DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(web_clients_cache.used.head, w, cache.prev, cache.next); + web_clients_cache.used.count++; + netdata_spinlock_unlock(&web_clients_cache.used.spinlock); // initialize it + w->use_count++; w->id = global_statistics_web_client_connected(); - w->mode = WEB_CLIENT_MODE_NORMAL; - - netdata_thread_enable_cancelability(); + w->mode = WEB_CLIENT_MODE_GET; return w; } -void web_client_release(struct web_client *w) { -#ifdef NETDATA_INTERNAL_CHECKS - if(unlikely(web_clients_cache.pid != 0 && web_clients_cache.pid != gettid())) - error("Oops! wrong thread accessing the cache. Expected %d, found %d", (int)web_clients_cache.pid, (int)gettid()); - - if(unlikely(w->running)) - error("%llu: releasing web client from %s port %s, but it still running.", w->id, w->client_ip, w->client_port); -#endif - - debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s port %s.", w->id, w->client_ip, w->client_port); - - web_server_log_connection(w, "DISCONNECTED"); - web_client_request_done(w); - global_statistics_web_client_disconnected(); - - netdata_thread_disable_cancelability(); - - if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) { - if (w->ifd != -1) close(w->ifd); - if (w->ofd != -1 && w->ofd != w->ifd) close(w->ofd); - w->ifd = w->ofd = -1; -#ifdef ENABLE_HTTPS - web_client_reuse_ssl(w); - w->ssl.flags = NETDATA_SSL_START; -#endif - - } - +void web_client_release_to_cache(struct web_client *w) { // unlink it from the used - if (w == web_clients_cache.used) web_clients_cache.used = w->next; - if(w->prev) w->prev->next = w->next; - if(w->next) w->next->prev = w->prev; - web_clients_cache.used_count--; + netdata_spinlock_lock(&web_clients_cache.used.spinlock); + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(web_clients_cache.used.head, w, cache.prev, cache.next); + ssize_t used_count = (ssize_t)--web_clients_cache.used.count; + netdata_spinlock_unlock(&web_clients_cache.used.spinlock); + + netdata_spinlock_lock(&web_clients_cache.avail.spinlock); + if(w->use_count > 100 || (used_count > 0 && web_clients_cache.avail.count >= 2 * (size_t)used_count) || (used_count <= 10 && web_clients_cache.avail.count >= 20)) { + netdata_spinlock_unlock(&web_clients_cache.avail.spinlock); - if(web_clients_cache.avail_count >= 2 * web_clients_cache.used_count) { // we have too many of them - free it web_client_free(w); } else { // link it to the avail - if (web_clients_cache.avail) web_clients_cache.avail->prev = w; - w->next = web_clients_cache.avail; - w->prev = NULL; - web_clients_cache.avail = w; - web_clients_cache.avail_count++; + DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(web_clients_cache.avail.head, w, cache.prev, cache.next); + web_clients_cache.avail.count++; + netdata_spinlock_unlock(&web_clients_cache.avail.spinlock); } - - netdata_thread_enable_cancelability(); } - |