web_client_cache.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #define WEB_SERVER_INTERNALS 1
  3. #include "web_client_cache.h"
  4. // ----------------------------------------------------------------------------
  5. // allocate and free web_clients
  6. // ----------------------------------------------------------------------------
  7. // web clients caching
  8. // When clients connect and disconnect, avoid allocating and releasing memory.
  9. // Instead, when new clients get connected, reuse any memory previously allocated
  10. // for serving web clients that are now disconnected.
  11. // The size of the cache is adaptive. It caches the structures of 2x
  12. // the number of currently connected clients.
  13. static struct clients_cache {
  14. struct {
  15. SPINLOCK spinlock;
  16. struct web_client *head; // the structures of the currently connected clients
  17. size_t count; // the count the currently connected clients
  18. size_t allocated; // the number of allocations
  19. size_t reused; // the number of re-uses
  20. } used;
  21. struct {
  22. SPINLOCK spinlock;
  23. struct web_client *head; // the cached structures, available for future clients
  24. size_t count; // the number of cached structures
  25. } avail;
  26. } web_clients_cache = {
  27. .used = {
  28. .spinlock = NETDATA_SPINLOCK_INITIALIZER,
  29. .head = NULL,
  30. .count = 0,
  31. .reused = 0,
  32. .allocated = 0,
  33. },
  34. .avail = {
  35. .spinlock = NETDATA_SPINLOCK_INITIALIZER,
  36. .head = NULL,
  37. .count = 0,
  38. },
  39. };
  40. // destroy the cache and free all the memory it uses
  41. void web_client_cache_destroy(void) {
  42. internal_error(true, "web_client_cache has %zu used and %zu available clients, allocated %zu, reused %zu (hit %zu%%)."
  43. , web_clients_cache.used.count
  44. , web_clients_cache.avail.count
  45. , web_clients_cache.used.allocated
  46. , web_clients_cache.used.reused
  47. , (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
  48. );
  49. struct web_client *w, *t;
  50. netdata_spinlock_lock(&web_clients_cache.avail.spinlock);
  51. w = web_clients_cache.avail.head;
  52. while(w) {
  53. t = w;
  54. w = w->cache.next;
  55. web_client_free(t);
  56. }
  57. web_clients_cache.avail.head = NULL;
  58. web_clients_cache.avail.count = 0;
  59. netdata_spinlock_unlock(&web_clients_cache.avail.spinlock);
  60. // DO NOT FREE THEM IF THEY ARE USED
  61. // netdata_spinlock_lock(&web_clients_cache.used.spinlock);
  62. // w = web_clients_cache.used.head;
  63. // while(w) {
  64. // t = w;
  65. // w = w->next;
  66. // web_client_free(t);
  67. // }
  68. // web_clients_cache.used.head = NULL;
  69. // web_clients_cache.used.count = 0;
  70. // web_clients_cache.used.reused = 0;
  71. // web_clients_cache.used.allocated = 0;
  72. // netdata_spinlock_unlock(&web_clients_cache.used.spinlock);
  73. }
  74. struct web_client *web_client_get_from_cache(void) {
  75. netdata_spinlock_lock(&web_clients_cache.avail.spinlock);
  76. struct web_client *w = web_clients_cache.avail.head;
  77. if(w) {
  78. // get it from avail
  79. DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(web_clients_cache.avail.head, w, cache.prev, cache.next);
  80. web_clients_cache.avail.count--;
  81. netdata_spinlock_unlock(&web_clients_cache.avail.spinlock);
  82. web_client_zero(w);
  83. netdata_spinlock_lock(&web_clients_cache.used.spinlock);
  84. web_clients_cache.used.reused++;
  85. }
  86. else {
  87. netdata_spinlock_unlock(&web_clients_cache.avail.spinlock);
  88. // allocate it
  89. w = web_client_create(&netdata_buffers_statistics.buffers_web);
  90. #ifdef ENABLE_HTTPS
  91. w->ssl.flags = NETDATA_SSL_START;
  92. debug(D_WEB_CLIENT_ACCESS,"Starting SSL structure with (w->ssl = NULL, w->accepted = %u)", w->ssl.flags);
  93. #endif
  94. netdata_spinlock_lock(&web_clients_cache.used.spinlock);
  95. web_clients_cache.used.allocated++;
  96. }
  97. // link it to used web clients
  98. DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(web_clients_cache.used.head, w, cache.prev, cache.next);
  99. web_clients_cache.used.count++;
  100. netdata_spinlock_unlock(&web_clients_cache.used.spinlock);
  101. // initialize it
  102. w->use_count++;
  103. w->id = global_statistics_web_client_connected();
  104. w->mode = WEB_CLIENT_MODE_GET;
  105. return w;
  106. }
  107. void web_client_release_to_cache(struct web_client *w) {
  108. // unlink it from the used
  109. netdata_spinlock_lock(&web_clients_cache.used.spinlock);
  110. DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(web_clients_cache.used.head, w, cache.prev, cache.next);
  111. ssize_t used_count = (ssize_t)--web_clients_cache.used.count;
  112. netdata_spinlock_unlock(&web_clients_cache.used.spinlock);
  113. netdata_spinlock_lock(&web_clients_cache.avail.spinlock);
  114. 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)) {
  115. netdata_spinlock_unlock(&web_clients_cache.avail.spinlock);
  116. // we have too many of them - free it
  117. web_client_free(w);
  118. }
  119. else {
  120. // link it to the avail
  121. DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(web_clients_cache.avail.head, w, cache.prev, cache.next);
  122. web_clients_cache.avail.count++;
  123. netdata_spinlock_unlock(&web_clients_cache.avail.spinlock);
  124. }
  125. }