master
c 152 lines 5.39 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #define WEB_SERVER_INTERNALS 1
4 #include "web_client_cache.h"
5
6 // ----------------------------------------------------------------------------
7 // allocate and free web_clients
8
9 // ----------------------------------------------------------------------------
10 // web clients caching
11
12 // When clients connect and disconnect, avoid allocating and releasing memory.
13 // Instead, when new clients get connected, reuse any memory previously allocated
14 // for serving web clients that are now disconnected.
15
16 // The size of the cache is adaptive. It caches the structures of 2x
17 // the number of currently connected clients.
18
19 static struct clients_cache {
20 unsigned long long client_id;
21
22 struct {
23 SPINLOCK spinlock;
24 struct web_client *head; // the structures of the currently connected clients
25 size_t count; // the count the currently connected clients
26
27 size_t allocated; // the number of allocations
28 size_t reused; // the number of re-uses
29 } used;
30
31 struct {
32 SPINLOCK spinlock;
33 struct web_client *head; // the cached structures, available for future clients
34 size_t count; // the number of cached structures
35 } avail;
36 } web_clients_cache = {
37 .used = {
38 .spinlock = SPINLOCK_INITIALIZER,
39 .head = NULL,
40 .count = 0,
41 .reused = 0,
42 .allocated = 0,
43 },
44 .avail = {
45 .spinlock = SPINLOCK_INITIALIZER,
46 .head = NULL,
47 .count = 0,
48 },
49 };
50
51 // destroy the cache and free all the memory it uses
52 void web_client_cache_destroy(void) {
53 internal_error(true, "web_client_cache has %zu used and %zu available clients, allocated %zu, reused %zu (hit %zu%%)."
54 , web_clients_cache.used.count
55 , web_clients_cache.avail.count
56 , web_clients_cache.used.allocated
57 , web_clients_cache.used.reused
58 , (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
59 );
60
61 struct web_client *w, *t;
62
63 spinlock_lock(&web_clients_cache.avail.spinlock);
64 w = web_clients_cache.avail.head;
65 while(w) {
66 t = w;
67 w = w->cache.next;
68 web_client_free(t);
69 }
70 web_clients_cache.avail.head = NULL;
71 web_clients_cache.avail.count = 0;
72 spinlock_unlock(&web_clients_cache.avail.spinlock);
73
74 // DO NOT FREE THEM IF THEY ARE USED
75 // spinlock_lock(&web_clients_cache.used.spinlock);
76 // w = web_clients_cache.used.head;
77 // while(w) {
78 // t = w;
79 // w = w->next;
80 // web_client_free(t);
81 // }
82 // web_clients_cache.used.head = NULL;
83 // web_clients_cache.used.count = 0;
84 // web_clients_cache.used.reused = 0;
85 // web_clients_cache.used.allocated = 0;
86 // spinlock_unlock(&web_clients_cache.used.spinlock);
87 }
88
89 struct web_client *web_client_get_from_cache(void) {
90 spinlock_lock(&web_clients_cache.avail.spinlock);
91 struct web_client *w = web_clients_cache.avail.head;
92 if(w) {
93 // get it from avail
94 DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(web_clients_cache.avail.head, w, cache.prev, cache.next);
95 web_clients_cache.avail.count--;
96
97 spinlock_unlock(&web_clients_cache.avail.spinlock);
98 web_client_reuse_from_cache(w);
99 spinlock_lock(&web_clients_cache.used.spinlock);
100
101 web_clients_cache.used.reused++;
102 }
103 else {
104 spinlock_unlock(&web_clients_cache.avail.spinlock);
105 w = web_client_create(&netdata_buffers_statistics.buffers_web);
106 spinlock_lock(&web_clients_cache.used.spinlock);
107
108 w->id = __atomic_add_fetch(&web_clients_cache.client_id, 1, __ATOMIC_RELAXED);
109 web_clients_cache.used.allocated++;
110 }
111
112 // link it to used web clients
113 DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(web_clients_cache.used.head, w, cache.prev, cache.next);
114 web_clients_cache.used.count++;
115 spinlock_unlock(&web_clients_cache.used.spinlock);
116
117 // initialize it
118 w->use_count++;
119 w->port_acl = HTTP_ACL_NONE;
120 w->acl = HTTP_ACL_NONE;
121 w->mode = HTTP_REQUEST_MODE_GET;
122 web_client_reset_permissions(w);
123 memset(w->transaction, 0, sizeof(w->transaction));
124 memset(w->mcp_session_id, 0, sizeof(w->mcp_session_id));
125 memset(&w->auth, 0, sizeof(w->auth));
126
127 return w;
128 }
129
130 void web_client_release_to_cache(struct web_client *w) {
131 netdata_ssl_close(&w->ssl);
132
133 // unlink it from the used
134 spinlock_lock(&web_clients_cache.used.spinlock);
135 DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(web_clients_cache.used.head, w, cache.prev, cache.next);
136 ssize_t used_count = (ssize_t)--web_clients_cache.used.count;
137 spinlock_unlock(&web_clients_cache.used.spinlock);
138
139 spinlock_lock(&web_clients_cache.avail.spinlock);
140 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)) {
141 spinlock_unlock(&web_clients_cache.avail.spinlock);
142
143 // we have too many of them - free it
144 web_client_free(w);
145 }
146 else {
147 // link it to the avail
148 DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(web_clients_cache.avail.head, w, cache.prev, cache.next);
149 web_clients_cache.avail.count++;
150 spinlock_unlock(&web_clients_cache.avail.spinlock);
151 }
152 }