| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #ifndef NETDATA_RRDENGINE_H |
| 4 | #define NETDATA_RRDENGINE_H |
| 5 | |
| 6 | #include <fcntl.h> |
| 7 | #include <lz4.h> |
| 8 | #include <Judy.h> |
| 9 | #include <openssl/sha.h> |
| 10 | #include <openssl/evp.h> |
| 11 | #include "../rrd.h" |
| 12 | #include "rrddiskprotocol.h" |
| 13 | #include "rrdenginelib.h" |
| 14 | #include "datafile.h" |
| 15 | #include "journalfile.h" |
| 16 | #include "rrdengineapi.h" |
| 17 | #include "pagecache.h" |
| 18 | #include "mrg.h" |
| 19 | #include "cache.h" |
| 20 | #include "pdc.h" |
| 21 | #include "page.h" |
| 22 | |
| 23 | #include "daemon/protected-access.h" |
| 24 | |
| 25 | extern unsigned rrdeng_pages_per_extent; |
| 26 | |
| 27 | #define BLOCK_TO_OFFSET(block) ((uint64_t)(block) << 12) |
| 28 | #define OFFSET_TO_BLOCK(ofs) ((uint64_t)(ofs) >> 12) |
| 29 | |
| 30 | #define UNLINK_FILE(ctx, path, ret_var) \ |
| 31 | do { \ |
| 32 | uv_fs_t _req; \ |
| 33 | (ret_var) = uv_fs_unlink(NULL, &(_req), (path), NULL); \ |
| 34 | if ((ret_var) < 0) { \ |
| 35 | netdata_log_error("DBENGINE: uv_fs_unlink(\"%s\"): %s", (path), uv_strerror(ret_var)); \ |
| 36 | ctx_fs_error(ctx); \ |
| 37 | } \ |
| 38 | uv_fs_req_cleanup(&(_req)); \ |
| 39 | } while (0) |
| 40 | |
| 41 | #define CLOSE_FILE(ctx, path, file, ret_var) \ |
| 42 | do { \ |
| 43 | uv_fs_t _req; \ |
| 44 | (ret_var) = uv_fs_close(NULL, &(_req), (file), NULL); \ |
| 45 | if ((ret_var) < 0) { \ |
| 46 | netdata_log_error("DBENGINE: uv_fs_close(\"%s\"): %s", (path), uv_strerror(ret_var)); \ |
| 47 | ctx_fs_error(ctx); \ |
| 48 | } \ |
| 49 | uv_fs_req_cleanup(&(_req)); \ |
| 50 | } while (0) |
| 51 | |
| 52 | /* Forward declarations */ |
| 53 | struct rrdengine_instance; |
| 54 | struct rrdeng_cmd; |
| 55 | |
| 56 | #define MAX_PAGES_PER_EXTENT (109) /* TODO: can go higher only when journal supports bigger than 4KiB transactions */ |
| 57 | #define DEFAULT_PAGES_PER_EXTENT (109) |
| 58 | |
| 59 | #define MAX_EXTENT_UNCOMPRESSED_SIZE (MAX_PAGES_PER_EXTENT * (RRDENG_BLOCK_SIZE + RRDENG_GORILLA_32BIT_BUFFER_SIZE)) |
| 60 | |
| 61 | static inline size_t rrdeng_min_extent_disk_size(void) { |
| 62 | return sizeof(struct rrdeng_df_extent_header) + |
| 63 | sizeof(struct rrdeng_extent_page_descr) + |
| 64 | sizeof(struct rrdeng_df_extent_trailer); |
| 65 | } |
| 66 | |
| 67 | static inline size_t rrdeng_max_extent_disk_size(void) { |
| 68 | return sizeof(struct rrdeng_df_extent_header) + |
| 69 | sizeof(struct rrdeng_extent_page_descr) * MAX_PAGES_PER_EXTENT + |
| 70 | MAX_EXTENT_UNCOMPRESSED_SIZE + |
| 71 | sizeof(struct rrdeng_df_extent_trailer); |
| 72 | } |
| 73 | |
| 74 | static inline bool rrdeng_valid_extent_disk_size(size_t size) { |
| 75 | return size >= rrdeng_min_extent_disk_size() && size <= rrdeng_max_extent_disk_size(); |
| 76 | } |
| 77 | |
| 78 | |
| 79 | #define RRDENG_FILE_NUMBER_SCAN_TMPL "%1u-%10u" |
| 80 | #define RRDENG_FILE_NUMBER_PRINT_TMPL "%1.1u-%10.10u" |
| 81 | |
| 82 | typedef struct dbengine_tier_stats { |
| 83 | RRDSET *st; |
| 84 | RRDDIM *rd_space; |
| 85 | RRDDIM *rd_time; |
| 86 | } DBENGINE_TIER_STATS; |
| 87 | |
| 88 | typedef enum __attribute__ ((__packed__)) { |
| 89 | // final status for all pages |
| 90 | // if a page does not have one of these, it is considered unroutable |
| 91 | PDC_PAGE_READY = (1 << 0), // ready to be processed (pd->page is not null) |
| 92 | PDC_PAGE_FAILED = (1 << 1), // failed to be loaded (pd->page is null) |
| 93 | PDC_PAGE_SKIP = (1 << 2), // don't use this page, it is not good for us |
| 94 | PDC_PAGE_INVALID = (1 << 3), // don't use this page, it is invalid |
| 95 | PDC_PAGE_EMPTY = (1 << 4), // the page is empty, does not have any data |
| 96 | |
| 97 | // other statuses for tracking issues |
| 98 | PDC_PAGE_PREPROCESSED = (1 << 5), // used during preprocessing |
| 99 | PDC_PAGE_PROCESSED = (1 << 6), // processed by the query caller |
| 100 | PDC_PAGE_RELEASED = (1 << 7), // already released |
| 101 | |
| 102 | // data found in cache (preloaded) or on disk? |
| 103 | PDC_PAGE_PRELOADED = (1 << 8), // data found in memory |
| 104 | PDC_PAGE_DISK_PENDING = (1 << 9), // data need to be loaded from disk |
| 105 | |
| 106 | // worker related statuses |
| 107 | PDC_PAGE_FAILED_INVALID_EXTENT = (1 << 10), |
| 108 | PDC_PAGE_FAILED_NOT_IN_EXTENT = (1 << 11), |
| 109 | PDC_PAGE_FAILED_TO_MAP_EXTENT = (1 << 12), |
| 110 | PDC_PAGE_FAILED_TO_ACQUIRE_DATAFILE= (1 << 13), |
| 111 | |
| 112 | PDC_PAGE_EXTENT_FROM_CACHE = (1 << 14), |
| 113 | PDC_PAGE_EXTENT_FROM_DISK = (1 << 15), |
| 114 | |
| 115 | PDC_PAGE_CANCELLED = (1 << 16), // the query thread had left when we try to load the page |
| 116 | |
| 117 | PDC_PAGE_SOURCE_MAIN_CACHE = (1 << 17), |
| 118 | PDC_PAGE_SOURCE_OPEN_CACHE = (1 << 18), |
| 119 | PDC_PAGE_SOURCE_JOURNAL_V2 = (1 << 19), |
| 120 | PDC_PAGE_PRELOADED_PASS4 = (1 << 20), |
| 121 | |
| 122 | // datafile acquired |
| 123 | PDC_PAGE_DATAFILE_ACQUIRED = (1 << 30), |
| 124 | } PDC_PAGE_STATUS; |
| 125 | |
| 126 | #define PDC_PAGE_QUERY_GLOBAL_SKIP_LIST (PDC_PAGE_FAILED | PDC_PAGE_SKIP | PDC_PAGE_INVALID | PDC_PAGE_RELEASED) |
| 127 | |
| 128 | typedef struct page_details_control { |
| 129 | struct rrdengine_instance *ctx; |
| 130 | struct metric *metric; |
| 131 | |
| 132 | struct completion prep_completion; |
| 133 | struct completion page_completion; // sync between the query thread and the workers |
| 134 | |
| 135 | Pvoid_t page_list_JudyL; // the list of page details |
| 136 | unsigned completed_jobs; // the number of jobs completed last time the query thread checked |
| 137 | bool workers_should_stop; // true when the query thread left and the workers should stop |
| 138 | bool prep_done; |
| 139 | |
| 140 | PDC_PAGE_STATUS common_status; |
| 141 | size_t pages_to_load_from_disk; |
| 142 | |
| 143 | SPINLOCK refcount_spinlock; // spinlock to protect refcount |
| 144 | int32_t refcount; // the number of workers currently working on this request + 1 for the query thread |
| 145 | size_t executed_with_gaps; |
| 146 | |
| 147 | time_t start_time_s; |
| 148 | time_t end_time_s; |
| 149 | STORAGE_PRIORITY priority; |
| 150 | |
| 151 | time_t optimal_end_time_s; |
| 152 | } PDC; |
| 153 | |
| 154 | PDC *pdc_get(void); |
| 155 | |
| 156 | struct page_details { |
| 157 | struct { |
| 158 | struct rrdengine_datafile *ptr; |
| 159 | uint32_t block; // the block in the datafile. Offset in the datafile is block * RRDENG_BLOCK_SIZE |
| 160 | uint32_t bytes; |
| 161 | } datafile; |
| 162 | |
| 163 | struct pgc_page *page; |
| 164 | Word_t metric_id; |
| 165 | time_t first_time_s; |
| 166 | time_t last_time_s; |
| 167 | uint32_t update_every_s; |
| 168 | PDC_PAGE_STATUS status; |
| 169 | |
| 170 | struct { |
| 171 | struct page_details *prev; |
| 172 | struct page_details *next; |
| 173 | } load; |
| 174 | }; |
| 175 | |
| 176 | struct page_details *page_details_get(void); |
| 177 | |
| 178 | #define pdc_page_status_check(pd, flag) (__atomic_load_n(&((pd)->status), __ATOMIC_ACQUIRE) & (flag)) |
| 179 | #define pdc_page_status_set(pd, flag) __atomic_or_fetch(&((pd)->status), flag, __ATOMIC_RELEASE) |
| 180 | #define pdc_page_status_clear(pd, flag) __atomic_and_fetch(&((od)->status), ~(flag), __ATOMIC_RELEASE) |
| 181 | |
| 182 | struct jv2_extents_info { |
| 183 | uint32_t index; |
| 184 | uint32_t block; |
| 185 | unsigned bytes; |
| 186 | uint32_t number_of_pages; |
| 187 | }; |
| 188 | |
| 189 | struct jv2_metrics_info { |
| 190 | nd_uuid_t *uuid; |
| 191 | void *metric; |
| 192 | uint32_t page_list_header; |
| 193 | uint32_t number_of_pages; |
| 194 | time_t first_time_s; |
| 195 | time_t last_time_s; |
| 196 | Pvoid_t JudyL_pages_by_start_time; |
| 197 | }; |
| 198 | |
| 199 | struct jv2_page_info { |
| 200 | time_t start_time_s; |
| 201 | time_t end_time_s; |
| 202 | uint32_t update_every_s; |
| 203 | uint32_t extent_index; |
| 204 | size_t page_length; |
| 205 | void *custom_data; |
| 206 | |
| 207 | // private |
| 208 | struct pgc_page *page; |
| 209 | }; |
| 210 | |
| 211 | typedef enum __attribute__ ((__packed__)) { |
| 212 | RRDENG_COLLECT_HANDLE_OPTION_NONE = 0, |
| 213 | |
| 214 | #ifdef NETDATA_INTERNAL_CHECKS |
| 215 | RRDENG_1ST_METRIC_WRITER = (1 << 0), |
| 216 | #endif |
| 217 | } RRDENG_COLLECT_HANDLE_OPTIONS; |
| 218 | |
| 219 | typedef enum __attribute__ ((__packed__)) { |
| 220 | RRDENG_PAGE_PAST_COLLECTION = (1 << 0), |
| 221 | RRDENG_PAGE_REPEATED_COLLECTION = (1 << 1), |
| 222 | RRDENG_PAGE_BIG_GAP = (1 << 2), |
| 223 | RRDENG_PAGE_GAP = (1 << 3), |
| 224 | RRDENG_PAGE_FUTURE_POINT = (1 << 4), |
| 225 | RRDENG_PAGE_CREATED_IN_FUTURE = (1 << 5), |
| 226 | RRDENG_PAGE_COMPLETED_IN_FUTURE = (1 << 6), |
| 227 | RRDENG_PAGE_UNALIGNED = (1 << 7), |
| 228 | RRDENG_PAGE_CONFLICT = (1 << 8), |
| 229 | RRDENG_PAGE_FULL = (1 << 9), |
| 230 | RRDENG_PAGE_COLLECT_FINALIZE = (1 << 10), |
| 231 | RRDENG_PAGE_UPDATE_EVERY_CHANGE = (1 << 11), |
| 232 | RRDENG_PAGE_STEP_TOO_SMALL = (1 << 12), |
| 233 | RRDENG_PAGE_STEP_UNALIGNED = (1 << 13), |
| 234 | } RRDENG_COLLECT_PAGE_FLAGS; |
| 235 | |
| 236 | struct rrdeng_collect_handle { |
| 237 | struct storage_collect_handle common; // has to be first item |
| 238 | |
| 239 | RRDENG_COLLECT_PAGE_FLAGS page_flags; |
| 240 | RRDENG_COLLECT_HANDLE_OPTIONS options; |
| 241 | uint8_t type; |
| 242 | |
| 243 | struct rrdengine_instance *ctx; |
| 244 | struct metric *metric; |
| 245 | struct pgc_page *pgc_page; |
| 246 | struct pgd *page_data; |
| 247 | struct pg_alignment *alignment; |
| 248 | uint32_t page_entries_max; |
| 249 | uint32_t page_position; // keep track of the current page size, to make sure we don't exceed it |
| 250 | usec_t page_start_time_ut; |
| 251 | usec_t page_end_time_ut; |
| 252 | usec_t update_every_ut; |
| 253 | }; |
| 254 | |
| 255 | struct rrdeng_query_handle { |
| 256 | struct metric *metric; |
| 257 | struct pgc_page *page; |
| 258 | struct rrdengine_instance *ctx; |
| 259 | struct pgd_cursor pgdc; |
| 260 | struct page_details_control *pdc; |
| 261 | |
| 262 | // the request |
| 263 | time_t start_time_s; |
| 264 | time_t end_time_s; |
| 265 | STORAGE_PRIORITY priority; |
| 266 | |
| 267 | // internal data |
| 268 | time_t now_s; |
| 269 | uint32_t dt_s; |
| 270 | |
| 271 | unsigned position; |
| 272 | unsigned entries; |
| 273 | |
| 274 | #ifdef NETDATA_INTERNAL_CHECKS |
| 275 | usec_t started_time_s; |
| 276 | pid_t query_pid; |
| 277 | struct rrdeng_query_handle *prev, *next; |
| 278 | #endif |
| 279 | }; |
| 280 | |
| 281 | struct rrdeng_query_handle *rrdeng_query_handle_get(void); |
| 282 | void rrdeng_query_handle_release(struct rrdeng_query_handle *handle); |
| 283 | |
| 284 | enum rrdeng_opcode { |
| 285 | /* can be used to return empty status or flush the command queue */ |
| 286 | RRDENG_OPCODE_NOOP = 0, |
| 287 | |
| 288 | RRDENG_OPCODE_QUERY, |
| 289 | RRDENG_OPCODE_EXTENT_WRITE, |
| 290 | RRDENG_OPCODE_EXTENT_READ, |
| 291 | RRDENG_OPCODE_DATABASE_ROTATE, |
| 292 | RRDENG_OPCODE_JOURNAL_INDEX, |
| 293 | RRDENG_OPCODE_FLUSH_MAIN, |
| 294 | RRDENG_OPCODE_EVICT_MAIN, |
| 295 | RRDENG_OPCODE_EVICT_OPEN, |
| 296 | RRDENG_OPCODE_EVICT_EXTENT, |
| 297 | RRDENG_OPCODE_CTX_SHUTDOWN, |
| 298 | RRDENG_OPCODE_CTX_FLUSH_DIRTY, |
| 299 | RRDENG_OPCODE_CTX_FLUSH_HOT_DIRTY, |
| 300 | RRDENG_OPCODE_CTX_QUIESCE, |
| 301 | RRDENG_OPCODE_CTX_POPULATE_MRG, |
| 302 | RRDENG_OPCODE_SHUTDOWN_EVLOOP, |
| 303 | RRDENG_OPCODE_PARALLEL_WEIGHT, |
| 304 | RRDENG_OPCODE_MRG_LOAD, |
| 305 | RRDENG_OPCODE_CLEANUP, |
| 306 | |
| 307 | RRDENG_OPCODE_MAX |
| 308 | }; |
| 309 | |
| 310 | // WORKERS IDS: |
| 311 | // RRDENG_MAX_OPCODE : reserved for the cleanup |
| 312 | // RRDENG_MAX_OPCODE + opcode : reserved for the callbacks of each opcode |
| 313 | // RRDENG_MAX_OPCODE + RRDENG_MAX_OPCODE : reserved for the timer |
| 314 | #define RRDENG_TIMER_CB (RRDENG_OPCODE_MAX + RRDENG_OPCODE_MAX) |
| 315 | #define RRDENG_OPCODES_WAITING (RRDENG_TIMER_CB + 1) |
| 316 | #define RRDENG_WORKS_DISPATCHED (RRDENG_TIMER_CB + 2) |
| 317 | #define RRDENG_WORKS_EXECUTING (RRDENG_TIMER_CB + 3) |
| 318 | #define RRDENG_RETENTION_TIMER_CB (RRDENG_TIMER_CB + 4) |
| 319 | |
| 320 | struct extent_io_data { |
| 321 | unsigned fileno; |
| 322 | uint32_t block; |
| 323 | unsigned bytes; |
| 324 | }; |
| 325 | |
| 326 | struct extent_io_descriptor { |
| 327 | struct rrdengine_instance *ctx; |
| 328 | void *buf; |
| 329 | uint64_t pos; |
| 330 | uint32_t descr_count; |
| 331 | uint32_t bytes; |
| 332 | uint32_t real_io_size; |
| 333 | struct wal *wal; |
| 334 | uv_file file; |
| 335 | struct page_descr_with_data *descr_array[MAX_PAGES_PER_EXTENT]; |
| 336 | struct rrdengine_datafile *datafile; |
| 337 | }; |
| 338 | |
| 339 | typedef struct wal { |
| 340 | uint64_t transaction_id; |
| 341 | void *buf; |
| 342 | size_t size; |
| 343 | size_t buf_size; |
| 344 | |
| 345 | struct { |
| 346 | struct wal *prev; |
| 347 | struct wal *next; |
| 348 | } cache; |
| 349 | } WAL; |
| 350 | |
| 351 | WAL *wal_get(struct rrdengine_instance *ctx, unsigned size); |
| 352 | void wal_release(WAL *wal); |
| 353 | |
| 354 | /* |
| 355 | * Debug statistics not used by code logic. |
| 356 | * They only describe operations since DB engine instance load time. |
| 357 | */ |
| 358 | struct rrdengine_statistics { |
| 359 | PAD64(rrdeng_stats_t) before_decompress_bytes; |
| 360 | PAD64(rrdeng_stats_t) after_decompress_bytes; |
| 361 | PAD64(rrdeng_stats_t) before_compress_bytes; |
| 362 | PAD64(rrdeng_stats_t) after_compress_bytes; |
| 363 | |
| 364 | PAD64(rrdeng_stats_t) io_write_bytes; |
| 365 | PAD64(rrdeng_stats_t) io_write_requests; |
| 366 | PAD64(rrdeng_stats_t) io_read_bytes; |
| 367 | PAD64(rrdeng_stats_t) io_read_requests; |
| 368 | |
| 369 | PAD64(rrdeng_stats_t) datafile_creations; |
| 370 | PAD64(rrdeng_stats_t) datafile_deletions; |
| 371 | PAD64(rrdeng_stats_t) journalfile_creations; |
| 372 | PAD64(rrdeng_stats_t) journalfile_deletions; |
| 373 | |
| 374 | PAD64(rrdeng_stats_t) io_errors; |
| 375 | PAD64(rrdeng_stats_t) fs_errors; |
| 376 | }; |
| 377 | |
| 378 | struct rrdeng_global_stats { |
| 379 | /* I/O errors global counter */ |
| 380 | PAD64(rrdeng_stats_t) global_io_errors; |
| 381 | |
| 382 | /* File-System errors global counter */ |
| 383 | PAD64(rrdeng_stats_t) global_fs_errors; |
| 384 | |
| 385 | /* number of File-Descriptors that have been reserved by dbengine */ |
| 386 | PAD64(rrdeng_stats_t) rrdeng_reserved_file_descriptors; |
| 387 | |
| 388 | /* inability to flush global counters */ |
| 389 | PAD64(rrdeng_stats_t) global_pg_cache_over_half_dirty_events; |
| 390 | PAD64(rrdeng_stats_t) global_flushing_pressure_page_deletions; /* number of deleted pages */ |
| 391 | }; |
| 392 | |
| 393 | extern struct rrdeng_global_stats global_stats; |
| 394 | |
| 395 | typedef struct tier_config_prototype { |
| 396 | int tier; // the tier of this ctx |
| 397 | uint8_t page_type; // default page type for this context |
| 398 | uint64_t max_disk_space; // the max disk space this ctx is allowed to use |
| 399 | time_t max_retention_s; // The max retention in seconds |
| 400 | uint8_t disk_percentage; // percentage of metadata that contribute towards tier space used |
| 401 | uint8_t global_compress_alg; // the wanted compression algorithm |
| 402 | char dbfiles_path[FILENAME_MAX + 1]; |
| 403 | |
| 404 | struct { |
| 405 | uint32_t uses; |
| 406 | bool enabled; |
| 407 | bool is_on_disk; |
| 408 | SPINLOCK spinlock; |
| 409 | } _internal; |
| 410 | } TIER_CONFIG_PROTOTYPE; |
| 411 | |
| 412 | struct rrdengine_instance { |
| 413 | TIER_CONFIG_PROTOTYPE config; |
| 414 | |
| 415 | struct { |
| 416 | netdata_rwlock_t rwlock; // the JudyL of datafiles is protected by this lock |
| 417 | bool disk_time; // true: delete for disk quota, false: delete for retention |
| 418 | bool pending_rotate; // Change from event loop |
| 419 | bool pending_index; // Change from event loop |
| 420 | Pvoid_t JudyL; // the datafiles, indexed by fileno |
| 421 | } datafiles; |
| 422 | |
| 423 | struct { |
| 424 | RW_SPINLOCK spinlock; |
| 425 | Pvoid_t JudyL; |
| 426 | } njfv2idx; |
| 427 | |
| 428 | struct { |
| 429 | PAD64(unsigned) last_fileno; // newest index of datafile and journalfile |
| 430 | PAD64(unsigned) last_flush_fileno; // newest index of datafile received data |
| 431 | |
| 432 | PAD64(size_t) collectors_running; |
| 433 | PAD64(size_t) collectors_running_duplicate; |
| 434 | PAD64(size_t) inflight_queries; // the number of queries currently running |
| 435 | PAD64(uint64_t) current_disk_space; // the current disk space size used |
| 436 | |
| 437 | PAD64(uint64_t) transaction_id; // the transaction id of the next extent flushing |
| 438 | |
| 439 | PAD64(bool) migration_to_v2_running; |
| 440 | PAD64(bool) now_deleting_files; |
| 441 | PAD64(bool) needs_indexing; |
| 442 | PAD64(unsigned) extents_currently_being_flushed; // non-zero until we commit data to disk (both datafile and journal file) |
| 443 | |
| 444 | PAD64(time_t) first_time_s; |
| 445 | PAD64(uint64_t) metrics; |
| 446 | PAD64(uint64_t) samples; |
| 447 | } atomic; |
| 448 | |
| 449 | struct { |
| 450 | bool exit_mode; |
| 451 | bool enabled; // when set (before shutdown), queries are prohibited |
| 452 | } quiesce; |
| 453 | |
| 454 | struct { |
| 455 | struct completion load_mrg; |
| 456 | bool create_new_datafile_pair; |
| 457 | } loading; |
| 458 | |
| 459 | struct rrdengine_statistics stats; |
| 460 | }; |
| 461 | |
| 462 | #define ctx_current_disk_space_get(ctx) __atomic_load_n(&(ctx)->atomic.current_disk_space, __ATOMIC_RELAXED) |
| 463 | #define ctx_current_disk_space_increase(ctx, size) __atomic_add_fetch(&(ctx)->atomic.current_disk_space, size, __ATOMIC_RELAXED) |
| 464 | #define ctx_current_disk_space_decrease(ctx, size) __atomic_sub_fetch(&(ctx)->atomic.current_disk_space, size, __ATOMIC_RELAXED) |
| 465 | |
| 466 | static inline void ctx_io_read_op_bytes(struct rrdengine_instance *ctx, size_t bytes) { |
| 467 | __atomic_add_fetch(&ctx->stats.io_read_bytes, bytes, __ATOMIC_RELAXED); |
| 468 | __atomic_add_fetch(&ctx->stats.io_read_requests, 1, __ATOMIC_RELAXED); |
| 469 | } |
| 470 | |
| 471 | static inline void ctx_io_write_op_bytes(struct rrdengine_instance *ctx, size_t bytes) { |
| 472 | __atomic_add_fetch(&ctx->stats.io_write_bytes, bytes, __ATOMIC_RELAXED); |
| 473 | __atomic_add_fetch(&ctx->stats.io_write_requests, 1, __ATOMIC_RELAXED); |
| 474 | } |
| 475 | |
| 476 | static inline void ctx_io_error(struct rrdengine_instance *ctx) { |
| 477 | __atomic_add_fetch(&ctx->stats.io_errors, 1, __ATOMIC_RELAXED); |
| 478 | rrd_stat_atomic_add(&global_stats.global_io_errors, 1); |
| 479 | } |
| 480 | |
| 481 | static inline void ctx_fs_error(struct rrdengine_instance *ctx) { |
| 482 | __atomic_add_fetch(&ctx->stats.fs_errors, 1, __ATOMIC_RELAXED); |
| 483 | rrd_stat_atomic_add(&global_stats.global_fs_errors, 1); |
| 484 | } |
| 485 | |
| 486 | #define ctx_last_fileno_get(ctx) __atomic_load_n(&(ctx)->atomic.last_fileno, __ATOMIC_RELAXED) |
| 487 | #define ctx_last_fileno_increment(ctx) __atomic_add_fetch(&(ctx)->atomic.last_fileno, 1, __ATOMIC_RELAXED) |
| 488 | |
| 489 | #define ctx_last_flush_fileno_get(ctx) __atomic_load_n(&(ctx)->atomic.last_flush_fileno, __ATOMIC_RELAXED) |
| 490 | static inline void ctx_last_flush_fileno_set(struct rrdengine_instance *ctx, unsigned fileno) { |
| 491 | unsigned old_fileno = ctx_last_flush_fileno_get(ctx); |
| 492 | |
| 493 | do { |
| 494 | if(old_fileno >= fileno) |
| 495 | return; |
| 496 | |
| 497 | } while(!__atomic_compare_exchange_n(&ctx->atomic.last_flush_fileno, &old_fileno, fileno, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)); |
| 498 | } |
| 499 | |
| 500 | #define ctx_is_available_for_queries(ctx) (__atomic_load_n(&(ctx)->quiesce.enabled, __ATOMIC_RELAXED) == false && __atomic_load_n(&(ctx)->quiesce.exit_mode, __ATOMIC_RELAXED) == false) |
| 501 | |
| 502 | bool rrdeng_ctx_tier_cap_exceeded(struct rrdengine_instance *ctx); |
| 503 | int init_rrd_files(struct rrdengine_instance *ctx); |
| 504 | void finalize_rrd_files(struct rrdengine_instance *ctx); |
| 505 | bool rrdeng_dbengine_spawn(struct rrdengine_instance *ctx); |
| 506 | void dbengine_event_loop(void *arg); |
| 507 | |
| 508 | typedef void (*enqueue_callback_t)(struct rrdeng_cmd *cmd); |
| 509 | typedef void (*dequeue_callback_t)(struct rrdeng_cmd *cmd); |
| 510 | |
| 511 | void rrdeng_enqueue_epdl_cmd(struct rrdeng_cmd *cmd); |
| 512 | void rrdeng_dequeue_epdl_cmd(struct rrdeng_cmd *cmd); |
| 513 | |
| 514 | typedef struct rrdeng_cmd *(*requeue_callback_t)(void *data); |
| 515 | void rrdeng_req_cmd(requeue_callback_t get_cmd_cb, void *data, STORAGE_PRIORITY priority); |
| 516 | |
| 517 | void rrdeng_enq_cmd(struct rrdengine_instance *ctx, enum rrdeng_opcode opcode, void *data, |
| 518 | struct completion *completion, enum storage_priority priority, |
| 519 | enqueue_callback_t enqueue_cb, dequeue_callback_t dequeue_cb); |
| 520 | |
| 521 | void pdc_route_asynchronously(struct rrdengine_instance *ctx, struct page_details_control *pdc); |
| 522 | void pdc_route_synchronously(struct rrdengine_instance *ctx, struct page_details_control *pdc); |
| 523 | void pdc_route_synchronously_first(struct rrdengine_instance *ctx, struct page_details_control *pdc); |
| 524 | |
| 525 | void pdc_acquire(PDC *pdc); |
| 526 | bool pdc_release_and_destroy_if_unreferenced(PDC *pdc, bool worker, bool router); |
| 527 | |
| 528 | uint64_t rrdeng_target_data_file_size(struct rrdengine_instance *ctx); |
| 529 | |
| 530 | struct page_descr_with_data *page_descriptor_get(void); |
| 531 | |
| 532 | typedef struct validated_page_descriptor { |
| 533 | time_t start_time_s; |
| 534 | time_t end_time_s; |
| 535 | uint32_t update_every_s; |
| 536 | size_t page_length; |
| 537 | size_t point_size; |
| 538 | size_t entries; |
| 539 | uint8_t type; |
| 540 | bool is_valid; |
| 541 | } VALIDATED_PAGE_DESCRIPTOR; |
| 542 | |
| 543 | #define page_entries_by_time(start_time_s, end_time_s, update_every_s) \ |
| 544 | ((update_every_s) ? (((end_time_s) - ((start_time_s) - (update_every_s))) / (update_every_s)) : 1) |
| 545 | |
| 546 | #define page_entries_by_size(page_length_in_bytes, point_size_in_bytes) \ |
| 547 | ((page_length_in_bytes) / (point_size_in_bytes)) |
| 548 | |
| 549 | VALIDATED_PAGE_DESCRIPTOR validate_page(nd_uuid_t *uuid, |
| 550 | time_t start_time_s, |
| 551 | time_t end_time_s, |
| 552 | uint32_t update_every_s, |
| 553 | size_t page_length, |
| 554 | uint8_t page_type, |
| 555 | size_t entries, |
| 556 | time_t now_s, |
| 557 | uint32_t overwrite_zero_update_every_s, |
| 558 | bool have_read_error, |
| 559 | const char *msg, |
| 560 | RRDENG_COLLECT_PAGE_FLAGS flags); |
| 561 | VALIDATED_PAGE_DESCRIPTOR validate_extent_page_descr(const struct rrdeng_extent_page_descr *descr, time_t now_s, uint32_t overwrite_zero_update_every_s, bool have_read_error); |
| 562 | void collect_page_flags_to_buffer(BUFFER *wb, RRDENG_COLLECT_PAGE_FLAGS flags); |
| 563 | |
| 564 | typedef enum { |
| 565 | PAGE_IS_IN_THE_PAST = -1, |
| 566 | PAGE_IS_IN_RANGE = 0, |
| 567 | PAGE_IS_IN_THE_FUTURE = 1, |
| 568 | } TIME_RANGE_COMPARE; |
| 569 | |
| 570 | TIME_RANGE_COMPARE is_page_in_time_range(time_t page_first_time_s, time_t page_last_time_s, time_t wanted_start_time_s, time_t wanted_end_time_s); |
| 571 | |
| 572 | static inline time_t max_acceptable_collected_time(void) { |
| 573 | return now_realtime_sec() + 1; |
| 574 | } |
| 575 | |
| 576 | void datafile_delete( |
| 577 | struct rrdengine_instance *ctx, |
| 578 | struct rrdengine_datafile *datafile, |
| 579 | bool update_retention, |
| 580 | bool disk_time, |
| 581 | bool worker); |
| 582 | |
| 583 | // -------------------------------------------------------------------------------------------------------------------- |
| 584 | // the following functions are used to sort UUIDs in the journal files |
| 585 | // DO NOT CHANGE, as this will break backwards compatibility with the data files users have. |
| 586 | |
| 587 | static inline int journal_uuid_memcmp(const nd_uuid_t *uu1, const nd_uuid_t *uu2) { |
| 588 | return memcmp(uu1, uu2, sizeof(nd_uuid_t)); |
| 589 | } |
| 590 | |
| 591 | static inline int journal_metric_uuid_compare(const void *key, const void *metric) { |
| 592 | return journal_uuid_memcmp((const nd_uuid_t *)key, (const nd_uuid_t *)&(((struct journal_metric_list *) metric)->uuid)); |
| 593 | } |
| 594 | |
| 595 | // -------------------------------------------------------------------------------------------------------------------- |
| 596 | uint64_t rrdeng_get_used_disk_space(struct rrdengine_instance *ctx, bool having_lock); |
| 597 | void rrdeng_calculate_tier_disk_space_percentage(void); |
| 598 | uint64_t rrdeng_get_directory_free_bytes_space(struct rrdengine_instance *ctx); |
| 599 | void dbengine_shutdown(); |
| 600 | size_t datafile_count(struct rrdengine_instance *ctx, bool with_lock); |
| 601 | struct rrdengine_datafile *get_first_ctx_datafile(struct rrdengine_instance *ctx, bool with_lock); |
| 602 | struct rrdengine_datafile *get_last_ctx_datafile(struct rrdengine_instance *ctx, bool with_lock); |
| 603 | struct rrdengine_datafile * |
| 604 | get_next_datafile(struct rrdengine_datafile *this_datafile, struct rrdengine_instance *ctx, bool with_lock); |
| 605 | |
| 606 | |
| 607 | #endif /* NETDATA_RRDENGINE_H */ |