| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | #ifndef DBENGINE_CACHE_H |
| 3 | #define DBENGINE_CACHE_H |
| 4 | |
| 5 | #include "datafile.h" |
| 6 | #include "../rrd.h" |
| 7 | |
| 8 | // CACHE COMPILE TIME CONFIGURATION |
| 9 | // #define PGC_COUNT_POINTS_COLLECTED 1 |
| 10 | |
| 11 | typedef struct pgc PGC; |
| 12 | typedef struct pgc_page PGC_PAGE; |
| 13 | #define PGC_NAME_MAX 23 |
| 14 | |
| 15 | typedef enum __attribute__ ((__packed__)) { |
| 16 | PGC_OPTIONS_NONE = 0, |
| 17 | PGC_OPTIONS_EVICT_PAGES_NO_INLINE = (1 << 0), |
| 18 | PGC_OPTIONS_FLUSH_PAGES_NO_INLINE = (1 << 1), |
| 19 | PGC_OPTIONS_AUTOSCALE = (1 << 2), |
| 20 | } PGC_OPTIONS; |
| 21 | |
| 22 | #define PGC_OPTIONS_DEFAULT (PGC_OPTIONS_EVICT_PAGES_NO_INLINE | PGC_OPTIONS_AUTOSCALE) |
| 23 | |
| 24 | typedef struct pgc_entry { |
| 25 | Word_t section; // the section this belongs to |
| 26 | Word_t metric_id; // the metric this belongs to |
| 27 | time_t start_time_s; // the start time of the page |
| 28 | time_t end_time_s; // the end time of the page |
| 29 | size_t size; // the size in bytes of the allocation, outside the cache |
| 30 | void *data; // a pointer to data outside the cache |
| 31 | uint32_t update_every_s; // the update every of the page |
| 32 | bool hot; // true if this entry is currently being collected |
| 33 | uint8_t *custom_data; |
| 34 | } PGC_ENTRY; |
| 35 | |
| 36 | struct pgc_size_histogram_entry { |
| 37 | size_t upto; |
| 38 | size_t count; |
| 39 | }; |
| 40 | |
| 41 | #define PGC_SIZE_HISTOGRAM_ENTRIES 15 |
| 42 | #define PGC_QUEUE_HOT 0 |
| 43 | #define PGC_QUEUE_DIRTY 1 |
| 44 | #define PGC_QUEUE_CLEAN 2 |
| 45 | |
| 46 | struct pgc_size_histogram { |
| 47 | struct pgc_size_histogram_entry array[PGC_SIZE_HISTOGRAM_ENTRIES]; |
| 48 | }; |
| 49 | |
| 50 | struct pgc_queue_statistics { |
| 51 | struct pgc_size_histogram size_histogram; |
| 52 | |
| 53 | PAD64(size_t) entries; |
| 54 | PAD64(int64_t) size; |
| 55 | |
| 56 | PAD64(size_t) max_entries; |
| 57 | PAD64(int64_t) max_size; |
| 58 | |
| 59 | PAD64(size_t) added_entries; |
| 60 | PAD64(int64_t) added_size; |
| 61 | |
| 62 | PAD64(size_t) removed_entries; |
| 63 | PAD64(int64_t) removed_size; |
| 64 | }; |
| 65 | |
| 66 | struct pgc_statistics { |
| 67 | PAD64(int64_t) wanted_cache_size; |
| 68 | PAD64(int64_t) current_cache_size; |
| 69 | |
| 70 | // ---------------------------------------------------------------------------------------------------------------- |
| 71 | // volume |
| 72 | |
| 73 | PAD64(size_t) entries; // all the entries (includes clean, dirty, hot) |
| 74 | PAD64(int64_t) size; // all the entries (includes clean, dirty, hot) |
| 75 | |
| 76 | PAD64(size_t) referenced_entries; // all the entries currently referenced |
| 77 | PAD64(int64_t) referenced_size; // all the entries currently referenced |
| 78 | |
| 79 | PAD64(size_t) added_entries; |
| 80 | PAD64(int64_t) added_size; |
| 81 | |
| 82 | PAD64(size_t) removed_entries; |
| 83 | PAD64(int64_t) removed_size; |
| 84 | |
| 85 | #ifdef PGC_COUNT_POINTS_COLLECTED |
| 86 | PAD64(size_t) points_collected; |
| 87 | #endif |
| 88 | |
| 89 | // ---------------------------------------------------------------------------------------------------------------- |
| 90 | // migrations |
| 91 | |
| 92 | PAD64(size_t) evicting_entries; |
| 93 | PAD64(int64_t) evicting_size; |
| 94 | |
| 95 | PAD64(size_t) flushing_entries; |
| 96 | PAD64(int64_t) flushing_size; |
| 97 | |
| 98 | PAD64(size_t) hot2dirty_entries; |
| 99 | PAD64(int64_t) hot2dirty_size; |
| 100 | |
| 101 | PAD64(size_t) hot_empty_pages_evicted_immediately; |
| 102 | PAD64(size_t) hot_empty_pages_evicted_later; |
| 103 | |
| 104 | // ---------------------------------------------------------------------------------------------------------------- |
| 105 | // workload |
| 106 | |
| 107 | PAD64(size_t) acquires; |
| 108 | PAD64(size_t) releases; |
| 109 | |
| 110 | PAD64(size_t) acquires_for_deletion; |
| 111 | |
| 112 | PAD64(size_t) searches_exact; |
| 113 | PAD64(size_t) searches_exact_hits; |
| 114 | PAD64(size_t) searches_exact_misses; |
| 115 | |
| 116 | PAD64(size_t) searches_closest; |
| 117 | PAD64(size_t) searches_closest_hits; |
| 118 | PAD64(size_t) searches_closest_misses; |
| 119 | |
| 120 | PAD64(size_t) flushes_completed; |
| 121 | PAD64(int64_t) flushes_completed_size; |
| 122 | PAD64(int64_t) flushes_cancelled_size; |
| 123 | |
| 124 | // ---------------------------------------------------------------------------------------------------------------- |
| 125 | // critical events |
| 126 | |
| 127 | PAD64(size_t) events_cache_under_severe_pressure; |
| 128 | PAD64(size_t) events_cache_needs_space_aggressively; |
| 129 | PAD64(size_t) events_flush_critical; |
| 130 | |
| 131 | // ---------------------------------------------------------------------------------------------------------------- |
| 132 | // worker threads |
| 133 | |
| 134 | PAD64(size_t) p2_workers_search; |
| 135 | PAD64(size_t) p2_workers_add; |
| 136 | PAD64(size_t) p0_workers_evict; // priority 0, we always need this when inline evictions are enabled |
| 137 | PAD64(size_t) p2_workers_flush; |
| 138 | PAD64(size_t) p2_workers_jv2_flush; |
| 139 | PAD64(size_t) p2_workers_hot2dirty; |
| 140 | |
| 141 | // ---------------------------------------------------------------------------------------------------------------- |
| 142 | // waste events |
| 143 | |
| 144 | // waste events - spins |
| 145 | PAD64(size_t) p2_waste_insert_spins; |
| 146 | PAD64(size_t) p2_waste_evict_useless_spins; |
| 147 | |
| 148 | // waste events - eviction |
| 149 | PAD64(size_t) p2_waste_evict_relocated; |
| 150 | PAD64(size_t) p2_waste_evict_thread_signals; |
| 151 | PAD64(size_t) p2_waste_evictions_inline_on_add; |
| 152 | PAD64(size_t) p2_waste_evictions_inline_on_release; |
| 153 | |
| 154 | // waste events - flushing |
| 155 | PAD64(size_t) p2_waste_flush_on_add; |
| 156 | PAD64(size_t) p2_waste_flush_on_release; |
| 157 | PAD64(size_t) p2_waste_flushes_cancelled; |
| 158 | |
| 159 | // ---------------------------------------------------------------------------------------------------------------- |
| 160 | // per queue statistics |
| 161 | |
| 162 | struct pgc_queue_statistics queues[3]; |
| 163 | }; |
| 164 | |
| 165 | typedef void (*free_clean_page_callback)(PGC *cache, PGC_ENTRY entry); |
| 166 | typedef void (*save_dirty_page_callback)(PGC *cache, PGC_ENTRY *entries_array, PGC_PAGE **pages_array, size_t entries); |
| 167 | typedef void (*save_dirty_init_callback)(PGC *cache, Word_t section); |
| 168 | // create a cache |
| 169 | PGC *pgc_create(const char *name, |
| 170 | size_t clean_size_bytes, free_clean_page_callback pgc_free_clean_cb, |
| 171 | size_t max_dirty_pages_per_flush, save_dirty_init_callback pgc_save_init_cb, save_dirty_page_callback pgc_save_dirty_cb, |
| 172 | size_t max_pages_per_inline_eviction, size_t max_inline_evictors, |
| 173 | size_t max_skip_pages_per_inline_eviction, |
| 174 | size_t max_flushes_inline, |
| 175 | PGC_OPTIONS options, size_t partitions, size_t additional_bytes_per_page); |
| 176 | |
| 177 | // destroy the cache |
| 178 | void pgc_destroy(PGC *cache, bool flush); |
| 179 | |
| 180 | #define PGC_SECTION_ALL ((Word_t)0) |
| 181 | void pgc_flush_dirty_pages(PGC *cache, Word_t section); |
| 182 | void pgc_flush_all_hot_and_dirty_pages(PGC *cache, Word_t section); |
| 183 | |
| 184 | // add a page to the cache and return a pointer to it |
| 185 | PGC_PAGE *pgc_page_add_and_acquire(PGC *cache, PGC_ENTRY entry, bool *added); |
| 186 | |
| 187 | // get another reference counter on an already referenced page |
| 188 | PGC_PAGE *pgc_page_dup(PGC *cache, PGC_PAGE *page); |
| 189 | |
| 190 | // release a page (all pointers to it are now invalid) |
| 191 | void pgc_page_release(PGC *cache, PGC_PAGE *page); |
| 192 | |
| 193 | // mark a hot page dirty, and release it |
| 194 | void pgc_page_hot_to_dirty_and_release(PGC *cache, PGC_PAGE *page, bool never_flush); |
| 195 | |
| 196 | // find a page from the cache |
| 197 | typedef enum { |
| 198 | PGC_SEARCH_EXACT, |
| 199 | PGC_SEARCH_CLOSEST, |
| 200 | PGC_SEARCH_FIRST, |
| 201 | PGC_SEARCH_NEXT, |
| 202 | PGC_SEARCH_LAST, |
| 203 | PGC_SEARCH_PREV, |
| 204 | } PGC_SEARCH; |
| 205 | |
| 206 | PGC_PAGE *pgc_page_get_and_acquire(PGC *cache, Word_t section, Word_t metric_id, time_t start_time_s, PGC_SEARCH method); |
| 207 | |
| 208 | // get information from an acquired page |
| 209 | Word_t pgc_page_section(PGC_PAGE *page); |
| 210 | Word_t pgc_page_metric(PGC_PAGE *page); |
| 211 | time_t pgc_page_start_time_s(PGC_PAGE *page); |
| 212 | time_t pgc_page_end_time_s(PGC_PAGE *page); |
| 213 | uint32_t pgc_page_update_every_s(PGC_PAGE *page); |
| 214 | uint32_t pgc_page_fix_update_every(PGC_PAGE *page, uint32_t update_every_s); |
| 215 | time_t pgc_page_fix_end_time_s(PGC_PAGE *page, time_t end_time_s); |
| 216 | void *pgc_page_data(PGC_PAGE *page); |
| 217 | void *pgc_page_custom_data(PGC *cache, PGC_PAGE *page); |
| 218 | size_t pgc_page_data_size(PGC *cache, PGC_PAGE *page); |
| 219 | bool pgc_is_page_hot(PGC_PAGE *page); |
| 220 | bool pgc_is_page_dirty(PGC_PAGE *page); |
| 221 | bool pgc_is_page_clean(PGC_PAGE *page); |
| 222 | void pgc_reset_hot_max(PGC *cache); |
| 223 | int64_t pgc_get_current_cache_size(PGC *cache); |
| 224 | int64_t pgc_get_wanted_cache_size(PGC *cache); |
| 225 | |
| 226 | // resetting the end time of a hot page |
| 227 | void pgc_page_hot_set_end_time_s(PGC *cache, PGC_PAGE *page, time_t end_time_s, size_t additional_bytes); |
| 228 | bool pgc_page_to_clean_evict_or_release(PGC *cache, PGC_PAGE *page); |
| 229 | |
| 230 | typedef bool (*migrate_to_v2_callback)(Word_t section, unsigned datafile_fileno, uint8_t type, Pvoid_t JudyL_metrics, Pvoid_t JudyL_extents_pos, size_t count_of_unique_extents, size_t count_of_unique_metrics, size_t count_of_unique_pages, void *data); |
| 231 | void pgc_open_cache_to_journal_v2( |
| 232 | PGC *cache, |
| 233 | Word_t section, |
| 234 | unsigned datafile_fileno, |
| 235 | uint8_t type, |
| 236 | migrate_to_v2_callback cb, |
| 237 | void *data, |
| 238 | bool startup); |
| 239 | void pgc_open_evict_clean_pages_of_datafile(PGC *cache, struct rrdengine_datafile *datafile); |
| 240 | size_t pgc_count_clean_pages_having_data_ptr(PGC *cache, Word_t section, void *ptr); |
| 241 | size_t pgc_count_hot_pages_having_data_ptr(PGC *cache, Word_t section, void *ptr); |
| 242 | |
| 243 | typedef int64_t (*dynamic_target_cache_size_callback)(void); |
| 244 | void pgc_set_dynamic_target_cache_size_callback(PGC *cache, dynamic_target_cache_size_callback callback); |
| 245 | |
| 246 | typedef size_t (*nominal_page_size_callback)(void *); |
| 247 | void pgc_set_nominal_page_size_callback(PGC *cache, nominal_page_size_callback callback); |
| 248 | |
| 249 | // return true when there is more work to do |
| 250 | bool pgc_evict_pages(PGC *cache, size_t max_skip, size_t max_evict); |
| 251 | bool pgc_flush_pages(PGC *cache); |
| 252 | |
| 253 | struct pgc_statistics pgc_get_statistics(PGC *cache); |
| 254 | size_t pgc_hot_and_dirty_entries(PGC *cache); |
| 255 | |
| 256 | struct aral_statistics *pgc_aral_stats(void); |
| 257 | |
| 258 | static inline size_t indexing_partition(Word_t ptr, Word_t modulo) __attribute__((const)); |
| 259 | static inline size_t indexing_partition(Word_t ptr, Word_t modulo) { |
| 260 | XXH64_hash_t hash = XXH3_64bits(&ptr, sizeof(ptr)); |
| 261 | return hash % modulo; |
| 262 | } |
| 263 | |
| 264 | static inline size_t pgc_max_evictors(void) { |
| 265 | return 1 + netdata_conf_cpus() / 2; |
| 266 | } |
| 267 | |
| 268 | static inline size_t pgc_max_flushers(void) { |
| 269 | return netdata_conf_cpus(); |
| 270 | } |
| 271 | |
| 272 | #endif // DBENGINE_CACHE_H |