diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 11:19:16 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:07:37 +0000 |
commit | b485aab7e71c1625cfc27e0f92c9509f42378458 (patch) | |
tree | ae9abe108601079d1679194de237c9a435ae5b55 /database | |
parent | Adding upstream version 1.44.3. (diff) | |
download | netdata-b485aab7e71c1625cfc27e0f92c9509f42378458.tar.xz netdata-b485aab7e71c1625cfc27e0f92c9509f42378458.zip |
Adding upstream version 1.45.3+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | database/Makefile.am | 15 | ||||
-rw-r--r-- | database/README.md | 162 | ||||
-rw-r--r-- | database/contexts/Makefile.am | 11 | ||||
-rw-r--r-- | database/engine/Makefile.am | 11 | ||||
-rw-r--r-- | database/engine/datafile.ksy | 74 | ||||
-rw-r--r-- | database/engine/journalfile_v2.ksy.in | 150 | ||||
-rw-r--r-- | database/ram/Makefile.am | 11 | ||||
-rw-r--r-- | database/ram/README.md | 11 | ||||
-rw-r--r-- | database/ram/rrddim_mem.c | 437 | ||||
-rw-r--r-- | database/ram/rrddim_mem.h | 54 | ||||
-rw-r--r-- | database/rrdcalc.c | 869 | ||||
-rw-r--r-- | database/rrdcalc.h | 272 | ||||
-rw-r--r-- | database/rrdcalctemplate.c | 242 | ||||
-rw-r--r-- | database/rrdcalctemplate.h | 130 | ||||
-rw-r--r-- | database/rrddim.c | 768 | ||||
-rw-r--r-- | database/rrddimvar.c | 273 | ||||
-rw-r--r-- | database/rrddimvar.h | 21 | ||||
-rw-r--r-- | database/rrdfamily.c | 69 | ||||
-rw-r--r-- | database/rrdfunctions.c | 1821 | ||||
-rw-r--r-- | database/rrdfunctions.h | 59 | ||||
-rw-r--r-- | database/rrdsetvar.c | 299 | ||||
-rw-r--r-- | database/rrdsetvar.h | 30 | ||||
-rw-r--r-- | database/rrdvar.c | 392 | ||||
-rw-r--r-- | database/rrdvar.h | 77 | ||||
-rw-r--r-- | database/sqlite/Makefile.am | 4 | ||||
-rw-r--r-- | database/sqlite/sqlite_functions.c | 934 | ||||
-rw-r--r-- | database/sqlite/sqlite_functions.h | 83 | ||||
-rw-r--r-- | database/sqlite/sqlite_metadata.c | 1994 | ||||
-rw-r--r-- | database/sqlite/sqlite_metadata.h | 25 | ||||
-rw-r--r-- | database/storage_engine.c | 118 | ||||
-rw-r--r-- | src/database/KolmogorovSmirnovDist.c (renamed from database/KolmogorovSmirnovDist.c) | 0 | ||||
-rw-r--r-- | src/database/KolmogorovSmirnovDist.h (renamed from database/KolmogorovSmirnovDist.h) | 0 | ||||
-rw-r--r-- | src/database/contexts/README.md (renamed from database/contexts/README.md) | 0 | ||||
-rw-r--r-- | src/database/contexts/api_v1.c (renamed from database/contexts/api_v1.c) | 8 | ||||
-rw-r--r-- | src/database/contexts/api_v2.c (renamed from database/contexts/api_v2.c) | 304 | ||||
-rw-r--r-- | src/database/contexts/context.c (renamed from database/contexts/context.c) | 0 | ||||
-rw-r--r-- | src/database/contexts/instance.c (renamed from database/contexts/instance.c) | 2 | ||||
-rw-r--r-- | src/database/contexts/internal.h (renamed from database/contexts/internal.h) | 0 | ||||
-rw-r--r-- | src/database/contexts/metric.c (renamed from database/contexts/metric.c) | 0 | ||||
-rw-r--r-- | src/database/contexts/query_scope.c (renamed from database/contexts/query_scope.c) | 0 | ||||
-rw-r--r-- | src/database/contexts/query_target.c (renamed from database/contexts/query_target.c) | 60 | ||||
-rw-r--r-- | src/database/contexts/rrdcontext.c (renamed from database/contexts/rrdcontext.c) | 0 | ||||
-rw-r--r-- | src/database/contexts/rrdcontext.h (renamed from database/contexts/rrdcontext.h) | 13 | ||||
-rw-r--r-- | src/database/contexts/worker.c (renamed from database/contexts/worker.c) | 2 | ||||
-rw-r--r-- | src/database/engine/README.md (renamed from database/engine/README.md) | 0 | ||||
-rw-r--r-- | src/database/engine/cache.c (renamed from database/engine/cache.c) | 93 | ||||
-rw-r--r-- | src/database/engine/cache.h (renamed from database/engine/cache.h) | 9 | ||||
-rw-r--r-- | src/database/engine/datafile.c (renamed from database/engine/datafile.c) | 4 | ||||
-rw-r--r-- | src/database/engine/datafile.h (renamed from database/engine/datafile.h) | 0 | ||||
-rw-r--r-- | src/database/engine/dbengine-diagram.xml (renamed from database/engine/dbengine-diagram.xml) | 0 | ||||
-rw-r--r-- | src/database/engine/journalfile.c (renamed from database/engine/journalfile.c) | 29 | ||||
-rw-r--r-- | src/database/engine/journalfile.h (renamed from database/engine/journalfile.h) | 1 | ||||
-rw-r--r-- | src/database/engine/metric.c (renamed from database/engine/metric.c) | 331 | ||||
-rw-r--r-- | src/database/engine/metric.h (renamed from database/engine/metric.h) | 15 | ||||
-rw-r--r-- | src/database/engine/page.c (renamed from database/engine/page.c) | 106 | ||||
-rw-r--r-- | src/database/engine/page.h (renamed from database/engine/page.h) | 0 | ||||
-rw-r--r-- | src/database/engine/page_test.cc (renamed from database/engine/page_test.cc) | 0 | ||||
-rw-r--r-- | src/database/engine/page_test.h (renamed from database/engine/page_test.h) | 0 | ||||
-rw-r--r-- | src/database/engine/pagecache.c (renamed from database/engine/pagecache.c) | 24 | ||||
-rw-r--r-- | src/database/engine/pagecache.h (renamed from database/engine/pagecache.h) | 6 | ||||
-rw-r--r-- | src/database/engine/pdc.c (renamed from database/engine/pdc.c) | 89 | ||||
-rw-r--r-- | src/database/engine/pdc.h (renamed from database/engine/pdc.h) | 0 | ||||
-rw-r--r-- | src/database/engine/rrddiskprotocol.h (renamed from database/engine/rrddiskprotocol.h) | 16 | ||||
-rw-r--r-- | src/database/engine/rrdengine.c (renamed from database/engine/rrdengine.c) | 92 | ||||
-rw-r--r-- | src/database/engine/rrdengine.h (renamed from database/engine/rrdengine.h) | 16 | ||||
-rwxr-xr-x | src/database/engine/rrdengineapi.c (renamed from database/engine/rrdengineapi.c) | 269 | ||||
-rw-r--r-- | src/database/engine/rrdengineapi.h (renamed from database/engine/rrdengineapi.h) | 45 | ||||
-rw-r--r-- | src/database/engine/rrdenginelib.c (renamed from database/engine/rrdenginelib.c) | 0 | ||||
-rw-r--r-- | src/database/engine/rrdenginelib.h (renamed from database/engine/rrdenginelib.h) | 0 | ||||
-rw-r--r-- | src/database/rrd.c (renamed from database/rrd.c) | 34 | ||||
-rw-r--r-- | src/database/rrd.h (renamed from database/rrd.h) | 392 | ||||
-rw-r--r-- | src/database/rrdhost.c (renamed from database/rrdhost.c) | 402 | ||||
-rw-r--r-- | src/database/rrdlabels.c (renamed from database/rrdlabels.c) | 372 | ||||
-rw-r--r-- | src/database/rrdlabels.h (renamed from database/rrdlabels.h) | 28 | ||||
-rw-r--r-- | src/database/rrdset.c (renamed from database/rrdset.c) | 367 | ||||
-rw-r--r-- | src/database/sqlite/dbdata.c (renamed from database/sqlite/dbdata.c) | 0 | ||||
-rw-r--r-- | src/database/sqlite/sqlite3.c (renamed from database/sqlite/sqlite3.c) | 0 | ||||
-rw-r--r-- | src/database/sqlite/sqlite3.h (renamed from database/sqlite/sqlite3.h) | 0 | ||||
-rw-r--r-- | src/database/sqlite/sqlite3recover.c (renamed from database/sqlite/sqlite3recover.c) | 0 | ||||
-rw-r--r-- | src/database/sqlite/sqlite3recover.h (renamed from database/sqlite/sqlite3recover.h) | 0 | ||||
-rw-r--r-- | src/database/sqlite/sqlite_aclk.c (renamed from database/sqlite/sqlite_aclk.c) | 22 | ||||
-rw-r--r-- | src/database/sqlite/sqlite_aclk.h (renamed from database/sqlite/sqlite_aclk.h) | 0 | ||||
-rw-r--r-- | src/database/sqlite/sqlite_aclk_alert.c (renamed from database/sqlite/sqlite_aclk_alert.c) | 15 | ||||
-rw-r--r-- | src/database/sqlite/sqlite_aclk_alert.h (renamed from database/sqlite/sqlite_aclk_alert.h) | 0 | ||||
-rw-r--r-- | src/database/sqlite/sqlite_aclk_node.c (renamed from database/sqlite/sqlite_aclk_node.c) | 0 | ||||
-rw-r--r-- | src/database/sqlite/sqlite_aclk_node.h (renamed from database/sqlite/sqlite_aclk_node.h) | 0 | ||||
-rw-r--r-- | src/database/sqlite/sqlite_context.c (renamed from database/sqlite/sqlite_context.c) | 37 | ||||
-rw-r--r-- | src/database/sqlite/sqlite_context.h (renamed from database/sqlite/sqlite_context.h) | 1 | ||||
-rw-r--r-- | src/database/sqlite/sqlite_db_migration.c (renamed from database/sqlite/sqlite_db_migration.c) | 60 | ||||
-rw-r--r-- | src/database/sqlite/sqlite_db_migration.h (renamed from database/sqlite/sqlite_db_migration.h) | 0 | ||||
-rw-r--r-- | src/database/sqlite/sqlite_health.c (renamed from database/sqlite/sqlite_health.c) | 365 | ||||
-rw-r--r-- | src/database/sqlite/sqlite_health.h (renamed from database/sqlite/sqlite_health.h) | 9 | ||||
-rw-r--r-- | src/database/storage_engine.h (renamed from database/storage_engine.h) | 0 | ||||
-rw-r--r-- | src/go/collectors/go.d.plugin/agent/testdata/agent-empty.conf (renamed from database/engine/metadata_log/README.md) | 0 |
94 files changed, 1868 insertions, 11186 deletions
diff --git a/database/Makefile.am b/database/Makefile.am deleted file mode 100644 index 21b4896d3..000000000 --- a/database/Makefile.am +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -AUTOMAKE_OPTIONS = subdir-objects -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in - -SUBDIRS = \ - engine \ - ram \ - sqlite \ - contexts \ - $(NULL) - -dist_noinst_DATA = \ - README.md \ - $(NULL) diff --git a/database/README.md b/database/README.md deleted file mode 100644 index c5750e114..000000000 --- a/database/README.md +++ /dev/null @@ -1,162 +0,0 @@ -# Database - -Netdata is fully capable of long-term metrics storage, at per-second granularity, via its default database engine -(`dbengine`). But to remain as flexible as possible, Netdata supports several storage options: - -1. `dbengine`, (the default) data are in database files. The [Database Engine](https://github.com/netdata/netdata/blob/master/database/engine/README.md) works like a - traditional database. There is some amount of RAM dedicated to data caching and indexing and the rest of the data - reside compressed on disk. The number of history entries is not fixed in this case, but depends on the configured - disk space and the effective compression ratio of the data stored. This is the **only mode** that supports changing - the data collection update frequency (`update every`) **without losing** the previously stored metrics. For more - details see [here](https://github.com/netdata/netdata/blob/master/database/engine/README.md). - -2. `ram`, data are purely in memory. Data are never saved on disk. This mode uses `mmap()` and supports [KSM](#ksm). - -3. `save`, data are only in RAM while Netdata runs and are saved to / loaded from disk on Netdata restart. It also - uses `mmap()` and supports [KSM](#ksm). - -4. `map`, data are in memory mapped files. This works like the swap. When Netdata writes data on its memory, the Linux - kernel marks the related memory pages as dirty and automatically starts updating them on disk. Unfortunately we - cannot control how frequently this works. The Linux kernel uses exactly the same algorithm it uses for its swap - memory. This mode uses `mmap()` but does not support [KSM](#ksm). _Keep in mind though, this option will have a - constant write on your disk._ - -5. `alloc`, like `ram` but it uses `calloc()` and does not support [KSM](#ksm). This mode is the fallback for all others - except `none`. - -6. `none`, without a database (collected metrics can only be streamed to another Netdata). - -## Which database mode to use - -The default mode `[db].mode = dbengine` has been designed to scale for longer retentions and is the only mode suitable -for parent Agents in the _Parent - Child_ setups - -The other available database modes are designed to minimize resource utilization and should only be considered on -[Parent - Child](https://github.com/netdata/netdata/blob/master/docs/metrics-storage-management/enable-streaming.md) setups at the children side and only when the -resource constraints are very strict. - -So, - -- On a single node setup, use `[db].mode = dbengine`. -- On a [Parent - Child](https://github.com/netdata/netdata/blob/master/docs/metrics-storage-management/enable-streaming.md) setup, use `[db].mode = dbengine` on the - parent to increase retention, and a more resource-efficient mode like, `dbengine` with light retention settings, `save`, `ram`, or `none` for the children to minimize resource utilization. - -## Choose your database mode - -You can select the database mode by editing `netdata.conf` and setting: - -```conf -[db] - # dbengine (default), ram, save (the default if dbengine not available), map (swap like), none, alloc - mode = dbengine -``` - -## Netdata Longer Metrics Retention - -Metrics retention is controlled only by the disk space allocated to storing metrics. But it also affects the memory and -CPU required by the agent to query longer timeframes. - -Since Netdata Agents usually run on the edge, on production systems, Netdata Agent **parents** should be considered. -When having a [**parent - child**](https://github.com/netdata/netdata/blob/master/docs/metrics-storage-management/enable-streaming.md) setup, the child (the -Netdata Agent running on a production system) delegates all of its functions, including longer metrics retention and -querying, to the parent node that can dedicate more resources to this task. A single Netdata Agent parent can centralize -multiple children Netdata Agents (dozens, hundreds, or even thousands depending on its available resources). - -## Running Netdata on embedded devices - -Embedded devices typically have very limited RAM resources available. - -There are two settings for you to configure: - -1. `[db].update every`, which controls the data collection frequency -2. `[db].retention`, which controls the size of the database in memory (except for `[db].mode = dbengine`) - -By default `[db].update every = 1` and `[db].retention = 3600`. This gives you an hour of data with per second updates. - -If you set `[db].update every = 2` and `[db].retention = 1800`, you will still have an hour of data, but collected once -every 2 seconds. This will **cut in half** both CPU and RAM resources consumed by Netdata. Of course experiment a bit to find the right setting. -On very weak devices you might have to use `[db].update every = 5` and `[db].retention = 720` (still 1 hour of data, but -1/5 of the CPU and RAM resources). - -You can also disable [data collection plugins](https://github.com/netdata/netdata/blob/master/collectors/README.md) that you don't need. Disabling such plugins will also -free both CPU and RAM resources. - -## Memory optimizations - -### KSM - -KSM performs memory deduplication by scanning through main memory for physical pages that have identical content, and -identifies the virtual pages that are mapped to those physical pages. It leaves one page unchanged, and re-maps each -duplicate page to point to the same physical page. Netdata offers all of its in-memory database to kernel for -deduplication. - -In the past, KSM has been criticized for consuming a lot of CPU resources. This is true when KSM is used for -deduplicating certain applications, but it is not true for Netdata. Agent's memory is written very infrequently -(if you have 24 hours of metrics in Netdata, each byte at the in-memory database will be updated just once per day). KSM -is a solution that will provide 60+% memory savings to Netdata. - -### Enable KSM in kernel - -To enable KSM in kernel, you need to run a kernel compiled with the following: - -```sh -CONFIG_KSM=y -``` - -When KSM is enabled at the kernel, it is just available for the user to enable it. - -If you build a kernel with `CONFIG_KSM=y`, you will just get a few files in `/sys/kernel/mm/ksm`. Nothing else -happens. There is no performance penalty (apart from the memory this code occupies into the kernel). - -The files that `CONFIG_KSM=y` offers include: - -- `/sys/kernel/mm/ksm/run` by default `0`. You have to set this to `1` for the kernel to spawn `ksmd`. -- `/sys/kernel/mm/ksm/sleep_millisecs`, by default `20`. The frequency ksmd should evaluate memory for deduplication. -- `/sys/kernel/mm/ksm/pages_to_scan`, by default `100`. The amount of pages ksmd will evaluate on each run. - -So, by default `ksmd` is just disabled. It will not harm performance and the user/admin can control the CPU resources -they are willing to have used by `ksmd`. - -### Run `ksmd` kernel daemon - -To activate / run `ksmd,` you need to run the following: - -```sh -echo 1 >/sys/kernel/mm/ksm/run -echo 1000 >/sys/kernel/mm/ksm/sleep_millisecs -``` - -With these settings, ksmd does not even appear in the running process list (it will run once per second and evaluate 100 -pages for de-duplication). - -Put the above lines in your boot sequence (`/etc/rc.local` or equivalent) to have `ksmd` run at boot. - -### Monitoring Kernel Memory de-duplication performance - -Netdata will create charts for kernel memory de-duplication performance, the **deduper (ksm)** charts can be seen under the **Memory** section in the Netdata UI. - -#### KSM summary - -The summary gives you a quick idea of how much savings (in terms of bytes and in terms of percentage) KSM is able to achieve. - -![image](https://user-images.githubusercontent.com/24860547/199454880-123ae7c4-071a-4811-95b8-18cf4e4f60a2.png) - -#### KSM pages merge performance - -This chart indicates the performance of page merging. **Shared** indicates used shared pages, **Unshared** indicates memory no longer shared (pages are unique but repeatedly checked for merging), **Sharing** indicates memory currently shared(how many more sites are sharing the pages, i.e. how much saved) and **Volatile** indicates volatile pages (changing too fast to be placed in a tree). - -A high ratio of Sharing to Shared indicates good sharing, but a high ratio of Unshared to Sharing indicates wasted effort. - -![image](https://user-images.githubusercontent.com/24860547/199455374-d63fd2c2-e12b-4ddf-947b-35371215eb05.png) - -#### KSM savings - -This chart shows the amount of memory saved by KSM. **Savings** indicates saved memory. **Offered** indicates memory marked as mergeable. - -![image](https://user-images.githubusercontent.com/24860547/199455604-43cd9248-1f6e-4c31-be56-e0b9e432f48a.png) - -#### KSM effectiveness - -This chart tells you how well KSM is doing at what it is supposed to. It does this by charting the percentage of the mergeable pages that are currently merged. - -![image](https://user-images.githubusercontent.com/24860547/199455770-4d7991ff-6b7e-4d96-9d23-33ffc572b370.png) diff --git a/database/contexts/Makefile.am b/database/contexts/Makefile.am deleted file mode 100644 index 59250a997..000000000 --- a/database/contexts/Makefile.am +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -AUTOMAKE_OPTIONS = subdir-objects -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in - -SUBDIRS = \ - $(NULL) - -dist_noinst_DATA = \ - README.md \ - $(NULL) diff --git a/database/engine/Makefile.am b/database/engine/Makefile.am deleted file mode 100644 index 59250a997..000000000 --- a/database/engine/Makefile.am +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -AUTOMAKE_OPTIONS = subdir-objects -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in - -SUBDIRS = \ - $(NULL) - -dist_noinst_DATA = \ - README.md \ - $(NULL) diff --git a/database/engine/datafile.ksy b/database/engine/datafile.ksy deleted file mode 100644 index 28d4b3935..000000000 --- a/database/engine/datafile.ksy +++ /dev/null @@ -1,74 +0,0 @@ -meta: - id: netdata_datafile - endian: le - -seq: - - id: hdr - type: header - size: 4096 - - id: extents - type: extent - repeat: eos - -types: - header: - seq: - - id: magic - contents: "netdata-data-file" - - id: reserved - size: 15 - - id: version - contents: "1.0" - - id: reserved1 - size: 13 - - id: tier - type: u1 - extent_page_descr: - seq: - - id: type - type: u1 - enum: page_type - - id: uuid - size: 16 - - id: page_len - type: u4 - - id: start_time_ut - type: u8 - - id: end_time_ut - type: u8 - enums: - page_type: - 0: metrics - 1: tier - extent_header: - seq: - - id: payload_length - type: u4 - - id: compression_algorithm - type: u1 - enum: compression_algos - - id: number_of_pages - type: u1 - - id: page_descriptors - type: extent_page_descr - repeat: expr - repeat-expr: number_of_pages - enums: - compression_algos: - 0: rrd_no_compression - 1: rrd_lz4 - extent_trailer: - seq: - - id: crc32_checksum - type: u4 - extent: - seq: - - id: header - type: extent_header - - id: payload - size: header.payload_length - - id: trailer - type: extent_trailer - - id: padding - size: (((_io.pos + 4095) / 4096) * 4096) - _io.pos - # the extent size is made to always be a multiple of 4096 diff --git a/database/engine/journalfile_v2.ksy.in b/database/engine/journalfile_v2.ksy.in deleted file mode 100644 index 6a656bc45..000000000 --- a/database/engine/journalfile_v2.ksy.in +++ /dev/null @@ -1,150 +0,0 @@ -meta: - id: journalfile_v2`'ifdef(`VIRT_MEMBERS',`_virtmemb') - endian: le - application: netdata - file-extension: njfv2 - license: GPL-3.0-or-later - -seq: - - id: journal_v2_header - type: journal_v2_header - size: 4096 - - id: extent_list - type: journal_v2_extent_list - repeat: expr - repeat-expr: journal_v2_header.extent_count - - id: extent_trailer - type: journal_v2_block_trailer - - id: metric_list - type: journal_v2_metric_list - repeat: expr - repeat-expr: journal_v2_header.metric_count - - id: metric_trailer - type: journal_v2_block_trailer - - id: page_blocs - type: journal_v2_page_block - repeat: expr - repeat-expr: _root.journal_v2_header.metric_count - - id: padding - size: _root._io.size - _root._io.pos - 4 - - id: journal_file_trailer - type: journal_v2_block_trailer - -types: - journal_v2_metric_list: - seq: - - id: uuid - size: 16 - - id: entries - type: u4 - - id: page_offset - type: u4 - - id: delta_start_s - type: u4 - - id: delta_end_s - type: u4 -ifdef(`VIRT_MEMBERS', -` instances: - page_block: - type: journal_v2_page_block - io: _root._io - pos: page_offset -')dnl - journal_v2_page_hdr: - seq: - - id: crc - type: u4 - - id: uuid_offset - type: u4 - - id: entries - type: u4 - - id: uuid - size: 16 - journal_v2_page_list: - seq: - - id: delta_start_s - type: u4 - - id: delta_end_s - type: u4 - - id: extent_idx - type: u4 - - id: update_every_s - type: u4 - - id: page_len - type: u2 - - id: type - type: u1 - - id: reserved - type: u1 -ifdef(`VIRT_MEMBERS', -` instances: - extent: - io: _root._io - type: journal_v2_extent_list - pos: _root.journal_v2_header.extent_offset + (extent_idx * 16) -')dnl - journal_v2_header: - seq: - - id: magic - contents: [ 0x19, 0x10, 0x22, 0x01 ] #0x01221019 - - id: reserved - type: u4 - - id: start_time_ut - type: u8 - - id: end_time_ut - type: u8 - - id: extent_count - type: u4 - - id: extent_offset - type: u4 - - id: metric_count - type: u4 - - id: metric_offset - type: u4 - - id: page_count - type: u4 - - id: page_offset - type: u4 - - id: extent_trailer_offset - type: u4 - - id: metric_trailer_offset - type: u4 - - id: original_file_size - type: u4 - - id: total_file_size - type: u4 - - id: data - type: u8 -ifdef(`VIRT_MEMBERS', -` instances: - trailer: - io: _root._io - type: journal_v2_block_trailer - pos: _root._io.size - 4 -')dnl - journal_v2_block_trailer: - seq: - - id: checksum - type: u4 - journal_v2_extent_list: - seq: - - id: datafile_offset - type: u8 - - id: datafile_size - type: u4 - - id: file_idx - type: u2 - - id: page_cnt - type: u1 - - id: padding - type: u1 - journal_v2_page_block: - seq: - - id: hdr - type: journal_v2_page_hdr - - id: page_list - type: journal_v2_page_list - repeat: expr - repeat-expr: hdr.entries - - id: block_trailer - type: journal_v2_block_trailer diff --git a/database/ram/Makefile.am b/database/ram/Makefile.am deleted file mode 100644 index 59250a997..000000000 --- a/database/ram/Makefile.am +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -AUTOMAKE_OPTIONS = subdir-objects -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in - -SUBDIRS = \ - $(NULL) - -dist_noinst_DATA = \ - README.md \ - $(NULL) diff --git a/database/ram/README.md b/database/ram/README.md deleted file mode 100644 index 56cb7275a..000000000 --- a/database/ram/README.md +++ /dev/null @@ -1,11 +0,0 @@ -<!-- -title: "RAM database modes" -description: "Netdata's RAM database modes." -custom_edit_url: https://github.com/netdata/netdata/edit/master/database/ram/README.md -sidebar_label: "RAM database modes" -learn_status: "Published" -learn_topic_type: "References" -learn_rel_path: "Developers/Database" ---> - -# RAM database modes diff --git a/database/ram/rrddim_mem.c b/database/ram/rrddim_mem.c deleted file mode 100644 index a434f57d1..000000000 --- a/database/ram/rrddim_mem.c +++ /dev/null @@ -1,437 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rrddim_mem.h" -#include "Judy.h" - -static Pvoid_t rrddim_JudyHS_array = NULL; -static netdata_rwlock_t rrddim_JudyHS_rwlock = NETDATA_RWLOCK_INITIALIZER; - -// ---------------------------------------------------------------------------- -// metrics groups - -STORAGE_METRICS_GROUP *rrddim_metrics_group_get(STORAGE_INSTANCE *db_instance __maybe_unused, uuid_t *uuid __maybe_unused) { - return NULL; -} - -void rrddim_metrics_group_release(STORAGE_INSTANCE *db_instance __maybe_unused, STORAGE_METRICS_GROUP *smg __maybe_unused) { - // if(!smg) return; // smg may be NULL - ; -} - -// ---------------------------------------------------------------------------- -// RRDDIM legacy data collection functions - -struct mem_metric_handle { - RRDDIM *rd; - - size_t counter; - size_t entries; - size_t current_entry; - time_t last_updated_s; - time_t update_every_s; - - int32_t refcount; -}; - -static void update_metric_handle_from_rrddim(struct mem_metric_handle *mh, RRDDIM *rd) { - mh->counter = rd->rrdset->counter; - mh->entries = rd->rrdset->db.entries; - mh->current_entry = rd->rrdset->db.current_entry; - mh->last_updated_s = rd->rrdset->last_updated.tv_sec; - mh->update_every_s = rd->rrdset->update_every; -} - -static void check_metric_handle_from_rrddim(struct mem_metric_handle *mh) { - RRDDIM *rd = mh->rd; (void)rd; - internal_fatal(mh->entries != (size_t)rd->rrdset->db.entries, "RRDDIM: entries do not match"); - internal_fatal(mh->update_every_s != rd->rrdset->update_every, "RRDDIM: update every does not match"); -} - -STORAGE_METRIC_HANDLE * -rrddim_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *db_instance __maybe_unused) { - struct mem_metric_handle *mh = (struct mem_metric_handle *)rrddim_metric_get(db_instance, &rd->metric_uuid); - while(!mh) { - netdata_rwlock_wrlock(&rrddim_JudyHS_rwlock); - Pvoid_t *PValue = JudyHSIns(&rrddim_JudyHS_array, &rd->metric_uuid, sizeof(uuid_t), PJE0); - mh = *PValue; - if(!mh) { - mh = callocz(1, sizeof(struct mem_metric_handle)); - mh->rd = rd; - mh->refcount = 1; - update_metric_handle_from_rrddim(mh, rd); - *PValue = mh; - __atomic_add_fetch(&rrddim_db_memory_size, sizeof(struct mem_metric_handle) + JUDYHS_INDEX_SIZE_ESTIMATE(sizeof(uuid_t)), __ATOMIC_RELAXED); - } - else { - if(__atomic_add_fetch(&mh->refcount, 1, __ATOMIC_RELAXED) <= 0) - mh = NULL; - } - netdata_rwlock_unlock(&rrddim_JudyHS_rwlock); - } - - internal_fatal(mh->rd != rd, "RRDDIM_MEM: incorrect pointer returned from index."); - - return (STORAGE_METRIC_HANDLE *)mh; -} - -STORAGE_METRIC_HANDLE * -rrddim_metric_get(STORAGE_INSTANCE *db_instance __maybe_unused, uuid_t *uuid) { - struct mem_metric_handle *mh = NULL; - netdata_rwlock_rdlock(&rrddim_JudyHS_rwlock); - Pvoid_t *PValue = JudyHSGet(rrddim_JudyHS_array, uuid, sizeof(uuid_t)); - if (likely(NULL != PValue)) { - mh = *PValue; - if(__atomic_add_fetch(&mh->refcount, 1, __ATOMIC_RELAXED) <= 0) - mh = NULL; - } - netdata_rwlock_unlock(&rrddim_JudyHS_rwlock); - - return (STORAGE_METRIC_HANDLE *)mh; -} - -STORAGE_METRIC_HANDLE *rrddim_metric_dup(STORAGE_METRIC_HANDLE *db_metric_handle) { - struct mem_metric_handle *mh = (struct mem_metric_handle *)db_metric_handle; - __atomic_add_fetch(&mh->refcount, 1, __ATOMIC_RELAXED); - return db_metric_handle; -} - -void rrddim_metric_release(STORAGE_METRIC_HANDLE *db_metric_handle __maybe_unused) { - struct mem_metric_handle *mh = (struct mem_metric_handle *)db_metric_handle; - - if(__atomic_sub_fetch(&mh->refcount, 1, __ATOMIC_RELAXED) == 0) { - // we are the last one holding this - - int32_t expected = 0; - if(__atomic_compare_exchange_n(&mh->refcount, &expected, -99999, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) { - // we can delete it - - RRDDIM *rd = mh->rd; - netdata_rwlock_wrlock(&rrddim_JudyHS_rwlock); - JudyHSDel(&rrddim_JudyHS_array, &rd->metric_uuid, sizeof(uuid_t), PJE0); - netdata_rwlock_unlock(&rrddim_JudyHS_rwlock); - - freez(mh); - __atomic_sub_fetch(&rrddim_db_memory_size, sizeof(struct mem_metric_handle) + JUDYHS_INDEX_SIZE_ESTIMATE(sizeof(uuid_t)), __ATOMIC_RELAXED); - } - } -} - -bool rrddim_metric_retention_by_uuid(STORAGE_INSTANCE *db_instance __maybe_unused, uuid_t *uuid, time_t *first_entry_s, time_t *last_entry_s) { - STORAGE_METRIC_HANDLE *db_metric_handle = rrddim_metric_get(db_instance, uuid); - if(!db_metric_handle) - return false; - - *first_entry_s = rrddim_query_oldest_time_s(db_metric_handle); - *last_entry_s = rrddim_query_latest_time_s(db_metric_handle); - - return true; -} - -void rrddim_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *collection_handle, int update_every) { - struct mem_collect_handle *ch = (struct mem_collect_handle *)collection_handle; - struct mem_metric_handle *mh = (struct mem_metric_handle *)ch->db_metric_handle; - - rrddim_store_metric_flush(collection_handle); - mh->update_every_s = update_every; -} - -STORAGE_COLLECT_HANDLE *rrddim_collect_init(STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every __maybe_unused, STORAGE_METRICS_GROUP *smg __maybe_unused) { - struct mem_metric_handle *mh = (struct mem_metric_handle *)db_metric_handle; - RRDDIM *rd = mh->rd; - - update_metric_handle_from_rrddim(mh, rd); - internal_fatal((uint32_t)mh->update_every_s != update_every, "RRDDIM: update requested does not match the dimension"); - - struct mem_collect_handle *ch = callocz(1, sizeof(struct mem_collect_handle)); - ch->common.backend = STORAGE_ENGINE_BACKEND_RRDDIM; - ch->rd = rd; - ch->db_metric_handle = db_metric_handle; - - __atomic_add_fetch(&rrddim_db_memory_size, sizeof(struct mem_collect_handle), __ATOMIC_RELAXED); - - return (STORAGE_COLLECT_HANDLE *)ch; -} - -void rrddim_store_metric_flush(STORAGE_COLLECT_HANDLE *collection_handle) { - struct mem_collect_handle *ch = (struct mem_collect_handle *)collection_handle; - struct mem_metric_handle *mh = (struct mem_metric_handle *)ch->db_metric_handle; - - RRDDIM *rd = mh->rd; - size_t entries = mh->entries; - storage_number empty = pack_storage_number(NAN, SN_FLAG_NONE); - - for(size_t i = 0; i < entries ;i++) - rd->db.data[i] = empty; - - mh->counter = 0; - mh->last_updated_s = 0; - mh->current_entry = 0; -} - -static inline void rrddim_fill_the_gap(STORAGE_COLLECT_HANDLE *collection_handle, time_t now_collect_s) { - struct mem_collect_handle *ch = (struct mem_collect_handle *)collection_handle; - struct mem_metric_handle *mh = (struct mem_metric_handle *)ch->db_metric_handle; - - RRDDIM *rd = mh->rd; - - internal_fatal(ch->rd != mh->rd, "RRDDIM: dimensions do not match"); - check_metric_handle_from_rrddim(mh); - - size_t entries = mh->entries; - time_t update_every_s = mh->update_every_s; - time_t last_stored_s = mh->last_updated_s; - size_t gap_entries = (now_collect_s - last_stored_s) / update_every_s; - if(gap_entries >= entries) - rrddim_store_metric_flush(collection_handle); - - else { - storage_number empty = pack_storage_number(NAN, SN_FLAG_NONE); - size_t current_entry = mh->current_entry; - time_t now_store_s = last_stored_s + update_every_s; - - // fill the dimension - size_t c; - for(c = 0; c < entries && now_store_s <= now_collect_s ; now_store_s += update_every_s, c++) { - rd->db.data[current_entry++] = empty; - - if(unlikely(current_entry >= entries)) - current_entry = 0; - } - mh->counter += c; - mh->current_entry = current_entry; - mh->last_updated_s = now_store_s; - } -} - -void rrddim_collect_store_metric(STORAGE_COLLECT_HANDLE *collection_handle, - usec_t point_in_time_ut, - NETDATA_DOUBLE n, - NETDATA_DOUBLE min_value __maybe_unused, - NETDATA_DOUBLE max_value __maybe_unused, - uint16_t count __maybe_unused, - uint16_t anomaly_count __maybe_unused, - SN_FLAGS flags) -{ - struct mem_collect_handle *ch = (struct mem_collect_handle *)collection_handle; - struct mem_metric_handle *mh = (struct mem_metric_handle *)ch->db_metric_handle; - - RRDDIM *rd = ch->rd; - time_t point_in_time_s = (time_t)(point_in_time_ut / USEC_PER_SEC); - - internal_fatal(ch->rd != mh->rd, "RRDDIM: dimensions do not match"); - check_metric_handle_from_rrddim(mh); - - if(unlikely(point_in_time_s <= mh->last_updated_s)) - return; - - if(unlikely(mh->last_updated_s && point_in_time_s - mh->update_every_s > mh->last_updated_s)) - rrddim_fill_the_gap(collection_handle, point_in_time_s); - - rd->db.data[mh->current_entry] = pack_storage_number(n, flags); - mh->counter++; - mh->current_entry = (mh->current_entry + 1) >= mh->entries ? 0 : mh->current_entry + 1; - mh->last_updated_s = point_in_time_s; -} - -int rrddim_collect_finalize(STORAGE_COLLECT_HANDLE *collection_handle) { - freez(collection_handle); - __atomic_sub_fetch(&rrddim_db_memory_size, sizeof(struct mem_collect_handle), __ATOMIC_RELAXED); - return 0; -} - -// ---------------------------------------------------------------------------- - -// get the total duration in seconds of the round-robin database -#define metric_duration(mh) (( (time_t)(mh)->counter >= (time_t)(mh)->entries ? (time_t)(mh)->entries : (time_t)(mh)->counter ) * (time_t)(mh)->update_every_s) - -// get the last slot updated in the round-robin database -#define rrddim_last_slot(mh) ((size_t)(((mh)->current_entry == 0) ? (mh)->entries - 1 : (mh)->current_entry - 1)) - -// return the slot that has the oldest value -#define rrddim_first_slot(mh) ((size_t)((mh)->counter >= (size_t)(mh)->entries ? (mh)->current_entry : 0)) - -// get the slot of the round-robin database, for the given timestamp (t) -// it always returns a valid slot, although it may not be for the time requested if the time is outside the round-robin database -// only valid when not using dbengine -static inline size_t rrddim_time2slot(STORAGE_METRIC_HANDLE *db_metric_handle, time_t t) { - struct mem_metric_handle *mh = (struct mem_metric_handle *)db_metric_handle; - RRDDIM *rd = mh->rd; - - size_t ret = 0; - time_t last_entry_s = rrddim_query_latest_time_s(db_metric_handle); - time_t first_entry_s = rrddim_query_oldest_time_s(db_metric_handle); - size_t entries = mh->entries; - size_t first_slot = rrddim_first_slot(mh); - size_t last_slot = rrddim_last_slot(mh); - size_t update_every = mh->update_every_s; - - if(t >= last_entry_s) { - // the requested time is after the last entry we have - ret = last_slot; - } - else { - if(t <= first_entry_s) { - // the requested time is before the first entry we have - ret = first_slot; - } - else { - if(last_slot >= (size_t)((last_entry_s - t) / update_every)) - ret = last_slot - ((last_entry_s - t) / update_every); - else - ret = last_slot - ((last_entry_s - t) / update_every) + entries; - } - } - - if(unlikely(ret >= entries)) { - netdata_log_error("INTERNAL ERROR: rrddim_time2slot() on %s returns values outside entries", rrddim_name(rd)); - ret = entries - 1; - } - - return ret; -} - -// get the timestamp of a specific slot in the round-robin database -// only valid when not using dbengine -static inline time_t rrddim_slot2time(STORAGE_METRIC_HANDLE *db_metric_handle, size_t slot) { - struct mem_metric_handle *mh = (struct mem_metric_handle *)db_metric_handle; - RRDDIM *rd = mh->rd; - - time_t ret; - time_t last_entry_s = rrddim_query_latest_time_s(db_metric_handle); - time_t first_entry_s = rrddim_query_oldest_time_s(db_metric_handle); - size_t entries = mh->entries; - size_t last_slot = rrddim_last_slot(mh); - size_t update_every = mh->update_every_s; - - if(slot >= entries) { - netdata_log_error("INTERNAL ERROR: caller of rrddim_slot2time() gives invalid slot %zu", slot); - slot = entries - 1; - } - - if(slot > last_slot) - ret = last_entry_s - (time_t)(update_every * (last_slot - slot + entries)); - else - ret = last_entry_s - (time_t)(update_every * (last_slot - slot)); - - if(unlikely(ret < first_entry_s)) { - netdata_log_error("INTERNAL ERROR: rrddim_slot2time() on dimension '%s' of chart '%s' returned time (%ld) too far in the past (before first_entry_s %ld) for slot %zu", - rrddim_name(rd), rrdset_id(rd->rrdset), ret, first_entry_s, slot); - - ret = first_entry_s; - } - - if(unlikely(ret > last_entry_s)) { - netdata_log_error("INTERNAL ERROR: rrddim_slot2time() on dimension '%s' of chart '%s' returned time (%ld) too far into the future (after last_entry_s %ld) for slot %zu", - rrddim_name(rd), rrdset_id(rd->rrdset), ret, last_entry_s, slot); - - ret = last_entry_s; - } - - return ret; -} - -// ---------------------------------------------------------------------------- -// RRDDIM legacy database query functions - -void rrddim_query_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct storage_engine_query_handle *handle, time_t start_time_s, time_t end_time_s, STORAGE_PRIORITY priority __maybe_unused) { - struct mem_metric_handle *mh = (struct mem_metric_handle *)db_metric_handle; - - check_metric_handle_from_rrddim(mh); - - handle->start_time_s = start_time_s; - handle->end_time_s = end_time_s; - handle->priority = priority; - handle->backend = STORAGE_ENGINE_BACKEND_RRDDIM; - struct mem_query_handle* h = mallocz(sizeof(struct mem_query_handle)); - h->db_metric_handle = db_metric_handle; - - h->slot = rrddim_time2slot(db_metric_handle, start_time_s); - h->last_slot = rrddim_time2slot(db_metric_handle, end_time_s); - h->dt = mh->update_every_s; - - h->next_timestamp = start_time_s; - h->slot_timestamp = rrddim_slot2time(db_metric_handle, h->slot); - h->last_timestamp = rrddim_slot2time(db_metric_handle, h->last_slot); - - // netdata_log_info("RRDDIM QUERY INIT: start %ld, end %ld, next %ld, first %ld, last %ld, dt %ld", start_time, end_time, h->next_timestamp, h->slot_timestamp, h->last_timestamp, h->dt); - - __atomic_add_fetch(&rrddim_db_memory_size, sizeof(struct mem_query_handle), __ATOMIC_RELAXED); - handle->handle = (STORAGE_QUERY_HANDLE *)h; -} - -// Returns the metric and sets its timestamp into current_time -// IT IS REQUIRED TO **ALWAYS** SET ALL RETURN VALUES (current_time, end_time, flags) -// IT IS REQUIRED TO **ALWAYS** KEEP TRACK OF TIME, EVEN OUTSIDE THE DATABASE BOUNDARIES -STORAGE_POINT rrddim_query_next_metric(struct storage_engine_query_handle *handle) { - struct mem_query_handle* h = (struct mem_query_handle*)handle->handle; - struct mem_metric_handle *mh = (struct mem_metric_handle *)h->db_metric_handle; - RRDDIM *rd = mh->rd; - - size_t entries = mh->entries; - size_t slot = h->slot; - - STORAGE_POINT sp; - sp.count = 1; - - time_t this_timestamp = h->next_timestamp; - h->next_timestamp += h->dt; - - // set this timestamp for our caller - sp.start_time_s = this_timestamp - h->dt; - sp.end_time_s = this_timestamp; - - if(unlikely(this_timestamp < h->slot_timestamp)) { - storage_point_empty(sp, sp.start_time_s, sp.end_time_s); - return sp; - } - - if(unlikely(this_timestamp > h->last_timestamp)) { - storage_point_empty(sp, sp.start_time_s, sp.end_time_s); - return sp; - } - - storage_number n = rd->db.data[slot++]; - if(unlikely(slot >= entries)) slot = 0; - - h->slot = slot; - h->slot_timestamp += h->dt; - - sp.anomaly_count = is_storage_number_anomalous(n) ? 1 : 0; - sp.flags = (n & SN_USER_FLAGS); - sp.min = sp.max = sp.sum = unpack_storage_number(n); - - return sp; -} - -int rrddim_query_is_finished(struct storage_engine_query_handle *handle) { - struct mem_query_handle *h = (struct mem_query_handle*)handle->handle; - return (h->next_timestamp > handle->end_time_s); -} - -void rrddim_query_finalize(struct storage_engine_query_handle *handle) { -#ifdef NETDATA_INTERNAL_CHECKS - struct mem_query_handle *h = (struct mem_query_handle*)handle->handle; - struct mem_metric_handle *mh = (struct mem_metric_handle *)h->db_metric_handle; - - internal_error(!rrddim_query_is_finished(handle), - "QUERY: query for chart '%s' dimension '%s' has been stopped unfinished", - rrdset_id(mh->rd->rrdset), rrddim_name(mh->rd)); - -#endif - freez(handle->handle); - __atomic_sub_fetch(&rrddim_db_memory_size, sizeof(struct mem_query_handle), __ATOMIC_RELAXED); -} - -time_t rrddim_query_align_to_optimal_before(struct storage_engine_query_handle *rrddim_handle) { - return rrddim_handle->end_time_s; -} - -time_t rrddim_query_latest_time_s(STORAGE_METRIC_HANDLE *db_metric_handle) { - struct mem_metric_handle *mh = (struct mem_metric_handle *)db_metric_handle; - return mh->last_updated_s; -} - -time_t rrddim_query_oldest_time_s(STORAGE_METRIC_HANDLE *db_metric_handle) { - struct mem_metric_handle *mh = (struct mem_metric_handle *)db_metric_handle; - return (time_t)(mh->last_updated_s - metric_duration(mh)); -} diff --git a/database/ram/rrddim_mem.h b/database/ram/rrddim_mem.h deleted file mode 100644 index a75814a0b..000000000 --- a/database/ram/rrddim_mem.h +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_RRDDIMMEM_H -#define NETDATA_RRDDIMMEM_H - -#include "database/rrd.h" - -struct mem_collect_handle { - struct storage_collect_handle common; // has to be first item - - STORAGE_METRIC_HANDLE *db_metric_handle; - RRDDIM *rd; -}; - -struct mem_query_handle { - STORAGE_METRIC_HANDLE *db_metric_handle; - time_t dt; - time_t next_timestamp; - time_t last_timestamp; - time_t slot_timestamp; - size_t slot; - size_t last_slot; -}; - -STORAGE_METRIC_HANDLE *rrddim_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *db_instance); -STORAGE_METRIC_HANDLE *rrddim_metric_get(STORAGE_INSTANCE *db_instance, uuid_t *uuid); -STORAGE_METRIC_HANDLE *rrddim_metric_dup(STORAGE_METRIC_HANDLE *db_metric_handle); -void rrddim_metric_release(STORAGE_METRIC_HANDLE *db_metric_handle); - -bool rrddim_metric_retention_by_uuid(STORAGE_INSTANCE *db_instance, uuid_t *uuid, time_t *first_entry_s, time_t *last_entry_s); - -STORAGE_METRICS_GROUP *rrddim_metrics_group_get(STORAGE_INSTANCE *db_instance, uuid_t *uuid); -void rrddim_metrics_group_release(STORAGE_INSTANCE *db_instance, STORAGE_METRICS_GROUP *smg); - -STORAGE_COLLECT_HANDLE *rrddim_collect_init(STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every, STORAGE_METRICS_GROUP *smg); -void rrddim_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *collection_handle, int update_every); -void rrddim_collect_store_metric(STORAGE_COLLECT_HANDLE *collection_handle, usec_t point_in_time_ut, NETDATA_DOUBLE n, - NETDATA_DOUBLE min_value, - NETDATA_DOUBLE max_value, - uint16_t count, - uint16_t anomaly_count, - SN_FLAGS flags); -void rrddim_store_metric_flush(STORAGE_COLLECT_HANDLE *collection_handle); -int rrddim_collect_finalize(STORAGE_COLLECT_HANDLE *collection_handle); - -void rrddim_query_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct storage_engine_query_handle *handle, time_t start_time_s, time_t end_time_s, STORAGE_PRIORITY priority); -STORAGE_POINT rrddim_query_next_metric(struct storage_engine_query_handle *handle); -int rrddim_query_is_finished(struct storage_engine_query_handle *handle); -void rrddim_query_finalize(struct storage_engine_query_handle *handle); -time_t rrddim_query_latest_time_s(STORAGE_METRIC_HANDLE *db_metric_handle); -time_t rrddim_query_oldest_time_s(STORAGE_METRIC_HANDLE *db_metric_handle); -time_t rrddim_query_align_to_optimal_before(struct storage_engine_query_handle *rrddim_handle); - -#endif diff --git a/database/rrdcalc.c b/database/rrdcalc.c deleted file mode 100644 index 199d90803..000000000 --- a/database/rrdcalc.c +++ /dev/null @@ -1,869 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rrd.h" - -// ---------------------------------------------------------------------------- -// RRDCALC helpers - -void rrdcalc_flags_to_json_array(BUFFER *wb, const char *key, RRDCALC_FLAGS flags) { - buffer_json_member_add_array(wb, key); - - if(flags & RRDCALC_FLAG_DB_ERROR) - buffer_json_add_array_item_string(wb, "DB_ERROR"); - if(flags & RRDCALC_FLAG_DB_NAN) - buffer_json_add_array_item_string(wb, "DB_NAN"); - if(flags & RRDCALC_FLAG_CALC_ERROR) - buffer_json_add_array_item_string(wb, "CALC_ERROR"); - if(flags & RRDCALC_FLAG_WARN_ERROR) - buffer_json_add_array_item_string(wb, "WARN_ERROR"); - if(flags & RRDCALC_FLAG_CRIT_ERROR) - buffer_json_add_array_item_string(wb, "CRIT_ERROR"); - if(flags & RRDCALC_FLAG_RUNNABLE) - buffer_json_add_array_item_string(wb, "RUNNABLE"); - if(flags & RRDCALC_FLAG_DISABLED) - buffer_json_add_array_item_string(wb, "DISABLED"); - if(flags & RRDCALC_FLAG_SILENCED) - buffer_json_add_array_item_string(wb, "SILENCED"); - if(flags & RRDCALC_FLAG_RUN_ONCE) - buffer_json_add_array_item_string(wb, "RUN_ONCE"); - if(flags & RRDCALC_FLAG_FROM_TEMPLATE) - buffer_json_add_array_item_string(wb, "FROM_TEMPLATE"); - - buffer_json_array_close(wb); -} - -inline const char *rrdcalc_status2string(RRDCALC_STATUS status) { - switch(status) { - case RRDCALC_STATUS_REMOVED: - return "REMOVED"; - - case RRDCALC_STATUS_UNDEFINED: - return "UNDEFINED"; - - case RRDCALC_STATUS_UNINITIALIZED: - return "UNINITIALIZED"; - - case RRDCALC_STATUS_CLEAR: - return "CLEAR"; - - case RRDCALC_STATUS_RAISED: - return "RAISED"; - - case RRDCALC_STATUS_WARNING: - return "WARNING"; - - case RRDCALC_STATUS_CRITICAL: - return "CRITICAL"; - - default: - netdata_log_error("Unknown alarm status %d", status); - return "UNKNOWN"; - } -} - -uint32_t rrdcalc_get_unique_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id) { - rw_spinlock_read_lock(&host->health_log.spinlock); - - // re-use old IDs, by looking them up in the alarm log - ALARM_ENTRY *ae = NULL; - for(ae = host->health_log.alarms; ae ;ae = ae->next) { - if(unlikely(name == ae->name && chart == ae->chart && !uuid_memcmp(&ae->config_hash_id, config_hash_id))) { - if(next_event_id) *next_event_id = ae->alarm_event_id + 1; - break; - } - } - - uint32_t alarm_id; - - if(ae) - alarm_id = ae->alarm_id; - - else { - alarm_id = sql_get_alarm_id(host, chart, name, next_event_id, config_hash_id); - - if (!alarm_id) { - //check possible stored config hash as zeroes or null - alarm_id = sql_get_alarm_id_check_zero_hash(host, chart, name, next_event_id, config_hash_id); - if (!alarm_id) { - if (unlikely(!host->health_log.next_alarm_id)) - host->health_log.next_alarm_id = (uint32_t)now_realtime_sec(); - - alarm_id = host->health_log.next_alarm_id++; - } - } - } - - rw_spinlock_read_unlock(&host->health_log.spinlock); - return alarm_id; -} - -// ---------------------------------------------------------------------------- -// RRDCALC replacing info/summary text variables with RRDSET labels - -static STRING *rrdcalc_replace_variables_with_rrdset_labels(const char *line, RRDCALC *rc) { - if (!line || !*line) - return NULL; - - size_t pos = 0; - char *temp = strdupz(line); - char var[RRDCALC_VAR_MAX]; - char *m, *lbl_value = NULL; - - while ((m = strchr(temp + pos, '$')) && *(m+1) == '{') { - int i = 0; - char *e = m; - while (*e) { - var[i++] = *e; - - if (*e == '}' || i == RRDCALC_VAR_MAX - 1) - break; - - e++; - } - - var[i] = '\0'; - pos = m - temp + 1; - - if (!strcmp(var, RRDCALC_VAR_FAMILY)) { - char *buf = find_and_replace(temp, var, (rc->rrdset && rc->rrdset->family) ? rrdset_family(rc->rrdset) : "", m); - freez(temp); - temp = buf; - } - else if (!strncmp(var, RRDCALC_VAR_LABEL, RRDCALC_VAR_LABEL_LEN)) { - char label_val[RRDCALC_VAR_MAX + RRDCALC_VAR_LABEL_LEN + 1] = { 0 }; - strcpy(label_val, var+RRDCALC_VAR_LABEL_LEN); - label_val[i - RRDCALC_VAR_LABEL_LEN - 1] = '\0'; - - if(likely(rc->rrdset && rc->rrdset->rrdlabels)) { - lbl_value = NULL; - rrdlabels_get_value_strdup_or_null(rc->rrdset->rrdlabels, &lbl_value, label_val); - if (lbl_value) { - char *buf = find_and_replace(temp, var, lbl_value, m); - freez(temp); - temp = buf; - freez(lbl_value); - } - } - } - } - - STRING *ret = string_strdupz(temp); - freez(temp); - - return ret; -} - -void rrdcalc_update_info_using_rrdset_labels(RRDCALC *rc) { - if(!rc->rrdset || !rc->original_info || !rc->rrdset->rrdlabels) return; - - size_t labels_version = rrdlabels_version(rc->rrdset->rrdlabels); - if(rc->labels_version != labels_version) { - - if (rc->original_info) { - STRING *old = rc->info; - rc->info = rrdcalc_replace_variables_with_rrdset_labels(rrdcalc_original_info(rc), rc); - string_freez(old); - } - - if (rc->original_summary) { - STRING *old = rc->summary; - rc->summary = rrdcalc_replace_variables_with_rrdset_labels(rrdcalc_original_summary(rc), rc); - string_freez(old); - } - - rc->labels_version = labels_version; - } -} - -// ---------------------------------------------------------------------------- -// RRDCALC index management for RRDSET - -// the dictionary requires a unique key for every item -// we use {chart id}.{alert name} for both the RRDHOST and RRDSET alert indexes. - -#define RRDCALC_MAX_KEY_SIZE 1024 -static size_t rrdcalc_key(char *dst, size_t dst_len, const char *chart, const char *alert) { - return snprintfz(dst, dst_len, "%s/%s", chart, alert); -} - -const RRDCALC_ACQUIRED *rrdcalc_from_rrdset_get(RRDSET *st, const char *alert_name) { - char key[RRDCALC_MAX_KEY_SIZE + 1]; - size_t key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, rrdset_id(st), alert_name); - - const RRDCALC_ACQUIRED *rca = (const RRDCALC_ACQUIRED *)dictionary_get_and_acquire_item_advanced(st->rrdhost->rrdcalc_root_index, key, (ssize_t)(key_len + 1)); - - if(!rca) { - key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, rrdset_name(st), alert_name); - rca = (const RRDCALC_ACQUIRED *)dictionary_get_and_acquire_item_advanced(st->rrdhost->rrdcalc_root_index, key, (ssize_t)(key_len + 1)); - } - - return rca; -} - -void rrdcalc_from_rrdset_release(RRDSET *st, const RRDCALC_ACQUIRED *rca) { - if(!rca) return; - - dictionary_acquired_item_release(st->rrdhost->rrdcalc_root_index, (const DICTIONARY_ITEM *)rca); -} - -RRDCALC *rrdcalc_acquired_to_rrdcalc(const RRDCALC_ACQUIRED *rca) { - if(rca) - return dictionary_acquired_item_value((const DICTIONARY_ITEM *)rca); - - return NULL; -} - -// ---------------------------------------------------------------------------- -// RRDCALC managing the linking with RRDSET - -static void rrdcalc_link_to_rrdset(RRDSET *st, RRDCALC *rc) { - RRDHOST *host = st->rrdhost; - - netdata_log_debug(D_HEALTH, "Health linking alarm '%s.%s' to chart '%s' of host '%s'", rrdcalc_chart_name(rc), rrdcalc_name(rc), rrdset_id(st), rrdhost_hostname(host)); - - rc->last_status_change_value = rc->value; - rc->last_status_change = now_realtime_sec(); - rc->rrdset = st; - - rw_spinlock_write_lock(&st->alerts.spinlock); - DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(st->alerts.base, rc, prev, next); - rw_spinlock_write_unlock(&st->alerts.spinlock); - - if(rc->update_every < rc->rrdset->update_every) { - netdata_log_info("Health alarm '%s.%s' has update every %d, less than chart update every %d. Setting alarm update frequency to %d.", rrdset_id(rc->rrdset), rrdcalc_name(rc), rc->update_every, rc->rrdset->update_every, rc->rrdset->update_every); - rc->update_every = rc->rrdset->update_every; - } - - if(!isnan(rc->green) && isnan(st->green)) { - netdata_log_debug(D_HEALTH, "Health alarm '%s.%s' green threshold set from " NETDATA_DOUBLE_FORMAT_AUTO - " to " NETDATA_DOUBLE_FORMAT_AUTO ".", rrdset_id(rc->rrdset), rrdcalc_name(rc), rc->rrdset->green, rc->green); - st->green = rc->green; - } - - if(!isnan(rc->red) && isnan(st->red)) { - netdata_log_debug(D_HEALTH, "Health alarm '%s.%s' red threshold set from " NETDATA_DOUBLE_FORMAT_AUTO " to " NETDATA_DOUBLE_FORMAT_AUTO - ".", rrdset_id(rc->rrdset), rrdcalc_name(rc), rc->rrdset->red, rc->red); - st->red = rc->red; - } - - char buf[RRDVAR_MAX_LENGTH + 1]; - snprintfz(buf, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_name(st), rrdcalc_name(rc)); - STRING *rrdset_name_rrdcalc_name = string_strdupz(buf); - snprintfz(buf, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_id(st), rrdcalc_name(rc)); - STRING *rrdset_id_rrdcalc_name = string_strdupz(buf); - - rc->rrdvar_local = rrdvar_add_and_acquire( - "local", - st->rrdvars, - rc->name, - RRDVAR_TYPE_CALCULATED, - RRDVAR_FLAG_RRDCALC_LOCAL_VAR, - &rc->value); - - rc->rrdvar_family = rrdvar_add_and_acquire( - "family", - rrdfamily_rrdvars_dict(st->rrdfamily), - rc->name, - RRDVAR_TYPE_CALCULATED, - RRDVAR_FLAG_RRDCALC_FAMILY_VAR, - &rc->value); - - rc->rrdvar_host_chart_name = rrdvar_add_and_acquire( - "host", - host->rrdvars, - rrdset_name_rrdcalc_name, - RRDVAR_TYPE_CALCULATED, - RRDVAR_FLAG_RRDCALC_HOST_CHARTNAME_VAR, - &rc->value); - - rc->rrdvar_host_chart_id = rrdvar_add_and_acquire( - "host", - host->rrdvars, - rrdset_id_rrdcalc_name, - RRDVAR_TYPE_CALCULATED, - RRDVAR_FLAG_RRDCALC_HOST_CHARTID_VAR | ((rc->rrdvar_host_chart_name) ? 0 : RRDVAR_FLAG_RRDCALC_HOST_CHARTNAME_VAR), - &rc->value); - - string_freez(rrdset_id_rrdcalc_name); - string_freez(rrdset_name_rrdcalc_name); - - if(!rc->units) - rc->units = string_dup(st->units); - - rrdvar_store_for_chart(host, st); - - rrdcalc_update_info_using_rrdset_labels(rc); - - if(!rc->summary) { - rc->summary = string_dup(rc->name); - rc->original_summary = string_dup(rc->name); - } - - time_t now = now_realtime_sec(); - - ALARM_ENTRY *ae = health_create_alarm_entry( - host, - rc->id, - rc->next_event_id++, - rc->config_hash_id, - now, - rc->name, - rc->rrdset->id, - rc->rrdset->context, - rc->rrdset->name, - rc->classification, - rc->component, - rc->type, - rc->exec, - rc->recipient, - now - rc->last_status_change, - rc->old_value, - rc->value, - RRDCALC_STATUS_REMOVED, - rc->status, - rc->source, - rc->units, - rc->summary, - rc->info, - 0, - rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0); - - rc->ae = ae; - health_alarm_log_add_entry(host, ae); - rrdset_flag_set(st, RRDSET_FLAG_HAS_RRDCALC_LINKED); -} - -static void rrdcalc_unlink_from_rrdset(RRDCALC *rc, bool having_ll_wrlock) { - RRDSET *st = rc->rrdset; - - if(!st) { - netdata_log_debug(D_HEALTH, "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rrdcalc_chart_name(rc), rrdcalc_name(rc)); - netdata_log_error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rrdcalc_chart_name(rc), rrdcalc_name(rc)); - return; - } - - RRDHOST *host = st->rrdhost; - - time_t now = now_realtime_sec(); - - if (likely(rc->status != RRDCALC_STATUS_REMOVED)) { - ALARM_ENTRY *ae = health_create_alarm_entry( - host, - rc->id, - rc->next_event_id++, - rc->config_hash_id, - now, - rc->name, - rc->rrdset->id, - rc->rrdset->context, - rc->rrdset->name, - rc->classification, - rc->component, - rc->type, - rc->exec, - rc->recipient, - now - rc->last_status_change, - rc->old_value, - rc->value, - rc->status, - RRDCALC_STATUS_REMOVED, - rc->source, - rc->units, - rc->summary, - rc->info, - 0, - 0); - - rc->ae = ae; - health_alarm_log_add_entry(host, ae); - } - - netdata_log_debug(D_HEALTH, "Health unlinking alarm '%s.%s' from chart '%s' of host '%s'", rrdcalc_chart_name(rc), rrdcalc_name(rc), rrdset_id(st), rrdhost_hostname(host)); - - // unlink it - - if(!having_ll_wrlock) - rw_spinlock_write_lock(&st->alerts.spinlock); - - DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(st->alerts.base, rc, prev, next); - - if(!having_ll_wrlock) - rw_spinlock_write_unlock(&st->alerts.spinlock); - - rc->rrdset = NULL; - - rrdvar_release_and_del(st->rrdvars, rc->rrdvar_local); - rc->rrdvar_local = NULL; - - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rc->rrdvar_family); - rc->rrdvar_family = NULL; - - rrdvar_release_and_del(host->rrdvars, rc->rrdvar_host_chart_id); - rc->rrdvar_host_chart_id = NULL; - - rrdvar_release_and_del(host->rrdvars, rc->rrdvar_host_chart_name); - rc->rrdvar_host_chart_name = NULL; - - // RRDCALC will remain in RRDHOST - // so that if the matching chart is found in the future - // it will be applied automatically -} - -static inline bool rrdcalc_check_if_it_matches_rrdset(RRDCALC *rc, RRDSET *st) { - if ( (rc->chart != st->id) - && (rc->chart != st->name)) - return false; - - if (rc->module_pattern && !simple_pattern_matches_string(rc->module_pattern, st->module_name)) - return false; - - if (rc->plugin_pattern && !simple_pattern_matches_string(rc->plugin_pattern, st->module_name)) - return false; - - if (st->rrdhost->rrdlabels && rc->host_labels_pattern && !rrdlabels_match_simple_pattern_parsed( - st->rrdhost->rrdlabels, rc->host_labels_pattern, '=', NULL)) - return false; - - if (st->rrdlabels && rc->chart_labels_pattern && !rrdlabels_match_simple_pattern_parsed( - st->rrdlabels, rc->chart_labels_pattern, '=', NULL)) - return false; - - return true; -} - -void rrdcalc_link_matching_alerts_to_rrdset(RRDSET *st) { - RRDHOST *host = st->rrdhost; - // netdata_log_debug(D_HEALTH, "find matching alarms for chart '%s'", st->id); - - RRDCALC *rc; - foreach_rrdcalc_in_rrdhost_read(host, rc) { - if(rc->rrdset) - continue; - - if(unlikely(rrdcalc_check_if_it_matches_rrdset(rc, st))) - rrdcalc_link_to_rrdset(st, rc); - } - foreach_rrdcalc_in_rrdhost_done(rc); -} - -static inline int rrdcalc_check_and_link_rrdset_callback(RRDSET *st, void *rrdcalc) { - RRDCALC *rc = rrdcalc; - - if(unlikely(rrdcalc_check_if_it_matches_rrdset(rc, st))) { - rrdcalc_link_to_rrdset(st, rc); - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// RRDCALC rrdhost index management - constructor - -struct rrdcalc_constructor { - RRDHOST *rrdhost; // the host we operate upon - RRDCALC *from_config; // points to the original RRDCALC, as loaded from the config - RRDCALCTEMPLATE *from_rrdcalctemplate; // the template this alert is generated from - RRDSET *rrdset; // when this comes from rrdcalctemplate, we have a matching rrdset - const char *overwrite_alert_name; // when we have a dimension foreach, the alert is renamed - const char *overwrite_dimensions; // when we have a dimension foreach, the dimensions filter is renamed - - enum { - RRDCALC_REACT_NONE, - RRDCALC_REACT_NEW, - } react_action; - - bool existing_from_template; -}; - -static void rrdcalc_rrdhost_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *constructor_data) { - RRDCALC *rc = rrdcalc; - struct rrdcalc_constructor *ctr = constructor_data; - RRDHOST *host = ctr->rrdhost; - - rc->key = string_strdupz(dictionary_acquired_item_name(item)); - - if(ctr->from_rrdcalctemplate) { - rc->run_flags |= RRDCALC_FLAG_FROM_TEMPLATE; - - RRDCALCTEMPLATE *rt = ctr->from_rrdcalctemplate; - RRDSET *st = ctr->rrdset; - - rc->next_event_id = 1; - rc->name = (ctr->overwrite_alert_name) ? string_strdupz(ctr->overwrite_alert_name) : string_dup(rt->name); - rc->chart = string_dup(st->id); - uuid_copy(rc->config_hash_id, rt->config_hash_id); - - rc->dimensions = (ctr->overwrite_dimensions) ? string_strdupz(ctr->overwrite_dimensions) : string_dup(rt->dimensions); - rc->foreach_dimension = NULL; - rc->foreach_dimension_pattern = NULL; - - rc->green = rt->green; - rc->red = rt->red; - rc->value = NAN; - rc->old_value = NAN; - - rc->delay_up_duration = rt->delay_up_duration; - rc->delay_down_duration = rt->delay_down_duration; - rc->delay_max_duration = rt->delay_max_duration; - rc->delay_multiplier = rt->delay_multiplier; - - rc->last_repeat = 0; - rc->times_repeat = 0; - rc->warn_repeat_every = rt->warn_repeat_every; - rc->crit_repeat_every = rt->crit_repeat_every; - - rc->group = rt->group; - rc->after = rt->after; - rc->before = rt->before; - rc->update_every = rt->update_every; - rc->options = rt->options; - - rc->exec = string_dup(rt->exec); - rc->recipient = string_dup(rt->recipient); - rc->source = string_dup(rt->source); - rc->units = string_dup(rt->units); - rc->info = string_dup(rt->info); - rc->original_info = string_dup(rt->info); - - if (!rt->summary) - rt->summary = string_dup(rc->name); - rc->summary = string_dup(rt->summary); - rc->original_summary = string_dup(rt->summary); - - rc->classification = string_dup(rt->classification); - rc->component = string_dup(rt->component); - rc->type = string_dup(rt->type); - - if(rt->calculation) { - rc->calculation = expression_parse(rt->calculation->source, NULL, NULL); - if(!rc->calculation) - netdata_log_error("Health alarm '%s.%s': failed to parse calculation expression '%s'", rrdset_id(st), rrdcalctemplate_name(rt), rt->calculation->source); - } - if(rt->warning) { - rc->warning = expression_parse(rt->warning->source, NULL, NULL); - if(!rc->warning) - netdata_log_error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", rrdset_id(st), rrdcalctemplate_name(rt), rt->warning->source); - } - if(rt->critical) { - rc->critical = expression_parse(rt->critical->source, NULL, NULL); - if(!rc->critical) - netdata_log_error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", rrdset_id(st), rrdcalctemplate_name(rt), rt->critical->source); - } - } - else if(ctr->from_config) { - // dictionary has already copied all the members values and pointers - // no need for additional work in this case - ; - } - - rc->id = rrdcalc_get_unique_id(host, rc->chart, rc->name, &rc->next_event_id, &rc->config_hash_id); - - if(rc->calculation) { - rc->calculation->status = &rc->status; - rc->calculation->myself = &rc->value; - rc->calculation->after = &rc->db_after; - rc->calculation->before = &rc->db_before; - rc->calculation->rrdcalc = rc; - } - - if(rc->warning) { - rc->warning->status = &rc->status; - rc->warning->myself = &rc->value; - rc->warning->after = &rc->db_after; - rc->warning->before = &rc->db_before; - rc->warning->rrdcalc = rc; - } - - if(rc->critical) { - rc->critical->status = &rc->status; - rc->critical->myself = &rc->value; - rc->critical->after = &rc->db_after; - rc->critical->before = &rc->db_before; - rc->critical->rrdcalc = rc; - } - - netdata_log_debug(D_HEALTH, "Health added alarm '%s.%s': exec '%s', recipient '%s', green " NETDATA_DOUBLE_FORMAT_AUTO - ", red " NETDATA_DOUBLE_FORMAT_AUTO - ", lookup: group %d, after %d, before %d, options %u, dimensions '%s', for each dimension '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f, warn_repeat_every %u, crit_repeat_every %u", - rrdcalc_chart_name(rc), - rrdcalc_name(rc), - (rc->exec)?rrdcalc_exec(rc):"DEFAULT", - (rc->recipient)?rrdcalc_recipient(rc):"DEFAULT", - rc->green, - rc->red, - (int)rc->group, - rc->after, - rc->before, - rc->options, - (rc->dimensions)?rrdcalc_dimensions(rc):"NONE", - (rc->foreach_dimension)?rrdcalc_foreachdim(rc):"NONE", - rc->update_every, - (rc->calculation)?rc->calculation->parsed_as:"NONE", - (rc->warning)?rc->warning->parsed_as:"NONE", - (rc->critical)?rc->critical->parsed_as:"NONE", - rrdcalc_source(rc), - rc->delay_up_duration, - rc->delay_down_duration, - rc->delay_max_duration, - rc->delay_multiplier, - rc->warn_repeat_every, - rc->crit_repeat_every - ); - - ctr->react_action = RRDCALC_REACT_NEW; -} - -static bool rrdcalc_rrdhost_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *rrdcalc_new __maybe_unused, void *constructor_data ) { - RRDCALC *rc = rrdcalc; - struct rrdcalc_constructor *ctr = constructor_data; - - if(rc->run_flags & RRDCALC_FLAG_FROM_TEMPLATE) - ctr->existing_from_template = true; - else - ctr->existing_from_template = false; - - ctr->react_action = RRDCALC_REACT_NONE; - - return false; -} - -static void rrdcalc_rrdhost_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *constructor_data) { - RRDCALC *rc = rrdcalc; - struct rrdcalc_constructor *ctr = constructor_data; - RRDHOST *host = ctr->rrdhost; - - if(ctr->react_action == RRDCALC_REACT_NEW) { - if(ctr->rrdset) - rrdcalc_link_to_rrdset(ctr->rrdset, rc); - - else if (ctr->from_rrdcalctemplate) - rrdcontext_foreach_instance_with_rrdset_in_context(host, string2str(ctr->from_rrdcalctemplate->context), rrdcalc_check_and_link_rrdset_callback, rc); - } -} - -// ---------------------------------------------------------------------------- -// RRDCALC rrdhost index management - destructor - -static void rrdcalc_free_internals(RRDCALC *rc) { - if(unlikely(!rc)) return; - - expression_free(rc->calculation); - expression_free(rc->warning); - expression_free(rc->critical); - - string_freez(rc->key); - string_freez(rc->name); - string_freez(rc->chart); - string_freez(rc->dimensions); - string_freez(rc->foreach_dimension); - string_freez(rc->exec); - string_freez(rc->recipient); - string_freez(rc->source); - string_freez(rc->units); - string_freez(rc->info); - string_freez(rc->original_info); - string_freez(rc->classification); - string_freez(rc->component); - string_freez(rc->type); - string_freez(rc->host_labels); - string_freez(rc->module_match); - string_freez(rc->plugin_match); - string_freez(rc->chart_labels); - - simple_pattern_free(rc->foreach_dimension_pattern); - simple_pattern_free(rc->host_labels_pattern); - simple_pattern_free(rc->module_pattern); - simple_pattern_free(rc->plugin_pattern); - simple_pattern_free(rc->chart_labels_pattern); -} - -static void rrdcalc_rrdhost_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *rrdhost __maybe_unused) { - RRDCALC *rc = rrdcalc; - //RRDHOST *host = rrdhost; - - if(unlikely(rc->rrdset)) - rrdcalc_unlink_from_rrdset(rc, false); - - // any destruction actions that require other locks - // have to be placed in rrdcalc_del(), because the object is actually locked for deletion - - rrdcalc_free_internals(rc); -} - -// ---------------------------------------------------------------------------- -// RRDCALC rrdhost index management - index API - -void rrdcalc_rrdhost_index_init(RRDHOST *host) { - if(!host->rrdcalc_root_index) { - host->rrdcalc_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, - &dictionary_stats_category_rrdhealth, sizeof(RRDCALC)); - - dictionary_register_insert_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_insert_callback, NULL); - dictionary_register_conflict_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_conflict_callback, NULL); - dictionary_register_react_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_react_callback, NULL); - dictionary_register_delete_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_delete_callback, host); - } -} - -void rrdcalc_rrdhost_index_destroy(RRDHOST *host) { - dictionary_destroy(host->rrdcalc_root_index); - host->rrdcalc_root_index = NULL; -} - -void rrdcalc_add_from_rrdcalctemplate(RRDHOST *host, RRDCALCTEMPLATE *rt, RRDSET *st, const char *overwrite_alert_name, const char *overwrite_dimensions) { - char key[RRDCALC_MAX_KEY_SIZE + 1]; - size_t key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, rrdset_id(st), - overwrite_alert_name?overwrite_alert_name:string2str(rt->name)); - - struct rrdcalc_constructor tmp = { - .rrdhost = host, - .from_config = NULL, - .from_rrdcalctemplate = rt, - .rrdset = st, - .overwrite_alert_name = overwrite_alert_name, - .overwrite_dimensions = overwrite_dimensions, - .react_action = RRDCALC_REACT_NONE, - .existing_from_template = false, - }; - - dictionary_set_advanced(host->rrdcalc_root_index, key, (ssize_t)(key_len + 1), NULL, sizeof(RRDCALC), &tmp); - if(tmp.react_action != RRDCALC_REACT_NEW && tmp.existing_from_template == false) - netdata_log_error("RRDCALC: from template '%s' on chart '%s' with key '%s', failed to be added to host '%s'. It is manually configured.", - string2str(rt->name), rrdset_id(st), key, rrdhost_hostname(host)); -} - -int rrdcalc_add_from_config(RRDHOST *host, RRDCALC *rc) { - if(!rc->chart) { - netdata_log_error("Health configuration for alarm '%s' does not have a chart", rrdcalc_name(rc)); - return 0; - } - - if(!rc->update_every) { - netdata_log_error("Health configuration for alarm '%s.%s' has no frequency (parameter 'every'). Ignoring it.", rrdcalc_chart_name(rc), rrdcalc_name(rc)); - return 0; - } - - if(!RRDCALC_HAS_DB_LOOKUP(rc) && !rc->calculation && !rc->warning && !rc->critical) { - netdata_log_error("Health configuration for alarm '%s.%s' is useless (no db lookup, no calculation, no warning and no critical expressions)", rrdcalc_chart_name(rc), rrdcalc_name(rc)); - return 0; - } - - char key[RRDCALC_MAX_KEY_SIZE + 1]; - size_t key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, string2str(rc->chart), string2str(rc->name)); - - struct rrdcalc_constructor tmp = { - .rrdhost = host, - .from_config = rc, - .from_rrdcalctemplate = NULL, - .rrdset = NULL, - .react_action = RRDCALC_REACT_NONE, - }; - - int ret = 1; - RRDCALC *t = dictionary_set_advanced(host->rrdcalc_root_index, key, (ssize_t)(key_len + 1), rc, sizeof(RRDCALC), &tmp); - if(tmp.react_action == RRDCALC_REACT_NEW) { - // we copied rc into the dictionary, so we have to free the container here - freez(rc); - rc = t; - - // since we loaded this config from configuration, we need to check if we can link it to alarms - RRDSET *st; - rrdset_foreach_read(st, host) { - if (unlikely(rrdcalc_check_and_link_rrdset_callback(st, rc) == -1)) - break; - } - rrdset_foreach_done(st); - } - else { - netdata_log_error( - "RRDCALC: from config '%s' on chart '%s' failed to be added to host '%s'. It already exists.", - string2str(rc->name), - string2str(rc->chart), - rrdhost_hostname(host)); - - ret = 0; - - // free all of it, internals and the container - rrdcalc_free_unused_rrdcalc_loaded_from_config(rc); - } - - return ret; -} - -static void rrdcalc_unlink_and_delete(RRDHOST *host, RRDCALC *rc, bool having_ll_wrlock) { - if(rc->rrdset) - rrdcalc_unlink_from_rrdset(rc, having_ll_wrlock); - - dictionary_del_advanced(host->rrdcalc_root_index, string2str(rc->key), (ssize_t)string_strlen(rc->key) + 1); -} - - -// ---------------------------------------------------------------------------- -// RRDCALC cleanup API functions - -void rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(RRDHOST *host) { - RRDCALC *rc; - foreach_rrdcalc_in_rrdhost_reentrant(host, rc) { - if (!rc->host_labels) - continue; - - if(!rrdlabels_match_simple_pattern_parsed(host->rrdlabels, rc->host_labels_pattern, '=', NULL)) { - nd_log(NDLS_DAEMON, NDLP_DEBUG, - "Health configuration for alarm '%s' cannot be applied, " - "because the host %s does not have the label(s) '%s'", - rrdcalc_name(rc), rrdhost_hostname(host), rrdcalc_host_labels(rc)); - - rrdcalc_unlink_and_delete(host, rc, false); - } - } - foreach_rrdcalc_in_rrdhost_done(rc); -} - -void rrdcalc_delete_alerts_not_matching_host_labels_from_all_hosts() { - RRDHOST *host; - dfe_start_reentrant(rrdhost_root_index, host) { - if (unlikely(!host->health.health_enabled)) - continue; - - if (host->rrdlabels) - rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(host); - } - dfe_done(host); -} - -void rrdcalc_unlink_all_rrdset_alerts(RRDSET *st) { - RRDCALC *rc, *last = NULL; - rw_spinlock_write_lock(&st->alerts.spinlock); - while((rc = st->alerts.base)) { - if(last == rc) { - netdata_log_error("RRDCALC: malformed list of alerts linked to chart - cannot cleanup - giving up."); - break; - } - last = rc; - - if(rc->run_flags & RRDCALC_FLAG_FROM_TEMPLATE) { - // if the alert comes from a template we can just delete it - rrdcalc_unlink_and_delete(st->rrdhost, rc, true); - } - else { - // this is a configuration for a specific chart - // it should stay in the list - rrdcalc_unlink_from_rrdset(rc, true); - } - - } - rw_spinlock_write_unlock(&st->alerts.spinlock); -} - -void rrdcalc_delete_all(RRDHOST *host) { - dictionary_flush(host->rrdcalc_root_index); -} - -void rrdcalc_free_unused_rrdcalc_loaded_from_config(RRDCALC *rc) { - if(rc->rrdset) - rrdcalc_unlink_from_rrdset(rc, false); - - rrdcalc_free_internals(rc); - freez(rc); -} diff --git a/database/rrdcalc.h b/database/rrdcalc.h deleted file mode 100644 index 71f43633c..000000000 --- a/database/rrdcalc.h +++ /dev/null @@ -1,272 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rrd.h" - -#ifndef NETDATA_RRDCALC_H -#define NETDATA_RRDCALC_H 1 - -// calculated variables (defined in health configuration) -// These aggregate time-series data at fixed intervals -// (defined in their update_every member below) -// They increase the overhead of netdata. -// -// These calculations are stored under RRDHOST. -// Then are also linked to RRDSET (of course only when a -// matching chart is found). - -typedef enum { - RRDCALC_FLAG_DB_ERROR = (1 << 0), - RRDCALC_FLAG_DB_NAN = (1 << 1), - // RRDCALC_FLAG_DB_STALE = (1 << 2), - RRDCALC_FLAG_CALC_ERROR = (1 << 3), - RRDCALC_FLAG_WARN_ERROR = (1 << 4), - RRDCALC_FLAG_CRIT_ERROR = (1 << 5), - RRDCALC_FLAG_RUNNABLE = (1 << 6), - RRDCALC_FLAG_DISABLED = (1 << 7), - RRDCALC_FLAG_SILENCED = (1 << 8), - RRDCALC_FLAG_RUN_ONCE = (1 << 9), - RRDCALC_FLAG_FROM_TEMPLATE = (1 << 10), // the rrdcalc has been created from a template -} RRDCALC_FLAGS; - -void rrdcalc_flags_to_json_array(BUFFER *wb, const char *key, RRDCALC_FLAGS flags); - -typedef enum { - // This list uses several other options from RRDR_OPTIONS for db lookups. - // To add an item here, you need to reserve a bit in RRDR_OPTIONS. - RRDCALC_OPTION_NO_CLEAR_NOTIFICATION = RRDR_OPTION_HEALTH_RSRVD1, -} RRDCALC_OPTIONS; - -#define RRDCALC_ALL_OPTIONS_EXCLUDING_THE_RRDR_ONES (RRDCALC_OPTION_NO_CLEAR_NOTIFICATION) - -struct rrdcalc { - STRING *key; // the unique key in the host's rrdcalc_root_index - - uint32_t id; // the unique id of this alarm - uint32_t next_event_id; // the next event id that will be used for this alarm - - uuid_t config_hash_id; // a predictable hash_id based on specific alert configuration - - STRING *name; // the name of this alarm - STRING *chart; // the chart id this should be linked to - - STRING *exec; // the command to execute when this alarm switches state - STRING *recipient; // the recipient of the alarm (the first parameter to exec) - - STRING *classification; // the class that this alarm belongs - STRING *component; // the component that this alarm refers to - STRING *type; // type of the alarm - - STRING *plugin_match; // the plugin name that should be linked to - SIMPLE_PATTERN *plugin_pattern; - - STRING *module_match; // the module name that should be linked to - SIMPLE_PATTERN *module_pattern; - - STRING *source; // the source of this alarm - STRING *units; // the units of the alarm - STRING *summary; // a short alert summary - STRING *original_summary; // the original summary field before any variable replacement - STRING *original_info; // the original info field before any variable replacement - STRING *info; // a description of the alarm - - int update_every; // update frequency for the alarm - - // the red and green threshold of this alarm (to be set to the chart) - NETDATA_DOUBLE green; - NETDATA_DOUBLE red; - - // ------------------------------------------------------------------------ - // database lookup settings - - STRING *dimensions; // the chart dimensions - STRING *foreach_dimension; // the group of dimensions that the `foreach` will be applied. - SIMPLE_PATTERN *foreach_dimension_pattern; // used if and only if there is a simple pattern for the chart. - RRDR_TIME_GROUPING group; // grouping method: average, max, etc. - int before; // ending point in time-series - int after; // starting point in time-series - RRDCALC_OPTIONS options; // configuration options - - // ------------------------------------------------------------------------ - // expressions related to the alarm - - EVAL_EXPRESSION *calculation; // expression to calculate the value of the alarm - EVAL_EXPRESSION *warning; // expression to check the warning condition - EVAL_EXPRESSION *critical; // expression to check the critical condition - - // ------------------------------------------------------------------------ - // notification delay settings - - int delay_up_duration; // duration to delay notifications when alarm raises - int delay_down_duration; // duration to delay notifications when alarm lowers - int delay_max_duration; // the absolute max delay to apply to this alarm - float delay_multiplier; // multiplier for all delays when alarms switch status - // while now < delay_up_to - - // ------------------------------------------------------------------------ - // notification repeat settings - - uint32_t warn_repeat_every; // interval between repeating warning notifications - uint32_t crit_repeat_every; // interval between repeating critical notifications - - // ------------------------------------------------------------------------ - // Labels settings - STRING *host_labels; // the label read from an alarm file - SIMPLE_PATTERN *host_labels_pattern; // the simple pattern of labels - - STRING *chart_labels; // the chart label read from an alarm file - SIMPLE_PATTERN *chart_labels_pattern; // the simple pattern of chart labels - - // ------------------------------------------------------------------------ - // runtime information - - RRDCALC_STATUS old_status; // the old status of the alarm - RRDCALC_STATUS status; // the current status of the alarm - - NETDATA_DOUBLE value; // the current value of the alarm - NETDATA_DOUBLE old_value; // the previous value of the alarm - NETDATA_DOUBLE last_status_change_value; // the value at the last status change - - RRDCALC_FLAGS run_flags; // check RRDCALC_FLAG_* - - time_t last_updated; // the last update timestamp of the alarm - time_t next_update; // the next update timestamp of the alarm - time_t last_status_change; // the timestamp of the last time this alarm changed status - time_t last_repeat; // the last time the alarm got repeated - uint32_t times_repeat; // number of times the alarm got repeated - - time_t db_after; // the first timestamp evaluated by the db lookup - time_t db_before; // the last timestamp evaluated by the db lookup - - time_t delay_up_to_timestamp; // the timestamp up to which we should delay notifications - int delay_up_current; // the current up notification delay duration - int delay_down_current; // the current down notification delay duration - int delay_last; // the last delay we used - ALARM_ENTRY *ae; // last alarm entry - - // ------------------------------------------------------------------------ - // variables this alarm exposes to the rest of the alarms - - const RRDVAR_ACQUIRED *rrdvar_local; - const RRDVAR_ACQUIRED *rrdvar_family; - const RRDVAR_ACQUIRED *rrdvar_host_chart_id; - const RRDVAR_ACQUIRED *rrdvar_host_chart_name; - - // ------------------------------------------------------------------------ - // the chart this alarm it is linked to - - size_t labels_version; - struct rrdset *rrdset; - - struct rrdcalc *next; - struct rrdcalc *prev; -}; - -#define rrdcalc_name(rc) string2str((rc)->name) -#define rrdcalc_chart_name(rc) string2str((rc)->chart) -#define rrdcalc_exec(rc) string2str((rc)->exec) -#define rrdcalc_recipient(rc) string2str((rc)->recipient) -#define rrdcalc_classification(rc) string2str((rc)->classification) -#define rrdcalc_component(rc) string2str((rc)->component) -#define rrdcalc_type(rc) string2str((rc)->type) -#define rrdcalc_plugin_match(rc) string2str((rc)->plugin_match) -#define rrdcalc_module_match(rc) string2str((rc)->module_match) -#define rrdcalc_source(rc) string2str((rc)->source) -#define rrdcalc_units(rc) string2str((rc)->units) -#define rrdcalc_original_summary(rc) string2str((rc)->original_summary) -#define rrdcalc_summary(rc) string2str((rc)->summary) -#define rrdcalc_original_info(rc) string2str((rc)->original_info) -#define rrdcalc_info(rc) string2str((rc)->info) -#define rrdcalc_dimensions(rc) string2str((rc)->dimensions) -#define rrdcalc_foreachdim(rc) string2str((rc)->foreach_dimension) -#define rrdcalc_host_labels(rc) string2str((rc)->host_labels) -#define rrdcalc_chart_labels(rc) string2str((rc)->chart_labels) - -#define foreach_rrdcalc_in_rrdhost_read(host, rc) \ - dfe_start_read((host)->rrdcalc_root_index, rc) \ - -#define foreach_rrdcalc_in_rrdhost_reentrant(host, rc) \ - dfe_start_reentrant((host)->rrdcalc_root_index, rc) - -#define foreach_rrdcalc_in_rrdhost_done(rc) \ - dfe_done(rc) - -struct alert_config { - STRING *alarm; - STRING *template_key; - STRING *os; - STRING *host; - STRING *on; - STRING *plugin; - STRING *module; - STRING *charts; - STRING *lookup; - STRING *calc; - STRING *warn; - STRING *crit; - STRING *every; - STRING *green; - STRING *red; - STRING *exec; - STRING *to; - STRING *units; - STRING *summary; - STRING *info; - STRING *classification; - STRING *component; - STRING *type; - STRING *delay; - STRING *options; - STRING *repeat; - STRING *host_labels; - STRING *chart_labels; - STRING *source; - - STRING *p_db_lookup_dimensions; - STRING *p_db_lookup_method; - - uint32_t p_db_lookup_options; - int32_t p_db_lookup_after; - int32_t p_db_lookup_before; - int32_t p_update_every; -}; - -#define RRDCALC_HAS_DB_LOOKUP(rc) ((rc)->after) - -void rrdcalc_update_info_using_rrdset_labels(RRDCALC *rc); - -void rrdcalc_link_matching_alerts_to_rrdset(RRDSET *st); - -const RRDCALC_ACQUIRED *rrdcalc_from_rrdset_get(RRDSET *st, const char *alert_name); -void rrdcalc_from_rrdset_release(RRDSET *st, const RRDCALC_ACQUIRED *rca); -RRDCALC *rrdcalc_acquired_to_rrdcalc(const RRDCALC_ACQUIRED *rca); - -const char *rrdcalc_status2string(RRDCALC_STATUS status); - -void rrdcalc_free_unused_rrdcalc_loaded_from_config(RRDCALC *rc); - -uint32_t rrdcalc_get_unique_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id); -void rrdcalc_add_from_rrdcalctemplate(RRDHOST *host, RRDCALCTEMPLATE *rt, RRDSET *st, const char *overwrite_alert_name, const char *overwrite_dimensions); -int rrdcalc_add_from_config(RRDHOST *host, RRDCALC *rc); - -void rrdcalc_delete_alerts_not_matching_host_labels_from_all_hosts(); -void rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(RRDHOST *host); - -static inline int rrdcalc_isrepeating(RRDCALC *rc) { - if (unlikely(rc->warn_repeat_every > 0 || rc->crit_repeat_every > 0)) { - return 1; - } - return 0; -} - -void rrdcalc_unlink_all_rrdset_alerts(RRDSET *st); -void rrdcalc_delete_all(RRDHOST *host); - -void rrdcalc_rrdhost_index_init(RRDHOST *host); -void rrdcalc_rrdhost_index_destroy(RRDHOST *host); - -#define RRDCALC_VAR_MAX 100 -#define RRDCALC_VAR_FAMILY "${family}" -#define RRDCALC_VAR_LABEL "${label:" -#define RRDCALC_VAR_LABEL_LEN (sizeof(RRDCALC_VAR_LABEL)-1) - -#endif //NETDATA_RRDCALC_H diff --git a/database/rrdcalctemplate.c b/database/rrdcalctemplate.c deleted file mode 100644 index f0e5da80b..000000000 --- a/database/rrdcalctemplate.c +++ /dev/null @@ -1,242 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rrd.h" - -// ---------------------------------------------------------------------------- -// RRDCALCTEMPLATE management -/** - * RRDCALC TEMPLATE LINK MATCHING - * - * @param rt is the template used to create the chart. - * @param st is the chart where the alarm will be attached. - */ - -static char *rrdcalc_alert_name_with_dimension(const char *name, size_t namelen, const char *dim, size_t dimlen) { - char *newname,*move; - - newname = mallocz(namelen + dimlen + 2); - move = newname; - memcpy(move, name, namelen); - move += namelen; - - *move++ = '_'; - memcpy(move, dim, dimlen); - move += dimlen; - *move = '\0'; - - return newname; -} - -bool rrdcalctemplate_check_rrdset_conditions(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host) { - if(rt->context != st->context) - return false; - - if(rt->foreach_dimension_pattern && !rrdset_number_of_dimensions(st)) - return false; - - if (rt->charts_pattern && !simple_pattern_matches_string(rt->charts_pattern, st->name) && !simple_pattern_matches_string(rt->charts_pattern, st->id)) - return false; - - if (rt->module_pattern && !simple_pattern_matches_string(rt->module_pattern, st->module_name)) - return false; - - if (rt->plugin_pattern && !simple_pattern_matches_string(rt->plugin_pattern, st->plugin_name)) - return false; - - if(host->rrdlabels && rt->host_labels_pattern && !rrdlabels_match_simple_pattern_parsed(host->rrdlabels, - rt->host_labels_pattern, - '=', NULL)) - return false; - - if(st->rrdlabels && rt->chart_labels_pattern && !rrdlabels_match_simple_pattern_parsed(st->rrdlabels, - rt->chart_labels_pattern, - '=', NULL)) - return false; - - return true; -} - -void rrdcalctemplate_check_rrddim_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDDIM *rd, RRDHOST *host) { - if (simple_pattern_matches_string(rt->foreach_dimension_pattern, rd->id) || - simple_pattern_matches_string(rt->foreach_dimension_pattern, rd->name)) { - char *overwrite_alert_name = rrdcalc_alert_name_with_dimension( - rrdcalctemplate_name(rt), string_strlen(rt->name), rrddim_name(rd), string_strlen(rd->name)); - rrdcalc_add_from_rrdcalctemplate(host, rt, st, overwrite_alert_name, rrddim_name(rd)); - freez(overwrite_alert_name); - } -} - -void rrdcalctemplate_check_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host) { - if(!rrdcalctemplate_check_rrdset_conditions(rt, st, host)) - return; - - if(!rt->foreach_dimension_pattern) { - rrdcalc_add_from_rrdcalctemplate(host, rt, st, NULL, NULL); - return; - } - - RRDDIM *rd; - rrddim_foreach_read(rd, st) { - rrdcalctemplate_check_rrddim_conditions_and_link(rt, st, rd, host); - } - rrddim_foreach_done(rd); -} - -void rrdcalctemplate_link_matching_templates_to_rrdset(RRDSET *st) { - RRDHOST *host = st->rrdhost; - - RRDCALCTEMPLATE *rt; - foreach_rrdcalctemplate_read(host, rt) { - rrdcalctemplate_check_conditions_and_link(rt, st, host); - } - foreach_rrdcalctemplate_done(rt); -} - -static void rrdcalctemplate_free_internals(RRDCALCTEMPLATE *rt) { - expression_free(rt->calculation); - expression_free(rt->warning); - expression_free(rt->critical); - - string_freez(rt->plugin_match); - simple_pattern_free(rt->plugin_pattern); - - string_freez(rt->module_match); - simple_pattern_free(rt->module_pattern); - - string_freez(rt->charts_match); - simple_pattern_free(rt->charts_pattern); - - string_freez(rt->name); - string_freez(rt->exec); - string_freez(rt->recipient); - string_freez(rt->classification); - string_freez(rt->component); - string_freez(rt->type); - string_freez(rt->context); - string_freez(rt->source); - string_freez(rt->units); - string_freez(rt->info); - string_freez(rt->dimensions); - string_freez(rt->foreach_dimension); - string_freez(rt->host_labels); - string_freez(rt->chart_labels); - simple_pattern_free(rt->foreach_dimension_pattern); - simple_pattern_free(rt->host_labels_pattern); - simple_pattern_free(rt->chart_labels_pattern); -} - -void rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(RRDCALCTEMPLATE *rt) { - if(unlikely(!rt)) return; - - rrdcalctemplate_free_internals(rt); - freez(rt); -} -static void rrdcalctemplate_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalctemplate, void *added_bool) { - RRDCALCTEMPLATE *rt = rrdcalctemplate; (void)rt; - - bool *added = added_bool; - *added = true; - - netdata_log_debug(D_HEALTH, "Health configuration adding template '%s'" - ": context '%s'" - ", exec '%s'" - ", recipient '%s'" - ", green " NETDATA_DOUBLE_FORMAT_AUTO - ", red " NETDATA_DOUBLE_FORMAT_AUTO - ", lookup: group %d" - ", after %d" - ", before %d" - ", options %u" - ", dimensions '%s'" - ", for each dimension '%s'" - ", update every %d" - ", calculation '%s'" - ", warning '%s'" - ", critical '%s'" - ", source '%s'" - ", delay up %d" - ", delay down %d" - ", delay max %d" - ", delay_multiplier %f" - ", warn_repeat_every %u" - ", crit_repeat_every %u", - rrdcalctemplate_name(rt), - (rt->context)?string2str(rt->context):"NONE", - (rt->exec)?rrdcalctemplate_exec(rt):"DEFAULT", - (rt->recipient)?rrdcalctemplate_recipient(rt):"DEFAULT", - rt->green, - rt->red, - (int)rt->group, - rt->after, - rt->before, - rt->options, - (rt->dimensions)?rrdcalctemplate_dimensions(rt):"NONE", - (rt->foreach_dimension)?rrdcalctemplate_foreachdim(rt):"NONE", - rt->update_every, - (rt->calculation)?rt->calculation->parsed_as:"NONE", - (rt->warning)?rt->warning->parsed_as:"NONE", - (rt->critical)?rt->critical->parsed_as:"NONE", - rrdcalctemplate_source(rt), - rt->delay_up_duration, - rt->delay_down_duration, - rt->delay_max_duration, - rt->delay_multiplier, - rt->warn_repeat_every, - rt->crit_repeat_every - ); -} - -static void rrdcalctemplate_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalctemplate, void *rrdhost __maybe_unused) { - RRDCALCTEMPLATE *rt = rrdcalctemplate; - rrdcalctemplate_free_internals(rt); -} - -void rrdcalctemplate_index_init(RRDHOST *host) { - if(!host->rrdcalctemplate_root_index) { - host->rrdcalctemplate_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, - &dictionary_stats_category_rrdhealth, sizeof(RRDCALCTEMPLATE)); - - dictionary_register_insert_callback(host->rrdcalctemplate_root_index, rrdcalctemplate_insert_callback, NULL); - dictionary_register_delete_callback(host->rrdcalctemplate_root_index, rrdcalctemplate_delete_callback, host); - } -} - -void rrdcalctemplate_index_destroy(RRDHOST *host) { - dictionary_destroy(host->rrdcalctemplate_root_index); - host->rrdcalctemplate_root_index = NULL; -} - -inline void rrdcalctemplate_delete_all(RRDHOST *host) { - dictionary_flush(host->rrdcalctemplate_root_index); -} - -#define RRDCALCTEMPLATE_MAX_KEY_SIZE 1024 -void rrdcalctemplate_add_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt) { - if(unlikely(!rt->context)) { - netdata_log_error("Health configuration for template '%s' does not have a context", rrdcalctemplate_name(rt)); - return; - } - - if(unlikely(!rt->update_every)) { - netdata_log_error("Health configuration for template '%s' has no frequency (parameter 'every'). Ignoring it.", rrdcalctemplate_name(rt)); - return; - } - - if(unlikely(!RRDCALCTEMPLATE_HAS_DB_LOOKUP(rt) && !rt->calculation && !rt->warning && !rt->critical)) { - netdata_log_error("Health configuration for template '%s' is useless (no calculation, no warning and no critical evaluation)", rrdcalctemplate_name(rt)); - return; - } - - char key[RRDCALCTEMPLATE_MAX_KEY_SIZE + 1]; - size_t key_len = snprintfz(key, RRDCALCTEMPLATE_MAX_KEY_SIZE, "%s", rrdcalctemplate_name(rt)); - - bool added = false; - dictionary_set_advanced(host->rrdcalctemplate_root_index, key, (ssize_t)(key_len + 1), rt, sizeof(*rt), &added); - - if(added) - freez(rt); - else { - netdata_log_info("Health configuration template '%s' already exists for host '%s'.", rrdcalctemplate_name(rt), rrdhost_hostname(host)); - rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(rt); - } -} diff --git a/database/rrdcalctemplate.h b/database/rrdcalctemplate.h deleted file mode 100644 index ca2c43656..000000000 --- a/database/rrdcalctemplate.h +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_RRDCALCTEMPLATE_H -#define NETDATA_RRDCALCTEMPLATE_H 1 - -#include "rrd.h" - -// RRDCALCTEMPLATE -// these are to be applied to charts found dynamically -// based on their context. -struct rrdcalctemplate { - uuid_t config_hash_id; - - STRING *name; - - STRING *exec; - STRING *recipient; - - STRING *classification; - STRING *component; - STRING *type; - - STRING *context; - - STRING *plugin_match; - SIMPLE_PATTERN *plugin_pattern; - - STRING *module_match; - SIMPLE_PATTERN *module_pattern; - - STRING *charts_match; - SIMPLE_PATTERN *charts_pattern; - - STRING *source; // the source of this alarm - STRING *units; // the units of the alarm - STRING *summary; // a short summary of the alarm - STRING *info; // a description of the alarm - - int update_every; // update frequency for the alarm - - // the red and green threshold of this alarm (to be set to the chart) - NETDATA_DOUBLE green; - NETDATA_DOUBLE red; - - // ------------------------------------------------------------------------ - // database lookup settings - - STRING *dimensions; // the chart dimensions - STRING *foreach_dimension; // the group of dimensions that the lookup will be applied. - SIMPLE_PATTERN *foreach_dimension_pattern; // used if and only if there is a simple pattern for the chart. - RRDR_TIME_GROUPING group; // grouping method: average, max, etc. - int before; // ending point in time-series - int after; // starting point in time-series - RRDCALC_OPTIONS options; // configuration options - - // ------------------------------------------------------------------------ - // notification delay settings - - int delay_up_duration; // duration to delay notifications when alarm raises - int delay_down_duration; // duration to delay notifications when alarm lowers - int delay_max_duration; // the absolute max delay to apply to this alarm - float delay_multiplier; // multiplier for all delays when alarms switch status - - // ------------------------------------------------------------------------ - // notification repeat settings - - uint32_t warn_repeat_every; // interval between repeating warning notifications - uint32_t crit_repeat_every; // interval between repeating critical notifications - - // ------------------------------------------------------------------------ - // Labels settings - STRING *host_labels; // the label read from an alarm file - SIMPLE_PATTERN *host_labels_pattern; // the simple pattern of labels - - STRING *chart_labels; // the chart label read from an alarm file - SIMPLE_PATTERN *chart_labels_pattern; // the simple pattern of chart labels - - // ------------------------------------------------------------------------ - // expressions related to the alarm - - EVAL_EXPRESSION *calculation; - EVAL_EXPRESSION *warning; - EVAL_EXPRESSION *critical; - - struct rrdcalctemplate *next; - struct rrdcalctemplate *prev; -}; - -#define foreach_rrdcalctemplate_read(host, rt) \ - dfe_start_read((host)->rrdcalctemplate_root_index, rt) - -#define foreach_rrdcalctemplate_done(rt) \ - dfe_done(rt) - -#define rrdcalctemplate_name(rt) string2str((rt)->name) -#define rrdcalctemplate_exec(rt) string2str((rt)->exec) -#define rrdcalctemplate_recipient(rt) string2str((rt)->recipient) -#define rrdcalctemplate_classification(rt) string2str((rt)->classification) -#define rrdcalctemplate_component(rt) string2str((rt)->component) -#define rrdcalctemplate_type(rt) string2str((rt)->type) -#define rrdcalctemplate_plugin_match(rt) string2str((rt)->plugin_match) -#define rrdcalctemplate_module_match(rt) string2str((rt)->module_match) -#define rrdcalctemplate_charts_match(rt) string2str((rt)->charts_match) -#define rrdcalctemplate_units(rt) string2str((rt)->units) -#define rrdcalctemplate_summary(rt) string2str((rt)->summary) -#define rrdcalctemplate_info(rt) string2str((rt)->info) -#define rrdcalctemplate_source(rt) string2str((rt)->source) -#define rrdcalctemplate_dimensions(rt) string2str((rt)->dimensions) -#define rrdcalctemplate_foreachdim(rt) string2str((rt)->foreach_dimension) -#define rrdcalctemplate_host_labels(rt) string2str((rt)->host_labels) -#define rrdcalctemplate_chart_labels(rt) string2str((rt)->chart_labels) - -#define RRDCALCTEMPLATE_HAS_DB_LOOKUP(rt) ((rt)->after) - -void rrdcalctemplate_link_matching_templates_to_rrdset(RRDSET *st); - -void rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(RRDCALCTEMPLATE *rt); -void rrdcalctemplate_delete_all(RRDHOST *host); -void rrdcalctemplate_add_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt); - -void rrdcalctemplate_check_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host); - -bool rrdcalctemplate_check_rrdset_conditions(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host); -void rrdcalctemplate_check_rrddim_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDDIM *rd, RRDHOST *host); - - -void rrdcalctemplate_index_init(RRDHOST *host); -void rrdcalctemplate_index_destroy(RRDHOST *host); - -#endif //NETDATA_RRDCALCTEMPLATE_H diff --git a/database/rrddim.c b/database/rrddim.c deleted file mode 100644 index 46226a548..000000000 --- a/database/rrddim.c +++ /dev/null @@ -1,768 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#define NETDATA_RRD_INTERNALS -#include "rrd.h" -#include "storage_engine.h" - -void rrddim_metadata_updated(RRDDIM *rd) { - rrdcontext_updated_rrddim(rd); - rrdset_metadata_updated(rd->rrdset); -} - -// ---------------------------------------------------------------------------- -// RRDDIM index - -struct rrddim_constructor { - RRDSET *st; - const char *id; - const char *name; - collected_number multiplier; - collected_number divisor; - RRD_ALGORITHM algorithm; - RRD_MEMORY_MODE memory_mode; - - enum { - RRDDIM_REACT_NONE = 0, - RRDDIM_REACT_NEW = (1 << 0), - RRDDIM_REACT_UPDATED = (1 << 2), - } react_action; - -}; - -// isolated call to appear -// separate in statistics -static void *rrddim_alloc_db(size_t entries) { - return callocz(entries, sizeof(storage_number)); -} - -static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *constructor_data) { - struct rrddim_constructor *ctr = constructor_data; - RRDDIM *rd = rrddim; - RRDSET *st = ctr->st; - RRDHOST *host = st->rrdhost; - - rd->flags = RRDDIM_FLAG_NONE; - - rd->id = string_strdupz(ctr->id); - rd->name = (ctr->name && *ctr->name)?rrd_string_strdupz(ctr->name):string_dup(rd->id); - - rd->algorithm = ctr->algorithm; - rd->multiplier = ctr->multiplier; - rd->divisor = ctr->divisor; - if(!rd->divisor) rd->divisor = 1; - - rd->rrdset = st; - - rd->rrdpush.sender.dim_slot = __atomic_add_fetch(&st->rrdpush.sender.dim_last_slot_used, 1, __ATOMIC_RELAXED); - - if(rrdset_flag_check(st, RRDSET_FLAG_STORE_FIRST)) - rd->collector.counter = 1; - - if(ctr->memory_mode == RRD_MEMORY_MODE_MAP || ctr->memory_mode == RRD_MEMORY_MODE_SAVE) { - if(!rrddim_memory_load_or_create_map_save(st, rd, ctr->memory_mode)) { - netdata_log_info("Failed to use memory mode %s for chart '%s', dimension '%s', falling back to ram", (ctr->memory_mode == RRD_MEMORY_MODE_MAP)?"map":"save", rrdset_name(st), rrddim_name(rd)); - ctr->memory_mode = RRD_MEMORY_MODE_RAM; - } - } - - if(ctr->memory_mode == RRD_MEMORY_MODE_RAM) { - size_t entries = st->db.entries; - if(!entries) entries = 5; - - rd->db.data = netdata_mmap(NULL, entries * sizeof(storage_number), MAP_PRIVATE, 1, false, NULL); - if(!rd->db.data) { - netdata_log_info("Failed to use memory mode ram for chart '%s', dimension '%s', falling back to alloc", rrdset_name(st), rrddim_name(rd)); - ctr->memory_mode = RRD_MEMORY_MODE_ALLOC; - } - else { - rd->db.memsize = entries * sizeof(storage_number); - __atomic_add_fetch(&rrddim_db_memory_size, rd->db.memsize, __ATOMIC_RELAXED); - } - } - - if(ctr->memory_mode == RRD_MEMORY_MODE_ALLOC || ctr->memory_mode == RRD_MEMORY_MODE_NONE) { - size_t entries = st->db.entries; - if(entries < 5) entries = 5; - - rd->db.data = rrddim_alloc_db(entries); - rd->db.memsize = entries * sizeof(storage_number); - __atomic_add_fetch(&rrddim_db_memory_size, rd->db.memsize, __ATOMIC_RELAXED); - } - - rd->rrd_memory_mode = ctr->memory_mode; - - if (unlikely(rrdcontext_find_dimension_uuid(st, rrddim_id(rd), &(rd->metric_uuid)))) - uuid_generate(rd->metric_uuid); - - // initialize the db tiers - { - size_t initialized = 0; - for(size_t tier = 0; tier < storage_tiers ; tier++) { - STORAGE_ENGINE *eng = host->db[tier].eng; - rd->tiers[tier].backend = eng->backend; - rd->tiers[tier].tier_grouping = host->db[tier].tier_grouping; - rd->tiers[tier].db_metric_handle = eng->api.metric_get_or_create(rd, host->db[tier].instance); - storage_point_unset(rd->tiers[tier].virtual_point); - initialized++; - - // internal_error(true, "TIER GROUPING of chart '%s', dimension '%s' for tier %d is set to %d", rd->rrdset->name, rd->name, tier, rd->tiers[tier]->tier_grouping); - } - - if(!initialized) - netdata_log_error("Failed to initialize all db tiers for chart '%s', dimension '%s", rrdset_name(st), rrddim_name(rd)); - - if(!rd->tiers[0].db_metric_handle) - netdata_log_error("Failed to initialize the first db tier for chart '%s', dimension '%s", rrdset_name(st), rrddim_name(rd)); - } - - // initialize data collection for all tiers - { - size_t initialized = 0; - for (size_t tier = 0; tier < storage_tiers; tier++) { - if (rd->tiers[tier].db_metric_handle) { - rd->tiers[tier].db_collection_handle = - storage_metric_store_init(rd->tiers[tier].backend, rd->tiers[tier].db_metric_handle, st->rrdhost->db[tier].tier_grouping * st->update_every, rd->rrdset->storage_metrics_groups[tier]); - initialized++; - } - } - - if(!initialized) - netdata_log_error("Failed to initialize data collection for all db tiers for chart '%s', dimension '%s", rrdset_name(st), rrddim_name(rd)); - } - - if(rrdset_number_of_dimensions(st) != 0) { - RRDDIM *td; - dfe_start_write(st->rrddim_root_index, td) { - if(td) break; - } - dfe_done(td); - - if(td && (td->algorithm != rd->algorithm || ABS(td->multiplier) != ABS(rd->multiplier) || ABS(td->divisor) != ABS(rd->divisor))) { - if(!rrdset_flag_check(st, RRDSET_FLAG_HETEROGENEOUS)) { -#ifdef NETDATA_INTERNAL_CHECKS - netdata_log_info("Dimension '%s' added on chart '%s' of host '%s' is not homogeneous to other dimensions already " - "present (algorithm is '%s' vs '%s', multiplier is %d vs %d, " - "divisor is %d vs %d).", - rrddim_name(rd), - rrdset_name(st), - rrdhost_hostname(host), - rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(td->algorithm), - rd->multiplier, td->multiplier, - rd->divisor, td->divisor - ); -#endif - rrdset_flag_set(st, RRDSET_FLAG_HETEROGENEOUS); - } - } - } - - rrddim_flag_set(rd, RRDDIM_FLAG_PENDING_HEALTH_INITIALIZATION); - rrdset_flag_set(rd->rrdset, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION); - rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION); - - // let the chart resync - rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK); - - ml_dimension_new(rd); - - ctr->react_action = RRDDIM_REACT_NEW; - - internal_error(false, "RRDDIM: inserted dimension '%s' of chart '%s' of host '%s'", - rrddim_name(rd), rrdset_name(st), rrdhost_hostname(st->rrdhost)); - -} - -bool rrddim_finalize_collection_and_check_retention(RRDDIM *rd) { - size_t tiers_available = 0, tiers_said_no_retention = 0; - - for(size_t tier = 0; tier < storage_tiers ;tier++) { - if(!rd->tiers[tier].db_collection_handle) - continue; - - tiers_available++; - - if(storage_engine_store_finalize(rd->tiers[tier].db_collection_handle)) - tiers_said_no_retention++; - - rd->tiers[tier].db_collection_handle = NULL; - } - - // return true if the dimension has retention in the db - return (!tiers_said_no_retention || tiers_available > tiers_said_no_retention); -} - -static void rrddim_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *rrdset) { - RRDDIM *rd = rrddim; - RRDSET *st = rrdset; - RRDHOST *host = st->rrdhost; - - internal_error(false, "RRDDIM: deleting dimension '%s' of chart '%s' of host '%s'", - rrddim_name(rd), rrdset_name(st), rrdhost_hostname(host)); - - rrdcontext_removed_rrddim(rd); - - ml_dimension_delete(rd); - - netdata_log_debug(D_RRD_CALLS, "rrddim_free() %s.%s", rrdset_name(st), rrddim_name(rd)); - - if (!rrddim_finalize_collection_and_check_retention(rd) && rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) { - /* This metric has no data and no references */ - metaqueue_delete_dimension_uuid(&rd->metric_uuid); - } - - rrddimvar_delete_all(rd); - - // free(rd->annotations); - //#ifdef ENABLE_ACLK - // if (!netdata_exit) - // aclk_send_dimension_update(rd); - //#endif - - // this will free MEMORY_MODE_SAVE and MEMORY_MODE_MAP structures - rrddim_memory_file_free(rd); - - for(size_t tier = 0; tier < storage_tiers ;tier++) { - if(!rd->tiers[tier].db_metric_handle) continue; - - STORAGE_ENGINE* eng = host->db[tier].eng; - eng->api.metric_release(rd->tiers[tier].db_metric_handle); - rd->tiers[tier].db_metric_handle = NULL; - } - - if(rd->db.data) { - __atomic_sub_fetch(&rrddim_db_memory_size, rd->db.memsize, __ATOMIC_RELAXED); - - if(rd->rrd_memory_mode == RRD_MEMORY_MODE_RAM) - netdata_munmap(rd->db.data, rd->db.memsize); - else - freez(rd->db.data); - } - - string_freez(rd->id); - string_freez(rd->name); -} - -static bool rrddim_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *new_rrddim, void *constructor_data) { - (void)new_rrddim; // it is NULL - - struct rrddim_constructor *ctr = constructor_data; - RRDDIM *rd = rrddim; - RRDSET *st = ctr->st; - - ctr->react_action = RRDDIM_REACT_NONE; - - int rc = rrddim_reset_name(st, rd, ctr->name); - rc += rrddim_set_algorithm(st, rd, ctr->algorithm); - rc += rrddim_set_multiplier(st, rd, ctr->multiplier); - rc += rrddim_set_divisor(st, rd, ctr->divisor); - - for(size_t tier = 0; tier < storage_tiers ;tier++) { - if (!rd->tiers[tier].db_collection_handle) - rd->tiers[tier].db_collection_handle = - storage_metric_store_init(rd->tiers[tier].backend, rd->tiers[tier].db_metric_handle, st->rrdhost->db[tier].tier_grouping * st->update_every, rd->rrdset->storage_metrics_groups[tier]); - } - - if(rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) { - rrddim_flag_clear(rd, RRDDIM_FLAG_ARCHIVED); - - rrddim_flag_set(rd, RRDDIM_FLAG_PENDING_HEALTH_INITIALIZATION); - rrdset_flag_set(rd->rrdset, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION); - rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION); - } - - if(unlikely(rc)) - ctr->react_action = RRDDIM_REACT_UPDATED; - - return ctr->react_action == RRDDIM_REACT_UPDATED; -} - -static void rrddim_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *constructor_data) { - struct rrddim_constructor *ctr = constructor_data; - RRDDIM *rd = rrddim; - RRDSET *st = ctr->st; - - if(ctr->react_action & (RRDDIM_REACT_UPDATED | RRDDIM_REACT_NEW)) { - rrddim_flag_set(rd, RRDDIM_FLAG_METADATA_UPDATE); - rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_METADATA_UPDATE); - } - - if(ctr->react_action == RRDDIM_REACT_UPDATED) { - // the chart needs to be updated to the parent - rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK); - } - - rrddim_metadata_updated(rd); -} - -size_t rrddim_size(void) { - return sizeof(RRDDIM) + storage_tiers * sizeof(struct rrddim_tier); -} - -void rrddim_index_init(RRDSET *st) { - if(!st->rrddim_root_index) { - st->rrddim_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, - &dictionary_stats_category_rrdset_rrddim, rrddim_size()); - - dictionary_register_insert_callback(st->rrddim_root_index, rrddim_insert_callback, NULL); - dictionary_register_conflict_callback(st->rrddim_root_index, rrddim_conflict_callback, NULL); - dictionary_register_delete_callback(st->rrddim_root_index, rrddim_delete_callback, st); - dictionary_register_react_callback(st->rrddim_root_index, rrddim_react_callback, st); - } -} - -void rrddim_index_destroy(RRDSET *st) { - dictionary_destroy(st->rrddim_root_index); - st->rrddim_root_index = NULL; -} - -static inline RRDDIM *rrddim_index_find(RRDSET *st, const char *id) { - return dictionary_get(st->rrddim_root_index, id); -} - -// ---------------------------------------------------------------------------- -// RRDDIM - find a dimension - -inline RRDDIM *rrddim_find(RRDSET *st, const char *id) { - netdata_log_debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", rrdset_name(st), id); - - return rrddim_index_find(st, id); -} - -inline RRDDIM_ACQUIRED *rrddim_find_and_acquire(RRDSET *st, const char *id) { - netdata_log_debug(D_RRD_CALLS, "rrddim_find_and_acquire() for chart %s, dimension %s", rrdset_name(st), id); - - return (RRDDIM_ACQUIRED *)dictionary_get_and_acquire_item(st->rrddim_root_index, id); -} - -RRDDIM *rrddim_acquired_to_rrddim(RRDDIM_ACQUIRED *rda) { - if(unlikely(!rda)) - return NULL; - - return (RRDDIM *) dictionary_acquired_item_value((const DICTIONARY_ITEM *)rda); -} - -void rrddim_acquired_release(RRDDIM_ACQUIRED *rda) { - if(unlikely(!rda)) - return; - - RRDDIM *rd = rrddim_acquired_to_rrddim(rda); - dictionary_acquired_item_release(rd->rrdset->rrddim_root_index, (const DICTIONARY_ITEM *)rda); -} - -// This will not return dimensions that are archived -RRDDIM *rrddim_find_active(RRDSET *st, const char *id) { - RRDDIM *rd = rrddim_find(st, id); - - if (unlikely(rd && rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED))) - return NULL; - - return rd; -} - -// ---------------------------------------------------------------------------- -// RRDDIM rename a dimension - -inline int rrddim_reset_name(RRDSET *st, RRDDIM *rd, const char *name) { - if(unlikely(!name || !*name || !strcmp(rrddim_name(rd), name))) - return 0; - - netdata_log_debug(D_RRD_CALLS, "rrddim_reset_name() from %s.%s to %s.%s", rrdset_name(st), rrddim_name(rd), rrdset_name(st), name); - - STRING *old = rd->name; - rd->name = rrd_string_strdupz(name); - string_freez(old); - - rrddimvar_rename_all(rd); - - rrddim_metadata_updated(rd); - - return 1; -} - -inline int rrddim_set_algorithm(RRDSET *st, RRDDIM *rd, RRD_ALGORITHM algorithm) { - if(unlikely(rd->algorithm == algorithm)) - return 0; - - netdata_log_debug(D_RRD_CALLS, "Updating algorithm of dimension '%s/%s' from %s to %s", rrdset_id(st), rrddim_name(rd), rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(algorithm)); - rd->algorithm = algorithm; - rrddim_metadata_updated(rd); - rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK); - rrdcontext_updated_rrddim_algorithm(rd); - return 1; -} - -inline int rrddim_set_multiplier(RRDSET *st, RRDDIM *rd, int32_t multiplier) { - if(unlikely(rd->multiplier == multiplier)) - return 0; - - netdata_log_debug(D_RRD_CALLS, "Updating multiplier of dimension '%s/%s' from %d to %d", - rrdset_id(st), rrddim_name(rd), rd->multiplier, multiplier); - rd->multiplier = multiplier; - rrddim_metadata_updated(rd); - rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK); - rrdcontext_updated_rrddim_multiplier(rd); - return 1; -} - -inline int rrddim_set_divisor(RRDSET *st, RRDDIM *rd, int32_t divisor) { - if(unlikely(rd->divisor == divisor)) - return 0; - - netdata_log_debug(D_RRD_CALLS, "Updating divisor of dimension '%s/%s' from %d to %d", - rrdset_id(st), rrddim_name(rd), rd->divisor, divisor); - rd->divisor = divisor; - rrddim_metadata_updated(rd); - rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK); - rrdcontext_updated_rrddim_divisor(rd); - return 1; -} - -// ---------------------------------------------------------------------------- - -time_t rrddim_last_entry_s_of_tier(RRDDIM *rd, size_t tier) { - if(unlikely(tier > storage_tiers || !rd->tiers[tier].db_metric_handle)) - return 0; - - return storage_engine_latest_time_s(rd->tiers[tier].backend, rd->tiers[tier].db_metric_handle); -} - -// get the timestamp of the last entry in the round-robin database -time_t rrddim_last_entry_s(RRDDIM *rd) { - time_t latest_time_s = rrddim_last_entry_s_of_tier(rd, 0); - - for(size_t tier = 1; tier < storage_tiers ;tier++) { - if(unlikely(!rd->tiers[tier].db_metric_handle)) continue; - - time_t t = rrddim_last_entry_s_of_tier(rd, tier); - if(t > latest_time_s) - latest_time_s = t; - } - - return latest_time_s; -} - -time_t rrddim_first_entry_s_of_tier(RRDDIM *rd, size_t tier) { - if(unlikely(tier > storage_tiers || !rd->tiers[tier].db_metric_handle)) - return 0; - - return storage_engine_oldest_time_s(rd->tiers[tier].backend, rd->tiers[tier].db_metric_handle); -} - -time_t rrddim_first_entry_s(RRDDIM *rd) { - time_t oldest_time_s = 0; - - for(size_t tier = 0; tier < storage_tiers ;tier++) { - time_t t = rrddim_first_entry_s_of_tier(rd, tier); - if(t != 0 && (oldest_time_s == 0 || t < oldest_time_s)) - oldest_time_s = t; - } - - return oldest_time_s; -} - -RRDDIM *rrddim_add_custom(RRDSET *st - , const char *id - , const char *name - , collected_number multiplier - , collected_number divisor - , RRD_ALGORITHM algorithm - , RRD_MEMORY_MODE memory_mode - ) { - struct rrddim_constructor tmp = { - .st = st, - .id = id, - .name = name, - .multiplier = multiplier, - .divisor = divisor, - .algorithm = algorithm, - .memory_mode = memory_mode, - }; - - RRDDIM *rd = dictionary_set_advanced(st->rrddim_root_index, tmp.id, -1, NULL, rrddim_size(), &tmp); - return(rd); -} - -// ---------------------------------------------------------------------------- -// RRDDIM remove / free a dimension - -void rrddim_free(RRDSET *st, RRDDIM *rd) { - dictionary_del(st->rrddim_root_index, string2str(rd->id)); -} - - -// ---------------------------------------------------------------------------- -// RRDDIM - set dimension options - -int rrddim_hide(RRDSET *st, const char *id) { - netdata_log_debug(D_RRD_CALLS, "rrddim_hide() for chart %s, dimension %s", rrdset_name(st), id); - - RRDHOST *host = st->rrdhost; - - RRDDIM *rd = rrddim_find(st, id); - if(unlikely(!rd)) { - netdata_log_error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, rrdset_name(st), rrdset_id(st), rrdhost_hostname(host)); - return 1; - } - if (!rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) { - rrddim_flag_set(rd, RRDDIM_FLAG_META_HIDDEN | RRDDIM_FLAG_METADATA_UPDATE); - rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_METADATA_UPDATE); - } - - rrddim_option_set(rd, RRDDIM_OPTION_HIDDEN); - rrdcontext_updated_rrddim_flags(rd); - return 0; -} - -int rrddim_unhide(RRDSET *st, const char *id) { - netdata_log_debug(D_RRD_CALLS, "rrddim_unhide() for chart %s, dimension %s", rrdset_name(st), id); - - RRDHOST *host = st->rrdhost; - RRDDIM *rd = rrddim_find(st, id); - if(unlikely(!rd)) { - netdata_log_error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, rrdset_name(st), rrdset_id(st), rrdhost_hostname(host)); - return 1; - } - if (rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) { - rrddim_flag_clear(rd, RRDDIM_FLAG_META_HIDDEN); - rrddim_flag_set(rd, RRDDIM_FLAG_METADATA_UPDATE); - rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_METADATA_UPDATE); - } - - rrddim_option_clear(rd, RRDDIM_OPTION_HIDDEN); - rrdcontext_updated_rrddim_flags(rd); - return 0; -} - -inline void rrddim_is_obsolete___safe_from_collector_thread(RRDSET *st, RRDDIM *rd) { - netdata_log_debug(D_RRD_CALLS, "rrddim_is_obsolete___safe_from_collector_thread() for chart %s, dimension %s", rrdset_name(st), rrddim_name(rd)); - - if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED))) { - netdata_log_info("Cannot obsolete already archived dimension %s from chart %s", rrddim_name(rd), rrdset_name(st)); - return; - } - rrddim_flag_set(rd, RRDDIM_FLAG_OBSOLETE); - rrdset_flag_set(st, RRDSET_FLAG_OBSOLETE_DIMENSIONS); - rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_PENDING_OBSOLETE_DIMENSIONS); - rrdcontext_updated_rrddim_flags(rd); -} - -inline void rrddim_isnot_obsolete___safe_from_collector_thread(RRDSET *st __maybe_unused, RRDDIM *rd) { - netdata_log_debug(D_RRD_CALLS, "rrddim_isnot_obsolete___safe_from_collector_thread() for chart %s, dimension %s", rrdset_name(st), rrddim_name(rd)); - - rrddim_flag_clear(rd, RRDDIM_FLAG_OBSOLETE); - rrdcontext_updated_rrddim_flags(rd); -} - -// ---------------------------------------------------------------------------- -// RRDDIM - collect values for a dimension - -inline collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value) { - struct timeval now; - now_realtime_timeval(&now); - - return rrddim_timed_set_by_pointer(st, rd, now, value); -} - -collected_number rrddim_timed_set_by_pointer(RRDSET *st __maybe_unused, RRDDIM *rd, struct timeval collected_time, collected_number value) { - netdata_log_debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, rrdset_name(st), rrddim_name(rd), value); - - rd->collector.last_collected_time = collected_time; - rd->collector.collected_value = value; - rrddim_set_updated(rd); - rd->collector.counter++; - - collected_number v = (value >= 0) ? value : -value; - if (unlikely(v > rd->collector.collected_value_max)) - rd->collector.collected_value_max = v; - - return rd->collector.last_collected_value; -} - - -collected_number rrddim_set(RRDSET *st, const char *id, collected_number value) { - RRDHOST *host = st->rrdhost; - RRDDIM *rd = rrddim_find(st, id); - if(unlikely(!rd)) { - netdata_log_error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, rrdset_name(st), rrdset_id(st), rrdhost_hostname(host)); - return 0; - } - - return rrddim_set_by_pointer(st, rd, value); -} - - -// ---------------------------------------------------------------------------- -// compatibility layer for RRDDIM files v019 - -#define RRDDIMENSION_MAGIC_V019 "NETDATA RRD DIMENSION FILE V019" - -struct avl_element_v019 { - void *avl_link[2]; - signed char avl_balance; -}; - -struct rrddim_map_save_v019 { - struct avl_element_v019 avl; // ignored - void *id; // ignored - void *name; // ignored - uint32_t algorithm; // print warning on mismatch - update on load - uint32_t rrd_memory_mode; // ignored - long long multiplier; // print warning on mismatch - update on load - long long divisor; // print warning on mismatch - update on load - uint32_t flags; // ignored - uint32_t hash; // ignored - uint32_t hash_name; // ignored - void *cache_filename; // ignored - we use it to keep the filename to save back - size_t collections_counter; // ignored - void *state; // ignored - size_t unused[8]; // ignored - long long collected_value_max; // ignored - unsigned int updated:1; // ignored - unsigned int exposed:1; // ignored - struct timeval last_collected_time; // check to reset all - ignored after load - long double calculated_value; // ignored - long double last_calculated_value; // ignored - long double last_stored_value; // ignored - long long collected_value; // ignored - long long last_collected_value; // load and save - long double collected_volume; // ignored - long double stored_volume; // ignored - void *next; // ignored - void *rrdset; // ignored - long entries; // check to reset all - update on load - int update_every; // check to reset all - update on load - size_t memsize; // check to reset all - update on load - char magic[sizeof(RRDDIMENSION_MAGIC_V019) + 1];// check to reset all - update on load - void *variables; // ignored - storage_number values[]; // the array of values -}; - -size_t rrddim_memory_file_header_size(void) { - return sizeof(struct rrddim_map_save_v019); -} - -void rrddim_memory_file_update(RRDDIM *rd) { - if(!rd || !rd->db.rd_on_file) return; - struct rrddim_map_save_v019 *rd_on_file = rd->db.rd_on_file; - - rd_on_file->last_collected_time.tv_sec = rd->collector.last_collected_time.tv_sec; - rd_on_file->last_collected_time.tv_usec = rd->collector.last_collected_time.tv_usec; - rd_on_file->last_collected_value = rd->collector.last_collected_value; -} - -void rrddim_memory_file_free(RRDDIM *rd) { - if(!rd || !rd->db.rd_on_file) return; - - // needed for memory mode map, to save the latest state - rrddim_memory_file_update(rd); - - struct rrddim_map_save_v019 *rd_on_file = rd->db.rd_on_file; - __atomic_sub_fetch(&rrddim_db_memory_size, rd_on_file->memsize + strlen(rd_on_file->cache_filename), __ATOMIC_RELAXED); - freez(rd_on_file->cache_filename); - netdata_munmap(rd_on_file, rd_on_file->memsize); - - // remove the pointers from the RRDDIM - rd->db.rd_on_file = NULL; - rd->db.data = NULL; -} - -const char *rrddim_cache_filename(RRDDIM *rd) { - if(!rd || !rd->db.rd_on_file) return NULL; - struct rrddim_map_save_v019 *rd_on_file = rd->db.rd_on_file; - return rd_on_file->cache_filename; -} - -void rrddim_memory_file_save(RRDDIM *rd) { - if(!rd || !rd->db.rd_on_file) return; - - rrddim_memory_file_update(rd); - - struct rrddim_map_save_v019 *rd_on_file = rd->db.rd_on_file; - if(rd_on_file->rrd_memory_mode != RRD_MEMORY_MODE_SAVE) return; - - memory_file_save(rd_on_file->cache_filename, rd_on_file, rd_on_file->memsize); -} - -bool rrddim_memory_load_or_create_map_save(RRDSET *st, RRDDIM *rd, RRD_MEMORY_MODE memory_mode) { - if(memory_mode != RRD_MEMORY_MODE_SAVE && memory_mode != RRD_MEMORY_MODE_MAP) - return false; - - struct rrddim_map_save_v019 *rd_on_file = NULL; - - unsigned long size = sizeof(struct rrddim_map_save_v019) + (st->db.entries * sizeof(storage_number)); - - char filename[FILENAME_MAX + 1]; - char fullfilename[FILENAME_MAX + 1]; - rrdset_strncpyz_name(filename, rrddim_id(rd), FILENAME_MAX); - snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", rrdset_cache_dir(st), filename); - - rd_on_file = (struct rrddim_map_save_v019 *)netdata_mmap( - fullfilename, size, ((memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 1, false, NULL); - - if(unlikely(!rd_on_file)) return false; - - struct timeval now; - now_realtime_timeval(&now); - - int reset = 0; - rd_on_file->magic[sizeof(RRDDIMENSION_MAGIC_V019)] = '\0'; - if(strcmp(rd_on_file->magic, RRDDIMENSION_MAGIC_V019) != 0) { - netdata_log_info("Initializing file %s.", fullfilename); - memset(rd_on_file, 0, size); - reset = 1; - } - else if(rd_on_file->memsize != size) { - netdata_log_error("File %s does not have the desired size, expected %lu but found %lu. Clearing it.", fullfilename, size, (unsigned long int) rd_on_file->memsize); - memset(rd_on_file, 0, size); - reset = 1; - } - else if(rd_on_file->update_every != st->update_every) { - netdata_log_error("File %s does not have the same update frequency, expected %d but found %d. Clearing it.", fullfilename, st->update_every, rd_on_file->update_every); - memset(rd_on_file, 0, size); - reset = 1; - } - else if(dt_usec(&now, &rd_on_file->last_collected_time) > (rd_on_file->entries * rd_on_file->update_every * USEC_PER_SEC)) { - netdata_log_info("File %s is too old (last collected %llu seconds ago, but the database is %ld seconds). Clearing it.", fullfilename, dt_usec(&now, &rd_on_file->last_collected_time) / USEC_PER_SEC, rd_on_file->entries * rd_on_file->update_every); - memset(rd_on_file, 0, size); - reset = 1; - } - - if(!reset) { - rd->collector.last_collected_value = rd_on_file->last_collected_value; - - if(rd_on_file->algorithm != rd->algorithm) - netdata_log_info("File %s does not have the expected algorithm (expected %u '%s', found %u '%s'). Previous values may be wrong.", - fullfilename, rd->algorithm, rrd_algorithm_name(rd->algorithm), rd_on_file->algorithm, rrd_algorithm_name(rd_on_file->algorithm)); - - if(rd_on_file->multiplier != rd->multiplier) - netdata_log_info("File %s does not have the expected multiplier (expected %d, found %ld). " - "Previous values may be wrong.", fullfilename, rd->multiplier, (long)rd_on_file->multiplier); - - if(rd_on_file->divisor != rd->divisor) - netdata_log_info("File %s does not have the expected divisor (expected %d, found %ld). " - "Previous values may be wrong.", fullfilename, rd->divisor, (long)rd_on_file->divisor); - } - - // zero the entire header - memset(rd_on_file, 0, sizeof(struct rrddim_map_save_v019)); - - // set the important fields - strcpy(rd_on_file->magic, RRDDIMENSION_MAGIC_V019); - rd_on_file->algorithm = rd->algorithm; - rd_on_file->multiplier = rd->multiplier; - rd_on_file->divisor = rd->divisor; - rd_on_file->entries = st->db.entries; - rd_on_file->update_every = rd->rrdset->update_every; - rd_on_file->memsize = size; - rd_on_file->rrd_memory_mode = memory_mode; - rd_on_file->cache_filename = strdupz(fullfilename); - - __atomic_add_fetch(&rrddim_db_memory_size, rd_on_file->memsize + strlen(rd_on_file->cache_filename), __ATOMIC_RELAXED); - - rd->db.data = &rd_on_file->values[0]; - rd->db.rd_on_file = rd_on_file; - rd->db.memsize = size; - rrddim_memory_file_update(rd); - - return true; -} diff --git a/database/rrddimvar.c b/database/rrddimvar.c deleted file mode 100644 index 5035d70a5..000000000 --- a/database/rrddimvar.c +++ /dev/null @@ -1,273 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rrd.h" - -typedef struct rrddimvar { - struct rrddim *rrddim; - - STRING *prefix; - STRING *suffix; - void *value; - - const RRDVAR_ACQUIRED *rrdvar_local_dim_id; - const RRDVAR_ACQUIRED *rrdvar_local_dim_name; - - const RRDVAR_ACQUIRED *rrdvar_family_id; - const RRDVAR_ACQUIRED *rrdvar_family_name; - const RRDVAR_ACQUIRED *rrdvar_family_context_dim_id; - const RRDVAR_ACQUIRED *rrdvar_family_context_dim_name; - - const RRDVAR_ACQUIRED *rrdvar_host_chart_id_dim_id; - const RRDVAR_ACQUIRED *rrdvar_host_chart_id_dim_name; - const RRDVAR_ACQUIRED *rrdvar_host_chart_name_dim_id; - const RRDVAR_ACQUIRED *rrdvar_host_chart_name_dim_name; - - RRDVAR_FLAGS flags:24; - RRDVAR_TYPE type:8; -} RRDDIMVAR; - -// ---------------------------------------------------------------------------- -// RRDDIMVAR management -// DIMENSION VARIABLES - -#define RRDDIMVAR_ID_MAX 1024 - -static inline void rrddimvar_free_variables_unsafe(RRDDIMVAR *rs) { - RRDDIM *rd = rs->rrddim; - RRDSET *st = rd->rrdset; - RRDHOST *host = st->rrdhost; - - // CHART VARIABLES FOR THIS DIMENSION - - if(st->rrdvars) { - rrdvar_release_and_del(st->rrdvars, rs->rrdvar_local_dim_id); - rs->rrdvar_local_dim_id = NULL; - - rrdvar_release_and_del(st->rrdvars, rs->rrdvar_local_dim_name); - rs->rrdvar_local_dim_name = NULL; - } - - // FAMILY VARIABLES FOR THIS DIMENSION - - if(st->rrdfamily) { - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_id); - rs->rrdvar_family_id = NULL; - - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_name); - rs->rrdvar_family_name = NULL; - - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_context_dim_id); - rs->rrdvar_family_context_dim_id = NULL; - - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_context_dim_name); - rs->rrdvar_family_context_dim_name = NULL; - } - - // HOST VARIABLES FOR THIS DIMENSION - - if(host->rrdvars && host->health.health_enabled) { - rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_id_dim_id); - rs->rrdvar_host_chart_id_dim_id = NULL; - - rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_id_dim_name); - rs->rrdvar_host_chart_id_dim_name = NULL; - - rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_name_dim_id); - rs->rrdvar_host_chart_name_dim_id = NULL; - - rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_name_dim_name); - rs->rrdvar_host_chart_name_dim_name = NULL; - } -} - -static inline void rrddimvar_update_variables_unsafe(RRDDIMVAR *rs) { - rrddimvar_free_variables_unsafe(rs); - - RRDDIM *rd = rs->rrddim; - RRDSET *st = rd->rrdset; - RRDHOST *host = st->rrdhost; - - char buffer[RRDDIMVAR_ID_MAX + 1]; - - // KEYS - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", string2str(rs->prefix), rrddim_id(rd), string2str(rs->suffix)); - STRING *key_dim_id = string_strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", string2str(rs->prefix), rrddim_name(rd), string2str(rs->suffix)); - STRING *key_dim_name = string_strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_id(st), string2str(key_dim_id)); - STRING *key_chart_id_dim_id = string_strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_id(st), string2str(key_dim_name)); - STRING *key_chart_id_dim_name = string_strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_context(st), string2str(key_dim_id)); - STRING *key_context_dim_id = string_strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_context(st), string2str(key_dim_name)); - STRING *key_context_dim_name = string_strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_name(st), string2str(key_dim_id)); - STRING *key_chart_name_dim_id = string_strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_name(st), string2str(key_dim_name)); - STRING *key_chart_name_dim_name = string_strdupz(buffer); - - // CHART VARIABLES FOR THIS DIMENSION - // ----------------------------------- - // - // dimensions are available as: - // - $id - // - $name - - if(st->rrdvars) { - rs->rrdvar_local_dim_id = rrdvar_add_and_acquire("local", st->rrdvars, key_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value); - rs->rrdvar_local_dim_name = rrdvar_add_and_acquire("local", st->rrdvars, key_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value); - } - - // FAMILY VARIABLES FOR THIS DIMENSION - // ----------------------------------- - // - // dimensions are available as: - // - $id (only the first, when multiple overlap) - // - $name (only the first, when multiple overlap) - // - $chart-context.id - // - $chart-context.name - - if(st->rrdfamily) { - rs->rrdvar_family_id = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value); - rs->rrdvar_family_name = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value); - rs->rrdvar_family_context_dim_id = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_context_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value); - rs->rrdvar_family_context_dim_name = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_context_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value); - } - - // HOST VARIABLES FOR THIS DIMENSION - // ----------------------------------- - // - // dimensions are available as: - // - $chart-id.id - // - $chart-id.name - // - $chart-name.id - // - $chart-name.name - - if(host->rrdvars && host->health.health_enabled) { - rs->rrdvar_host_chart_id_dim_id = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_id_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value); - rs->rrdvar_host_chart_id_dim_name = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_id_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value); - rs->rrdvar_host_chart_name_dim_id = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_name_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value); - rs->rrdvar_host_chart_name_dim_name = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_name_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value); - } - - // free the keys - - string_freez(key_dim_id); - string_freez(key_dim_name); - string_freez(key_chart_id_dim_id); - string_freez(key_chart_id_dim_name); - string_freez(key_context_dim_id); - string_freez(key_context_dim_name); - string_freez(key_chart_name_dim_id); - string_freez(key_chart_name_dim_name); -} - -struct rrddimvar_constructor { - RRDDIM *rrddim; - const char *prefix; - const char *suffix; - void *value; - RRDVAR_FLAGS flags :16; - RRDVAR_TYPE type:8; -}; - -static void rrddimvar_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddimvar, void *constructor_data) { - RRDDIMVAR *rs = rrddimvar; - struct rrddimvar_constructor *ctr = constructor_data; - - if(!ctr->prefix) ctr->prefix = ""; - if(!ctr->suffix) ctr->suffix = ""; - - rs->prefix = string_strdupz(ctr->prefix); - rs->suffix = string_strdupz(ctr->suffix); - - rs->type = ctr->type; - rs->value = ctr->value; - rs->flags = ctr->flags; - rs->rrddim = ctr->rrddim; - - rrddimvar_update_variables_unsafe(rs); -} - -static bool rrddimvar_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddimvar, void *new_rrddimvar __maybe_unused, void *constructor_data __maybe_unused) { - RRDDIMVAR *rs = rrddimvar; - rrddimvar_update_variables_unsafe(rs); - - return true; -} - -static void rrddimvar_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddimvar, void *rrdset __maybe_unused) { - RRDDIMVAR *rs = rrddimvar; - rrddimvar_free_variables_unsafe(rs); - string_freez(rs->prefix); - string_freez(rs->suffix); -} - -void rrddimvar_index_init(RRDSET *st) { - if(!st->rrddimvar_root_index) { - st->rrddimvar_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, - &dictionary_stats_category_rrdhealth, sizeof(RRDDIMVAR)); - - dictionary_register_insert_callback(st->rrddimvar_root_index, rrddimvar_insert_callback, NULL); - dictionary_register_conflict_callback(st->rrddimvar_root_index, rrddimvar_conflict_callback, NULL); - dictionary_register_delete_callback(st->rrddimvar_root_index, rrddimvar_delete_callback, st); - } -} - -void rrddimvar_index_destroy(RRDSET *st) { - dictionary_destroy(st->rrddimvar_root_index); - st->rrddimvar_root_index = NULL; -} - -void rrddimvar_add_and_leave_released(RRDDIM *rd, RRDVAR_TYPE type, const char *prefix, const char *suffix, void *value, RRDVAR_FLAGS flags) { - if(!prefix) prefix = ""; - if(!suffix) suffix = ""; - - char key[RRDDIMVAR_ID_MAX + 1]; - size_t key_len = snprintfz(key, RRDDIMVAR_ID_MAX, "%s_%s_%s", prefix, rrddim_id(rd), suffix); - - struct rrddimvar_constructor tmp = { - .suffix = suffix, - .prefix = prefix, - .type = type, - .flags = flags, - .value = value, - .rrddim = rd - }; - dictionary_set_advanced(rd->rrdset->rrddimvar_root_index, key, (ssize_t)(key_len + 1), NULL, sizeof(RRDDIMVAR), &tmp); -} - -void rrddimvar_rename_all(RRDDIM *rd) { - RRDSET *st = rd->rrdset; - - netdata_log_debug(D_VARIABLES, "RRDDIMVAR rename for chart id '%s' name '%s', dimension id '%s', name '%s'", rrdset_id(st), rrdset_name(st), rrddim_id(rd), rrddim_name(rd)); - - RRDDIMVAR *rs; - dfe_start_write(st->rrddimvar_root_index, rs) { - if(unlikely(rs->rrddim == rd)) - rrddimvar_update_variables_unsafe(rs); - } - dfe_done(rs); -} - -void rrddimvar_delete_all(RRDDIM *rd) { - RRDSET *st = rd->rrdset; - - netdata_log_debug(D_VARIABLES, "RRDDIMVAR delete for chart id '%s' name '%s', dimension id '%s', name '%s'", rrdset_id(st), rrdset_name(st), rrddim_id(rd), rrddim_name(rd)); - - RRDDIMVAR *rs; - dfe_start_write(st->rrddimvar_root_index, rs) { - if(unlikely(rs->rrddim == rd)) - dictionary_del(st->rrddimvar_root_index, rs_dfe.name); - } - dfe_done(rs); -} diff --git a/database/rrddimvar.h b/database/rrddimvar.h deleted file mode 100644 index a803ea753..000000000 --- a/database/rrddimvar.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_RRDDIMVAR_H -#define NETDATA_RRDDIMVAR_H 1 - -#include "rrd.h" - -// variables linked to individual dimensions -// We link variables to point the values that are already -// calculated / processed by the normal data collection process -// This means, there will be no speed penalty for using -// these variables - -void rrddimvar_rename_all(RRDDIM *rd); -void rrddimvar_add_and_leave_released(RRDDIM *rd, RRDVAR_TYPE type, const char *prefix, const char *suffix, void *value, RRDVAR_FLAGS flags); -void rrddimvar_delete_all(RRDDIM *rd); - -void rrddimvar_index_init(RRDSET *st); -void rrddimvar_index_destroy(RRDSET *st); - -#endif //NETDATA_RRDDIMVAR_H diff --git a/database/rrdfamily.c b/database/rrdfamily.c deleted file mode 100644 index 011cb28b4..000000000 --- a/database/rrdfamily.c +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#define NETDATA_RRD_INTERNALS -#include "rrd.h" - -typedef struct rrdfamily { - STRING *family; - DICTIONARY *rrdvars; -} RRDFAMILY; - -// ---------------------------------------------------------------------------- -// RRDFAMILY index - -struct rrdfamily_constructor { - const char *family; -}; - -static void rrdfamily_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdfamily, void *constructor_data) { - RRDFAMILY *rf = rrdfamily; - struct rrdfamily_constructor *ctr = constructor_data; - - rf->family = string_strdupz(ctr->family); - rf->rrdvars = rrdvariables_create(); -} - -static void rrdfamily_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdfamily, void *rrdhost __maybe_unused) { - RRDFAMILY *rf = rrdfamily; - string_freez(rf->family); - rrdvariables_destroy(rf->rrdvars); - rf->family = NULL; - rf->rrdvars = NULL; -} - -void rrdfamily_index_init(RRDHOST *host) { - if(!host->rrdfamily_root_index) { - host->rrdfamily_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, - &dictionary_stats_category_rrdhealth, sizeof(RRDFAMILY)); - - dictionary_register_insert_callback(host->rrdfamily_root_index, rrdfamily_insert_callback, NULL); - dictionary_register_delete_callback(host->rrdfamily_root_index, rrdfamily_delete_callback, host); - } -} - -void rrdfamily_index_destroy(RRDHOST *host) { - dictionary_destroy(host->rrdfamily_root_index); - host->rrdfamily_root_index = NULL; -} - - -// ---------------------------------------------------------------------------- -// RRDFAMILY management - -const RRDFAMILY_ACQUIRED *rrdfamily_add_and_acquire(RRDHOST *host, const char *id) { - struct rrdfamily_constructor tmp = { - .family = id, - }; - return (const RRDFAMILY_ACQUIRED *)dictionary_set_and_acquire_item_advanced(host->rrdfamily_root_index, id, -1, NULL, sizeof(RRDFAMILY), &tmp); -} - -void rrdfamily_release(RRDHOST *host, const RRDFAMILY_ACQUIRED *rfa) { - if(unlikely(!rfa)) return; - dictionary_acquired_item_release(host->rrdfamily_root_index, (const DICTIONARY_ITEM *)rfa); -} - -DICTIONARY *rrdfamily_rrdvars_dict(const RRDFAMILY_ACQUIRED *rfa) { - if(unlikely(!rfa)) return NULL; - RRDFAMILY *rf = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rfa); - return(rf->rrdvars); -} diff --git a/database/rrdfunctions.c b/database/rrdfunctions.c deleted file mode 100644 index 2659130f0..000000000 --- a/database/rrdfunctions.c +++ /dev/null @@ -1,1821 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -#define NETDATA_RRD_INTERNALS -#include "rrd.h" - -#define MAX_FUNCTION_LENGTH (PLUGINSD_LINE_MAX - 512) // we need some space for the rest of the line - -static unsigned char functions_allowed_chars[256] = { - [0] = '\0', // - [1] = '_', // - [2] = '_', // - [3] = '_', // - [4] = '_', // - [5] = '_', // - [6] = '_', // - [7] = '_', // - [8] = '_', // - [9] = ' ', // Horizontal Tab - [10] = ' ', // Line Feed - [11] = ' ', // Vertical Tab - [12] = ' ', // Form Feed - [13] = ' ', // Carriage Return - [14] = '_', // - [15] = '_', // - [16] = '_', // - [17] = '_', // - [18] = '_', // - [19] = '_', // - [20] = '_', // - [21] = '_', // - [22] = '_', // - [23] = '_', // - [24] = '_', // - [25] = '_', // - [26] = '_', // - [27] = '_', // - [28] = '_', // - [29] = '_', // - [30] = '_', // - [31] = '_', // - [32] = ' ', // SPACE keep - [33] = '!', // ! keep - [34] = '"', // " keep - [35] = '#', // # keep - [36] = '$', // $ keep - [37] = '%', // % keep - [38] = '&', // & keep - [39] = '\'', // ' keep - [40] = '(', // ( keep - [41] = ')', // ) keep - [42] = '*', // * keep - [43] = '+', // + keep - [44] = ',', // , keep - [45] = '-', // - keep - [46] = '.', // . keep - [47] = '/', // / keep - [48] = '0', // 0 keep - [49] = '1', // 1 keep - [50] = '2', // 2 keep - [51] = '3', // 3 keep - [52] = '4', // 4 keep - [53] = '5', // 5 keep - [54] = '6', // 6 keep - [55] = '7', // 7 keep - [56] = '8', // 8 keep - [57] = '9', // 9 keep - [58] = ':', // : keep - [59] = ';', // ; keep - [60] = '<', // < keep - [61] = '=', // = keep - [62] = '>', // > keep - [63] = '?', // ? keep - [64] = '@', // @ keep - [65] = 'A', // A keep - [66] = 'B', // B keep - [67] = 'C', // C keep - [68] = 'D', // D keep - [69] = 'E', // E keep - [70] = 'F', // F keep - [71] = 'G', // G keep - [72] = 'H', // H keep - [73] = 'I', // I keep - [74] = 'J', // J keep - [75] = 'K', // K keep - [76] = 'L', // L keep - [77] = 'M', // M keep - [78] = 'N', // N keep - [79] = 'O', // O keep - [80] = 'P', // P keep - [81] = 'Q', // Q keep - [82] = 'R', // R keep - [83] = 'S', // S keep - [84] = 'T', // T keep - [85] = 'U', // U keep - [86] = 'V', // V keep - [87] = 'W', // W keep - [88] = 'X', // X keep - [89] = 'Y', // Y keep - [90] = 'Z', // Z keep - [91] = '[', // [ keep - [92] = '\\', // backslash keep - [93] = ']', // ] keep - [94] = '^', // ^ keep - [95] = '_', // _ keep - [96] = '`', // ` keep - [97] = 'a', // a keep - [98] = 'b', // b keep - [99] = 'c', // c keep - [100] = 'd', // d keep - [101] = 'e', // e keep - [102] = 'f', // f keep - [103] = 'g', // g keep - [104] = 'h', // h keep - [105] = 'i', // i keep - [106] = 'j', // j keep - [107] = 'k', // k keep - [108] = 'l', // l keep - [109] = 'm', // m keep - [110] = 'n', // n keep - [111] = 'o', // o keep - [112] = 'p', // p keep - [113] = 'q', // q keep - [114] = 'r', // r keep - [115] = 's', // s keep - [116] = 't', // t keep - [117] = 'u', // u keep - [118] = 'v', // v keep - [119] = 'w', // w keep - [120] = 'x', // x keep - [121] = 'y', // y keep - [122] = 'z', // z keep - [123] = '{', // { keep - [124] = '|', // | keep - [125] = '}', // } keep - [126] = '~', // ~ keep - [127] = '_', // - [128] = '_', // - [129] = '_', // - [130] = '_', // - [131] = '_', // - [132] = '_', // - [133] = '_', // - [134] = '_', // - [135] = '_', // - [136] = '_', // - [137] = '_', // - [138] = '_', // - [139] = '_', // - [140] = '_', // - [141] = '_', // - [142] = '_', // - [143] = '_', // - [144] = '_', // - [145] = '_', // - [146] = '_', // - [147] = '_', // - [148] = '_', // - [149] = '_', // - [150] = '_', // - [151] = '_', // - [152] = '_', // - [153] = '_', // - [154] = '_', // - [155] = '_', // - [156] = '_', // - [157] = '_', // - [158] = '_', // - [159] = '_', // - [160] = '_', // - [161] = '_', // - [162] = '_', // - [163] = '_', // - [164] = '_', // - [165] = '_', // - [166] = '_', // - [167] = '_', // - [168] = '_', // - [169] = '_', // - [170] = '_', // - [171] = '_', // - [172] = '_', // - [173] = '_', // - [174] = '_', // - [175] = '_', // - [176] = '_', // - [177] = '_', // - [178] = '_', // - [179] = '_', // - [180] = '_', // - [181] = '_', // - [182] = '_', // - [183] = '_', // - [184] = '_', // - [185] = '_', // - [186] = '_', // - [187] = '_', // - [188] = '_', // - [189] = '_', // - [190] = '_', // - [191] = '_', // - [192] = '_', // - [193] = '_', // - [194] = '_', // - [195] = '_', // - [196] = '_', // - [197] = '_', // - [198] = '_', // - [199] = '_', // - [200] = '_', // - [201] = '_', // - [202] = '_', // - [203] = '_', // - [204] = '_', // - [205] = '_', // - [206] = '_', // - [207] = '_', // - [208] = '_', // - [209] = '_', // - [210] = '_', // - [211] = '_', // - [212] = '_', // - [213] = '_', // - [214] = '_', // - [215] = '_', // - [216] = '_', // - [217] = '_', // - [218] = '_', // - [219] = '_', // - [220] = '_', // - [221] = '_', // - [222] = '_', // - [223] = '_', // - [224] = '_', // - [225] = '_', // - [226] = '_', // - [227] = '_', // - [228] = '_', // - [229] = '_', // - [230] = '_', // - [231] = '_', // - [232] = '_', // - [233] = '_', // - [234] = '_', // - [235] = '_', // - [236] = '_', // - [237] = '_', // - [238] = '_', // - [239] = '_', // - [240] = '_', // - [241] = '_', // - [242] = '_', // - [243] = '_', // - [244] = '_', // - [245] = '_', // - [246] = '_', // - [247] = '_', // - [248] = '_', // - [249] = '_', // - [250] = '_', // - [251] = '_', // - [252] = '_', // - [253] = '_', // - [254] = '_', // - [255] = '_' // -}; - -static inline size_t sanitize_function_text(char *dst, const char *src, size_t dst_len) { - return text_sanitize((unsigned char *)dst, (const unsigned char *)src, dst_len, - functions_allowed_chars, true, "", NULL); -} - -// we keep a dictionary per RRDSET with these functions -// the dictionary is created on demand (only when a function is added to an RRDSET) - -typedef enum __attribute__((packed)) { - RRD_FUNCTION_LOCAL = (1 << 0), - RRD_FUNCTION_GLOBAL = (1 << 1), - - // this is 8-bit -} RRD_FUNCTION_OPTIONS; - -struct rrd_host_function { - bool sync; // when true, the function is called synchronously - RRD_FUNCTION_OPTIONS options; // RRD_FUNCTION_OPTIONS - STRING *help; - int timeout; // the default timeout of the function - - rrd_function_execute_cb_t execute_cb; - - void *execute_cb_data; - struct rrd_collector *collector; -}; - -// Each function points to this collector structure -// so that when the collector exits, all of them will -// be invalidated (running == false) -// The last function that is using this collector -// frees the structure too (or when the collector calls -// rrdset_collector_finished()). - -struct rrd_collector { - int32_t refcount; - int32_t refcount_canceller; - pid_t tid; - bool running; -}; - -// Each thread that adds RRDSET functions, has to call -// rrdset_collector_started() and rrdset_collector_finished() -// to create the collector structure. - -static __thread struct rrd_collector *thread_rrd_collector = NULL; - -static void rrd_collector_free(struct rrd_collector *rdc) { - if(rdc->running) - return; - - int32_t expected = 0; - if(!__atomic_compare_exchange_n(&rdc->refcount, &expected, -1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) { - // the collector is still referenced by charts. - // leave it hanging there, the last chart will actually free it. - return; - } - - // we can free it now - freez(rdc); -} - -// called once per collector -void rrd_collector_started(void) { - if(!thread_rrd_collector) - thread_rrd_collector = callocz(1, sizeof(struct rrd_collector)); - - thread_rrd_collector->tid = gettid(); - __atomic_store_n(&thread_rrd_collector->running, true, __ATOMIC_RELAXED); -} - -// called once per collector -void rrd_collector_finished(void) { - if(!thread_rrd_collector) - return; - - __atomic_store_n(&thread_rrd_collector->running, false, __ATOMIC_RELAXED); - - int32_t expected = 0; - while(!__atomic_compare_exchange_n(&thread_rrd_collector->refcount_canceller, &expected, -1, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) { - expected = 0; - sleep_usec(1 * USEC_PER_MS); - } - - rrd_collector_free(thread_rrd_collector); - thread_rrd_collector = NULL; -} - -#define rrd_collector_running(c) __atomic_load_n(&(c)->running, __ATOMIC_RELAXED) - -static struct rrd_collector *rrd_collector_acquire(void) { - rrd_collector_started(); - - int32_t expected = __atomic_load_n(&thread_rrd_collector->refcount, __ATOMIC_RELAXED), wanted = 0; - do { - if(expected < 0 || !rrd_collector_running(thread_rrd_collector)) { - internal_fatal(true, "FUNCTIONS: Trying to acquire a collector that is exiting."); - return thread_rrd_collector; - } - - wanted = expected + 1; - - } while(!__atomic_compare_exchange_n(&thread_rrd_collector->refcount, &expected, wanted, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)); - - return thread_rrd_collector; -} - -static void rrd_collector_release(struct rrd_collector *rdc) { - if(unlikely(!rdc)) return; - - int32_t expected = __atomic_load_n(&rdc->refcount, __ATOMIC_RELAXED), wanted = 0; - do { - if(expected < 0) { - internal_fatal(true, "FUNCTIONS: Trying to release a collector that is exiting."); - return; - } - - if(expected == 0) { - internal_fatal(true, "FUNCTIONS: Trying to release a collector that is not acquired."); - return; - } - - wanted = expected - 1; - - } while(!__atomic_compare_exchange_n(&rdc->refcount, &expected, wanted, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED)); - - if(wanted == 0) - rrd_collector_free(rdc); -} - -static void rrd_functions_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func, void *rrdhost) { - RRDHOST *host = rrdhost; (void)host; - struct rrd_host_function *rdcf = func; - - rrd_collector_started(); - rdcf->collector = rrd_collector_acquire(); - -// internal_error(true, "FUNCTIONS: adding function '%s' on host '%s', collection tid %d, %s", -// dictionary_acquired_item_name(item), rrdhost_hostname(host), -// rdcf->collector->tid, rdcf->collector->running ? "running" : "NOT running"); -} - -static void rrd_functions_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func, - void *rrdhost __maybe_unused) { - struct rrd_host_function *rdcf = func; - rrd_collector_release(rdcf->collector); -} - -static bool rrd_functions_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func, - void *new_func, void *rrdhost) { - RRDHOST *host = rrdhost; (void)host; - struct rrd_host_function *rdcf = func; - struct rrd_host_function *new_rdcf = new_func; - - rrd_collector_started(); - - bool changed = false; - - if(rdcf->collector != thread_rrd_collector) { - netdata_log_info("FUNCTIONS: function '%s' of host '%s' changed collector from %d to %d", - dictionary_acquired_item_name(item), rrdhost_hostname(host), rdcf->collector->tid, thread_rrd_collector->tid); - - struct rrd_collector *old_rdc = rdcf->collector; - rdcf->collector = rrd_collector_acquire(); - rrd_collector_release(old_rdc); - changed = true; - } - - if(rdcf->execute_cb != new_rdcf->execute_cb) { - netdata_log_info("FUNCTIONS: function '%s' of host '%s' changed execute callback", - dictionary_acquired_item_name(item), rrdhost_hostname(host)); - - rdcf->execute_cb = new_rdcf->execute_cb; - changed = true; - } - - if(rdcf->help != new_rdcf->help) { - netdata_log_info("FUNCTIONS: function '%s' of host '%s' changed help text", - dictionary_acquired_item_name(item), rrdhost_hostname(host)); - - STRING *old = rdcf->help; - rdcf->help = new_rdcf->help; - string_freez(old); - changed = true; - } - else - string_freez(new_rdcf->help); - - if(rdcf->timeout != new_rdcf->timeout) { - netdata_log_info("FUNCTIONS: function '%s' of host '%s' changed timeout", - dictionary_acquired_item_name(item), rrdhost_hostname(host)); - - rdcf->timeout = new_rdcf->timeout; - changed = true; - } - - if(rdcf->sync != new_rdcf->sync) { - netdata_log_info("FUNCTIONS: function '%s' of host '%s' changed sync/async mode", - dictionary_acquired_item_name(item), rrdhost_hostname(host)); - - rdcf->sync = new_rdcf->sync; - changed = true; - } - - if(rdcf->execute_cb_data != new_rdcf->execute_cb_data) { - netdata_log_info("FUNCTIONS: function '%s' of host '%s' changed execute callback data", - dictionary_acquired_item_name(item), rrdhost_hostname(host)); - - rdcf->execute_cb_data = new_rdcf->execute_cb_data; - changed = true; - } - -// internal_error(true, "FUNCTIONS: adding function '%s' on host '%s', collection tid %d, %s", -// dictionary_acquired_item_name(item), rrdhost_hostname(host), -// rdcf->collector->tid, rdcf->collector->running ? "running" : "NOT running"); - - return changed; -} - -void rrdfunctions_host_init(RRDHOST *host) { - if(host->functions) return; - - host->functions = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, - &dictionary_stats_category_functions, sizeof(struct rrd_host_function)); - - dictionary_register_insert_callback(host->functions, rrd_functions_insert_callback, host); - dictionary_register_delete_callback(host->functions, rrd_functions_delete_callback, host); - dictionary_register_conflict_callback(host->functions, rrd_functions_conflict_callback, host); -} - -void rrdfunctions_host_destroy(RRDHOST *host) { - dictionary_destroy(host->functions); -} - -void rrd_function_add(RRDHOST *host, RRDSET *st, const char *name, int timeout, const char *help, - bool sync, rrd_function_execute_cb_t execute_cb, void *execute_cb_data) { - - // RRDSET *st may be NULL in this function - // to create a GLOBAL function - - if(st && !st->functions_view) - st->functions_view = dictionary_create_view(host->functions); - - char key[PLUGINSD_LINE_MAX + 1]; - sanitize_function_text(key, name, PLUGINSD_LINE_MAX); - - struct rrd_host_function tmp = { - .sync = sync, - .timeout = timeout, - .options = (st)?RRD_FUNCTION_LOCAL:RRD_FUNCTION_GLOBAL, - .execute_cb = execute_cb, - .execute_cb_data = execute_cb_data, - .help = string_strdupz(help), - }; - const DICTIONARY_ITEM *item = dictionary_set_and_acquire_item(host->functions, key, &tmp, sizeof(tmp)); - - if(st) - dictionary_view_set(st->functions_view, key, item); - else - rrdhost_flag_set(host, RRDHOST_FLAG_GLOBAL_FUNCTIONS_UPDATED); - - dictionary_acquired_item_release(host->functions, item); -} - -void rrd_functions_expose_rrdpush(RRDSET *st, BUFFER *wb) { - if(!st->functions_view) - return; - - struct rrd_host_function *tmp; - dfe_start_read(st->functions_view, tmp) { - buffer_sprintf(wb - , PLUGINSD_KEYWORD_FUNCTION " \"%s\" %d \"%s\"\n" - , tmp_dfe.name - , tmp->timeout - , string2str(tmp->help) - ); - } - dfe_done(tmp); -} - -void rrd_functions_expose_global_rrdpush(RRDHOST *host, BUFFER *wb) { - rrdhost_flag_clear(host, RRDHOST_FLAG_GLOBAL_FUNCTIONS_UPDATED); - - struct rrd_host_function *tmp; - dfe_start_read(host->functions, tmp) { - if(!(tmp->options & RRD_FUNCTION_GLOBAL)) - continue; - - buffer_sprintf(wb - , PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\"\n" - , tmp_dfe.name - , tmp->timeout - , string2str(tmp->help) - ); - } - dfe_done(tmp); -} - -struct { - const char *format; - HTTP_CONTENT_TYPE content_type; -} function_formats[] = { - { .format = "application/json", CT_APPLICATION_JSON }, - { .format = "text/plain", CT_TEXT_PLAIN }, - { .format = "application/xml", CT_APPLICATION_XML }, - { .format = "prometheus", CT_PROMETHEUS }, - { .format = "text", CT_TEXT_PLAIN }, - { .format = "txt", CT_TEXT_PLAIN }, - { .format = "json", CT_APPLICATION_JSON }, - { .format = "html", CT_TEXT_HTML }, - { .format = "text/html", CT_TEXT_HTML }, - { .format = "xml", CT_APPLICATION_XML }, - - // terminator - { .format = NULL, CT_TEXT_PLAIN }, -}; - -uint8_t functions_format_to_content_type(const char *format) { - if(format && *format) { - for (int i = 0; function_formats[i].format; i++) - if (strcmp(function_formats[i].format, format) == 0) - return function_formats[i].content_type; - } - - return CT_TEXT_PLAIN; -} - -const char *functions_content_type_to_format(HTTP_CONTENT_TYPE content_type) { - for (int i = 0; function_formats[i].format; i++) - if (function_formats[i].content_type == content_type) - return function_formats[i].format; - - return "text/plain"; -} - -int rrd_call_function_error(BUFFER *wb, const char *msg, int code) { - char buffer[PLUGINSD_LINE_MAX]; - json_escape_string(buffer, msg, PLUGINSD_LINE_MAX); - - buffer_flush(wb); - buffer_sprintf(wb, "{\"status\":%d,\"error_message\":\"%s\"}", code, buffer); - wb->content_type = CT_APPLICATION_JSON; - buffer_no_cacheable(wb); - return code; -} - -static int rrd_call_function_find(RRDHOST *host, BUFFER *wb, const char *name, size_t key_length, const DICTIONARY_ITEM **item) { - char buffer[MAX_FUNCTION_LENGTH + 1]; - - strncpyz(buffer, name, MAX_FUNCTION_LENGTH); - char *s = NULL; - - bool found = false; - *item = NULL; - if(host->functions) { - while (buffer[0]) { - if((*item = dictionary_get_and_acquire_item(host->functions, buffer))) { - found = true; - - struct rrd_host_function *rdcf = dictionary_acquired_item_value(*item); - if(rrd_collector_running(rdcf->collector)) { - break; - } - else { - dictionary_acquired_item_release(host->functions, *item); - *item = NULL; - } - } - - // if s == NULL, set it to the end of the buffer - // this should happen only the first time - if (unlikely(!s)) - s = &buffer[key_length - 1]; - - // skip a word from the end - while (s >= buffer && !isspace(*s)) *s-- = '\0'; - - // skip all spaces - while (s >= buffer && isspace(*s)) *s-- = '\0'; - } - } - - buffer_flush(wb); - - if(!(*item)) { - if(found) - return rrd_call_function_error(wb, - "The collector that registered this function, is not currently running.", - HTTP_RESP_SERVICE_UNAVAILABLE); - else - return rrd_call_function_error(wb, - "No collector is supplying this function on this host at this time.", - HTTP_RESP_NOT_FOUND); - } - - return HTTP_RESP_OK; -} - -// ---------------------------------------------------------------------------- - -struct rrd_function_inflight { - bool used; - - RRDHOST *host; - const char *transaction; - const char *cmd; - const char *sanitized_cmd; - size_t sanitized_cmd_length; - int timeout; - bool cancelled; - - const DICTIONARY_ITEM *host_function_acquired; - - // the collector - // we acquire this structure at the beginning, - // and we release it at the end - struct rrd_host_function *rdcf; - - struct { - BUFFER *wb; - - // in async mode, - // the function to call to send the result back - rrd_function_result_callback_t cb; - void *data; - } result; - - struct { - // to be called in sync mode - // while the function is running - // to check if the function has been cancelled - rrd_function_is_cancelled_cb_t cb; - void *data; - } is_cancelled; - - struct { - // to be registered by the function itself - // used to signal the function to cancel - rrd_function_canceller_cb_t cb; - void *data; - } canceller; -}; - -static DICTIONARY *rrd_functions_inflight_requests = NULL; - -static void rrd_functions_inflight_delete_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { - struct rrd_function_inflight *r = value; - - // internal_error(true, "FUNCTIONS: transaction '%s' finished", r->transaction); - - freez((void *)r->transaction); - freez((void *)r->cmd); - freez((void *)r->sanitized_cmd); - dictionary_acquired_item_release(r->host->functions, r->host_function_acquired); -} - -void rrd_functions_inflight_init(void) { - if(rrd_functions_inflight_requests) - return; - - rrd_functions_inflight_requests = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct rrd_function_inflight)); - - dictionary_register_delete_callback(rrd_functions_inflight_requests, rrd_functions_inflight_delete_cb, NULL); -} - -void rrd_functions_inflight_destroy(void) { - if(!rrd_functions_inflight_requests) - return; - - dictionary_destroy(rrd_functions_inflight_requests); - rrd_functions_inflight_requests = NULL; -} - -static void rrd_inflight_async_function_register_canceller_cb(void *register_canceller_cb_data, rrd_function_canceller_cb_t canceller_cb, void *canceller_cb_data) { - struct rrd_function_inflight *r = register_canceller_cb_data; - r->canceller.cb = canceller_cb; - r->canceller.data = canceller_cb_data; -} - -// ---------------------------------------------------------------------------- -// waiting for async function completion - -struct rrd_function_call_wait { - RRDHOST *host; - const DICTIONARY_ITEM *host_function_acquired; - char *transaction; - - bool free_with_signal; - bool data_are_ready; - netdata_mutex_t mutex; - pthread_cond_t cond; - int code; -}; - -static void rrd_inflight_function_cleanup(RRDHOST *host __maybe_unused, - const DICTIONARY_ITEM *host_function_acquired __maybe_unused, - const char *transaction) { - dictionary_del(rrd_functions_inflight_requests, transaction); - dictionary_garbage_collect(rrd_functions_inflight_requests); -} - -static void rrd_function_call_wait_free(struct rrd_function_call_wait *tmp) { - rrd_inflight_function_cleanup(tmp->host, tmp->host_function_acquired, tmp->transaction); - freez(tmp->transaction); - - pthread_cond_destroy(&tmp->cond); - netdata_mutex_destroy(&tmp->mutex); - freez(tmp); -} - -static void rrd_async_function_signal_when_ready(BUFFER *temp_wb __maybe_unused, int code, void *callback_data) { - struct rrd_function_call_wait *tmp = callback_data; - bool we_should_free = false; - - netdata_mutex_lock(&tmp->mutex); - - // since we got the mutex, - // the waiting thread is either in pthread_cond_timedwait() - // or gave up and left. - - tmp->code = code; - tmp->data_are_ready = true; - - if(tmp->free_with_signal) - we_should_free = true; - - pthread_cond_signal(&tmp->cond); - - netdata_mutex_unlock(&tmp->mutex); - - if(we_should_free) { - buffer_free(temp_wb); - rrd_function_call_wait_free(tmp); - } -} - -static void rrd_inflight_async_function_nowait_finished(BUFFER *wb, int code, void *data) { - struct rrd_function_inflight *r = data; - - if(r->result.cb) - r->result.cb(wb, code, r->result.data); - - rrd_inflight_function_cleanup(r->host, r->host_function_acquired, r->transaction); -} - -static bool rrd_inflight_async_function_is_cancelled(void *data) { - struct rrd_function_inflight *r = data; - return __atomic_load_n(&r->cancelled, __ATOMIC_RELAXED); -} - -static inline int rrd_call_function_async_and_dont_wait(struct rrd_function_inflight *r) { - int code = r->rdcf->execute_cb(r->result.wb, r->timeout, r->sanitized_cmd, r->rdcf->execute_cb_data, - rrd_inflight_async_function_nowait_finished, r, - rrd_inflight_async_function_is_cancelled, r, - rrd_inflight_async_function_register_canceller_cb, r); - - if(code != HTTP_RESP_OK) { - if (!buffer_strlen(r->result.wb)) - rrd_call_function_error(r->result.wb, "Failed to send request to the collector.", code); - - rrd_inflight_function_cleanup(r->host, r->host_function_acquired, r->transaction); - } - - return code; -} - -static int rrd_call_function_async_and_wait(struct rrd_function_inflight *r) { - struct timespec tp; - clock_gettime(CLOCK_REALTIME, &tp); - usec_t now_ut = tp.tv_sec * USEC_PER_SEC + tp.tv_nsec / NSEC_PER_USEC; - usec_t end_ut = now_ut + r->timeout * USEC_PER_SEC + RRDFUNCTIONS_TIMEOUT_EXTENSION_UT; - - struct rrd_function_call_wait *tmp = mallocz(sizeof(struct rrd_function_call_wait)); - tmp->free_with_signal = false; - tmp->data_are_ready = false; - tmp->host = r->host; - tmp->host_function_acquired = r->host_function_acquired; - tmp->transaction = strdupz(r->transaction); - netdata_mutex_init(&tmp->mutex); - pthread_cond_init(&tmp->cond, NULL); - - // we need a temporary BUFFER, because we may time out and the caller supplied one may vanish - // so, we create a new one we guarantee will survive until the collector finishes... - - bool we_should_free = true; - BUFFER *temp_wb = buffer_create(PLUGINSD_LINE_MAX + 1, &netdata_buffers_statistics.buffers_functions); // we need it because we may give up on it - temp_wb->content_type = r->result.wb->content_type; - - int code = r->rdcf->execute_cb(temp_wb, r->timeout, r->sanitized_cmd, r->rdcf->execute_cb_data, - // we overwrite the result callbacks, - // so that we can clean up the allocations made - rrd_async_function_signal_when_ready, tmp, - rrd_inflight_async_function_is_cancelled, r, - rrd_inflight_async_function_register_canceller_cb, r); - - if (code == HTTP_RESP_OK) { - netdata_mutex_lock(&tmp->mutex); - - bool cancelled = false; - int rc = 0; - while (rc == 0 && !cancelled && !tmp->data_are_ready) { - clock_gettime(CLOCK_REALTIME, &tp); - now_ut = tp.tv_sec * USEC_PER_SEC + tp.tv_nsec / NSEC_PER_USEC; - - if(now_ut >= end_ut) { - rc = ETIMEDOUT; - break; - } - - tp.tv_nsec += 10 * NSEC_PER_MSEC; - if(tp.tv_nsec > (long)(1 * NSEC_PER_SEC)) { - tp.tv_sec++; - tp.tv_nsec -= 1 * NSEC_PER_SEC; - } - - // the mutex is unlocked within pthread_cond_timedwait() - rc = pthread_cond_timedwait(&tmp->cond, &tmp->mutex, &tp); - // the mutex is again ours - - if(rc == ETIMEDOUT) { - rc = 0; - if (!tmp->data_are_ready && r->is_cancelled.cb && - r->is_cancelled.cb(r->is_cancelled.data)) { -// internal_error(true, "FUNCTIONS: transaction '%s' is cancelled while waiting for response", -// r->transaction); - rc = 0; - cancelled = true; - rrd_function_cancel(r->transaction); - break; - } - } - } - - if (tmp->data_are_ready) { - // we have a response - buffer_fast_strcat(r->result.wb, buffer_tostring(temp_wb), buffer_strlen(temp_wb)); - r->result.wb->content_type = temp_wb->content_type; - r->result.wb->expires = temp_wb->expires; - - if(r->result.wb->expires) - buffer_cacheable(r->result.wb); - else - buffer_no_cacheable(r->result.wb); - - code = tmp->code; - } - else if (rc == ETIMEDOUT || cancelled) { - // timeout - // we will go away and let the callback free the structure - tmp->free_with_signal = true; - we_should_free = false; - - if(cancelled) - code = rrd_call_function_error(r->result.wb, - "Request cancelled", - HTTP_RESP_CLIENT_CLOSED_REQUEST); - else - code = rrd_call_function_error(r->result.wb, - "Timeout while waiting for a response from the collector.", - HTTP_RESP_GATEWAY_TIMEOUT); - } - else - code = rrd_call_function_error(r->result.wb, - "Internal error while communicating with the collector", - HTTP_RESP_INTERNAL_SERVER_ERROR); - - netdata_mutex_unlock(&tmp->mutex); - } - else { - if(!buffer_strlen(r->result.wb)) - rrd_call_function_error(r->result.wb, "The collector returned an error.", code); - } - - if (we_should_free) { - rrd_function_call_wait_free(tmp); - buffer_free(temp_wb); - } - - return code; -} - -static inline int rrd_call_function_async(struct rrd_function_inflight *r, bool wait) { - if(wait) - return rrd_call_function_async_and_wait(r); - else - return rrd_call_function_async_and_dont_wait(r); -} - - -void call_virtual_function_async(BUFFER *wb, RRDHOST *host, const char *name, const char *payload, rrd_function_result_callback_t callback, void *callback_data); -// ---------------------------------------------------------------------------- - -int rrd_function_run(RRDHOST *host, BUFFER *result_wb, int timeout, const char *cmd, - bool wait, const char *transaction, - rrd_function_result_callback_t result_cb, void *result_cb_data, - rrd_function_is_cancelled_cb_t is_cancelled_cb, void *is_cancelled_cb_data, const char *payload) { - - int code; - char sanitized_cmd[PLUGINSD_LINE_MAX + 1]; - const DICTIONARY_ITEM *host_function_acquired = NULL; - - // ------------------------------------------------------------------------ - // find the function - - size_t sanitized_cmd_length = sanitize_function_text(sanitized_cmd, cmd, PLUGINSD_LINE_MAX); - - if (is_dyncfg_function(sanitized_cmd, DYNCFG_FUNCTION_TYPE_ALL)) { - call_virtual_function_async(result_wb, host, sanitized_cmd, payload, result_cb, result_cb_data); - return HTTP_RESP_OK; - } - - code = rrd_call_function_find(host, result_wb, sanitized_cmd, sanitized_cmd_length, &host_function_acquired); - if(code != HTTP_RESP_OK) - return code; - - struct rrd_host_function *rdcf = dictionary_acquired_item_value(host_function_acquired); - - if(timeout <= 0) - timeout = rdcf->timeout; - - - // ------------------------------------------------------------------------ - // the function can only be executed in sync mode - - if(rdcf->sync) { - // the caller has to wait - - code = rdcf->execute_cb(result_wb, timeout, sanitized_cmd, rdcf->execute_cb_data, - result_cb, result_cb_data, - is_cancelled_cb, is_cancelled_cb_data, // it is ok to pass these, we block the caller - NULL, NULL); // no need to pass, we will wait - - if (code != HTTP_RESP_OK && !buffer_strlen(result_wb)) - rrd_call_function_error(result_wb, "Collector reported error.", code); - - dictionary_acquired_item_release(host->functions, host_function_acquired); - return code; - } - - - // ------------------------------------------------------------------------ - // the function can only be executed in async mode - // put the function into the inflight requests - - char uuid_str[UUID_COMPACT_STR_LEN]; - if(!transaction) { - uuid_t uuid; - uuid_generate_random(uuid); - uuid_unparse_lower_compact(uuid, uuid_str); - transaction = uuid_str; - } - - // put the request into the inflight requests - struct rrd_function_inflight t = { - .used = false, - .host = host, - .cmd = strdupz(cmd), - .sanitized_cmd = strdupz(sanitized_cmd), - .sanitized_cmd_length = sanitized_cmd_length, - .transaction = strdupz(transaction), - .timeout = timeout, - .cancelled = false, - .host_function_acquired = host_function_acquired, - .rdcf = rdcf, - .result = { - .wb = result_wb, - .cb = result_cb, - .data = result_cb_data, - }, - .is_cancelled = { - .cb = is_cancelled_cb, - .data = is_cancelled_cb_data, - } - }; - struct rrd_function_inflight *r = dictionary_set(rrd_functions_inflight_requests, transaction, &t, sizeof(t)); - if(r->used) { - netdata_log_info("FUNCTIONS: duplicate transaction '%s', function: '%s'", t.transaction, t.cmd); - code = rrd_call_function_error(result_wb, "duplicate transaction", HTTP_RESP_BAD_REQUEST); - freez((void *)t.transaction); - freez((void *)t.cmd); - freez((void *)t.sanitized_cmd); - dictionary_acquired_item_release(r->host->functions, t.host_function_acquired); - return code; - } - r->used = true; - // internal_error(true, "FUNCTIONS: transaction '%s' started", r->transaction); - - return rrd_call_function_async(r, wait); -} - -void rrd_function_cancel(const char *transaction) { - // internal_error(true, "FUNCTIONS: request to cancel transaction '%s'", transaction); - - const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(rrd_functions_inflight_requests, transaction); - if(!item) { - netdata_log_info("FUNCTIONS: received a cancel request for transaction '%s', but the transaction is not running.", - transaction); - return; - } - - struct rrd_function_inflight *r = dictionary_acquired_item_value(item); - - bool cancelled = __atomic_load_n(&r->cancelled, __ATOMIC_RELAXED); - if(cancelled) { - netdata_log_info("FUNCTIONS: received a cancel request for transaction '%s', but it is already cancelled.", - transaction); - goto cleanup; - } - - __atomic_store_n(&r->cancelled, true, __ATOMIC_RELAXED); - - int32_t expected = __atomic_load_n(&r->rdcf->collector->refcount_canceller, __ATOMIC_RELAXED); - int32_t wanted; - do { - if(expected < 0) { - netdata_log_info("FUNCTIONS: received a cancel request for transaction '%s', but the collector is not running.", - transaction); - goto cleanup; - } - - wanted = expected + 1; - } while(!__atomic_compare_exchange_n(&r->rdcf->collector->refcount_canceller, &expected, wanted, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)); - - if(r->canceller.cb) - r->canceller.cb(r->canceller.data); - - __atomic_sub_fetch(&r->rdcf->collector->refcount_canceller, 1, __ATOMIC_RELAXED); - -cleanup: - dictionary_acquired_item_release(rrd_functions_inflight_requests, item); -} - -// ---------------------------------------------------------------------------- - -static void functions2json(DICTIONARY *functions, BUFFER *wb) -{ - struct rrd_host_function *t; - dfe_start_read(functions, t) - { - if (!rrd_collector_running(t->collector)) - continue; - - buffer_json_member_add_object(wb, t_dfe.name); - buffer_json_member_add_string_or_empty(wb, "help", string2str(t->help)); - buffer_json_member_add_int64(wb, "timeout", (int64_t)t->timeout); - - char options[65]; - snprintfz( - options, - 64, - "%s%s", - (t->options & RRD_FUNCTION_LOCAL) ? "LOCAL " : "", - (t->options & RRD_FUNCTION_GLOBAL) ? "GLOBAL" : ""); - - buffer_json_member_add_string_or_empty(wb, "options", options); - buffer_json_object_close(wb); - } - dfe_done(t); -} - -void chart_functions2json(RRDSET *st, BUFFER *wb) { - if(!st || !st->functions_view) return; - - functions2json(st->functions_view, wb); -} - -void host_functions2json(RRDHOST *host, BUFFER *wb) { - if(!host || !host->functions) return; - - buffer_json_member_add_object(wb, "functions"); - - struct rrd_host_function *t; - dfe_start_read(host->functions, t) { - if(!rrd_collector_running(t->collector)) continue; - - buffer_json_member_add_object(wb, t_dfe.name); - buffer_json_member_add_string(wb, "help", string2str(t->help)); - buffer_json_member_add_int64(wb, "timeout", t->timeout); - buffer_json_member_add_array(wb, "options"); - if(t->options & RRD_FUNCTION_GLOBAL) - buffer_json_add_array_item_string(wb, "GLOBAL"); - if(t->options & RRD_FUNCTION_LOCAL) - buffer_json_add_array_item_string(wb, "LOCAL"); - buffer_json_array_close(wb); - buffer_json_object_close(wb); - } - dfe_done(t); - - buffer_json_object_close(wb); -} - -void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst, void *value, size_t value_size) { - if(!rrdset_functions_view || !dst) return; - - struct rrd_host_function *t; - dfe_start_read(rrdset_functions_view, t) { - if(!rrd_collector_running(t->collector)) continue; - - dictionary_set(dst, t_dfe.name, value, value_size); - } - dfe_done(t); -} - -void host_functions_to_dict(RRDHOST *host, DICTIONARY *dst, void *value, size_t value_size, STRING **help) { - if(!host || !host->functions || !dictionary_entries(host->functions) || !dst) return; - - struct rrd_host_function *t; - dfe_start_read(host->functions, t) { - if(!rrd_collector_running(t->collector)) continue; - - if(help) - *help = t->help; - - dictionary_set(dst, t_dfe.name, value, value_size); - } - dfe_done(t); -} - -// ---------------------------------------------------------------------------- - -int rrdhost_function_streaming(BUFFER *wb, int timeout __maybe_unused, const char *function __maybe_unused, - void *collector_data __maybe_unused, - rrd_function_result_callback_t result_cb, void *result_cb_data, - rrd_function_is_cancelled_cb_t is_cancelled_cb, void *is_cancelled_cb_data, - rrd_function_register_canceller_cb_t register_canceller_cb __maybe_unused, - void *register_canceller_cb_data __maybe_unused) { - - time_t now = now_realtime_sec(); - - buffer_flush(wb); - wb->content_type = CT_APPLICATION_JSON; - buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT); - - buffer_json_member_add_string(wb, "hostname", rrdhost_hostname(localhost)); - buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK); - buffer_json_member_add_string(wb, "type", "table"); - buffer_json_member_add_time_t(wb, "update_every", 1); - buffer_json_member_add_string(wb, "help", RRDFUNCTIONS_STREAMING_HELP); - buffer_json_member_add_array(wb, "data"); - - size_t max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_MAX]; - size_t max_db_metrics = 0, max_db_instances = 0, max_db_contexts = 0; - size_t max_collection_replication_instances = 0, max_streaming_replication_instances = 0; - size_t max_ml_anomalous = 0, max_ml_normal = 0, max_ml_trained = 0, max_ml_pending = 0, max_ml_silenced = 0; - { - RRDHOST *host; - dfe_start_read(rrdhost_root_index, host) { - RRDHOST_STATUS s; - rrdhost_status(host, now, &s); - buffer_json_add_array_item_array(wb); - - if(s.db.metrics > max_db_metrics) - max_db_metrics = s.db.metrics; - - if(s.db.instances > max_db_instances) - max_db_instances = s.db.instances; - - if(s.db.contexts > max_db_contexts) - max_db_contexts = s.db.contexts; - - if(s.ingest.replication.instances > max_collection_replication_instances) - max_collection_replication_instances = s.ingest.replication.instances; - - if(s.stream.replication.instances > max_streaming_replication_instances) - max_streaming_replication_instances = s.stream.replication.instances; - - for(int i = 0; i < STREAM_TRAFFIC_TYPE_MAX ;i++) { - if (s.stream.sent_bytes_on_this_connection_per_type[i] > - max_sent_bytes_on_this_connection_per_type[i]) - max_sent_bytes_on_this_connection_per_type[i] = - s.stream.sent_bytes_on_this_connection_per_type[i]; - } - - // retention - buffer_json_add_array_item_string(wb, rrdhost_hostname(s.host)); // Node - buffer_json_add_array_item_uint64(wb, s.db.first_time_s * MSEC_PER_SEC); // dbFrom - buffer_json_add_array_item_uint64(wb, s.db.last_time_s * MSEC_PER_SEC); // dbTo - - if(s.db.first_time_s && s.db.last_time_s && s.db.last_time_s > s.db.first_time_s) - buffer_json_add_array_item_uint64(wb, s.db.last_time_s - s.db.first_time_s); // dbDuration - else - buffer_json_add_array_item_string(wb, NULL); // dbDuration - - buffer_json_add_array_item_uint64(wb, s.db.metrics); // dbMetrics - buffer_json_add_array_item_uint64(wb, s.db.instances); // dbInstances - buffer_json_add_array_item_uint64(wb, s.db.contexts); // dbContexts - - // statuses - buffer_json_add_array_item_string(wb, rrdhost_ingest_status_to_string(s.ingest.status)); // InStatus - buffer_json_add_array_item_string(wb, rrdhost_streaming_status_to_string(s.stream.status)); // OutStatus - buffer_json_add_array_item_string(wb, rrdhost_ml_status_to_string(s.ml.status)); // MLStatus - - // collection - if(s.ingest.since) { - buffer_json_add_array_item_uint64(wb, s.ingest.since * MSEC_PER_SEC); // InSince - buffer_json_add_array_item_time_t(wb, s.now - s.ingest.since); // InAge - } - else { - buffer_json_add_array_item_string(wb, NULL); // InSince - buffer_json_add_array_item_string(wb, NULL); // InAge - } - buffer_json_add_array_item_string(wb, stream_handshake_error_to_string(s.ingest.reason)); // InReason - buffer_json_add_array_item_uint64(wb, s.ingest.hops); // InHops - buffer_json_add_array_item_double(wb, s.ingest.replication.completion); // InReplCompletion - buffer_json_add_array_item_uint64(wb, s.ingest.replication.instances); // InReplInstances - buffer_json_add_array_item_string(wb, s.ingest.peers.local.ip); // InLocalIP - buffer_json_add_array_item_uint64(wb, s.ingest.peers.local.port); // InLocalPort - buffer_json_add_array_item_string(wb, s.ingest.peers.peer.ip); // InRemoteIP - buffer_json_add_array_item_uint64(wb, s.ingest.peers.peer.port); // InRemotePort - buffer_json_add_array_item_string(wb, s.ingest.ssl ? "SSL" : "PLAIN"); // InSSL - stream_capabilities_to_json_array(wb, s.ingest.capabilities, NULL); // InCapabilities - - // streaming - if(s.stream.since) { - buffer_json_add_array_item_uint64(wb, s.stream.since * MSEC_PER_SEC); // OutSince - buffer_json_add_array_item_time_t(wb, s.now - s.stream.since); // OutAge - } - else { - buffer_json_add_array_item_string(wb, NULL); // OutSince - buffer_json_add_array_item_string(wb, NULL); // OutAge - } - buffer_json_add_array_item_string(wb, stream_handshake_error_to_string(s.stream.reason)); // OutReason - buffer_json_add_array_item_uint64(wb, s.stream.hops); // OutHops - buffer_json_add_array_item_double(wb, s.stream.replication.completion); // OutReplCompletion - buffer_json_add_array_item_uint64(wb, s.stream.replication.instances); // OutReplInstances - buffer_json_add_array_item_string(wb, s.stream.peers.local.ip); // OutLocalIP - buffer_json_add_array_item_uint64(wb, s.stream.peers.local.port); // OutLocalPort - buffer_json_add_array_item_string(wb, s.stream.peers.peer.ip); // OutRemoteIP - buffer_json_add_array_item_uint64(wb, s.stream.peers.peer.port); // OutRemotePort - buffer_json_add_array_item_string(wb, s.stream.ssl ? "SSL" : "PLAIN"); // OutSSL - buffer_json_add_array_item_string(wb, s.stream.compression ? "COMPRESSED" : "UNCOMPRESSED"); // OutCompression - stream_capabilities_to_json_array(wb, s.stream.capabilities, NULL); // OutCapabilities - buffer_json_add_array_item_uint64(wb, s.stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_DATA]); - buffer_json_add_array_item_uint64(wb, s.stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_METADATA]); - buffer_json_add_array_item_uint64(wb, s.stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_REPLICATION]); - buffer_json_add_array_item_uint64(wb, s.stream.sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_FUNCTIONS]); - - buffer_json_add_array_item_array(wb); // OutAttemptHandshake - time_t last_attempt = 0; - for(struct rrdpush_destinations *d = host->destinations; d ; d = d->next) { - if(d->since > last_attempt) - last_attempt = d->since; - - buffer_json_add_array_item_string(wb, stream_handshake_error_to_string(d->reason)); - } - buffer_json_array_close(wb); // // OutAttemptHandshake - - if(!last_attempt) { - buffer_json_add_array_item_string(wb, NULL); // OutAttemptSince - buffer_json_add_array_item_string(wb, NULL); // OutAttemptAge - } - else { - buffer_json_add_array_item_uint64(wb, last_attempt * 1000); // OutAttemptSince - buffer_json_add_array_item_time_t(wb, s.now - last_attempt); // OutAttemptAge - } - - // ML - if(s.ml.status == RRDHOST_ML_STATUS_RUNNING) { - buffer_json_add_array_item_uint64(wb, s.ml.metrics.anomalous); // MlAnomalous - buffer_json_add_array_item_uint64(wb, s.ml.metrics.normal); // MlNormal - buffer_json_add_array_item_uint64(wb, s.ml.metrics.trained); // MlTrained - buffer_json_add_array_item_uint64(wb, s.ml.metrics.pending); // MlPending - buffer_json_add_array_item_uint64(wb, s.ml.metrics.silenced); // MlSilenced - - if(s.ml.metrics.anomalous > max_ml_anomalous) - max_ml_anomalous = s.ml.metrics.anomalous; - - if(s.ml.metrics.normal > max_ml_normal) - max_ml_normal = s.ml.metrics.normal; - - if(s.ml.metrics.trained > max_ml_trained) - max_ml_trained = s.ml.metrics.trained; - - if(s.ml.metrics.pending > max_ml_pending) - max_ml_pending = s.ml.metrics.pending; - - if(s.ml.metrics.silenced > max_ml_silenced) - max_ml_silenced = s.ml.metrics.silenced; - - } - else { - buffer_json_add_array_item_string(wb, NULL); // MlAnomalous - buffer_json_add_array_item_string(wb, NULL); // MlNormal - buffer_json_add_array_item_string(wb, NULL); // MlTrained - buffer_json_add_array_item_string(wb, NULL); // MlPending - buffer_json_add_array_item_string(wb, NULL); // MlSilenced - } - - // close - buffer_json_array_close(wb); - } - dfe_done(host); - } - buffer_json_array_close(wb); // data - buffer_json_member_add_object(wb, "columns"); - { - size_t field_id = 0; - - // Node - buffer_rrdf_table_add_field(wb, field_id++, "Node", "Node's Hostname", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY, - NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "dbFrom", "DB Data Retention From", - RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME_MS, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "dbTo", "DB Data Retention To", - RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME_MS, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "dbDuration", "DB Data Retention Duration", - RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION_S, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "dbMetrics", "Time-series Metrics in the DB", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, NULL, max_db_metrics, RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "dbInstances", "Instances in the DB", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, NULL, max_db_instances, RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "dbContexts", "Contexts in the DB", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, NULL, max_db_contexts, RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - // --- statuses --- - - buffer_rrdf_table_add_field(wb, field_id++, "InStatus", "Data Collection Online Status", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - - buffer_rrdf_table_add_field(wb, field_id++, "OutStatus", "Streaming Online Status", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "MlStatus", "ML Status", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - // --- collection --- - - buffer_rrdf_table_add_field(wb, field_id++, "InSince", "Last Data Collection Status Change", - RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME_MS, - 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "InAge", "Last Data Collection Online Status Change Age", - RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION_S, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "InReason", "Data Collection Online Status Reason", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "InHops", "Data Collection Distance Hops from Origin Node", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "InReplCompletion", "Inbound Replication Completion", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, - 1, "%", 100.0, RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "InReplInstances", "Inbound Replicating Instances", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, "instances", max_collection_replication_instances, RRDF_FIELD_SORT_DESCENDING, - NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "InLocalIP", "Inbound Local IP", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "InLocalPort", "Inbound Local Port", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "InRemoteIP", "Inbound Remote IP", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "InRemotePort", "Inbound Remote Port", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "InSSL", "Inbound SSL Connection", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "InCapabilities", "Inbound Connection Capabilities", - RRDF_FIELD_TYPE_ARRAY, RRDF_FIELD_VISUAL_PILL, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_NONE, NULL); - - // --- streaming --- - - buffer_rrdf_table_add_field(wb, field_id++, "OutSince", "Last Streaming Status Change", - RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME_MS, - 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutAge", "Last Streaming Status Change Age", - RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION_S, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutReason", "Streaming Status Reason", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutHops", "Streaming Distance Hops from Origin Node", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutReplCompletion", "Outbound Replication Completion", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, - 1, "%", 100.0, RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutReplInstances", "Outbound Replicating Instances", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, "instances", max_streaming_replication_instances, RRDF_FIELD_SORT_DESCENDING, - NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutLocalIP", "Outbound Local IP", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutLocalPort", "Outbound Local Port", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutRemoteIP", "Outbound Remote IP", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutRemotePort", "Outbound Remote Port", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutSSL", "Outbound SSL Connection", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutCompression", "Outbound Compressed Connection", - RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutCapabilities", "Outbound Connection Capabilities", - RRDF_FIELD_TYPE_ARRAY, RRDF_FIELD_VISUAL_PILL, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutTrafficData", "Outbound Metric Data Traffic", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, "bytes", max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_DATA], - RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutTrafficMetadata", "Outbound Metric Metadata Traffic", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, "bytes", - max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_METADATA], - RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutTrafficReplication", "Outbound Metric Replication Traffic", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, "bytes", - max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_REPLICATION], - RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutTrafficFunctions", "Outbound Metric Functions Traffic", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, "bytes", - max_sent_bytes_on_this_connection_per_type[STREAM_TRAFFIC_TYPE_FUNCTIONS], - RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutAttemptHandshake", - "Outbound Connection Attempt Handshake Status", - RRDF_FIELD_TYPE_ARRAY, RRDF_FIELD_VISUAL_PILL, RRDF_FIELD_TRANSFORM_NONE, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutAttemptSince", - "Last Outbound Connection Attempt Status Change Time", - RRDF_FIELD_TYPE_TIMESTAMP, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DATETIME_MS, - 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "OutAttemptAge", - "Last Outbound Connection Attempt Status Change Age", - RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION_S, - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, - RRDF_FIELD_SUMMARY_MIN, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_VISIBLE, NULL); - - // --- ML --- - - buffer_rrdf_table_add_field(wb, field_id++, "MlAnomalous", "Number of Anomalous Metrics", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, "metrics", - max_ml_anomalous, - RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "MlNormal", "Number of Not Anomalous Metrics", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, "metrics", - max_ml_normal, - RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "MlTrained", "Number of Trained Metrics", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, "metrics", - max_ml_trained, - RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "MlPending", "Number of Pending Metrics", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, "metrics", - max_ml_pending, - RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - - buffer_rrdf_table_add_field(wb, field_id++, "MlSilenced", "Number of Silenced Metrics", - RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, - 0, "metrics", - max_ml_silenced, - RRDF_FIELD_SORT_DESCENDING, NULL, - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_OPTS_NONE, NULL); - } - buffer_json_object_close(wb); // columns - buffer_json_member_add_string(wb, "default_sort_column", "Node"); - buffer_json_member_add_object(wb, "charts"); - { - // Data Collection Age chart - buffer_json_member_add_object(wb, "InAge"); - { - buffer_json_member_add_string(wb, "name", "Data Collection Age"); - buffer_json_member_add_string(wb, "type", "stacked-bar"); - buffer_json_member_add_array(wb, "columns"); - { - buffer_json_add_array_item_string(wb, "InAge"); - } - buffer_json_array_close(wb); - } - buffer_json_object_close(wb); - - // Streaming Age chart - buffer_json_member_add_object(wb, "OutAge"); - { - buffer_json_member_add_string(wb, "name", "Streaming Age"); - buffer_json_member_add_string(wb, "type", "stacked-bar"); - buffer_json_member_add_array(wb, "columns"); - { - buffer_json_add_array_item_string(wb, "OutAge"); - } - buffer_json_array_close(wb); - } - buffer_json_object_close(wb); - - // DB Duration - buffer_json_member_add_object(wb, "dbDuration"); - { - buffer_json_member_add_string(wb, "name", "Retention Duration"); - buffer_json_member_add_string(wb, "type", "stacked-bar"); - buffer_json_member_add_array(wb, "columns"); - { - buffer_json_add_array_item_string(wb, "dbDuration"); - } - buffer_json_array_close(wb); - } - buffer_json_object_close(wb); - } - buffer_json_object_close(wb); // charts - - buffer_json_member_add_array(wb, "default_charts"); - { - buffer_json_add_array_item_array(wb); - buffer_json_add_array_item_string(wb, "InAge"); - buffer_json_add_array_item_string(wb, "Node"); - buffer_json_array_close(wb); - - buffer_json_add_array_item_array(wb); - buffer_json_add_array_item_string(wb, "OutAge"); - buffer_json_add_array_item_string(wb, "Node"); - buffer_json_array_close(wb); - } - buffer_json_array_close(wb); - - buffer_json_member_add_object(wb, "group_by"); - { - buffer_json_member_add_object(wb, "Node"); - { - buffer_json_member_add_string(wb, "name", "Node"); - buffer_json_member_add_array(wb, "columns"); - { - buffer_json_add_array_item_string(wb, "Node"); - } - buffer_json_array_close(wb); - } - buffer_json_object_close(wb); - - buffer_json_member_add_object(wb, "InStatus"); - { - buffer_json_member_add_string(wb, "name", "Nodes by Collection Status"); - buffer_json_member_add_array(wb, "columns"); - { - buffer_json_add_array_item_string(wb, "InStatus"); - } - buffer_json_array_close(wb); - } - buffer_json_object_close(wb); - - buffer_json_member_add_object(wb, "OutStatus"); - { - buffer_json_member_add_string(wb, "name", "Nodes by Streaming Status"); - buffer_json_member_add_array(wb, "columns"); - { - buffer_json_add_array_item_string(wb, "OutStatus"); - } - buffer_json_array_close(wb); - } - buffer_json_object_close(wb); - - buffer_json_member_add_object(wb, "MlStatus"); - { - buffer_json_member_add_string(wb, "name", "Nodes by ML Status"); - buffer_json_member_add_array(wb, "columns"); - { - buffer_json_add_array_item_string(wb, "MlStatus"); - } - buffer_json_array_close(wb); - } - buffer_json_object_close(wb); - - buffer_json_member_add_object(wb, "InRemoteIP"); - { - buffer_json_member_add_string(wb, "name", "Nodes by Inbound IP"); - buffer_json_member_add_array(wb, "columns"); - { - buffer_json_add_array_item_string(wb, "InRemoteIP"); - } - buffer_json_array_close(wb); - } - buffer_json_object_close(wb); - - buffer_json_member_add_object(wb, "OutRemoteIP"); - { - buffer_json_member_add_string(wb, "name", "Nodes by Outbound IP"); - buffer_json_member_add_array(wb, "columns"); - { - buffer_json_add_array_item_string(wb, "OutRemoteIP"); - } - buffer_json_array_close(wb); - } - buffer_json_object_close(wb); - } - buffer_json_object_close(wb); // group_by - - buffer_json_member_add_time_t(wb, "expires", now_realtime_sec() + 1); - buffer_json_finalize(wb); - - int response = HTTP_RESP_OK; - if(is_cancelled_cb && is_cancelled_cb(is_cancelled_cb_data)) { - buffer_flush(wb); - response = HTTP_RESP_CLIENT_CLOSED_REQUEST; - } - - if(result_cb) - result_cb(wb, response, result_cb_data); - - return response; -} diff --git a/database/rrdfunctions.h b/database/rrdfunctions.h deleted file mode 100644 index 21ca5c734..000000000 --- a/database/rrdfunctions.h +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -#ifndef NETDATA_RRDFUNCTIONS_H -#define NETDATA_RRDFUNCTIONS_H 1 - -// ---------------------------------------------------------------------------- - -#include "rrd.h" - -#define RRDFUNCTIONS_TIMEOUT_EXTENSION_UT (1 * USEC_PER_SEC) - -typedef void (*rrd_function_result_callback_t)(BUFFER *wb, int code, void *result_cb_data); -typedef bool (*rrd_function_is_cancelled_cb_t)(void *is_cancelled_cb_data); -typedef void (*rrd_function_canceller_cb_t)(void *data); -typedef void (*rrd_function_register_canceller_cb_t)(void *register_cancel_cb_data, rrd_function_canceller_cb_t cancel_cb, void *cancel_cb_data); -typedef int (*rrd_function_execute_cb_t)(BUFFER *wb, int timeout, const char *function, void *collector_data, - rrd_function_result_callback_t result_cb, void *result_cb_data, - rrd_function_is_cancelled_cb_t is_cancelled_cb, void *is_cancelled_cb_data, - rrd_function_register_canceller_cb_t register_cancel_cb, void *register_cancel_db_data); - -void rrd_functions_inflight_init(void); -void rrdfunctions_host_init(RRDHOST *host); -void rrdfunctions_host_destroy(RRDHOST *host); - -void rrd_collector_started(void); -void rrd_collector_finished(void); - -// add a function, to be run from the collector -void rrd_function_add(RRDHOST *host, RRDSET *st, const char *name, int timeout, const char *help, - bool sync, rrd_function_execute_cb_t execute_cb, void *execute_cb_data); - -// call a function, to be run from anywhere -int rrd_function_run(RRDHOST *host, BUFFER *result_wb, int timeout, const char *cmd, - bool wait, const char *transaction, - rrd_function_result_callback_t result_cb, void *result_cb_data, - rrd_function_is_cancelled_cb_t is_cancelled_cb, void *is_cancelled_cb_data, const char *payload); - -// cancel a running function, to be run from anywhere -void rrd_function_cancel(const char *transaction); - -void rrd_functions_expose_rrdpush(RRDSET *st, BUFFER *wb); -void rrd_functions_expose_global_rrdpush(RRDHOST *host, BUFFER *wb); - -void chart_functions2json(RRDSET *st, BUFFER *wb); -void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst, void *value, size_t value_size); -void host_functions_to_dict(RRDHOST *host, DICTIONARY *dst, void *value, size_t value_size, STRING **help); -void host_functions2json(RRDHOST *host, BUFFER *wb); - -uint8_t functions_format_to_content_type(const char *format); -const char *functions_content_type_to_format(HTTP_CONTENT_TYPE content_type); -int rrd_call_function_error(BUFFER *wb, const char *msg, int code); - -int rrdhost_function_streaming(BUFFER *wb, int timeout, const char *function, void *collector_data, - rrd_function_result_callback_t result_cb, void *result_cb_data, - rrd_function_is_cancelled_cb_t is_cancelled_cb, void *is_cancelled_cb_data, - rrd_function_register_canceller_cb_t register_canceller_cb, void *register_canceller_cb_data); - -#define RRDFUNCTIONS_STREAMING_HELP "Streaming status for parents and children." - -#endif // NETDATA_RRDFUNCTIONS_H diff --git a/database/rrdsetvar.c b/database/rrdsetvar.c deleted file mode 100644 index 379f92eec..000000000 --- a/database/rrdsetvar.c +++ /dev/null @@ -1,299 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rrd.h" - -typedef struct rrdsetvar { - STRING *name; // variable name - void *value; // we need this to maintain the allocation for custom chart variables - - const RRDVAR_ACQUIRED *rrdvar_local; - const RRDVAR_ACQUIRED *rrdvar_family_chart_id; - const RRDVAR_ACQUIRED *rrdvar_family_chart_name; - const RRDVAR_ACQUIRED *rrdvar_host_chart_id; - const RRDVAR_ACQUIRED *rrdvar_host_chart_name; - - RRDVAR_FLAGS flags:24; - RRDVAR_TYPE type:8; -} RRDSETVAR; - -// should only be called while the rrdsetvar dict is write locked -// otherwise, 2+ threads may be setting the same variables at the same time -static inline void rrdsetvar_free_rrdvars_unsafe(RRDSET *st, RRDSETVAR *rs) { - RRDHOST *host = st->rrdhost; - - // ------------------------------------------------------------------------ - // CHART - - if(st->rrdvars) { - rrdvar_release_and_del(st->rrdvars, rs->rrdvar_local); - rs->rrdvar_local = NULL; - } - - // ------------------------------------------------------------------------ - // FAMILY - - if(st->rrdfamily) { - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_chart_id); - rs->rrdvar_family_chart_id = NULL; - - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_chart_name); - rs->rrdvar_family_chart_name = NULL; - } - - // ------------------------------------------------------------------------ - // HOST - - if(host->rrdvars && host->health.health_enabled) { - rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_id); - rs->rrdvar_host_chart_id = NULL; - - rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_name); - rs->rrdvar_host_chart_name = NULL; - } -} - -// should only be called while the rrdsetvar dict is write locked -// otherwise, 2+ threads may be setting the same variables at the same time -static inline void rrdsetvar_update_rrdvars_unsafe(RRDSET *st, RRDSETVAR *rs) { - RRDHOST *host = st->rrdhost; - - RRDVAR_FLAGS options = rs->flags; - options &= ~RRDVAR_OPTIONS_REMOVED_WHEN_PROPAGATING_TO_RRDVAR; - - // ------------------------------------------------------------------------ - // free the old ones (if any) - - rrdsetvar_free_rrdvars_unsafe(st, rs); - - // ------------------------------------------------------------------------ - // KEYS - - char buffer[RRDVAR_MAX_LENGTH + 1]; - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_id(st), string2str(rs->name)); - STRING *key_chart_id = string_strdupz(buffer); - - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_name(st), string2str(rs->name)); - STRING *key_chart_name = string_strdupz(buffer); - - // ------------------------------------------------------------------------ - // CHART - - if(st->rrdvars) { - rs->rrdvar_local = rrdvar_add_and_acquire("local", st->rrdvars, rs->name, rs->type, options, rs->value); - } - - // ------------------------------------------------------------------------ - // FAMILY - - if(st->rrdfamily) { - rs->rrdvar_family_chart_id = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_chart_id, rs->type, options, rs->value); - rs->rrdvar_family_chart_name = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_chart_name, rs->type, options, rs->value); - } - - // ------------------------------------------------------------------------ - // HOST - - if(host->rrdvars && host->health.health_enabled) { - rs->rrdvar_host_chart_id = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_id, rs->type, options, rs->value); - rs->rrdvar_host_chart_name = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_name, rs->type, options, rs->value); - } - - // free the keys - string_freez(key_chart_id); - string_freez(key_chart_name); -} - -static void rrdsetvar_free_value_unsafe(RRDSETVAR *rs) { - if(rs->flags & RRDVAR_FLAG_ALLOCATED) { - void *old = rs->value; - rs->value = NULL; - rs->flags &= ~RRDVAR_FLAG_ALLOCATED; - freez(old); - } -} - -static void rrdsetvar_set_value_unsafe(RRDSETVAR *rs, void *new_value) { - rrdsetvar_free_value_unsafe(rs); - - if(new_value) - rs->value = new_value; - else { - NETDATA_DOUBLE *n = mallocz(sizeof(NETDATA_DOUBLE)); - *n = NAN; - rs->value = n; - rs->flags |= RRDVAR_FLAG_ALLOCATED; - } -} - -struct rrdsetvar_constructor { - RRDSET *rrdset; - const char *name; - void *value; - RRDVAR_FLAGS flags :16; - RRDVAR_TYPE type:8; -}; - -static void rrdsetvar_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdsetvar, void *constructor_data) { - RRDSETVAR *rs = rrdsetvar; - struct rrdsetvar_constructor *ctr = constructor_data; - - ctr->flags &= ~RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS; - - rs->name = string_strdupz(ctr->name); - rs->type = ctr->type; - rs->flags = ctr->flags; - rrdsetvar_set_value_unsafe(rs, ctr->value); - - // create the rrdvariables while we are having a write lock to the dictionary - rrdsetvar_update_rrdvars_unsafe(ctr->rrdset, rs); -} - -static bool rrdsetvar_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdsetvar, void *new_rrdsetvar __maybe_unused, void *constructor_data) { - RRDSETVAR *rs = rrdsetvar; - struct rrdsetvar_constructor *ctr = constructor_data; - - ctr->flags &= ~RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS; - - RRDVAR_FLAGS options = rs->flags; - options &= ~RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS; - - if(((ctr->value == NULL && rs->value != NULL && rs->flags & RRDVAR_FLAG_ALLOCATED) || (rs->value == ctr->value)) - && ctr->flags == options && rs->type == ctr->type) { - // don't reset it - everything is the same, or as it should... - return false; - } - - internal_error(true, "RRDSETVAR: resetting variable '%s' of chart '%s' of host '%s', options from 0x%x to 0x%x, type from %d to %d", - string2str(rs->name), rrdset_id(ctr->rrdset), rrdhost_hostname(ctr->rrdset->rrdhost), - options, ctr->flags, rs->type, ctr->type); - - rrdsetvar_free_value_unsafe(rs); // we are going to change the options, so free it before setting it - rs->flags = ctr->flags; - rs->type = ctr->type; - rrdsetvar_set_value_unsafe(rs, ctr->value); - - // recreate the rrdvariables while we are having a write lock to the dictionary - rrdsetvar_update_rrdvars_unsafe(ctr->rrdset, rs); - return true; -} - -static void rrdsetvar_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdsetvar, void *rrdset __maybe_unused) { - RRDSET *st = rrdset; - RRDSETVAR *rs = rrdsetvar; - - rrdsetvar_free_rrdvars_unsafe(st, rs); - rrdsetvar_free_value_unsafe(rs); - string_freez(rs->name); - rs->name = NULL; -} - -void rrdsetvar_index_init(RRDSET *st) { - if(!st->rrdsetvar_root_index) { - st->rrdsetvar_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, - &dictionary_stats_category_rrdhealth, sizeof(RRDSETVAR)); - - dictionary_register_insert_callback(st->rrdsetvar_root_index, rrdsetvar_insert_callback, NULL); - dictionary_register_conflict_callback(st->rrdsetvar_root_index, rrdsetvar_conflict_callback, NULL); - dictionary_register_delete_callback(st->rrdsetvar_root_index, rrdsetvar_delete_callback, st); - } -} - -void rrdsetvar_index_destroy(RRDSET *st) { - dictionary_destroy(st->rrdsetvar_root_index); - st->rrdsetvar_root_index = NULL; -} - -const RRDSETVAR_ACQUIRED *rrdsetvar_add_and_acquire(RRDSET *st, const char *name, RRDVAR_TYPE type, void *value, RRDVAR_FLAGS flags) { - struct rrdsetvar_constructor tmp = { - .name = name, - .type = type, - .value = value, - .flags = flags, - .rrdset = st, - }; - - const RRDSETVAR_ACQUIRED *rsa = (const RRDSETVAR_ACQUIRED *)dictionary_set_and_acquire_item_advanced(st->rrdsetvar_root_index, name, -1, NULL, sizeof(RRDSETVAR), &tmp); - return rsa; -} - -void rrdsetvar_add_and_leave_released(RRDSET *st, const char *name, RRDVAR_TYPE type, void *value, RRDVAR_FLAGS flags) { - const RRDSETVAR_ACQUIRED *rsa = rrdsetvar_add_and_acquire(st, name, type, value, flags); - dictionary_acquired_item_release(st->rrdsetvar_root_index, (const DICTIONARY_ITEM *)rsa); -} - -void rrdsetvar_rename_all(RRDSET *st) { - netdata_log_debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", rrdset_id(st), rrdset_name(st)); - - RRDSETVAR *rs; - dfe_start_write(st->rrdsetvar_root_index, rs) { - // should only be called while the rrdsetvar dict is write locked - rrdsetvar_update_rrdvars_unsafe(st, rs); - } - dfe_done(rs); - - rrdcalc_link_matching_alerts_to_rrdset(st); -} - -void rrdsetvar_release_and_delete_all(RRDSET *st) { - RRDSETVAR *rs; - dfe_start_write(st->rrdsetvar_root_index, rs) { - dictionary_del_advanced(st->rrdsetvar_root_index, string2str(rs->name), (ssize_t)string_strlen(rs->name) + 1); - } - dfe_done(rs); -} - -void rrdsetvar_release(DICTIONARY *dict, const RRDSETVAR_ACQUIRED *rsa) { - dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rsa); -} - -// -------------------------------------------------------------------------------------------------------------------- -// custom chart variables - -const RRDSETVAR_ACQUIRED *rrdsetvar_custom_chart_variable_add_and_acquire(RRDSET *st, const char *name) { - STRING *name_string = rrdvar_name_to_string(name); - const RRDSETVAR_ACQUIRED *rs = rrdsetvar_add_and_acquire(st, string2str(name_string), RRDVAR_TYPE_CALCULATED, NULL, RRDVAR_FLAG_CUSTOM_CHART_VAR); - string_freez(name_string); - return rs; -} - -void rrdsetvar_custom_chart_variable_set(RRDSET *st, const RRDSETVAR_ACQUIRED *rsa, NETDATA_DOUBLE value) { - if(!rsa) return; - - RRDSETVAR *rs = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rsa); - - if(rs->type != RRDVAR_TYPE_CALCULATED || !(rs->flags & RRDVAR_FLAG_CUSTOM_CHART_VAR) || !(rs->flags & RRDVAR_FLAG_ALLOCATED)) { - netdata_log_error("RRDSETVAR: requested to set variable '%s' of chart '%s' on host '%s' to value " NETDATA_DOUBLE_FORMAT - " but the variable is not a custom chart one (it has options 0x%x, value pointer %p). Ignoring request.", - string2str(rs->name), - rrdset_id(st), - rrdhost_hostname(st->rrdhost), - value, - (uint32_t)rs->flags, rs->value); - } - else { - NETDATA_DOUBLE *v = rs->value; - if(*v != value) { - *v = value; - rrdset_flag_set(st, RRDSET_FLAG_UPSTREAM_SEND_VARIABLES); - } - } -} - -void rrdsetvar_print_to_streaming_custom_chart_variables(RRDSET *st, BUFFER *wb) { - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_SEND_VARIABLES); - - // send the chart local custom variables - RRDSETVAR *rs; - dfe_start_read(st->rrdsetvar_root_index, rs) { - if(unlikely(rs->type == RRDVAR_TYPE_CALCULATED && rs->flags & RRDVAR_FLAG_CUSTOM_CHART_VAR)) { - NETDATA_DOUBLE *value = (NETDATA_DOUBLE *) rs->value; - - buffer_sprintf(wb - , "VARIABLE CHART %s = " NETDATA_DOUBLE_FORMAT "\n" - , string2str(rs->name) - , *value - ); - } - } - dfe_done(rs); -} diff --git a/database/rrdsetvar.h b/database/rrdsetvar.h deleted file mode 100644 index 0c2e66712..000000000 --- a/database/rrdsetvar.h +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_RRDSETVAR_H -#define NETDATA_RRDSETVAR_H 1 - -#include "rrd.h" - -// variables linked to charts -// We link variables to point to the values that are already -// calculated / processed by the normal data collection process -// This means, there will be no speed penalty for using -// these variables - -void rrdsetvar_index_init(RRDSET *st); -void rrdsetvar_index_destroy(RRDSET *st); -void rrdsetvar_release_and_delete_all(RRDSET *st); - -#define rrdsetvar_custom_chart_variable_release(st, rsa) rrdsetvar_release((st)->rrdsetvar_root_index, rsa) -void rrdsetvar_release(DICTIONARY *dict, const RRDSETVAR_ACQUIRED *rsa); - -const RRDSETVAR_ACQUIRED *rrdsetvar_custom_chart_variable_add_and_acquire(RRDSET *st, const char *name); -void rrdsetvar_custom_chart_variable_set(RRDSET *st, const RRDSETVAR_ACQUIRED *rsa, NETDATA_DOUBLE value); - -void rrdsetvar_rename_all(RRDSET *st); -const RRDSETVAR_ACQUIRED *rrdsetvar_add_and_acquire(RRDSET *st, const char *name, RRDVAR_TYPE type, void *value, RRDVAR_FLAGS flags); -void rrdsetvar_add_and_leave_released(RRDSET *st, const char *name, RRDVAR_TYPE type, void *value, RRDVAR_FLAGS flags); - -void rrdsetvar_print_to_streaming_custom_chart_variables(RRDSET *st, BUFFER *wb); - -#endif //NETDATA_RRDSETVAR_H diff --git a/database/rrdvar.c b/database/rrdvar.c deleted file mode 100644 index 68d22abb9..000000000 --- a/database/rrdvar.c +++ /dev/null @@ -1,392 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rrd.h" - -// the variables as stored in the variables indexes -// there are 3 indexes: -// 1. at each chart (RRDSET.rrdvar_root_index) -// 2. at each context (RRDFAMILY.rrdvar_root_index) -// 3. at each host (RRDHOST.rrdvar_root_index) -typedef struct rrdvar { - STRING *name; - void *value; - RRDVAR_FLAGS flags:24; - RRDVAR_TYPE type:8; -} RRDVAR; - -// ---------------------------------------------------------------------------- -// RRDVAR management - -inline int rrdvar_fix_name(char *variable) { - int fixed = 0; - while(*variable) { - if (!isalnum(*variable) && *variable != '.' && *variable != '_') { - *variable++ = '_'; - fixed++; - } - else - variable++; - } - - return fixed; -} - -inline STRING *rrdvar_name_to_string(const char *name) { - char *variable = strdupz(name); - rrdvar_fix_name(variable); - STRING *name_string = string_strdupz(variable); - freez(variable); - return name_string; -} - -struct rrdvar_constructor { - STRING *name; - void *value; - RRDVAR_FLAGS options:16; - RRDVAR_TYPE type:8; - - enum { - RRDVAR_REACT_NONE = 0, - RRDVAR_REACT_NEW = (1 << 0), - } react_action; -}; - -static void rrdvar_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdvar, void *constructor_data) { - RRDVAR *rv = rrdvar; - struct rrdvar_constructor *ctr = constructor_data; - - ctr->options &= ~RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS; - - rv->name = string_dup(ctr->name); - rv->type = ctr->type; - rv->flags = ctr->options; - - if(!ctr->value) { - NETDATA_DOUBLE *v = mallocz(sizeof(NETDATA_DOUBLE)); - *v = NAN; - rv->value = v; - rv->flags |= RRDVAR_FLAG_ALLOCATED; - } - else - rv->value = ctr->value; - - ctr->react_action = RRDVAR_REACT_NEW; -} - -static void rrdvar_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdvar, void *nothing __maybe_unused) { - RRDVAR *rv = rrdvar; - - if(rv->flags & RRDVAR_FLAG_ALLOCATED) - freez(rv->value); - - string_freez(rv->name); - rv->name = NULL; -} - -DICTIONARY *rrdvariables_create(void) { - DICTIONARY *dict = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, - &dictionary_stats_category_rrdhealth, sizeof(RRDVAR)); - - dictionary_register_insert_callback(dict, rrdvar_insert_callback, NULL); - dictionary_register_delete_callback(dict, rrdvar_delete_callback, NULL); - - return dict; -} - -DICTIONARY *health_rrdvariables_create(void) { - DICTIONARY *dict = dictionary_create_advanced(DICT_OPTION_NONE, &dictionary_stats_category_rrdhealth, 0); - - dictionary_register_insert_callback(dict, rrdvar_insert_callback, NULL); - dictionary_register_delete_callback(dict, rrdvar_delete_callback, NULL); - - return dict; -} - -void rrdvariables_destroy(DICTIONARY *dict) { - dictionary_destroy(dict); -} - -static inline const RRDVAR_ACQUIRED *rrdvar_get_and_acquire(DICTIONARY *dict, STRING *name) { - return (const RRDVAR_ACQUIRED *)dictionary_get_and_acquire_item_advanced(dict, string2str(name), (ssize_t)string_strlen(name) + 1); -} - -inline void rrdvar_release_and_del(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva) { - if(unlikely(!dict || !rva)) return; - - RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva); - - dictionary_del_advanced(dict, string2str(rv->name), (ssize_t)string_strlen(rv->name) + 1); - - dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rva); -} - -inline const RRDVAR_ACQUIRED *rrdvar_add_and_acquire(const char *scope __maybe_unused, DICTIONARY *dict, STRING *name, RRDVAR_TYPE type, RRDVAR_FLAGS options, void *value) { - if(unlikely(!dict || !name)) return NULL; - - struct rrdvar_constructor tmp = { - .name = name, - .value = value, - .type = type, - .options = options, - .react_action = RRDVAR_REACT_NONE, - }; - return (const RRDVAR_ACQUIRED *)dictionary_set_and_acquire_item_advanced(dict, string2str(name), (ssize_t)string_strlen(name) + 1, NULL, sizeof(RRDVAR), &tmp); -} - -inline void rrdvar_add(const char *scope __maybe_unused, DICTIONARY *dict, STRING *name, RRDVAR_TYPE type, RRDVAR_FLAGS options, void *value) { - if(unlikely(!dict || !name)) return; - - struct rrdvar_constructor tmp = { - .name = name, - .value = value, - .type = type, - .options = options, - .react_action = RRDVAR_REACT_NONE, - }; - dictionary_set_advanced(dict, string2str(name), (ssize_t)string_strlen(name) + 1, NULL, sizeof(RRDVAR), &tmp); -} - -void rrdvar_delete_all(DICTIONARY *dict) { - dictionary_flush(dict); -} - - -// ---------------------------------------------------------------------------- -// CUSTOM HOST VARIABLES - -inline int rrdvar_walkthrough_read(DICTIONARY *dict, int (*callback)(const DICTIONARY_ITEM *item, void *rrdvar, void *data), void *data) { - if(unlikely(!dict)) return 0; // when health is not enabled - return dictionary_walkthrough_read(dict, callback, data); -} - -const RRDVAR_ACQUIRED *rrdvar_custom_host_variable_add_and_acquire(RRDHOST *host, const char *name) { - DICTIONARY *dict = host->rrdvars; - if(unlikely(!dict)) return NULL; // when health is not enabled - - STRING *name_string = rrdvar_name_to_string(name); - - const RRDVAR_ACQUIRED *rva = rrdvar_add_and_acquire("host", dict, name_string, RRDVAR_TYPE_CALCULATED, RRDVAR_FLAG_CUSTOM_HOST_VAR, NULL); - - string_freez(name_string); - return rva; -} - -void rrdvar_custom_host_variable_set(RRDHOST *host, const RRDVAR_ACQUIRED *rva, NETDATA_DOUBLE value) { - if(unlikely(!host->rrdvars || !rva)) return; // when health is not enabled - - if(rrdvar_type(rva) != RRDVAR_TYPE_CALCULATED || !(rrdvar_flags(rva) & (RRDVAR_FLAG_CUSTOM_HOST_VAR | RRDVAR_FLAG_ALLOCATED))) - netdata_log_error("requested to set variable '%s' to value " NETDATA_DOUBLE_FORMAT " but the variable is not a custom one.", rrdvar_name(rva), value); - else { - RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva); - NETDATA_DOUBLE *v = rv->value; - if(*v != value) { - *v = value; - - // if the host is streaming, send this variable upstream immediately - rrdpush_sender_send_this_host_variable_now(host, rva); - } - } -} - -void rrdvar_release(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva) { - if(unlikely(!dict || !rva)) return; // when health is not enabled - dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rva); -} - -// ---------------------------------------------------------------------------- -// RRDVAR lookup - -NETDATA_DOUBLE rrdvar2number(const RRDVAR_ACQUIRED *rva) { - if(unlikely(!rva)) return NAN; - - RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva); - - switch(rv->type) { - case RRDVAR_TYPE_CALCULATED: { - NETDATA_DOUBLE *n = (NETDATA_DOUBLE *)rv->value; - return *n; - } - - case RRDVAR_TYPE_TIME_T: { - time_t *n = (time_t *)rv->value; - return (NETDATA_DOUBLE)*n; - } - - case RRDVAR_TYPE_COLLECTED: { - collected_number *n = (collected_number *)rv->value; - return (NETDATA_DOUBLE)*n; - } - - case RRDVAR_TYPE_TOTAL: { - total_number *n = (total_number *)rv->value; - return (NETDATA_DOUBLE)*n; - } - - case RRDVAR_TYPE_INT: { - int *n = (int *)rv->value; - return *n; - } - - default: - netdata_log_error("I don't know how to convert RRDVAR type %u to NETDATA_DOUBLE", rv->type); - return NAN; - } -} - -int health_variable_check(DICTIONARY *dict, RRDSET *st, RRDDIM *rd) { - if (!dict || !st || !rd) return 0; - - STRING *helper_str; - char helper[RRDVAR_MAX_LENGTH + 1]; - snprintfz(helper, RRDVAR_MAX_LENGTH, "%s.%s", string2str(st->name), string2str(rd->name)); - helper_str = string_strdupz(helper); - - const RRDVAR_ACQUIRED *rva; - rva = rrdvar_get_and_acquire(dict, helper_str); - if(rva) { - dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rva); - string_freez(helper_str); - return 1; - } - - string_freez(helper_str); - - return 0; -} - -void rrdvar_store_for_chart(RRDHOST *host, RRDSET *st) { - if (!st) return; - - if(!st->rrdfamily) - st->rrdfamily = rrdfamily_add_and_acquire(host, rrdset_family(st)); - - if(!st->rrdvars) - st->rrdvars = rrdvariables_create(); - - rrddimvar_index_init(st); - - rrdsetvar_add_and_leave_released(st, "last_collected_t", RRDVAR_TYPE_TIME_T, &st->last_collected_time.tv_sec, RRDVAR_FLAG_NONE); - rrdsetvar_add_and_leave_released(st, "green", RRDVAR_TYPE_CALCULATED, &st->green, RRDVAR_FLAG_NONE); - rrdsetvar_add_and_leave_released(st, "red", RRDVAR_TYPE_CALCULATED, &st->red, RRDVAR_FLAG_NONE); - rrdsetvar_add_and_leave_released(st, "update_every", RRDVAR_TYPE_INT, &st->update_every, RRDVAR_FLAG_NONE); - - RRDDIM *rd; - rrddim_foreach_read(rd, st) { - rrddimvar_add_and_leave_released(rd, RRDVAR_TYPE_CALCULATED, NULL, NULL, &rd->collector.last_stored_value, RRDVAR_FLAG_NONE); - rrddimvar_add_and_leave_released(rd, RRDVAR_TYPE_COLLECTED, NULL, "_raw", &rd->collector.last_collected_value, RRDVAR_FLAG_NONE); - rrddimvar_add_and_leave_released(rd, RRDVAR_TYPE_TIME_T, NULL, "_last_collected_t", &rd->collector.last_collected_time.tv_sec, RRDVAR_FLAG_NONE); - } - rrddim_foreach_done(rd); -} - -int health_variable_lookup(STRING *variable, RRDCALC *rc, NETDATA_DOUBLE *result) { - RRDSET *st = rc->rrdset; - if(!st) return 0; - - RRDHOST *host = st->rrdhost; - const RRDVAR_ACQUIRED *rva; - - rva = rrdvar_get_and_acquire(st->rrdvars, variable); - if(rva) { - *result = rrdvar2number(rva); - dictionary_acquired_item_release(st->rrdvars, (const DICTIONARY_ITEM *)rva); - return 1; - } - - rva = rrdvar_get_and_acquire(rrdfamily_rrdvars_dict(st->rrdfamily), variable); - if(rva) { - *result = rrdvar2number(rva); - dictionary_acquired_item_release(rrdfamily_rrdvars_dict(st->rrdfamily), (const DICTIONARY_ITEM *)rva); - return 1; - } - - rva = rrdvar_get_and_acquire(host->rrdvars, variable); - if(rva) { - *result = rrdvar2number(rva); - dictionary_acquired_item_release(host->rrdvars, (const DICTIONARY_ITEM *)rva); - return 1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// RRDVAR to JSON - -struct variable2json_helper { - BUFFER *buf; - RRDVAR_FLAGS options; -}; - -static int single_variable2json_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry __maybe_unused, void *helper_data) { - struct variable2json_helper *helper = (struct variable2json_helper *)helper_data; - const RRDVAR_ACQUIRED *rva = (const RRDVAR_ACQUIRED *)item; - NETDATA_DOUBLE value = rrdvar2number(rva); - - if (helper->options == RRDVAR_FLAG_NONE || rrdvar_flags(rva) & helper->options) { - if(unlikely(isnan(value) || isinf(value))) - buffer_json_member_add_string(helper->buf, rrdvar_name(rva), NULL); - else - buffer_json_member_add_double(helper->buf, rrdvar_name(rva), (NETDATA_DOUBLE)value); - } - - return 0; -} - -void health_api_v1_chart_custom_variables2json(RRDSET *st, BUFFER *buf) { - struct variable2json_helper helper = {.buf = buf, .options = RRDVAR_FLAG_CUSTOM_CHART_VAR}; - - rrdvar_walkthrough_read(st->rrdvars, single_variable2json_callback, &helper); -} - -void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) { - RRDHOST *host = st->rrdhost; - - struct variable2json_helper helper = {.buf = buf, .options = RRDVAR_FLAG_NONE}; - - buffer_json_initialize(buf, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT); - - buffer_json_member_add_string(buf, "chart", rrdset_id(st)); - buffer_json_member_add_string(buf, "chart_name", rrdset_name(st)); - buffer_json_member_add_string(buf, "chart_context", rrdset_context(st)); - - { - buffer_json_member_add_object(buf, "chart_variables"); - rrdvar_walkthrough_read(st->rrdvars, single_variable2json_callback, &helper); - buffer_json_object_close(buf); - } - - buffer_json_member_add_string(buf, "family", rrdset_family(st)); - - { - buffer_json_member_add_object(buf, "family_variables"); - rrdvar_walkthrough_read(rrdfamily_rrdvars_dict(st->rrdfamily), single_variable2json_callback, &helper); - buffer_json_object_close(buf); - } - - buffer_json_member_add_string(buf, "host", rrdhost_hostname(host)); - - { - buffer_json_member_add_object(buf, "host_variables"); - rrdvar_walkthrough_read(host->rrdvars, single_variable2json_callback, &helper); - buffer_json_object_close(buf); - } - - buffer_json_finalize(buf); -} - -// ---------------------------------------------------------------------------- -// RRDVAR private members examination - -const char *rrdvar_name(const RRDVAR_ACQUIRED *rva) { - return dictionary_acquired_item_name((const DICTIONARY_ITEM *)rva); -} - -RRDVAR_FLAGS rrdvar_flags(const RRDVAR_ACQUIRED *rva) { - RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva); - return rv->flags; -} -RRDVAR_TYPE rrdvar_type(const RRDVAR_ACQUIRED *rva) { - RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva); - return rv->type; -} diff --git a/database/rrdvar.h b/database/rrdvar.h deleted file mode 100644 index 3b6e9cb97..000000000 --- a/database/rrdvar.h +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_RRDVAR_H -#define NETDATA_RRDVAR_H 1 - -#include "libnetdata/libnetdata.h" - -typedef enum rrdvar_type { - RRDVAR_TYPE_CALCULATED = 1, - RRDVAR_TYPE_TIME_T = 2, - RRDVAR_TYPE_COLLECTED = 3, - RRDVAR_TYPE_TOTAL = 4, - RRDVAR_TYPE_INT = 5 - - // this is 8 bit - // to increase it you have to set change the bitfield in - // rrdvar, rrdsetvar, rrddimvar -} RRDVAR_TYPE; - -typedef enum rrdvar_options { - RRDVAR_FLAG_NONE = 0, - RRDVAR_FLAG_ALLOCATED = (1 << 0), // the value ptr is allocated (not a reference) - RRDVAR_FLAG_CUSTOM_HOST_VAR = (1 << 1), // this is a custom host variable, not associated with a dimension - RRDVAR_FLAG_CUSTOM_CHART_VAR = (1 << 2), // this is a custom chart variable, not associated with a dimension - RRDVAR_FLAG_RRDCALC_LOCAL_VAR = (1 << 3), // this is a an alarm variable, attached to a chart - RRDVAR_FLAG_RRDCALC_FAMILY_VAR = (1 << 4), // this is a an alarm variable, attached to a family - RRDVAR_FLAG_RRDCALC_HOST_CHARTID_VAR = (1 << 5), // this is a an alarm variable, attached to the host, using the chart id - RRDVAR_FLAG_RRDCALC_HOST_CHARTNAME_VAR = (1 << 6), // this is a an alarm variable, attached to the host, using the chart name - RRDVAR_FLAG_CONFIG_VAR = (1 << 7), // this is a an alarm variable, read from alarm config - - // this is 24 bit - // to increase it you have to set change the bitfield in - // rrdvar, rrdsetvar, rrddimvar -} RRDVAR_FLAGS; - -#define RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS \ - (RRDVAR_FLAG_ALLOCATED) - -#define RRDVAR_OPTIONS_REMOVED_WHEN_PROPAGATING_TO_RRDVAR \ - (RRDVAR_FLAG_ALLOCATED) - -#define RRDVAR_MAX_LENGTH 1024 - -int rrdvar_fix_name(char *variable); - -#include "rrd.h" - -STRING *rrdvar_name_to_string(const char *name); - -const RRDVAR_ACQUIRED *rrdvar_custom_host_variable_add_and_acquire(RRDHOST *host, const char *name); -void rrdvar_add(const char *scope __maybe_unused, DICTIONARY *dict, STRING *name, RRDVAR_TYPE type, RRDVAR_FLAGS options, void *value); -void rrdvar_custom_host_variable_set(RRDHOST *host, const RRDVAR_ACQUIRED *rva, NETDATA_DOUBLE value); - -int rrdvar_walkthrough_read(DICTIONARY *dict, int (*callback)(const DICTIONARY_ITEM *item, void *rrdvar, void *data), void *data); - -#define rrdvar_custom_host_variable_release(host, rva) rrdvar_release((host)->rrdvars, rva) -void rrdvar_release(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva); - -NETDATA_DOUBLE rrdvar2number(const RRDVAR_ACQUIRED *rva); - -const RRDVAR_ACQUIRED *rrdvar_add_and_acquire(const char *scope, DICTIONARY *dict, STRING *name, RRDVAR_TYPE type, RRDVAR_FLAGS options, void *value); -void rrdvar_release_and_del(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva); - -DICTIONARY *rrdvariables_create(void); -DICTIONARY *health_rrdvariables_create(void); -void rrdvariables_destroy(DICTIONARY *dict); - -void rrdvar_store_for_chart(RRDHOST *host, RRDSET *st); -int health_variable_check(DICTIONARY *dict, RRDSET *st, RRDDIM *rd); - -void rrdvar_delete_all(DICTIONARY *dict); - -const char *rrdvar_name(const RRDVAR_ACQUIRED *rva); -RRDVAR_FLAGS rrdvar_flags(const RRDVAR_ACQUIRED *rva); -RRDVAR_TYPE rrdvar_type(const RRDVAR_ACQUIRED *rva); - -#endif //NETDATA_RRDVAR_H diff --git a/database/sqlite/Makefile.am b/database/sqlite/Makefile.am deleted file mode 100644 index babdcf0df..000000000 --- a/database/sqlite/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -AUTOMAKE_OPTIONS = subdir-objects -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in diff --git a/database/sqlite/sqlite_functions.c b/database/sqlite/sqlite_functions.c deleted file mode 100644 index e3537bf5a..000000000 --- a/database/sqlite/sqlite_functions.c +++ /dev/null @@ -1,934 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "sqlite_functions.h" -#include "sqlite3recover.h" -#include "sqlite_db_migration.h" - -#define DB_METADATA_VERSION 16 - -const char *database_config[] = { - "CREATE TABLE IF NOT EXISTS host(host_id BLOB PRIMARY KEY, hostname TEXT NOT NULL, " - "registry_hostname TEXT NOT NULL default 'unknown', update_every INT NOT NULL default 1, " - "os TEXT NOT NULL default 'unknown', timezone TEXT NOT NULL default 'unknown', tags TEXT NOT NULL default ''," - "hops INT NOT NULL DEFAULT 0," - "memory_mode INT DEFAULT 0, abbrev_timezone TEXT DEFAULT '', utc_offset INT NOT NULL DEFAULT 0," - "program_name TEXT NOT NULL DEFAULT 'unknown', program_version TEXT NOT NULL DEFAULT 'unknown', " - "entries INT NOT NULL DEFAULT 0," - "health_enabled INT NOT NULL DEFAULT 0, last_connected INT NOT NULL DEFAULT 0)", - - "CREATE TABLE IF NOT EXISTS chart(chart_id blob PRIMARY KEY, host_id blob, type text, id text, name text, " - "family text, context text, title text, unit text, plugin text, module text, priority int, update_every int, " - "chart_type int, memory_mode int, history_entries)", - - "CREATE TABLE IF NOT EXISTS dimension(dim_id blob PRIMARY KEY, chart_id blob, id text, name text, " - "multiplier int, divisor int , algorithm int, options text)", - - "CREATE TABLE IF NOT EXISTS metadata_migration(filename text, file_size, date_created int)", - - "CREATE INDEX IF NOT EXISTS ind_d2 on dimension (chart_id)", - - "CREATE INDEX IF NOT EXISTS ind_c3 on chart (host_id)", - - "CREATE TABLE IF NOT EXISTS chart_label(chart_id blob, source_type int, label_key text, " - "label_value text, date_created int, PRIMARY KEY (chart_id, label_key))", - - "CREATE TABLE IF NOT EXISTS node_instance (host_id blob PRIMARY KEY, claim_id, node_id, date_created)", - - "CREATE TABLE IF NOT EXISTS alert_hash(hash_id blob PRIMARY KEY, date_updated int, alarm text, template text, " - "on_key text, class text, component text, type text, os text, hosts text, lookup text, " - "every text, units text, calc text, families text, plugin text, module text, charts text, green text, " - "red text, warn text, crit text, exec text, to_key text, info text, delay text, options text, " - "repeat text, host_labels text, p_db_lookup_dimensions text, p_db_lookup_method text, p_db_lookup_options int, " - "p_db_lookup_after int, p_db_lookup_before int, p_update_every int, source text, chart_labels text, summary text)", - - "CREATE TABLE IF NOT EXISTS host_info(host_id blob, system_key text NOT NULL, system_value text NOT NULL, " - "date_created INT, PRIMARY KEY(host_id, system_key))", - - "CREATE TABLE IF NOT EXISTS host_label(host_id blob, source_type int, label_key text NOT NULL, " - "label_value text NOT NULL, date_created INT, PRIMARY KEY (host_id, label_key))", - - "CREATE TRIGGER IF NOT EXISTS ins_host AFTER INSERT ON host BEGIN INSERT INTO node_instance (host_id, date_created)" - " SELECT new.host_id, unixepoch() WHERE new.host_id NOT IN (SELECT host_id FROM node_instance); END", - - "CREATE TABLE IF NOT EXISTS health_log (health_log_id INTEGER PRIMARY KEY, host_id blob, alarm_id int, " - "config_hash_id blob, name text, chart text, family text, recipient text, units text, exec text, " - "chart_context text, last_transition_id blob, chart_name text, UNIQUE (host_id, alarm_id))", - - "CREATE INDEX IF NOT EXISTS health_log_ind_1 ON health_log (host_id)", - - "CREATE TABLE IF NOT EXISTS health_log_detail (health_log_id int, unique_id int, alarm_id int, alarm_event_id int, " - "updated_by_id int, updates_id int, when_key int, duration int, non_clear_duration int, " - "flags int, exec_run_timestamp int, delay_up_to_timestamp int, " - "info text, exec_code int, new_status real, old_status real, delay int, " - "new_value double, old_value double, last_repeat int, transition_id blob, global_id int, summary text)", - - "CREATE INDEX IF NOT EXISTS health_log_d_ind_2 ON health_log_detail (global_id)", - "CREATE INDEX IF NOT EXISTS health_log_d_ind_3 ON health_log_detail (transition_id)", - "CREATE INDEX IF NOT EXISTS health_log_d_ind_9 ON health_log_detail (unique_id DESC, health_log_id)", - "CREATE INDEX IF NOT EXISTS health_log_d_ind_6 on health_log_detail (health_log_id, when_key)", - "CREATE INDEX IF NOT EXISTS health_log_d_ind_7 on health_log_detail (alarm_id)", - "CREATE INDEX IF NOT EXISTS health_log_d_ind_8 on health_log_detail (new_status, updated_by_id)", - - NULL -}; - -const char *database_cleanup[] = { - "DELETE FROM host WHERE host_id NOT IN (SELECT host_id FROM chart)", - "DELETE FROM node_instance WHERE host_id NOT IN (SELECT host_id FROM host)", - "DELETE FROM host_info WHERE host_id NOT IN (SELECT host_id FROM host)", - "DELETE FROM host_label WHERE host_id NOT IN (SELECT host_id FROM host)", - "DROP TRIGGER IF EXISTS tr_dim_del", - "DROP INDEX IF EXISTS ind_d1", - "DROP INDEX IF EXISTS ind_c1", - "DROP INDEX IF EXISTS ind_c2", - "DROP INDEX IF EXISTS alert_hash_index", - "DROP INDEX IF EXISTS health_log_d_ind_4", - "DROP INDEX IF EXISTS health_log_d_ind_1", - "DROP INDEX IF EXISTS health_log_d_ind_5", - NULL -}; - -sqlite3 *db_meta = NULL; - -#define MAX_PREPARED_STATEMENTS (32) -pthread_key_t key_pool[MAX_PREPARED_STATEMENTS]; - -SQLITE_API int sqlite3_exec_monitored( - sqlite3 *db, /* An open database */ - const char *sql, /* SQL to be evaluated */ - int (*callback)(void*,int,char**,char**), /* Callback function */ - void *data, /* 1st argument to callback */ - char **errmsg /* Error msg written here */ -) { - int rc = sqlite3_exec(db, sql, callback, data, errmsg); - global_statistics_sqlite3_query_completed(rc == SQLITE_OK, rc == SQLITE_BUSY, rc == SQLITE_LOCKED); - return rc; -} - -SQLITE_API int sqlite3_step_monitored(sqlite3_stmt *stmt) { - int rc; - int cnt = 0; - - while (cnt++ < SQL_MAX_RETRY) { - rc = sqlite3_step(stmt); - switch (rc) { - case SQLITE_DONE: - global_statistics_sqlite3_query_completed(1, 0, 0); - break; - case SQLITE_ROW: - global_statistics_sqlite3_row_completed(); - break; - case SQLITE_BUSY: - case SQLITE_LOCKED: - global_statistics_sqlite3_query_completed(rc == SQLITE_DONE, rc == SQLITE_BUSY, rc == SQLITE_LOCKED); - usleep(SQLITE_INSERT_DELAY * USEC_PER_MS); - continue; - default: - break; - } - break; - } - return rc; -} - -static bool mark_database_to_recover(sqlite3_stmt *res, sqlite3 *database) -{ - - if (!res && !database) - return false; - - if (!database) - database = sqlite3_db_handle(res); - - if (db_meta == database) { - char recover_file[FILENAME_MAX + 1]; - snprintfz(recover_file, FILENAME_MAX, "%s/.netdata-meta.db.recover", netdata_configured_cache_dir); - int fd = open(recover_file, O_WRONLY | O_CREAT | O_TRUNC, 444); - if (fd >= 0) { - close(fd); - return true; - } - } - return false; -} - -static void recover_database(const char *sqlite_database, const char *new_sqlite_database) -{ - sqlite3 *database; - int rc = sqlite3_open(sqlite_database, &database); - if (rc != SQLITE_OK) - return; - - netdata_log_info("Recover %s", sqlite_database); - netdata_log_info(" to %s", new_sqlite_database); - - // This will remove the -shm and -wal files when we close the database - (void) db_execute(database, "select count(*) from sqlite_master limit 0"); - - sqlite3_recover *recover = sqlite3_recover_init(database, "main", new_sqlite_database); - if (recover) { - - rc = sqlite3_recover_run(recover); - - if (rc == SQLITE_OK) - netdata_log_info("Recover complete"); - else - netdata_log_info("Recover encountered an error but the database may be usable"); - - rc = sqlite3_recover_finish(recover); - - (void) sqlite3_close(database); - - if (rc == SQLITE_OK) { - rc = rename(new_sqlite_database, sqlite_database); - if (rc == 0) { - netdata_log_info("Renamed %s", new_sqlite_database); - netdata_log_info(" to %s", sqlite_database); - } - } - } - else - (void) sqlite3_close(database); -} - -int execute_insert(sqlite3_stmt *res) -{ - int rc; - rc = sqlite3_step_monitored(res); - if (rc == SQLITE_CORRUPT) { - (void)mark_database_to_recover(res, NULL); - error_report("SQLite error %d", rc); - } - return rc; -} - -int configure_sqlite_database(sqlite3 *database, int target_version) -{ - char buf[1024 + 1] = ""; - const char *list[2] = { buf, NULL }; - - // https://www.sqlite.org/pragma.html#pragma_auto_vacuum - // PRAGMA schema.auto_vacuum = 0 | NONE | 1 | FULL | 2 | INCREMENTAL; - snprintfz(buf, sizeof(buf) - 1, "PRAGMA auto_vacuum=%s", config_get(CONFIG_SECTION_SQLITE, "auto vacuum", "INCREMENTAL")); - if (init_database_batch(database, list)) - return 1; - - // https://www.sqlite.org/pragma.html#pragma_synchronous - // PRAGMA schema.synchronous = 0 | OFF | 1 | NORMAL | 2 | FULL | 3 | EXTRA; - snprintfz(buf, sizeof(buf) - 1, "PRAGMA synchronous=%s", config_get(CONFIG_SECTION_SQLITE, "synchronous", "NORMAL")); - if (init_database_batch(database, list)) - return 1; - - // https://www.sqlite.org/pragma.html#pragma_journal_mode - // PRAGMA schema.journal_mode = DELETE | TRUNCATE | PERSIST | MEMORY | WAL | OFF - snprintfz(buf, sizeof(buf) - 1, "PRAGMA journal_mode=%s", config_get(CONFIG_SECTION_SQLITE, "journal mode", "WAL")); - if (init_database_batch(database, list)) - return 1; - - // https://www.sqlite.org/pragma.html#pragma_temp_store - // PRAGMA temp_store = 0 | DEFAULT | 1 | FILE | 2 | MEMORY; - snprintfz(buf, sizeof(buf) - 1, "PRAGMA temp_store=%s", config_get(CONFIG_SECTION_SQLITE, "temp store", "MEMORY")); - if (init_database_batch(database, list)) - return 1; - - // https://www.sqlite.org/pragma.html#pragma_journal_size_limit - // PRAGMA schema.journal_size_limit = N ; - snprintfz(buf, sizeof(buf) - 1, "PRAGMA journal_size_limit=%lld", config_get_number(CONFIG_SECTION_SQLITE, "journal size limit", 16777216)); - if (init_database_batch(database, list)) - return 1; - - // https://www.sqlite.org/pragma.html#pragma_cache_size - // PRAGMA schema.cache_size = pages; - // PRAGMA schema.cache_size = -kibibytes; - snprintfz(buf, sizeof(buf) - 1, "PRAGMA cache_size=%lld", config_get_number(CONFIG_SECTION_SQLITE, "cache size", -2000)); - if (init_database_batch(database, list)) - return 1; - - snprintfz(buf, sizeof(buf) - 1, "PRAGMA user_version=%d", target_version); - if (init_database_batch(database, list)) - return 1; - - return 0; -} - -#define MAX_OPEN_STATEMENTS (512) - -static void add_stmt_to_list(sqlite3_stmt *res) -{ - static int idx = 0; - static sqlite3_stmt *statements[MAX_OPEN_STATEMENTS]; - - if (unlikely(!res)) { - if (idx) - netdata_log_info("Finilizing %d statements", idx); - else - netdata_log_info("No statements pending to finalize"); - while (idx > 0) { - int rc; - rc = sqlite3_finalize(statements[--idx]); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize statement during shutdown, rc = %d", rc); - } - return; - } - - if (unlikely(idx == MAX_OPEN_STATEMENTS)) - return; -} - -static void release_statement(void *statement) -{ - int rc; -#ifdef NETDATA_DEV_MODE - netdata_log_info("Thread %d: Cleaning prepared statement on %p", gettid(), statement); -#endif - if (unlikely(rc = sqlite3_finalize((sqlite3_stmt *) statement) != SQLITE_OK)) - error_report("Failed to finalize statement, rc = %d", rc); -} - -void initialize_thread_key_pool(void) -{ - for (int i = 0; i < MAX_PREPARED_STATEMENTS; i++) - (void)pthread_key_create(&key_pool[i], release_statement); -} - -int prepare_statement(sqlite3 *database, const char *query, sqlite3_stmt **statement) -{ - static __thread uint32_t keys_used = 0; - - pthread_key_t *key = NULL; - int ret = 1; - - if (likely(keys_used < MAX_PREPARED_STATEMENTS)) - key = &key_pool[keys_used++]; - - int rc = sqlite3_prepare_v2(database, query, -1, statement, 0); - if (likely(rc == SQLITE_OK)) { - if (likely(key)) { - ret = pthread_setspecific(*key, *statement); -#ifdef NETDATA_DEV_MODE - netdata_log_info("Thread %d: Using key %u on statement %p", gettid(), keys_used, *statement); -#endif - } - if (ret) - add_stmt_to_list(*statement); - } - return rc; -} - -int init_database_batch(sqlite3 *database, const char *batch[]) -{ - int rc; - char *err_msg = NULL; - for (int i = 0; batch[i]; i++) { - rc = sqlite3_exec_monitored(database, batch[i], 0, 0, &err_msg); - if (rc != SQLITE_OK) { - error_report("SQLite error during database initialization, rc = %d (%s)", rc, err_msg); - error_report("SQLite failed statement %s", batch[i]); - analytics_set_data_str(&analytics_data.netdata_fail_reason, sqlite3_errstr(sqlite3_extended_errcode(database))); - sqlite3_free(err_msg); - if (SQLITE_CORRUPT == rc) { - if (mark_database_to_recover(NULL, database)) - error_report("Database is corrupted will attempt to fix"); - return SQLITE_CORRUPT; - } - return 1; - } - } - return 0; -} - -static void sqlite_uuid_parse(sqlite3_context *context, int argc, sqlite3_value **argv) -{ - uuid_t uuid; - - if ( argc != 1 ){ - sqlite3_result_null(context); - return ; - } - int rc = uuid_parse((const char *) sqlite3_value_text(argv[0]), uuid); - if (rc == -1) { - sqlite3_result_null(context); - return ; - } - - sqlite3_result_blob(context, &uuid, sizeof(uuid_t), SQLITE_TRANSIENT); -} - -void sqlite_now_usec(sqlite3_context *context, int argc, sqlite3_value **argv) -{ - if (argc != 1 ){ - sqlite3_result_null(context); - return ; - } - - if (sqlite3_value_int(argv[0]) != 0) { - struct timespec req = {.tv_sec = 0, .tv_nsec = 1}; - nanosleep(&req, NULL); - } - - sqlite3_result_int64(context, (sqlite_int64) now_realtime_usec()); -} - -void sqlite_uuid_random(sqlite3_context *context, int argc, sqlite3_value **argv) -{ - (void)argc; - (void)argv; - - uuid_t uuid; - uuid_generate_random(uuid); - sqlite3_result_blob(context, &uuid, sizeof(uuid_t), SQLITE_TRANSIENT); -} - -/* - * Initialize the SQLite database - * Return 0 on success - */ -int sql_init_database(db_check_action_type_t rebuild, int memory) -{ - char *err_msg = NULL; - char sqlite_database[FILENAME_MAX + 1]; - int rc; - - if (likely(!memory)) { - snprintfz(sqlite_database, sizeof(sqlite_database) - 1, "%s/.netdata-meta.db.recover", netdata_configured_cache_dir); - rc = unlink(sqlite_database); - snprintfz(sqlite_database, FILENAME_MAX, "%s/netdata-meta.db", netdata_configured_cache_dir); - - if (rc == 0 || (rebuild & DB_CHECK_RECOVER)) { - char new_sqlite_database[FILENAME_MAX + 1]; - snprintfz(new_sqlite_database, sizeof(new_sqlite_database) - 1, "%s/netdata-meta-recover.db", netdata_configured_cache_dir); - recover_database(sqlite_database, new_sqlite_database); - if (rebuild & DB_CHECK_RECOVER) - return 0; - } - } - else - strcpy(sqlite_database, ":memory:"); - - rc = sqlite3_open(sqlite_database, &db_meta); - if (rc != SQLITE_OK) { - error_report("Failed to initialize database at %s, due to \"%s\"", sqlite_database, sqlite3_errstr(rc)); - analytics_set_data_str(&analytics_data.netdata_fail_reason, sqlite3_errstr(sqlite3_extended_errcode(db_meta))); - sqlite3_close(db_meta); - db_meta = NULL; - return 1; - } - - if (rebuild & DB_CHECK_RECLAIM_SPACE) { - netdata_log_info("Reclaiming space of %s", sqlite_database); - rc = sqlite3_exec_monitored(db_meta, "VACUUM", 0, 0, &err_msg); - if (rc != SQLITE_OK) { - error_report("Failed to execute VACUUM rc = %d (%s)", rc, err_msg); - sqlite3_free(err_msg); - } - else { - (void) db_execute(db_meta, "select count(*) from sqlite_master limit 0"); - (void) sqlite3_close(db_meta); - } - return 1; - } - - if (rebuild & DB_CHECK_ANALYZE) { - netdata_log_info("Running ANALYZE on %s", sqlite_database); - rc = sqlite3_exec_monitored(db_meta, "ANALYZE", 0, 0, &err_msg); - if (rc != SQLITE_OK) { - error_report("Failed to execute ANALYZE rc = %d (%s)", rc, err_msg); - sqlite3_free(err_msg); - } - else { - (void) db_execute(db_meta, "select count(*) from sqlite_master limit 0"); - (void) sqlite3_close(db_meta); - } - return 1; - } - - netdata_log_info("SQLite database %s initialization", sqlite_database); - - rc = sqlite3_create_function(db_meta, "u2h", 1, SQLITE_ANY | SQLITE_DETERMINISTIC, 0, sqlite_uuid_parse, 0, 0); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to register internal u2h function"); - - rc = sqlite3_create_function(db_meta, "now_usec", 1, SQLITE_ANY, 0, sqlite_now_usec, 0, 0); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to register internal now_usec function"); - - rc = sqlite3_create_function(db_meta, "uuid_random", 0, SQLITE_ANY, 0, sqlite_uuid_random, 0, 0); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to register internal uuid_random function"); - - int target_version = DB_METADATA_VERSION; - - if (likely(!memory)) - target_version = perform_database_migration(db_meta, DB_METADATA_VERSION); - - if (configure_sqlite_database(db_meta, target_version)) - return 1; - - if (init_database_batch(db_meta, &database_config[0])) - return 1; - - if (init_database_batch(db_meta, &database_cleanup[0])) - return 1; - - netdata_log_info("SQLite database initialization completed"); - - initialize_thread_key_pool(); - - return 0; -} - -/* - * Close the sqlite database - */ - -void sql_close_database(void) -{ - int rc; - if (unlikely(!db_meta)) - return; - - netdata_log_info("Closing SQLite database"); - - add_stmt_to_list(NULL); - - (void) db_execute(db_meta, "PRAGMA analysis_limit=10000"); - (void) db_execute(db_meta, "PRAGMA optimize"); - - rc = sqlite3_close_v2(db_meta); - if (unlikely(rc != SQLITE_OK)) - error_report("Error %d while closing the SQLite database, %s", rc, sqlite3_errstr(rc)); -} - -int exec_statement_with_uuid(const char *sql, uuid_t *uuid) -{ - int rc, result = 1; - sqlite3_stmt *res = NULL; - - rc = sqlite3_prepare_v2(db_meta, sql, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement %s, rc = %d", sql, rc); - return 1; - } - - rc = sqlite3_bind_blob(res, 1, uuid, sizeof(*uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind UUID parameter to %s, rc = %d", sql, rc); - goto skip; - } - - rc = execute_insert(res); - if (likely(rc == SQLITE_DONE)) - result = SQLITE_OK; - else - error_report("Failed to execute %s, rc = %d", sql, rc); - -skip: - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize statement %s, rc = %d", sql, rc); - return result; -} -// Return 0 OK -// Return 1 Failed -int db_execute(sqlite3 *db, const char *cmd) -{ - int rc; - int cnt = 0; - - while (cnt < SQL_MAX_RETRY) { - char *err_msg; - rc = sqlite3_exec_monitored(db, cmd, 0, 0, &err_msg); - if (likely(rc == SQLITE_OK)) - break; - - ++cnt; - error_report("Failed to execute '%s', rc = %d (%s) -- attempt %d", cmd, rc, err_msg, cnt); - sqlite3_free(err_msg); - - if (likely(rc == SQLITE_BUSY || rc == SQLITE_LOCKED)) { - usleep(SQLITE_INSERT_DELAY * USEC_PER_MS); - continue; - } - - if (rc == SQLITE_CORRUPT) - mark_database_to_recover(NULL, db); - break; - } - return (rc != SQLITE_OK); -} - -static inline void set_host_node_id(RRDHOST *host, uuid_t *node_id) -{ - if (unlikely(!host)) - return; - - if (unlikely(!node_id)) { - freez(host->node_id); - __atomic_store_n(&host->node_id, NULL, __ATOMIC_RELAXED); - return; - } - - struct aclk_sync_cfg_t *wc = host->aclk_config; - - if (unlikely(!host->node_id)) { - uuid_t *t = mallocz(sizeof(*host->node_id)); - uuid_copy(*t, *node_id); - __atomic_store_n(&host->node_id, t, __ATOMIC_RELAXED); - } - else { - uuid_copy(*(host->node_id), *node_id); - } - - if (unlikely(!wc)) - sql_create_aclk_table(host, &host->host_uuid, node_id); - else - uuid_unparse_lower(*node_id, wc->node_id); -} - -#define SQL_UPDATE_NODE_ID "UPDATE node_instance SET node_id = @node_id WHERE host_id = @host_id" - -int update_node_id(uuid_t *host_id, uuid_t *node_id) -{ - sqlite3_stmt *res = NULL; - RRDHOST *host = NULL; - int rc = 2; - - char host_guid[GUID_LEN + 1]; - uuid_unparse_lower(*host_id, host_guid); - rrd_wrlock(); - host = rrdhost_find_by_guid(host_guid); - if (likely(host)) - set_host_node_id(host, node_id); - rrd_unlock(); - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("Database has not been initialized"); - return 1; - } - - rc = sqlite3_prepare_v2(db_meta, SQL_UPDATE_NODE_ID, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store node instance information"); - return 1; - } - - rc = sqlite3_bind_blob(res, 1, node_id, sizeof(*node_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host_id parameter to store node instance information"); - goto failed; - } - - rc = sqlite3_bind_blob(res, 2, host_id, sizeof(*host_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host_id parameter to store node instance information"); - goto failed; - } - - rc = execute_insert(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to store node instance information, rc = %d", rc); - rc = sqlite3_changes(db_meta); - -failed: - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when storing node instance information"); - - return rc - 1; -} - -#define SQL_SELECT_NODE_ID "SELECT node_id FROM node_instance WHERE host_id = @host_id AND node_id IS NOT NULL" - -int get_node_id(uuid_t *host_id, uuid_t *node_id) -{ - sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("Database has not been initialized"); - return 1; - } - - rc = sqlite3_prepare_v2(db_meta, SQL_SELECT_NODE_ID, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to select node instance information for a host"); - return 1; - } - - rc = sqlite3_bind_blob(res, 1, host_id, sizeof(*host_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host_id parameter to select node instance information"); - goto failed; - } - - rc = sqlite3_step_monitored(res); - if (likely(rc == SQLITE_ROW && node_id)) - uuid_copy(*node_id, *((uuid_t *) sqlite3_column_blob(res, 0))); - -failed: - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when selecting node instance information"); - - return (rc == SQLITE_ROW) ? 0 : -1; -} - -#define SQL_INVALIDATE_NODE_INSTANCES \ - "UPDATE node_instance SET node_id = NULL WHERE EXISTS " \ - "(SELECT host_id FROM node_instance WHERE host_id = @host_id AND (@claim_id IS NULL OR claim_id <> @claim_id))" - -void invalidate_node_instances(uuid_t *host_id, uuid_t *claim_id) -{ - sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("Database has not been initialized"); - return; - } - - rc = sqlite3_prepare_v2(db_meta, SQL_INVALIDATE_NODE_INSTANCES, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to invalidate node instance ids"); - return; - } - - rc = sqlite3_bind_blob(res, 1, host_id, sizeof(*host_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host_id parameter to invalidate node instance information"); - goto failed; - } - - if (claim_id) - rc = sqlite3_bind_blob(res, 2, claim_id, sizeof(*claim_id), SQLITE_STATIC); - else - rc = sqlite3_bind_null(res, 2); - - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind claim_id parameter to invalidate node instance information"); - goto failed; - } - - rc = execute_insert(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to invalidate node instance information, rc = %d", rc); - -failed: - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when invalidating node instance information"); -} - -#define SQL_GET_NODE_INSTANCE_LIST \ - "SELECT ni.node_id, ni.host_id, h.hostname " \ - "FROM node_instance ni, host h WHERE ni.host_id = h.host_id AND h.hops >=0" - -struct node_instance_list *get_node_list(void) -{ - struct node_instance_list *node_list = NULL; - sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("Database has not been initialized"); - return NULL; - } - - rc = sqlite3_prepare_v2(db_meta, SQL_GET_NODE_INSTANCE_LIST, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to get node instance information"); - return NULL; - }; - - int row = 0; - char host_guid[37]; - while (sqlite3_step_monitored(res) == SQLITE_ROW) - row++; - - if (sqlite3_reset(res) != SQLITE_OK) { - error_report("Failed to reset the prepared statement while fetching node instance information"); - goto failed; - } - node_list = callocz(row + 1, sizeof(*node_list)); - int max_rows = row; - row = 0; - // TODO: Check to remove lock - rrd_rdlock(); - while (sqlite3_step_monitored(res) == SQLITE_ROW) { - if (sqlite3_column_bytes(res, 0) == sizeof(uuid_t)) - uuid_copy(node_list[row].node_id, *((uuid_t *)sqlite3_column_blob(res, 0))); - if (sqlite3_column_bytes(res, 1) == sizeof(uuid_t)) { - uuid_t *host_id = (uuid_t *)sqlite3_column_blob(res, 1); - uuid_unparse_lower(*host_id, host_guid); - RRDHOST *host = rrdhost_find_by_guid(host_guid); - if (!host) - continue; - if (rrdhost_flag_check(host, RRDHOST_FLAG_PENDING_CONTEXT_LOAD)) { - netdata_log_info("ACLK: 'host:%s' skipping get node list because context is initializing", rrdhost_hostname(host)); - continue; - } - uuid_copy(node_list[row].host_id, *host_id); - node_list[row].queryable = 1; - node_list[row].live = (host && (host == localhost || host->receiver - || !(rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN)))) ? 1 : 0; - node_list[row].hops = (host && host->system_info) ? host->system_info->hops : - uuid_memcmp(host_id, &localhost->host_uuid) ? 1 : 0; - node_list[row].hostname = - sqlite3_column_bytes(res, 2) ? strdupz((char *)sqlite3_column_text(res, 2)) : NULL; - } - row++; - if (row == max_rows) - break; - } - rrd_unlock(); - -failed: - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when fetching node instance information"); - - return node_list; -}; - -#define SQL_GET_HOST_NODE_ID "SELECT node_id FROM node_instance WHERE host_id = @host_id" - -void sql_load_node_id(RRDHOST *host) -{ - sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("Database has not been initialized"); - return; - } - - rc = sqlite3_prepare_v2(db_meta, SQL_GET_HOST_NODE_ID, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch node id"); - return; - }; - - rc = sqlite3_bind_blob(res, 1, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host_id parameter to load node instance information"); - goto failed; - } - - rc = sqlite3_step_monitored(res); - if (likely(rc == SQLITE_ROW)) { - if (likely(sqlite3_column_bytes(res, 0) == sizeof(uuid_t))) - set_host_node_id(host, (uuid_t *)sqlite3_column_blob(res, 0)); - else - set_host_node_id(host, NULL); - } - -failed: - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when loading node instance information"); -}; - - -#define SELECT_HOST_INFO "SELECT system_key, system_value FROM host_info WHERE host_id = @host_id" - -void sql_build_host_system_info(uuid_t *host_id, struct rrdhost_system_info *system_info) -{ - int rc; - - sqlite3_stmt *res = NULL; - - rc = sqlite3_prepare_v2(db_meta, SELECT_HOST_INFO, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to read host information"); - return; - } - - rc = sqlite3_bind_blob(res, 1, host_id, sizeof(*host_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host parameter host information"); - goto skip; - } - - while (sqlite3_step_monitored(res) == SQLITE_ROW) { - rrdhost_set_system_info_variable(system_info, (char *) sqlite3_column_text(res, 0), - (char *) sqlite3_column_text(res, 1)); - } - -skip: - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when reading host information"); -} - -#define SELECT_HOST_LABELS "SELECT label_key, label_value, source_type FROM host_label WHERE host_id = @host_id " \ - "AND label_key IS NOT NULL AND label_value IS NOT NULL" - -RRDLABELS *sql_load_host_labels(uuid_t *host_id) -{ - int rc; - - RRDLABELS *labels = NULL; - sqlite3_stmt *res = NULL; - - rc = sqlite3_prepare_v2(db_meta, SELECT_HOST_LABELS, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to read host information"); - return NULL; - } - - rc = sqlite3_bind_blob(res, 1, host_id, sizeof(*host_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host parameter host information"); - goto skip; - } - - labels = rrdlabels_create(); - - while (sqlite3_step_monitored(res) == SQLITE_ROW) { - rrdlabels_add(labels, (const char *)sqlite3_column_text(res, 0), (const char *)sqlite3_column_text(res, 1), sqlite3_column_int(res, 2)); - } - -skip: - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when reading host information"); - return labels; -} - -// Utils -int bind_text_null(sqlite3_stmt *res, int position, const char *text, bool can_be_null) -{ - if (likely(text)) - return sqlite3_bind_text(res, position, text, -1, SQLITE_STATIC); - if (!can_be_null) - return 1; - return sqlite3_bind_null(res, position); -} - -int sql_metadata_cache_stats(int op) -{ - int count, dummy; - - if (unlikely(!db_meta)) - return 0; - - netdata_thread_disable_cancelability(); - sqlite3_db_status(db_meta, op, &count, &dummy, 0); - netdata_thread_enable_cancelability(); - return count; -} - -#define SQL_DROP_TABLE "DROP table %s" - -void sql_drop_table(const char *table) -{ - if (!table) - return; - - char wstr[255]; - snprintfz(wstr, sizeof(wstr) - 1, SQL_DROP_TABLE, table); - - int rc = sqlite3_exec_monitored(db_meta, wstr, 0, 0, NULL); - if (rc != SQLITE_OK) { - error_report("DES SQLite error during drop table operation for %s, rc = %d", table, rc); - } -} diff --git a/database/sqlite/sqlite_functions.h b/database/sqlite/sqlite_functions.h deleted file mode 100644 index 40b5af464..000000000 --- a/database/sqlite/sqlite_functions.h +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_SQLITE_FUNCTIONS_H -#define NETDATA_SQLITE_FUNCTIONS_H - -#include "daemon/common.h" -#include "sqlite3.h" - -void analytics_set_data_str(char **name, const char *value); - -// return a node list -struct node_instance_list { - uuid_t node_id; - uuid_t host_id; - char *hostname; - int live; - int queryable; - int hops; -}; - -typedef enum db_check_action_type { - DB_CHECK_NONE = (1 << 0), - DB_CHECK_RECLAIM_SPACE = (1 << 1), - DB_CHECK_ANALYZE = (1 << 2), - DB_CHECK_CONT = (1 << 3), - DB_CHECK_RECOVER = (1 << 4), -} db_check_action_type_t; - -#define SQL_MAX_RETRY (100) -#define SQLITE_INSERT_DELAY (10) // Insert delay in case of lock - -#define CHECK_SQLITE_CONNECTION(db_meta) \ - if (unlikely(!db_meta)) { \ - if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) { \ - return 1; \ - } \ - error_report("Database has not been initialized"); \ - return 1; \ - } - -SQLITE_API int sqlite3_step_monitored(sqlite3_stmt *stmt); -SQLITE_API int sqlite3_exec_monitored( - sqlite3 *db, /* An open database */ - const char *sql, /* SQL to be evaluated */ - int (*callback)(void*,int,char**,char**), /* Callback function */ - void *data, /* 1st argument to callback */ - char **errmsg /* Error msg written here */ - ); - -// Initialization and shutdown -int init_database_batch(sqlite3 *database, const char *batch[]); -int sql_init_database(db_check_action_type_t rebuild, int memory); -void sql_close_database(void); -int configure_sqlite_database(sqlite3 *database, int target_version); - -// Helpers -int bind_text_null(sqlite3_stmt *res, int position, const char *text, bool can_be_null); -int prepare_statement(sqlite3 *database, const char *query, sqlite3_stmt **statement); -int execute_insert(sqlite3_stmt *res); -int exec_statement_with_uuid(const char *sql, uuid_t *uuid); -int db_execute(sqlite3 *database, const char *cmd); -void initialize_thread_key_pool(void); - -// Look up functions -int get_node_id(uuid_t *host_id, uuid_t *node_id); -struct node_instance_list *get_node_list(void); -void sql_load_node_id(RRDHOST *host); - -// Help build archived hosts in memory when agent starts -void sql_build_host_system_info(uuid_t *host_id, struct rrdhost_system_info *system_info); -RRDLABELS *sql_load_host_labels(uuid_t *host_id); - -// TODO: move to metadata -int update_node_id(uuid_t *host_id, uuid_t *node_id); - -void invalidate_node_instances(uuid_t *host_id, uuid_t *claim_id); - -// Provide statistics -int sql_metadata_cache_stats(int op); - -void sql_drop_table(const char *table); -void sqlite_now_usec(sqlite3_context *context, int argc, sqlite3_value **argv); -#endif //NETDATA_SQLITE_FUNCTIONS_H diff --git a/database/sqlite/sqlite_metadata.c b/database/sqlite/sqlite_metadata.c deleted file mode 100644 index 636f51966..000000000 --- a/database/sqlite/sqlite_metadata.c +++ /dev/null @@ -1,1994 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "sqlite_metadata.h" - -// SQL statements - -#define SQL_STORE_CLAIM_ID \ - "INSERT INTO node_instance " \ - "(host_id, claim_id, date_created) VALUES (@host_id, @claim_id, UNIXEPOCH()) " \ - "ON CONFLICT(host_id) DO UPDATE SET claim_id = excluded.claim_id" - -#define SQL_DELETE_HOST_LABELS "DELETE FROM host_label WHERE host_id = @uuid" - -#define STORE_HOST_LABEL \ - "INSERT INTO host_label (host_id, source_type, label_key, label_value, date_created) VALUES " - -#define STORE_CHART_LABEL \ - "INSERT INTO chart_label (chart_id, source_type, label_key, label_value, date_created) VALUES " - -#define STORE_HOST_OR_CHART_LABEL_VALUE "(u2h('%s'), %d,'%s','%s', unixepoch())" - -#define DELETE_DIMENSION_UUID "DELETE FROM dimension WHERE dim_id = @uuid" - -#define SQL_STORE_HOST_INFO \ - "INSERT OR REPLACE INTO host (host_id, hostname, registry_hostname, update_every, os, timezone, tags, hops, " \ - "memory_mode, abbrev_timezone, utc_offset, program_name, program_version, entries, health_enabled, last_connected) " \ - "VALUES (@host_id, @hostname, @registry_hostname, @update_every, @os, @timezone, @tags, @hops, " \ - "@memory_mode, @abbrev_tz, @utc_offset, @prog_name, @prog_version, @entries, @health_enabled, @last_connected)" - -#define SQL_STORE_CHART \ - "INSERT INTO chart (chart_id, host_id, type, id, name, family, context, title, unit, plugin, module, priority, " \ - "update_every, chart_type, memory_mode, history_entries) " \ - "values (@chart_id, @host_id, @type, @id, @name, @family, @context, @title, @unit, @plugin, @module, @priority, " \ - "@update_every, @chart_type, @memory_mode, @history_entries) " \ - "ON CONFLICT(chart_id) DO UPDATE SET type=excluded.type, id=excluded.id, name=excluded.name, " \ - "family=excluded.family, context=excluded.context, title=excluded.title, unit=excluded.unit, " \ - "plugin=excluded.plugin, module=excluded.module, priority=excluded.priority, update_every=excluded.update_every, " \ - "chart_type=excluded.chart_type, memory_mode = excluded.memory_mode, history_entries = excluded.history_entries" - -#define SQL_STORE_DIMENSION \ - "INSERT INTO dimension (dim_id, chart_id, id, name, multiplier, divisor , algorithm, options) " \ - "VALUES (@dim_id, @chart_id, @id, @name, @multiplier, @divisor, @algorithm, @options) " \ - "ON CONFLICT(dim_id) DO UPDATE SET id=excluded.id, name=excluded.name, multiplier=excluded.multiplier, " \ - "divisor=excluded.divisor, algorithm=excluded.algorithm, options=excluded.options" - -#define SELECT_DIMENSION_LIST "SELECT dim_id, rowid FROM dimension WHERE rowid > @row_id" -#define SELECT_CHART_LIST "SELECT chart_id, rowid FROM chart WHERE rowid > @row_id" -#define SELECT_CHART_LABEL_LIST "SELECT chart_id, rowid FROM chart_label WHERE rowid > @row_id" - -#define SQL_STORE_HOST_SYSTEM_INFO_VALUES \ - "INSERT OR REPLACE INTO host_info (host_id, system_key, system_value, date_created) VALUES " \ - "(@uuid, @name, @value, UNIXEPOCH())" - -#define MIGRATE_LOCALHOST_TO_NEW_MACHINE_GUID \ - "UPDATE chart SET host_id = @host_id WHERE host_id in (SELECT host_id FROM host where host_id <> @host_id and hops = 0)" -#define DELETE_NON_EXISTING_LOCALHOST "DELETE FROM host WHERE hops = 0 AND host_id <> @host_id" -#define DELETE_MISSING_NODE_INSTANCES "DELETE FROM node_instance WHERE host_id NOT IN (SELECT host_id FROM host)" - -#define METADATA_MAINTENANCE_FIRST_CHECK (1800) // Maintenance first run after agent startup in seconds -#define METADATA_MAINTENANCE_REPEAT (60) // Repeat if last run for dimensions, charts, labels needs more work -#define METADATA_HEALTH_LOG_INTERVAL (3600) // Repeat maintenance for health -#define METADATA_DIM_CHECK_INTERVAL (3600) // Repeat maintenance for dimensions -#define METADATA_CHART_CHECK_INTERVAL (3600) // Repeat maintenance for charts -#define METADATA_LABEL_CHECK_INTERVAL (3600) // Repeat maintenance for labels -#define METADATA_RUNTIME_THRESHOLD (5) // Run time threshold for cleanup task - -#define METADATA_HOST_CHECK_FIRST_CHECK (5) // First check for pending metadata -#define METADATA_HOST_CHECK_INTERVAL (30) // Repeat check for pending metadata -#define METADATA_HOST_CHECK_IMMEDIATE (5) // Repeat immediate run because we have more metadata to write -#define MAX_METADATA_CLEANUP (500) // Maximum metadata write operations (e.g deletes before retrying) -#define METADATA_MAX_BATCH_SIZE (512) // Maximum commands to execute before running the event loop - -#define DATABASE_FREE_PAGES_THRESHOLD_PC (5) // Percentage of free pages to trigger vacuum -#define DATABASE_FREE_PAGES_VACUUM_PC (10) // Percentage of free pages to vacuum - -enum metadata_opcode { - METADATA_DATABASE_NOOP = 0, - METADATA_DATABASE_TIMER, - METADATA_DEL_DIMENSION, - METADATA_STORE_CLAIM_ID, - METADATA_ADD_HOST_INFO, - METADATA_SCAN_HOSTS, - METADATA_LOAD_HOST_CONTEXT, - METADATA_DELETE_HOST_CHART_LABELS, - METADATA_MAINTENANCE, - METADATA_SYNC_SHUTDOWN, - METADATA_UNITTEST, - // leave this last - // we need it to check for worker utilization - METADATA_MAX_ENUMERATIONS_DEFINED -}; - -#define MAX_PARAM_LIST (2) -struct metadata_cmd { - enum metadata_opcode opcode; - struct completion *completion; - const void *param[MAX_PARAM_LIST]; - struct metadata_cmd *prev, *next; -}; - -typedef enum { - METADATA_FLAG_PROCESSING = (1 << 0), // store or cleanup - METADATA_FLAG_SHUTDOWN = (1 << 1), // Shutting down -} METADATA_FLAG; - -struct metadata_wc { - uv_thread_t thread; - uv_loop_t *loop; - uv_async_t async; - uv_timer_t timer_req; - time_t metadata_check_after; - METADATA_FLAG flags; - struct completion start_stop_complete; - struct completion *scan_complete; - /* FIFO command queue */ - SPINLOCK cmd_queue_lock; - struct metadata_cmd *cmd_base; -}; - -#define metadata_flag_check(target_flags, flag) (__atomic_load_n(&((target_flags)->flags), __ATOMIC_SEQ_CST) & (flag)) -#define metadata_flag_set(target_flags, flag) __atomic_or_fetch(&((target_flags)->flags), (flag), __ATOMIC_SEQ_CST) -#define metadata_flag_clear(target_flags, flag) __atomic_and_fetch(&((target_flags)->flags), ~(flag), __ATOMIC_SEQ_CST) - -struct metadata_wc metasync_worker = {.loop = NULL}; - -// -// For unittest -// -struct thread_unittest { - int join; - unsigned added; - unsigned processed; - unsigned *done; -}; - - -// Metadata functions - -struct query_build { - BUFFER *sql; - int count; - char uuid_str[UUID_STR_LEN]; -}; - -#define SQL_DELETE_CHART_LABELS_BY_HOST \ - "DELETE FROM chart_label WHERE chart_id in (SELECT chart_id FROM chart WHERE host_id = @host_id)" - -static void delete_host_chart_labels(uuid_t *host_uuid) -{ - sqlite3_stmt *res = NULL; - - int rc = sqlite3_prepare_v2(db_meta, SQL_DELETE_CHART_LABELS_BY_HOST, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to delete chart labels by host"); - return; - } - - rc = sqlite3_bind_blob(res, 1, host_uuid, sizeof(*host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host_id parameter to host chart labels"); - goto failed; - } - rc = sqlite3_step_monitored(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to execute command to remove host chart labels"); - -failed: - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize statement to remove host chart labels"); -} - -static int host_label_store_to_sql_callback(const char *name, const char *value, RRDLABEL_SRC ls, void *data) { - struct query_build *lb = data; - if (unlikely(!lb->count)) - buffer_sprintf(lb->sql, STORE_HOST_LABEL); - else - buffer_strcat(lb->sql, ", "); - buffer_sprintf(lb->sql, STORE_HOST_OR_CHART_LABEL_VALUE, lb->uuid_str, (int) (ls & ~(RRDLABEL_FLAG_INTERNAL)), name, value); - lb->count++; - return 1; -} - -static int chart_label_store_to_sql_callback(const char *name, const char *value, RRDLABEL_SRC ls, void *data) { - struct query_build *lb = data; - if (unlikely(!lb->count)) - buffer_sprintf(lb->sql, STORE_CHART_LABEL); - else - buffer_strcat(lb->sql, ", "); - buffer_sprintf(lb->sql, STORE_HOST_OR_CHART_LABEL_VALUE, lb->uuid_str, (int) (ls & ~(RRDLABEL_FLAG_INTERNAL)), name, value); - lb->count++; - return 1; -} - -#define SQL_DELETE_CHART_LABEL "DELETE FROM chart_label WHERE chart_id = @chart_id" -#define SQL_DELETE_CHART_LABEL_HISTORY "DELETE FROM chart_label WHERE date_created < %ld AND chart_id = @chart_id" - -static void clean_old_chart_labels(RRDSET *st) -{ - char sql[512]; - time_t first_time_s = rrdset_first_entry_s(st); - - if (unlikely(!first_time_s)) - snprintfz(sql, sizeof(sql) - 1, SQL_DELETE_CHART_LABEL); - else - snprintfz(sql, sizeof(sql) - 1, SQL_DELETE_CHART_LABEL_HISTORY, first_time_s); - - int rc = exec_statement_with_uuid(sql, &st->chart_uuid); - if (unlikely(rc)) - error_report("METADATA: 'host:%s' Failed to clean old labels for chart %s", rrdhost_hostname(st->rrdhost), rrdset_name(st)); -} - -static int check_and_update_chart_labels(RRDSET *st, BUFFER *work_buffer, size_t *query_counter) -{ - size_t old_version = st->rrdlabels_last_saved_version; - size_t new_version = rrdlabels_version(st->rrdlabels); - - if (new_version == old_version) - return 0; - - struct query_build tmp = {.sql = work_buffer, .count = 0}; - uuid_unparse_lower(st->chart_uuid, tmp.uuid_str); - rrdlabels_walkthrough_read(st->rrdlabels, chart_label_store_to_sql_callback, &tmp); - buffer_strcat(work_buffer, " ON CONFLICT (chart_id, label_key) DO UPDATE SET source_type = excluded.source_type, label_value=excluded.label_value, date_created=UNIXEPOCH()"); - int rc = db_execute(db_meta, buffer_tostring(work_buffer)); - if (likely(!rc)) { - st->rrdlabels_last_saved_version = new_version; - (*query_counter)++; - } - - clean_old_chart_labels(st); - return rc; -} - -// Migrate all hosts with hops zero to this host_uuid -void migrate_localhost(uuid_t *host_uuid) -{ - int rc; - - rc = exec_statement_with_uuid(MIGRATE_LOCALHOST_TO_NEW_MACHINE_GUID, host_uuid); - if (!rc) - rc = exec_statement_with_uuid(DELETE_NON_EXISTING_LOCALHOST, host_uuid); - if (!rc) { - if (unlikely(db_execute(db_meta, DELETE_MISSING_NODE_INSTANCES))) - error_report("Failed to remove deleted hosts from node instances"); - } -} - -static int store_claim_id(uuid_t *host_id, uuid_t *claim_id) -{ - sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("Database has not been initialized"); - return 1; - } - - rc = sqlite3_prepare_v2(db_meta, SQL_STORE_CLAIM_ID, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store host claim id"); - return 1; - } - - rc = sqlite3_bind_blob(res, 1, host_id, sizeof(*host_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host_id parameter to store claim id"); - goto failed; - } - - if (claim_id) - rc = sqlite3_bind_blob(res, 2, claim_id, sizeof(*claim_id), SQLITE_STATIC); - else - rc = sqlite3_bind_null(res, 2); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind claim_id parameter to host claim id"); - goto failed; - } - - rc = execute_insert(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to store host claim id rc = %d", rc); - -failed: - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when storing a host claim id"); - - return rc != SQLITE_DONE; -} - -static void delete_dimension_uuid(uuid_t *dimension_uuid, sqlite3_stmt **action_res __maybe_unused, bool flag __maybe_unused) -{ - static __thread sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!res)) { - rc = prepare_statement(db_meta, DELETE_DIMENSION_UUID, &res); - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement to delete a dimension uuid"); - return; - } - } - - rc = sqlite3_bind_blob(res, 1, dimension_uuid, sizeof(*dimension_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto skip_execution; - - rc = sqlite3_step_monitored(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to delete dimension uuid, rc = %d", rc); - -skip_execution: - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement when deleting dimension UUID, rc = %d", rc); -} - -// -// Store host and host system info information in the database -static int store_host_metadata(RRDHOST *host) -{ - static __thread sqlite3_stmt *res = NULL; - int rc, param = 0; - - if (unlikely((!res))) { - rc = prepare_statement(db_meta, SQL_STORE_HOST_INFO, &res); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store host, rc = %d", rc); - return 1; - } - } - - rc = sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, ++param, rrdhost_hostname(host), 0); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, ++param, rrdhost_registry_hostname(host), 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, host->rrd_update_every); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, ++param, rrdhost_os(host), 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, ++param, rrdhost_timezone(host), 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, ++param, rrdhost_tags(host), 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, host->system_info ? host->system_info->hops : 0); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, host->rrd_memory_mode); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, ++param, rrdhost_abbrev_timezone(host), 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, host->utc_offset); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, ++param, rrdhost_program_name(host), 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, ++param, rrdhost_program_version(host), 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int64(res, ++param, host->rrd_history_entries); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, (int ) host->health.health_enabled); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int64(res, ++param, (sqlite3_int64) host->last_connected); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - int store_rc = sqlite3_step_monitored(res); - if (unlikely(store_rc != SQLITE_DONE)) - error_report("Failed to store host %s, rc = %d", rrdhost_hostname(host), rc); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement to store host %s, rc = %d", rrdhost_hostname(host), rc); - - return store_rc != SQLITE_DONE; -bind_fail: - error_report("Failed to bind %d parameter to store host %s, rc = %d", param, rrdhost_hostname(host), rc); - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement to store host %s, rc = %d", rrdhost_hostname(host), rc); - return 1; -} - -static int add_host_sysinfo_key_value(const char *name, const char *value, uuid_t *uuid) -{ - static __thread sqlite3_stmt *res = NULL; - int rc, param = 0; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) - return 0; - error_report("Database has not been initialized"); - return 0; - } - - if (unlikely((!res))) { - rc = prepare_statement(db_meta, SQL_STORE_HOST_SYSTEM_INFO_VALUES, &res); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store host info values, rc = %d", rc); - return 0; - } - } - - rc = sqlite3_bind_blob(res, ++param, uuid, sizeof(*uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, ++param, name, 0); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, ++param, value ? value : "unknown", 0); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - int store_rc = sqlite3_step_monitored(res); - if (unlikely(store_rc != SQLITE_DONE)) - error_report("Failed to store host info value %s, rc = %d", name, rc); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement to store host info value %s, rc = %d", name, rc); - - return store_rc == SQLITE_DONE; -bind_fail: - error_report("Failed to bind %d parameter to store host info values %s, rc = %d", param, name, rc); - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement to store host info values %s, rc = %d", name, rc); - return 0; -} - -static bool store_host_systeminfo(RRDHOST *host) -{ - struct rrdhost_system_info *system_info = host->system_info; - - if (unlikely(!system_info)) - return false; - - int ret = 0; - - ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_NAME", system_info->container_os_name, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_ID", system_info->container_os_id, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_ID_LIKE", system_info->container_os_id_like, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_VERSION", system_info->container_os_version, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_VERSION_ID", system_info->container_os_version_id, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_CONTAINER_OS_DETECTION", system_info->host_os_detection, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_NAME", system_info->host_os_name, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_ID", system_info->host_os_id, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_ID_LIKE", system_info->host_os_id_like, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_VERSION", system_info->host_os_version, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_VERSION_ID", system_info->host_os_version_id, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_HOST_OS_DETECTION", system_info->host_os_detection, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_KERNEL_NAME", system_info->kernel_name, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_CPU_LOGICAL_CPU_COUNT", system_info->host_cores, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_CPU_FREQ", system_info->host_cpu_freq, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_TOTAL_RAM", system_info->host_ram_total, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_TOTAL_DISK_SIZE", system_info->host_disk_space, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_KERNEL_VERSION", system_info->kernel_version, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_ARCHITECTURE", system_info->architecture, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_VIRTUALIZATION", system_info->virtualization, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_VIRT_DETECTION", system_info->virt_detection, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_CONTAINER", system_info->container, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_SYSTEM_CONTAINER_DETECTION", system_info->container_detection, &host->host_uuid); - ret += add_host_sysinfo_key_value("NETDATA_HOST_IS_K8S_NODE", system_info->is_k8s_node, &host->host_uuid); - - return !(24 == ret); -} - - -/* - * Store a chart in the database - */ - -static int store_chart_metadata(RRDSET *st) -{ - static __thread sqlite3_stmt *res = NULL; - int rc, param = 0, store_rc = 0; - - if (unlikely(!res)) { - rc = prepare_statement(db_meta, SQL_STORE_CHART, &res); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store chart, rc = %d", rc); - return 1; - } - } - - rc = sqlite3_bind_blob(res, ++param, &st->chart_uuid, sizeof(st->chart_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_blob(res, ++param, &st->rrdhost->host_uuid, sizeof(st->rrdhost->host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, ++param, string2str(st->parts.type), -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, ++param, string2str(st->parts.id), -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - const char *name = string2str(st->parts.name); - if (name && *name) - rc = sqlite3_bind_text(res, ++param, name, -1, SQLITE_STATIC); - else - rc = sqlite3_bind_null(res, ++param); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, ++param, rrdset_family(st), -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, ++param, rrdset_context(st), -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, ++param, rrdset_title(st), -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, ++param, rrdset_units(st), -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, ++param, rrdset_plugin_name(st), -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, ++param, rrdset_module_name(st), -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, (int) st->priority); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, st->update_every); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, st->chart_type); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, st->rrd_memory_mode); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, (int) st->db.entries); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - store_rc = execute_insert(res); - if (unlikely(store_rc != SQLITE_DONE)) - error_report("Failed to store chart, rc = %d", store_rc); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement in chart store function, rc = %d", rc); - - return store_rc != SQLITE_DONE; - -bind_fail: - error_report("Failed to bind parameter %d to store chart, rc = %d", param, rc); - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement in chart store function, rc = %d", rc); - return 1; -} - -/* - * Store a dimension - */ -static int store_dimension_metadata(RRDDIM *rd) -{ - static __thread sqlite3_stmt *res = NULL; - int rc, param = 0; - - if (unlikely(!res)) { - rc = prepare_statement(db_meta, SQL_STORE_DIMENSION, &res); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store dimension, rc = %d", rc); - return 1; - } - } - - rc = sqlite3_bind_blob(res, ++param, &rd->metric_uuid, sizeof(rd->metric_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_blob(res, ++param, &rd->rrdset->chart_uuid, sizeof(rd->rrdset->chart_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, ++param, string2str(rd->id), -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, ++param, string2str(rd->name), -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, (int) rd->multiplier); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, (int ) rd->divisor); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, ++param, rd->algorithm); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - if (rrddim_option_check(rd, RRDDIM_OPTION_HIDDEN)) - rc = sqlite3_bind_text(res, ++param, "hidden", -1, SQLITE_STATIC); - else - rc = sqlite3_bind_null(res, ++param); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = execute_insert(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to store dimension, rc = %d", rc); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement in store dimension, rc = %d", rc); - return 0; - -bind_fail: - error_report("Failed to bind parameter %d to store dimension, rc = %d", param, rc); - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement in store dimension, rc = %d", rc); - return 1; -} - -static bool dimension_can_be_deleted(uuid_t *dim_uuid __maybe_unused, sqlite3_stmt **res __maybe_unused, bool flag __maybe_unused) -{ -#ifdef ENABLE_DBENGINE - if(dbengine_enabled) { - bool no_retention = true; - for (size_t tier = 0; tier < storage_tiers; tier++) { - if (!multidb_ctx[tier]) - continue; - time_t first_time_t = 0, last_time_t = 0; - if (rrdeng_metric_retention_by_uuid((void *) multidb_ctx[tier], dim_uuid, &first_time_t, &last_time_t)) { - if (first_time_t > 0) { - no_retention = false; - break; - } - } - } - return no_retention; - } - else - return false; -#else - return false; -#endif -} - -int get_pragma_value(sqlite3 *database, const char *sql) -{ - sqlite3_stmt *res = NULL; - int rc = sqlite3_prepare_v2(database, sql, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) - return -1; - - int result = -1; - rc = sqlite3_step_monitored(res); - if (likely(rc == SQLITE_ROW)) - result = sqlite3_column_int(res, 0); - - rc = sqlite3_finalize(res); - (void) rc; - - return result; -} - - -int get_free_page_count(sqlite3 *database) -{ - return get_pragma_value(database, "PRAGMA freelist_count"); -} - -int get_database_page_count(sqlite3 *database) -{ - return get_pragma_value(database, "PRAGMA page_count"); -} - -static bool run_cleanup_loop( - sqlite3_stmt *res, - struct metadata_wc *wc, - bool (*check_cb)(uuid_t *, sqlite3_stmt **, bool), - void (*action_cb)(uuid_t *, sqlite3_stmt **, bool), - uint32_t *total_checked, - uint32_t *total_deleted, - uint64_t *row_id, - sqlite3_stmt **check_stmt, - sqlite3_stmt **action_stmt, - bool check_flag, - bool action_flag) -{ - if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) - return true; - - int rc = sqlite3_bind_int64(res, 1, (sqlite3_int64) *row_id); - if (unlikely(rc != SQLITE_OK)) - return true; - - time_t start_running = now_monotonic_sec(); - bool time_expired = false; - while (!time_expired && sqlite3_step_monitored(res) == SQLITE_ROW && - (*total_deleted < MAX_METADATA_CLEANUP && *total_checked < MAX_METADATA_CLEANUP)) { - if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) - break; - - *row_id = sqlite3_column_int64(res, 1); - rc = check_cb((uuid_t *)sqlite3_column_blob(res, 0), check_stmt, check_flag); - - if (rc == true) { - action_cb((uuid_t *)sqlite3_column_blob(res, 0), action_stmt, action_flag); - (*total_deleted)++; - } - - (*total_checked)++; - time_expired = ((now_monotonic_sec() - start_running) > METADATA_RUNTIME_THRESHOLD); - } - return time_expired || (*total_checked == MAX_METADATA_CLEANUP) || (*total_deleted == MAX_METADATA_CLEANUP); -} - - -#define SQL_CHECK_CHART_EXISTENCE_IN_DIMENSION "SELECT count(1) FROM dimension WHERE chart_id = @chart_id" -#define SQL_CHECK_CHART_EXISTENCE_IN_CHART "SELECT count(1) FROM chart WHERE chart_id = @chart_id" - -static bool chart_can_be_deleted(uuid_t *chart_uuid, sqlite3_stmt **check_res, bool check_in_dimension) -{ - int rc, result = 1; - sqlite3_stmt *res = check_res ? *check_res : NULL; - - if (!res) { - if (check_in_dimension) - rc = sqlite3_prepare_v2(db_meta, SQL_CHECK_CHART_EXISTENCE_IN_DIMENSION, -1, &res, 0); - else - rc = sqlite3_prepare_v2(db_meta, SQL_CHECK_CHART_EXISTENCE_IN_CHART, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to check for chart existence, rc = %d", rc); - return 0; - } - if (check_res) - *check_res = res; - } - - rc = sqlite3_bind_blob(res, 1, chart_uuid, sizeof(*chart_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind chart uuid parameter, rc = %d", rc); - goto skip; - } - - rc = sqlite3_step_monitored(res); - if (likely(rc == SQLITE_ROW)) - result = sqlite3_column_int(res, 0); - -skip: - if (check_res) - rc = sqlite3_reset(res); - else - rc = sqlite3_finalize(res); - - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to %s statement that checks chart uuid existence rc = %d", check_res ? "reset" : "finalize", rc); - return result == 0; -} - -#define SQL_DELETE_CHART_BY_UUID "DELETE FROM chart WHERE chart_id = @chart_id" -#define SQL_DELETE_CHART_LABEL_BY_UUID "DELETE FROM chart_label WHERE chart_id = @chart_id" - -static void delete_chart_uuid(uuid_t *chart_uuid, sqlite3_stmt **action_res, bool label_only) -{ - int rc; - sqlite3_stmt *res = action_res ? *action_res : NULL; - - if (!res) { - if (label_only) - rc = sqlite3_prepare_v2(db_meta, SQL_DELETE_CHART_LABEL_BY_UUID, -1, &res, 0); - else - rc = sqlite3_prepare_v2(db_meta, SQL_DELETE_CHART_BY_UUID, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to check for chart existence, rc = %d", rc); - return; - } - if (action_res) - *action_res = res; - } - - rc = sqlite3_bind_blob(res, 1, chart_uuid, sizeof(*chart_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind chart uuid parameter, rc = %d", rc); - goto skip; - } - - rc = sqlite3_step_monitored(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to delete a chart uuid from the %s table, rc = %d", label_only ? "labels" : "chart", rc); - -skip: - if (action_res) - rc = sqlite3_reset(res); - else - rc = sqlite3_finalize(res); - - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to %s statement that deletes a chart uuid rc = %d", action_res ? "reset" : "finalize", rc); -} - -static void check_dimension_metadata(struct metadata_wc *wc) -{ - static time_t next_execution_t = 0; - static uint64_t last_row_id = 0; - - time_t now = now_realtime_sec(); - - if (!next_execution_t) - next_execution_t = now + METADATA_MAINTENANCE_FIRST_CHECK; - - if (next_execution_t && next_execution_t > now) - return; - - int rc; - sqlite3_stmt *res = NULL; - - rc = sqlite3_prepare_v2(db_meta, SELECT_DIMENSION_LIST, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch host dimensions"); - return; - } - - uint32_t total_checked = 0; - uint32_t total_deleted = 0; - - internal_error(true, "METADATA: Checking dimensions starting after row %"PRIu64, last_row_id); - - bool more_to_do = run_cleanup_loop( - res, - wc, - dimension_can_be_deleted, - delete_dimension_uuid, - &total_checked, - &total_deleted, - &last_row_id, - NULL, - NULL, - false, - false); - - now = now_realtime_sec(); - if (more_to_do) - next_execution_t = now + METADATA_MAINTENANCE_REPEAT; - else { - last_row_id = 0; - next_execution_t = now + METADATA_DIM_CHECK_INTERVAL; - } - - internal_error(true, - "METADATA: Dimensions checked %u, deleted %u. Checks will %s in %lld seconds", - total_checked, - total_deleted, - last_row_id ? "resume" : "restart", - (long long)(next_execution_t - now)); - - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize the prepared statement to check dimensions"); -} - -static void check_chart_metadata(struct metadata_wc *wc) -{ - static time_t next_execution_t = 0; - static uint64_t last_row_id = 0; - - time_t now = now_realtime_sec(); - - if (!next_execution_t) - next_execution_t = now + METADATA_MAINTENANCE_FIRST_CHECK; - - if (next_execution_t && next_execution_t > now) - return; - - sqlite3_stmt *res = NULL; - - int rc = sqlite3_prepare_v2(db_meta, SELECT_CHART_LIST, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch charts"); - return; - } - - uint32_t total_checked = 0; - uint32_t total_deleted = 0; - - internal_error(true, "METADATA: Checking charts starting after row %"PRIu64, last_row_id); - - sqlite3_stmt *check_res = NULL; - sqlite3_stmt *action_res = NULL; - bool more_to_do = run_cleanup_loop( - res, - wc, - chart_can_be_deleted, - delete_chart_uuid, - &total_checked, - &total_deleted, - &last_row_id, - &check_res, - &action_res, - true, - false); - - if (check_res) - sqlite3_finalize(check_res); - - if (action_res) - sqlite3_finalize(action_res); - - now = now_realtime_sec(); - if (more_to_do) - next_execution_t = now + METADATA_MAINTENANCE_REPEAT; - else { - last_row_id = 0; - next_execution_t = now + METADATA_CHART_CHECK_INTERVAL; - } - - internal_error(true, - "METADATA: Charts checked %u, deleted %u. Checks will %s in %lld seconds", - total_checked, - total_deleted, - last_row_id ? "resume" : "restart", - (long long)(next_execution_t - now)); - - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when reading charts"); -} - -static void check_label_metadata(struct metadata_wc *wc) -{ - static time_t next_execution_t = 0; - static uint64_t last_row_id = 0; - - time_t now = now_realtime_sec(); - - if (!next_execution_t) - next_execution_t = now + METADATA_MAINTENANCE_FIRST_CHECK; - - if (next_execution_t && next_execution_t > now) - return; - - int rc; - sqlite3_stmt *res = NULL; - - rc = sqlite3_prepare_v2(db_meta, SELECT_CHART_LABEL_LIST, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch charts"); - return; - } - - uint32_t total_checked = 0; - uint32_t total_deleted = 0; - - internal_error(true,"METADATA: Checking charts labels starting after row %"PRIu64, last_row_id); - - sqlite3_stmt *check_res = NULL; - sqlite3_stmt *action_res = NULL; - - bool more_to_do = run_cleanup_loop( - res, - wc, - chart_can_be_deleted, - delete_chart_uuid, - &total_checked, - &total_deleted, - &last_row_id, - &check_res, - &action_res, - false, - true); - - if (check_res) - sqlite3_finalize(check_res); - - if (action_res) - sqlite3_finalize(action_res); - - now = now_realtime_sec(); - if (more_to_do) - next_execution_t = now + METADATA_MAINTENANCE_REPEAT; - else { - last_row_id = 0; - next_execution_t = now + METADATA_LABEL_CHECK_INTERVAL; - } - - internal_error(true, - "METADATA: Chart labels checked %u, deleted %u. Checks will %s in %lld seconds", - total_checked, - total_deleted, - last_row_id ? "resume" : "restart", - (long long)(next_execution_t - now)); - - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when checking charts"); -} - - -static void cleanup_health_log(struct metadata_wc *wc) -{ - static time_t next_execution_t = 0; - - time_t now = now_realtime_sec(); - - if (!next_execution_t) - next_execution_t = now + METADATA_MAINTENANCE_FIRST_CHECK; - - if (next_execution_t && next_execution_t > now) - return; - - next_execution_t = now + METADATA_HEALTH_LOG_INTERVAL; - - RRDHOST *host; - - bool is_claimed = claimed(); - dfe_start_reentrant(rrdhost_root_index, host){ - if (rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED)) - continue; - sql_health_alarm_log_cleanup(host, is_claimed); - if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) - break; - } - dfe_done(host); - - if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) - return; - - (void) db_execute(db_meta,"DELETE FROM health_log WHERE host_id NOT IN (SELECT host_id FROM host)"); - (void) db_execute(db_meta,"DELETE FROM health_log_detail WHERE health_log_id NOT IN (SELECT health_log_id FROM health_log)"); -} - -// -// EVENT LOOP STARTS HERE -// - -static void metadata_free_cmd_queue(struct metadata_wc *wc) -{ - spinlock_lock(&wc->cmd_queue_lock); - while(wc->cmd_base) { - struct metadata_cmd *t = wc->cmd_base; - DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(wc->cmd_base, t, prev, next); - freez(t); - } - spinlock_unlock(&wc->cmd_queue_lock); -} - -static void metadata_enq_cmd(struct metadata_wc *wc, struct metadata_cmd *cmd) -{ - if (cmd->opcode == METADATA_SYNC_SHUTDOWN) { - metadata_flag_set(wc, METADATA_FLAG_SHUTDOWN); - goto wakeup_event_loop; - } - - if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) - goto wakeup_event_loop; - - struct metadata_cmd *t = mallocz(sizeof(*t)); - *t = *cmd; - t->prev = t->next = NULL; - - spinlock_lock(&wc->cmd_queue_lock); - DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(wc->cmd_base, t, prev, next); - spinlock_unlock(&wc->cmd_queue_lock); - -wakeup_event_loop: - (void) uv_async_send(&wc->async); -} - -static struct metadata_cmd metadata_deq_cmd(struct metadata_wc *wc) -{ - struct metadata_cmd ret; - - spinlock_lock(&wc->cmd_queue_lock); - if(wc->cmd_base) { - struct metadata_cmd *t = wc->cmd_base; - DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(wc->cmd_base, t, prev, next); - ret = *t; - freez(t); - } - else { - ret.opcode = METADATA_DATABASE_NOOP; - ret.completion = NULL; - } - spinlock_unlock(&wc->cmd_queue_lock); - - return ret; -} - -static void async_cb(uv_async_t *handle) -{ - uv_stop(handle->loop); - uv_update_time(handle->loop); -} - -#define TIMER_INITIAL_PERIOD_MS (1000) -#define TIMER_REPEAT_PERIOD_MS (1000) - -static void timer_cb(uv_timer_t* handle) -{ - uv_stop(handle->loop); - uv_update_time(handle->loop); - - struct metadata_wc *wc = handle->data; - struct metadata_cmd cmd; - memset(&cmd, 0, sizeof(cmd)); - - if (wc->metadata_check_after < now_realtime_sec()) { - cmd.opcode = METADATA_SCAN_HOSTS; - metadata_enq_cmd(wc, &cmd); - } -} - -void vacuum_database(sqlite3 *database, const char *db_alias, int threshold, int vacuum_pc) -{ - int free_pages = get_free_page_count(database); - int total_pages = get_database_page_count(database); - - if (!threshold) - threshold = DATABASE_FREE_PAGES_THRESHOLD_PC; - - if (!vacuum_pc) - vacuum_pc = DATABASE_FREE_PAGES_VACUUM_PC; - - if (free_pages > (total_pages * threshold / 100)) { - - int do_free_pages = (int) (free_pages * vacuum_pc / 100); - nd_log(NDLS_DAEMON, NDLP_DEBUG, "%s: Freeing %d database pages", db_alias, do_free_pages); - - char sql[128]; - snprintfz(sql, sizeof(sql) - 1, "PRAGMA incremental_vacuum(%d)", do_free_pages); - (void) db_execute(database, sql); - } -} - -void run_metadata_cleanup(struct metadata_wc *wc) -{ - if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) - return; - - check_dimension_metadata(wc); - check_chart_metadata(wc); - check_label_metadata(wc); - cleanup_health_log(wc); - - if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) - return; - - vacuum_database(db_meta, "METADATA", DATABASE_FREE_PAGES_THRESHOLD_PC, DATABASE_FREE_PAGES_VACUUM_PC); - - (void) sqlite3_wal_checkpoint(db_meta, NULL); -} - -struct scan_metadata_payload { - uv_work_t request; - struct metadata_wc *wc; - void *data; - BUFFER *work_buffer; - uint32_t max_count; -}; - -struct host_context_load_thread { - uv_thread_t thread; - RRDHOST *host; - bool busy; - bool finished; -}; - -static void restore_host_context(void *arg) -{ - struct host_context_load_thread *hclt = arg; - RRDHOST *host = hclt->host; - - usec_t started_ut = now_monotonic_usec(); (void)started_ut; - rrdhost_load_rrdcontext_data(host); - usec_t ended_ut = now_monotonic_usec(); (void)ended_ut; - - rrdhost_flag_clear(host, RRDHOST_FLAG_PENDING_CONTEXT_LOAD | RRDHOST_FLAG_CONTEXT_LOAD_IN_PROGRESS); - -#ifdef ENABLE_ACLK - aclk_queue_node_info(host, false); -#endif - - internal_error(true, "METADATA: 'host:%s' context load in %0.2f ms", rrdhost_hostname(host), - (double)(ended_ut - started_ut) / USEC_PER_MS); - - __atomic_store_n(&hclt->finished, true, __ATOMIC_RELEASE); -} - -// Callback after scan of hosts is done -static void after_start_host_load_context(uv_work_t *req, int status __maybe_unused) -{ - struct scan_metadata_payload *data = req->data; - freez(data); -} - -#define MAX_FIND_THREAD_RETRIES (10) - -static void cleanup_finished_threads(struct host_context_load_thread *hclt, size_t max_thread_slots, bool wait) -{ - for (size_t index = 0; index < max_thread_slots; index++) { - if (__atomic_load_n(&(hclt[index].finished), __ATOMIC_RELAXED) - || (wait && __atomic_load_n(&(hclt[index].busy), __ATOMIC_ACQUIRE))) { - int rc = uv_thread_join(&(hclt[index].thread)); - if (rc) - netdata_log_error("Failed to join thread, rc = %d",rc); - __atomic_store_n(&(hclt[index].busy), false, __ATOMIC_RELEASE); - __atomic_store_n(&(hclt[index].finished), false, __ATOMIC_RELEASE); - } - } -} - -static size_t find_available_thread_slot(struct host_context_load_thread *hclt, size_t max_thread_slots, size_t *found_index) -{ - size_t retries = MAX_FIND_THREAD_RETRIES; - while (retries--) { - size_t index = 0; - while (index < max_thread_slots) { - if (false == __atomic_load_n(&(hclt[index].busy), __ATOMIC_ACQUIRE)) { - *found_index = index; - return true; - } - index++; - } - sleep_usec(10 * USEC_PER_MS); - } - return false; -} - -static void start_all_host_load_context(uv_work_t *req __maybe_unused) -{ - register_libuv_worker_jobs(); - - struct scan_metadata_payload *data = req->data; - struct metadata_wc *wc = data->wc; - - worker_is_busy(UV_EVENT_HOST_CONTEXT_LOAD); - usec_t started_ut = now_monotonic_usec(); (void)started_ut; - - RRDHOST *host; - - size_t max_threads = MIN(get_netdata_cpus() / 2, 6); - if (max_threads < 1) - max_threads = 1; - nd_log(NDLS_DAEMON, NDLP_DEBUG, "METADATA: Using %zu threads for context loading", max_threads); - struct host_context_load_thread *hclt = callocz(max_threads, sizeof(*hclt)); - - size_t thread_index; - dfe_start_reentrant(rrdhost_root_index, host) { - if (rrdhost_flag_check(host, RRDHOST_FLAG_CONTEXT_LOAD_IN_PROGRESS) || - !rrdhost_flag_check(host, RRDHOST_FLAG_PENDING_CONTEXT_LOAD)) - continue; - - rrdhost_flag_set(host, RRDHOST_FLAG_CONTEXT_LOAD_IN_PROGRESS); - internal_error(true, "METADATA: 'host:%s' loading context", rrdhost_hostname(host)); - - bool found_slot = false; - do { - if (metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN)) - break; - - cleanup_finished_threads(hclt, max_threads, false); - found_slot = find_available_thread_slot(hclt, max_threads, &thread_index); - } while (!found_slot); - - if (metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN)) - break; - - __atomic_store_n(&hclt[thread_index].busy, true, __ATOMIC_RELAXED); - hclt[thread_index].host = host; - fatal_assert(0 == uv_thread_create(&hclt[thread_index].thread, restore_host_context, &hclt[thread_index])); - } - dfe_done(host); - - cleanup_finished_threads(hclt, max_threads, true); - freez(hclt); - usec_t ended_ut = now_monotonic_usec(); (void)ended_ut; - nd_log(NDLS_DAEMON, NDLP_DEBUG, "METADATA: host contexts loaded in %0.2f ms", (double)(ended_ut - started_ut) / USEC_PER_MS); - - worker_is_idle(); -} - -// Callback after scan of hosts is done -static void after_metadata_hosts(uv_work_t *req, int status __maybe_unused) -{ - struct scan_metadata_payload *data = req->data; - struct metadata_wc *wc = data->wc; - - metadata_flag_clear(wc, METADATA_FLAG_PROCESSING); - internal_error(true, "METADATA: scanning hosts complete"); - if (unlikely(wc->scan_complete)) { - completion_mark_complete(wc->scan_complete); - internal_error(true, "METADATA: Sending completion done"); - } - freez(data); -} - -static bool metadata_scan_host(RRDHOST *host, uint32_t max_count, bool use_transaction, BUFFER *work_buffer, size_t *query_counter) { - RRDSET *st; - int rc; - - bool more_to_do = false; - uint32_t scan_count = 1; - - sqlite3_stmt *ml_load_stmt = NULL; - - bool load_ml_models = max_count; - - if (use_transaction) - (void)db_execute(db_meta, "BEGIN TRANSACTION"); - - rrdset_foreach_reentrant(st, host) { - if (scan_count == max_count) { - more_to_do = true; - break; - } - if(rrdset_flag_check(st, RRDSET_FLAG_METADATA_UPDATE)) { - (*query_counter)++; - - rrdset_flag_clear(st, RRDSET_FLAG_METADATA_UPDATE); - scan_count++; - - buffer_flush(work_buffer); - rc = check_and_update_chart_labels(st, work_buffer, query_counter); - if (unlikely(rc)) - error_report("METADATA: 'host:%s': Failed to update labels for chart %s", rrdhost_hostname(host), rrdset_name(st)); - else - (*query_counter)++; - - rc = store_chart_metadata(st); - if (unlikely(rc)) - error_report("METADATA: 'host:%s': Failed to store metadata for chart %s", rrdhost_hostname(host), rrdset_name(st)); - } - - RRDDIM *rd; - rrddim_foreach_read(rd, st) { - if(rrddim_flag_check(rd, RRDDIM_FLAG_METADATA_UPDATE)) { - (*query_counter)++; - - rrddim_flag_clear(rd, RRDDIM_FLAG_METADATA_UPDATE); - - if (rrddim_option_check(rd, RRDDIM_OPTION_HIDDEN)) - rrddim_flag_set(rd, RRDDIM_FLAG_META_HIDDEN); - else - rrddim_flag_clear(rd, RRDDIM_FLAG_META_HIDDEN); - - rc = store_dimension_metadata(rd); - if (unlikely(rc)) - error_report("METADATA: 'host:%s': Failed to dimension metadata for chart %s. dimension %s", - rrdhost_hostname(host), rrdset_name(st), - rrddim_name(rd)); - } - - if(rrddim_flag_check(rd, RRDDIM_FLAG_ML_MODEL_LOAD)) { - rrddim_flag_clear(rd, RRDDIM_FLAG_ML_MODEL_LOAD); - if (likely(load_ml_models)) - (void) ml_dimension_load_models(rd, &ml_load_stmt); - } - - worker_is_idle(); - } - rrddim_foreach_done(rd); - } - rrdset_foreach_done(st); - - if (use_transaction) - (void)db_execute(db_meta, "COMMIT TRANSACTION"); - - if (ml_load_stmt) { - sqlite3_finalize(ml_load_stmt); - ml_load_stmt = NULL; - } - - return more_to_do; -} - -static void store_host_and_system_info(RRDHOST *host, size_t *query_counter) -{ - if (unlikely(store_host_systeminfo(host))) { - error_report("METADATA: 'host:%s': Failed to store host updated system information in the database", rrdhost_hostname(host)); - rrdhost_flag_set(host, RRDHOST_FLAG_METADATA_INFO | RRDHOST_FLAG_METADATA_UPDATE); - } - else { - if (likely(query_counter)) - (*query_counter)++; - } - - if (unlikely(store_host_metadata(host))) { - error_report("METADATA: 'host:%s': Failed to store host info in the database", rrdhost_hostname(host)); - rrdhost_flag_set(host, RRDHOST_FLAG_METADATA_INFO | RRDHOST_FLAG_METADATA_UPDATE); - } - else { - if (likely(query_counter)) - (*query_counter)++; - } -} - -struct host_chart_label_cleanup { - Pvoid_t JudyL; - Word_t count; -}; - -// Worker thread to scan hosts for pending metadata to store -static void start_metadata_hosts(uv_work_t *req __maybe_unused) -{ - register_libuv_worker_jobs(); - - RRDHOST *host; - int transaction_started = 0; - - struct scan_metadata_payload *data = req->data; - struct metadata_wc *wc = data->wc; - - BUFFER *work_buffer = data->work_buffer; - usec_t all_started_ut = now_monotonic_usec(); (void)all_started_ut; - internal_error(true, "METADATA: checking all hosts..."); - usec_t started_ut = now_monotonic_usec(); (void)started_ut; - - struct host_chart_label_cleanup *cl_cleanup_data = data->data; - - if (cl_cleanup_data) { - Word_t Index = 0; - bool first = true; - Pvoid_t *PValue; - while ((PValue = JudyLFirstThenNext(cl_cleanup_data->JudyL, &Index, &first))) { - char *machine_guid = *PValue; - - host = rrdhost_find_by_guid(machine_guid); - if (likely(!host)) { - uuid_t host_uuid; - if (!uuid_parse(machine_guid, host_uuid)) - delete_host_chart_labels(&host_uuid); - } - - freez(machine_guid); - } - JudyLFreeArray(&cl_cleanup_data->JudyL, PJE0); - freez(cl_cleanup_data); - } - - bool run_again = false; - worker_is_busy(UV_EVENT_METADATA_STORE); - - if (!data->max_count) - transaction_started = !db_execute(db_meta, "BEGIN TRANSACTION"); - - dfe_start_reentrant(rrdhost_root_index, host) { - if (rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED) || !rrdhost_flag_check(host, RRDHOST_FLAG_METADATA_UPDATE)) - continue; - - size_t query_counter = 0; (void)query_counter; - - rrdhost_flag_clear(host,RRDHOST_FLAG_METADATA_UPDATE); - - if (unlikely(rrdhost_flag_check(host, RRDHOST_FLAG_METADATA_LABELS))) { - rrdhost_flag_clear(host, RRDHOST_FLAG_METADATA_LABELS); - - int rc = exec_statement_with_uuid(SQL_DELETE_HOST_LABELS, &host->host_uuid); - if (likely(!rc)) { - query_counter++; - - buffer_flush(work_buffer); - struct query_build tmp = {.sql = work_buffer, .count = 0}; - uuid_unparse_lower(host->host_uuid, tmp.uuid_str); - rrdlabels_walkthrough_read(host->rrdlabels, host_label_store_to_sql_callback, &tmp); - buffer_strcat(work_buffer, " ON CONFLICT (host_id, label_key) DO UPDATE SET source_type = excluded.source_type, label_value=excluded.label_value, date_created=UNIXEPOCH()"); - rc = db_execute(db_meta, buffer_tostring(work_buffer)); - - if (unlikely(rc)) { - error_report("METADATA: 'host:%s': failed to update metadata host labels", rrdhost_hostname(host)); - rrdhost_flag_set(host, RRDHOST_FLAG_METADATA_LABELS | RRDHOST_FLAG_METADATA_UPDATE); - } - else - query_counter++; - } else { - error_report("METADATA: 'host:%s': failed to delete old host labels", rrdhost_hostname(host)); - rrdhost_flag_set(host, RRDHOST_FLAG_METADATA_LABELS | RRDHOST_FLAG_METADATA_UPDATE); - } - } - - if (unlikely(rrdhost_flag_check(host, RRDHOST_FLAG_METADATA_CLAIMID))) { - rrdhost_flag_clear(host, RRDHOST_FLAG_METADATA_CLAIMID); - uuid_t uuid; - int rc; - if (likely(host->aclk_state.claimed_id && !uuid_parse(host->aclk_state.claimed_id, uuid))) - rc = store_claim_id(&host->host_uuid, &uuid); - else - rc = store_claim_id(&host->host_uuid, NULL); - - if (unlikely(rc)) - rrdhost_flag_set(host, RRDHOST_FLAG_METADATA_CLAIMID | RRDHOST_FLAG_METADATA_UPDATE); - else - query_counter++; - } - if (unlikely(rrdhost_flag_check(host, RRDHOST_FLAG_METADATA_INFO))) { - rrdhost_flag_clear(host, RRDHOST_FLAG_METADATA_INFO); - store_host_and_system_info(host, &query_counter); - } - - // For clarity - bool use_transaction = data->max_count; - if (unlikely(metadata_scan_host(host, data->max_count, use_transaction, work_buffer, &query_counter))) { - run_again = true; - rrdhost_flag_set(host,RRDHOST_FLAG_METADATA_UPDATE); - internal_error(true,"METADATA: 'host:%s': scheduling another run, more charts to store", rrdhost_hostname(host)); - } - usec_t ended_ut = now_monotonic_usec(); (void)ended_ut; - internal_error(true, "METADATA: 'host:%s': saved metadata with %zu SQL statements, in %0.2f ms", - rrdhost_hostname(host), query_counter, - (double)(ended_ut - started_ut) / USEC_PER_MS); - } - dfe_done(host); - - if (!data->max_count && transaction_started) - transaction_started = db_execute(db_meta, "COMMIT TRANSACTION"); - - usec_t all_ended_ut = now_monotonic_usec(); (void)all_ended_ut; - internal_error(true, "METADATA: checking all hosts completed in %0.2f ms", - (double)(all_ended_ut - all_started_ut) / USEC_PER_MS); - - if (unlikely(run_again)) - wc->metadata_check_after = now_realtime_sec() + METADATA_HOST_CHECK_IMMEDIATE; - else { - wc->metadata_check_after = now_realtime_sec() + METADATA_HOST_CHECK_INTERVAL; - run_metadata_cleanup(wc); - } - worker_is_idle(); -} - -static void metadata_event_loop(void *arg) -{ - worker_register("METASYNC"); - worker_register_job_name(METADATA_DATABASE_NOOP, "noop"); - worker_register_job_name(METADATA_DATABASE_TIMER, "timer"); - worker_register_job_name(METADATA_DEL_DIMENSION, "delete dimension"); - worker_register_job_name(METADATA_STORE_CLAIM_ID, "add claim id"); - worker_register_job_name(METADATA_ADD_HOST_INFO, "add host info"); - worker_register_job_name(METADATA_MAINTENANCE, "maintenance"); - - int ret; - uv_loop_t *loop; - unsigned cmd_batch_size; - struct metadata_wc *wc = arg; - enum metadata_opcode opcode; - - uv_thread_set_name_np(wc->thread, "METASYNC"); - loop = wc->loop = mallocz(sizeof(uv_loop_t)); - ret = uv_loop_init(loop); - if (ret) { - netdata_log_error("uv_loop_init(): %s", uv_strerror(ret)); - goto error_after_loop_init; - } - loop->data = wc; - - ret = uv_async_init(wc->loop, &wc->async, async_cb); - if (ret) { - netdata_log_error("uv_async_init(): %s", uv_strerror(ret)); - goto error_after_async_init; - } - wc->async.data = wc; - - ret = uv_timer_init(loop, &wc->timer_req); - if (ret) { - netdata_log_error("uv_timer_init(): %s", uv_strerror(ret)); - goto error_after_timer_init; - } - wc->timer_req.data = wc; - fatal_assert(0 == uv_timer_start(&wc->timer_req, timer_cb, TIMER_INITIAL_PERIOD_MS, TIMER_REPEAT_PERIOD_MS)); - - nd_log(NDLS_DAEMON, NDLP_DEBUG, "Starting metadata sync thread"); - - struct metadata_cmd cmd; - memset(&cmd, 0, sizeof(cmd)); - metadata_flag_clear(wc, METADATA_FLAG_PROCESSING); - - wc->metadata_check_after = now_realtime_sec() + METADATA_HOST_CHECK_FIRST_CHECK; - - int shutdown = 0; - completion_mark_complete(&wc->start_stop_complete); - BUFFER *work_buffer = buffer_create(1024, &netdata_buffers_statistics.buffers_sqlite); - struct scan_metadata_payload *data; - struct host_chart_label_cleanup *cl_cleanup_data = NULL; - - while (shutdown == 0 || (wc->flags & METADATA_FLAG_PROCESSING)) { - uuid_t *uuid; - RRDHOST *host = NULL; - - worker_is_idle(); - uv_run(loop, UV_RUN_DEFAULT); - - /* wait for commands */ - cmd_batch_size = 0; - do { - if (unlikely(cmd_batch_size >= METADATA_MAX_BATCH_SIZE)) - break; - - cmd = metadata_deq_cmd(wc); - opcode = cmd.opcode; - - if (unlikely(opcode == METADATA_DATABASE_NOOP && metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) { - shutdown = 1; - continue; - } - - ++cmd_batch_size; - - if (likely(opcode != METADATA_DATABASE_NOOP)) - worker_is_busy(opcode); - - switch (opcode) { - case METADATA_DATABASE_NOOP: - case METADATA_DATABASE_TIMER: - break; - case METADATA_DEL_DIMENSION: - uuid = (uuid_t *) cmd.param[0]; - if (likely(dimension_can_be_deleted(uuid, NULL, false))) - delete_dimension_uuid(uuid, NULL, false); - freez(uuid); - break; - case METADATA_STORE_CLAIM_ID: - store_claim_id((uuid_t *) cmd.param[0], (uuid_t *) cmd.param[1]); - freez((void *) cmd.param[0]); - freez((void *) cmd.param[1]); - break; - case METADATA_ADD_HOST_INFO: - host = (RRDHOST *) cmd.param[0]; - store_host_and_system_info(host, NULL); - break; - case METADATA_SCAN_HOSTS: - if (unlikely(metadata_flag_check(wc, METADATA_FLAG_PROCESSING))) - break; - - if (unittest_running) - break; - - data = mallocz(sizeof(*data)); - data->request.data = data; - data->wc = wc; - data->data = cl_cleanup_data; - data->work_buffer = work_buffer; - cl_cleanup_data = NULL; - - if (unlikely(cmd.completion)) { - data->max_count = 0; // 0 will process all pending updates - cmd.completion = NULL; // Do not complete after launching worker (worker will do) - } - else - data->max_count = 5000; - - metadata_flag_set(wc, METADATA_FLAG_PROCESSING); - if (unlikely( - uv_queue_work(loop,&data->request, - start_metadata_hosts, - after_metadata_hosts))) { - // Failed to launch worker -- let the event loop handle completion - cmd.completion = wc->scan_complete; - cl_cleanup_data = data->data; - freez(data); - metadata_flag_clear(wc, METADATA_FLAG_PROCESSING); - } - break; - case METADATA_LOAD_HOST_CONTEXT:; - if (unittest_running) - break; - - data = callocz(1,sizeof(*data)); - data->request.data = data; - data->wc = wc; - if (unlikely( - uv_queue_work(loop,&data->request, start_all_host_load_context, - after_start_host_load_context))) { - freez(data); - } - break; - case METADATA_DELETE_HOST_CHART_LABELS:; - if (!cl_cleanup_data) - cl_cleanup_data = callocz(1,sizeof(*cl_cleanup_data)); - - Pvoid_t *PValue = JudyLIns(&cl_cleanup_data->JudyL, (Word_t) ++cl_cleanup_data->count, PJE0); - if (PValue) - *PValue = (void *) cmd.param[0]; - - break; - case METADATA_UNITTEST:; - struct thread_unittest *tu = (struct thread_unittest *) cmd.param[0]; - sleep_usec(1000); // processing takes 1ms - __atomic_fetch_add(&tu->processed, 1, __ATOMIC_SEQ_CST); - break; - default: - break; - } - - if (cmd.completion) - completion_mark_complete(cmd.completion); - } while (opcode != METADATA_DATABASE_NOOP); - } - - if (!uv_timer_stop(&wc->timer_req)) - uv_close((uv_handle_t *)&wc->timer_req, NULL); - - uv_close((uv_handle_t *)&wc->async, NULL); - int rc; - do { - rc = uv_loop_close(loop); - } while (rc != UV_EBUSY); - - buffer_free(work_buffer); - freez(loop); - worker_unregister(); - - nd_log(NDLS_DAEMON, NDLP_DEBUG, "Shutting down metadata thread"); - completion_mark_complete(&wc->start_stop_complete); - if (wc->scan_complete) { - completion_destroy(wc->scan_complete); - freez(wc->scan_complete); - } - metadata_free_cmd_queue(wc); - return; - -error_after_timer_init: - uv_close((uv_handle_t *)&wc->async, NULL); -error_after_async_init: - fatal_assert(0 == uv_loop_close(loop)); -error_after_loop_init: - freez(loop); - worker_unregister(); -} - -void metadata_sync_shutdown(void) -{ - completion_init(&metasync_worker.start_stop_complete); - - struct metadata_cmd cmd; - memset(&cmd, 0, sizeof(cmd)); - nd_log(NDLS_DAEMON, NDLP_DEBUG, "METADATA: Sending a shutdown command"); - cmd.opcode = METADATA_SYNC_SHUTDOWN; - metadata_enq_cmd(&metasync_worker, &cmd); - - /* wait for metadata thread to shut down */ - nd_log(NDLS_DAEMON, NDLP_DEBUG, "METADATA: Waiting for shutdown ACK"); - completion_wait_for(&metasync_worker.start_stop_complete); - completion_destroy(&metasync_worker.start_stop_complete); - nd_log(NDLS_DAEMON, NDLP_DEBUG, "METADATA: Shutdown complete"); -} - -void metadata_sync_shutdown_prepare(void) -{ - if (unlikely(!metasync_worker.loop)) - return; - - struct metadata_cmd cmd; - memset(&cmd, 0, sizeof(cmd)); - - struct metadata_wc *wc = &metasync_worker; - - struct completion *compl = mallocz(sizeof(*compl)); - completion_init(compl); - __atomic_store_n(&wc->scan_complete, compl, __ATOMIC_RELAXED); - - nd_log(NDLS_DAEMON, NDLP_DEBUG, "METADATA: Sending a scan host command"); - uint32_t max_wait_iterations = 2000; - while (unlikely(metadata_flag_check(&metasync_worker, METADATA_FLAG_PROCESSING)) && max_wait_iterations--) { - if (max_wait_iterations == 1999) - nd_log(NDLS_DAEMON, NDLP_DEBUG, "METADATA: Current worker is running; waiting to finish"); - sleep_usec(1000); - } - - cmd.opcode = METADATA_SCAN_HOSTS; - metadata_enq_cmd(&metasync_worker, &cmd); - - nd_log(NDLS_DAEMON, NDLP_DEBUG, "METADATA: Waiting for host scan completion"); - completion_wait_for(wc->scan_complete); - nd_log(NDLS_DAEMON, NDLP_DEBUG, "METADATA: Host scan complete; can continue with shutdown"); -} - -// ------------------------------------------------------------- -// Init function called on agent startup - -void metadata_sync_init(void) -{ - struct metadata_wc *wc = &metasync_worker; - - memset(wc, 0, sizeof(*wc)); - completion_init(&wc->start_stop_complete); - - fatal_assert(0 == uv_thread_create(&(wc->thread), metadata_event_loop, wc)); - - completion_wait_for(&wc->start_stop_complete); - completion_destroy(&wc->start_stop_complete); - - nd_log(NDLS_DAEMON, NDLP_DEBUG, "SQLite metadata sync initialization complete"); -} - - -// Helpers - -static inline void queue_metadata_cmd(enum metadata_opcode opcode, const void *param0, const void *param1) -{ - struct metadata_cmd cmd; - cmd.opcode = opcode; - cmd.param[0] = param0; - cmd.param[1] = param1; - cmd.completion = NULL; - metadata_enq_cmd(&metasync_worker, &cmd); -} - -// Public -void metaqueue_delete_dimension_uuid(uuid_t *uuid) -{ - if (unlikely(!metasync_worker.loop)) - return; - uuid_t *use_uuid = mallocz(sizeof(*uuid)); - uuid_copy(*use_uuid, *uuid); - queue_metadata_cmd(METADATA_DEL_DIMENSION, use_uuid, NULL); -} - -void metaqueue_store_claim_id(uuid_t *host_uuid, uuid_t *claim_uuid) -{ - if (unlikely(!host_uuid)) - return; - - uuid_t *local_host_uuid = mallocz(sizeof(*host_uuid)); - uuid_t *local_claim_uuid = NULL; - - uuid_copy(*local_host_uuid, *host_uuid); - if (likely(claim_uuid)) { - local_claim_uuid = mallocz(sizeof(*claim_uuid)); - uuid_copy(*local_claim_uuid, *claim_uuid); - } - queue_metadata_cmd(METADATA_STORE_CLAIM_ID, local_host_uuid, local_claim_uuid); -} - -void metaqueue_host_update_info(RRDHOST *host) -{ - if (unlikely(!metasync_worker.loop)) - return; - queue_metadata_cmd(METADATA_ADD_HOST_INFO, host, NULL); -} - -void metaqueue_ml_load_models(RRDDIM *rd) -{ - rrddim_flag_set(rd, RRDDIM_FLAG_ML_MODEL_LOAD); -} - -void metadata_queue_load_host_context(RRDHOST *host) -{ - if (unlikely(!metasync_worker.loop)) - return; - queue_metadata_cmd(METADATA_LOAD_HOST_CONTEXT, host, NULL); - nd_log(NDLS_DAEMON, NDLP_DEBUG, "Queued command to load host contexts"); -} - -void metadata_delete_host_chart_labels(char *machine_guid) -{ - if (unlikely(!metasync_worker.loop)) { - freez(machine_guid); - return; - } - - // Node machine guid is already strdup-ed - queue_metadata_cmd(METADATA_DELETE_HOST_CHART_LABELS, machine_guid, NULL); - nd_log(NDLS_DAEMON, NDLP_DEBUG, "Queued command delete chart labels for host %s", machine_guid); -} - - -// -// unitests -// - -static void *unittest_queue_metadata(void *arg) { - struct thread_unittest *tu = arg; - - struct metadata_cmd cmd; - cmd.opcode = METADATA_UNITTEST; - cmd.param[0] = tu; - cmd.param[1] = NULL; - cmd.completion = NULL; - metadata_enq_cmd(&metasync_worker, &cmd); - - do { - __atomic_fetch_add(&tu->added, 1, __ATOMIC_SEQ_CST); - metadata_enq_cmd(&metasync_worker, &cmd); - sleep_usec(10000); - } while (!__atomic_load_n(&tu->join, __ATOMIC_RELAXED)); - return arg; -} - -static void *metadata_unittest_threads(void) -{ - - unsigned done; - - struct thread_unittest tu = { - .join = 0, - .added = 0, - .processed = 0, - .done = &done, - }; - - // Queue messages / Time it - time_t seconds_to_run = 5; - int threads_to_create = 4; - fprintf( - stderr, - "\nChecking metadata queue using %d threads for %lld seconds...\n", - threads_to_create, - (long long)seconds_to_run); - - netdata_thread_t threads[threads_to_create]; - tu.join = 0; - for (int i = 0; i < threads_to_create; i++) { - char buf[100 + 1]; - snprintf(buf, sizeof(buf) - 1, "META[%d]", i); - netdata_thread_create( - &threads[i], - buf, - NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE, - unittest_queue_metadata, - &tu); - } - (void) uv_async_send(&metasync_worker.async); - sleep_usec(seconds_to_run * USEC_PER_SEC); - - __atomic_store_n(&tu.join, 1, __ATOMIC_RELAXED); - for (int i = 0; i < threads_to_create; i++) { - void *retval; - netdata_thread_join(threads[i], &retval); - } - sleep_usec(5 * USEC_PER_SEC); - - fprintf(stderr, "Added %u elements, processed %u\n", tu.added, tu.processed); - - return 0; -} - -int metadata_unittest(void) -{ - metadata_sync_init(); - - // Queue items for a specific period of time - metadata_unittest_threads(); - - metadata_sync_shutdown(); - - return 0; -} diff --git a/database/sqlite/sqlite_metadata.h b/database/sqlite/sqlite_metadata.h deleted file mode 100644 index 6860cfedf..000000000 --- a/database/sqlite/sqlite_metadata.h +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_SQLITE_METADATA_H -#define NETDATA_SQLITE_METADATA_H - -#include "sqlite3.h" -#include "sqlite_functions.h" - -// To initialize and shutdown -void metadata_sync_init(void); -void metadata_sync_shutdown(void); -void metadata_sync_shutdown_prepare(void); - -void metaqueue_delete_dimension_uuid(uuid_t *uuid); -void metaqueue_store_claim_id(uuid_t *host_uuid, uuid_t *claim_uuid); -void metaqueue_host_update_info(RRDHOST *host); -void metaqueue_ml_load_models(RRDDIM *rd); -void migrate_localhost(uuid_t *host_uuid); -void metadata_queue_load_host_context(RRDHOST *host); -void metadata_delete_host_chart_labels(char *machine_guid); -void vacuum_database(sqlite3 *database, const char *db_alias, int threshold, int vacuum_pc); - -// UNIT TEST -int metadata_unittest(void); -#endif //NETDATA_SQLITE_METADATA_H diff --git a/database/storage_engine.c b/database/storage_engine.c deleted file mode 100644 index 199823822..000000000 --- a/database/storage_engine.c +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "storage_engine.h" -#include "ram/rrddim_mem.h" -#ifdef ENABLE_DBENGINE -#include "engine/rrdengineapi.h" -#endif - -static STORAGE_ENGINE engines[] = { - { - .id = RRD_MEMORY_MODE_NONE, - .name = RRD_MEMORY_MODE_NONE_NAME, - .backend = STORAGE_ENGINE_BACKEND_RRDDIM, - .api = { - .metric_get = rrddim_metric_get, - .metric_get_or_create = rrddim_metric_get_or_create, - .metric_dup = rrddim_metric_dup, - .metric_release = rrddim_metric_release, - .metric_retention_by_uuid = rrddim_metric_retention_by_uuid, - } - }, - { - .id = RRD_MEMORY_MODE_RAM, - .name = RRD_MEMORY_MODE_RAM_NAME, - .backend = STORAGE_ENGINE_BACKEND_RRDDIM, - .api = { - .metric_get = rrddim_metric_get, - .metric_get_or_create = rrddim_metric_get_or_create, - .metric_dup = rrddim_metric_dup, - .metric_release = rrddim_metric_release, - .metric_retention_by_uuid = rrddim_metric_retention_by_uuid, - } - }, - { - .id = RRD_MEMORY_MODE_MAP, - .name = RRD_MEMORY_MODE_MAP_NAME, - .backend = STORAGE_ENGINE_BACKEND_RRDDIM, - .api = { - .metric_get = rrddim_metric_get, - .metric_get_or_create = rrddim_metric_get_or_create, - .metric_dup = rrddim_metric_dup, - .metric_release = rrddim_metric_release, - .metric_retention_by_uuid = rrddim_metric_retention_by_uuid, - } - }, - { - .id = RRD_MEMORY_MODE_SAVE, - .name = RRD_MEMORY_MODE_SAVE_NAME, - .backend = STORAGE_ENGINE_BACKEND_RRDDIM, - .api = { - .metric_get = rrddim_metric_get, - .metric_get_or_create = rrddim_metric_get_or_create, - .metric_dup = rrddim_metric_dup, - .metric_release = rrddim_metric_release, - .metric_retention_by_uuid = rrddim_metric_retention_by_uuid, - } - }, - { - .id = RRD_MEMORY_MODE_ALLOC, - .name = RRD_MEMORY_MODE_ALLOC_NAME, - .backend = STORAGE_ENGINE_BACKEND_RRDDIM, - .api = { - .metric_get = rrddim_metric_get, - .metric_get_or_create = rrddim_metric_get_or_create, - .metric_dup = rrddim_metric_dup, - .metric_release = rrddim_metric_release, - .metric_retention_by_uuid = rrddim_metric_retention_by_uuid, - } - }, -#ifdef ENABLE_DBENGINE - { - .id = RRD_MEMORY_MODE_DBENGINE, - .name = RRD_MEMORY_MODE_DBENGINE_NAME, - .backend = STORAGE_ENGINE_BACKEND_DBENGINE, - .api = { - .metric_get = rrdeng_metric_get, - .metric_get_or_create = rrdeng_metric_get_or_create, - .metric_dup = rrdeng_metric_dup, - .metric_release = rrdeng_metric_release, - .metric_retention_by_uuid = rrdeng_metric_retention_by_uuid, - } - }, -#endif - { .id = RRD_MEMORY_MODE_NONE, .name = NULL } -}; - -STORAGE_ENGINE* storage_engine_find(const char* name) -{ - for (STORAGE_ENGINE* it = engines; it->name; it++) { - if (strcmp(it->name, name) == 0) - return it; - } - return NULL; -} - -STORAGE_ENGINE* storage_engine_get(RRD_MEMORY_MODE mmode) -{ - for (STORAGE_ENGINE* it = engines; it->name; it++) { - if (it->id == mmode) - return it; - } - return NULL; -} - -STORAGE_ENGINE* storage_engine_foreach_init() -{ - // Assuming at least one engine exists - return &engines[0]; -} - -STORAGE_ENGINE* storage_engine_foreach_next(STORAGE_ENGINE* it) -{ - if (!it || !it->name) - return NULL; - - it++; - return it->name ? it : NULL; -} diff --git a/database/KolmogorovSmirnovDist.c b/src/database/KolmogorovSmirnovDist.c index 1486abc7b..1486abc7b 100644 --- a/database/KolmogorovSmirnovDist.c +++ b/src/database/KolmogorovSmirnovDist.c diff --git a/database/KolmogorovSmirnovDist.h b/src/database/KolmogorovSmirnovDist.h index cf455042a..cf455042a 100644 --- a/database/KolmogorovSmirnovDist.h +++ b/src/database/KolmogorovSmirnovDist.h diff --git a/database/contexts/README.md b/src/database/contexts/README.md index e69de29bb..e69de29bb 100644 --- a/database/contexts/README.md +++ b/src/database/contexts/README.md diff --git a/database/contexts/api_v1.c b/src/database/contexts/api_v1.c index f144e6f7b..355aaf91a 100644 --- a/database/contexts/api_v1.c +++ b/src/database/contexts/api_v1.c @@ -131,13 +131,13 @@ static inline int rrdinstance_to_json_callback(const DICTIONARY_ITEM *item, void if(before && (!ri->first_time_s || before < ri->first_time_s)) return 0; - if(t_parent->chart_label_key && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, t_parent->chart_label_key, - '\0', NULL)) + if(t_parent->chart_label_key && rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, t_parent->chart_label_key, + '\0', NULL) != SP_MATCHED_POSITIVE) return 0; - if(t_parent->chart_labels_filter && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, + if(t_parent->chart_labels_filter && rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, t_parent->chart_labels_filter, ':', - NULL)) + NULL) != SP_MATCHED_POSITIVE) return 0; time_t first_time_s = ri->first_time_s; diff --git a/database/contexts/api_v2.c b/src/database/contexts/api_v2.c index 3ca49a319..edfeab1d6 100644 --- a/database/contexts/api_v2.c +++ b/src/database/contexts/api_v2.c @@ -167,6 +167,9 @@ struct function_v2_entry { size_t used; size_t *node_ids; STRING *help; + STRING *tags; + HTTP_ACCESS access; + int priority; }; struct context_v2_entry { @@ -180,6 +183,13 @@ struct context_v2_entry { FTS_MATCH match; }; +struct alert_counts { + size_t critical; + size_t warning; + size_t clear; + size_t error; +}; + struct alert_v2_entry { RRDCALC *tmp; @@ -188,16 +198,25 @@ struct alert_v2_entry { size_t ati; - size_t critical; - size_t warning; - size_t clear; - size_t error; + struct alert_counts counts; size_t instances; DICTIONARY *nodes; DICTIONARY *configs; }; +struct alert_by_x_entry { + struct { + struct alert_counts counts; + size_t silent; + size_t total; + } running; + + struct { + size_t available; + } prototypes; +}; + typedef struct full_text_search_index { size_t searches; size_t string_searches; @@ -251,8 +270,14 @@ struct rrdcontext_to_json_v2_data { size_t ati; - DICTIONARY *alerts; + DICTIONARY *summary; DICTIONARY *alert_instances; + + DICTIONARY *by_type; + DICTIONARY *by_component; + DICTIONARY *by_classification; + DICTIONARY *by_recipient; + DICTIONARY *by_module; } alerts; struct { @@ -276,9 +301,7 @@ struct rrdcontext_to_json_v2_data { struct query_timings timings; }; -static void alerts_v2_add(struct alert_v2_entry *t, RRDCALC *rc) { - t->instances++; - +static void alert_counts_add(struct alert_counts *t, RRDCALC *rc) { switch(rc->status) { case RRDCALC_STATUS_CRITICAL: t->critical++; @@ -303,20 +326,51 @@ static void alerts_v2_add(struct alert_v2_entry *t, RRDCALC *rc) { break; } +} + +static void alerts_v2_add(struct alert_v2_entry *t, RRDCALC *rc) { + t->instances++; + + alert_counts_add(&t->counts, rc); dictionary_set(t->nodes, rc->rrdset->rrdhost->machine_guid, NULL, 0); char key[UUID_STR_LEN + 1]; - uuid_unparse_lower(rc->config_hash_id, key); + uuid_unparse_lower(rc->config.hash_id, key); dictionary_set(t->configs, key, NULL, 0); } +static void alerts_by_x_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) { + static STRING *silent = NULL; + if(unlikely(!silent)) silent = string_strdupz("silent"); + + struct alert_by_x_entry *b = value; + RRDCALC *rc = data; + if(!rc) { + // prototype + b->prototypes.available++; + } + else { + alert_counts_add(&b->running.counts, rc); + + b->running.total++; + + if (rc->config.recipient == silent) + b->running.silent++; + } +} + +static bool alerts_by_x_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value __maybe_unused, void *data __maybe_unused) { + alerts_by_x_insert_callback(item, old_value, data); + return false; +} + static void alerts_v2_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) { struct rrdcontext_to_json_v2_data *ctl = data; struct alert_v2_entry *t = value; RRDCALC *rc = t->tmp; - t->name = rc->name; - t->summary = rc->summary; + t->name = rc->config.name; + t->summary = rc->config.summary; // the original summary t->ati = ctl->alerts.ati++; t->nodes = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_VALUE_LINK_DONT_CLONE|DICT_OPTION_NAME_LINK_DONT_CLONE); @@ -347,16 +401,16 @@ static void alert_instances_v2_insert_callback(const DICTIONARY_ITEM *item __may t->chart_id = rc->rrdset->id; t->chart_name = rc->rrdset->name; t->family = rc->rrdset->family; - t->units = rc->units; - t->classification = rc->classification; - t->type = rc->type; - t->recipient = rc->recipient; - t->component = rc->component; - t->name = rc->name; - t->source = rc->source; + t->units = rc->config.units; + t->classification = rc->config.classification; + t->type = rc->config.type; + t->recipient = rc->config.recipient; + t->component = rc->config.component; + t->name = rc->config.name; + t->source = rc->config.source; t->status = rc->status; t->flags = rc->run_flags; - t->info = rc->info; + t->info = rc->config.info; t->summary = rc->summary; t->value = rc->value; t->last_updated = rc->last_updated; @@ -365,12 +419,9 @@ static void alert_instances_v2_insert_callback(const DICTIONARY_ITEM *item __may t->host = rc->rrdset->rrdhost; t->alarm_id = rc->id; t->ni = ctl->nodes.ni; - t->global_id = rc->ae ? rc->ae->global_id : 0; - t->name = rc->name; - uuid_copy(t->config_hash_id, rc->config_hash_id); - if(rc->ae) - uuid_copy(t->last_transition_id, rc->ae->transition_id); + uuid_copy(t->config_hash_id, rc->config.hash_id); + health_alarm_log_get_global_id_and_transition_id_for_rrdcalc(rc, &t->global_id, &t->last_transition_id); } static bool alert_instances_v2_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value __maybe_unused, void *new_value __maybe_unused, void *data __maybe_unused) { @@ -422,7 +473,7 @@ static FTS_MATCH rrdcontext_to_json_v2_full_text_search(struct rrdcontext_to_jso size_t label_searches = 0; if(unlikely(ri->rrdlabels && rrdlabels_entries(ri->rrdlabels) && - rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, q, ':', &label_searches))) { + rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, q, ':', &label_searches) == SP_MATCHED_POSITIVE)) { ctl->q.fts.searches += label_searches; ctl->q.fts.char_searches += label_searches; matched = FTS_MATCHED_LABEL; @@ -435,12 +486,12 @@ static FTS_MATCH rrdcontext_to_json_v2_full_text_search(struct rrdcontext_to_jso RRDSET *st = ri->rrdset; rw_spinlock_read_lock(&st->alerts.spinlock); for (RRDCALC *rcl = st->alerts.base; rcl; rcl = rcl->next) { - if(unlikely(full_text_search_string(&ctl->q.fts, q, rcl->name))) { + if(unlikely(full_text_search_string(&ctl->q.fts, q, rcl->config.name))) { matched = FTS_MATCHED_ALERT; break; } - if(unlikely(full_text_search_string(&ctl->q.fts, q, rcl->info))) { + if(unlikely(full_text_search_string(&ctl->q.fts, q, rcl->config.info))) { matched = FTS_MATCHED_ALERT_INFO; break; } @@ -460,7 +511,7 @@ static bool rrdcontext_matches_alert(struct rrdcontext_to_json_v2_data *ctl, RRD RRDSET *st = ri->rrdset; rw_spinlock_read_lock(&st->alerts.spinlock); for (RRDCALC *rcl = st->alerts.base; rcl; rcl = rcl->next) { - if(ctl->alerts.alert_name_pattern && !simple_pattern_matches_string(ctl->alerts.alert_name_pattern, rcl->name)) + if(ctl->alerts.alert_name_pattern && !simple_pattern_matches_string(ctl->alerts.alert_name_pattern, rcl->config.name)) continue; if(ctl->alerts.alarm_id_filter && ctl->alerts.alarm_id_filter != rcl->id) @@ -500,11 +551,51 @@ static bool rrdcontext_matches_alert(struct rrdcontext_to_json_v2_data *ctl, RRD struct alert_v2_entry t = { .tmp = rcl, }; - struct alert_v2_entry *a2e = dictionary_set(ctl->alerts.alerts, string2str(rcl->name), &t, - sizeof(struct alert_v2_entry)); + struct alert_v2_entry *a2e = + dictionary_set(ctl->alerts.summary, string2str(rcl->config.name), + &t, sizeof(struct alert_v2_entry)); size_t ati = a2e->ati; matches++; + dictionary_set_advanced(ctl->alerts.by_type, + string2str(rcl->config.type), + (ssize_t)string_strlen(rcl->config.type), + NULL, + sizeof(struct alert_by_x_entry), + rcl); + + dictionary_set_advanced(ctl->alerts.by_component, + string2str(rcl->config.component), + (ssize_t)string_strlen(rcl->config.component), + NULL, + sizeof(struct alert_by_x_entry), + rcl); + + dictionary_set_advanced(ctl->alerts.by_classification, + string2str(rcl->config.classification), + (ssize_t)string_strlen(rcl->config.classification), + NULL, + sizeof(struct alert_by_x_entry), + rcl); + + dictionary_set_advanced(ctl->alerts.by_recipient, + string2str(rcl->config.recipient), + (ssize_t)string_strlen(rcl->config.recipient), + NULL, + sizeof(struct alert_by_x_entry), + rcl); + + char *module = NULL; + rrdlabels_get_value_strdup_or_null(st->rrdlabels, &module, "_collect_module"); + if(!module || !*module) module = "[unset]"; + + dictionary_set_advanced(ctl->alerts.by_module, + module, + -1, + NULL, + sizeof(struct alert_by_x_entry), + rcl); + if (ctl->options & (CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES | CONTEXT_V2_OPTION_ALERTS_WITH_VALUES)) { char key[20 + 1]; snprintfz(key, sizeof(key) - 1, "%p", rcl); @@ -726,6 +817,15 @@ static void agent_capabilities_to_json(BUFFER *wb, RRDHOST *host, const char *ke freez(capas); } +static inline void host_dyncfg_to_json_v2(BUFFER *wb, const char *key, RRDHOST_STATUS *s) { + buffer_json_member_add_object(wb, key); + { + buffer_json_member_add_string(wb, "status", rrdhost_dyncfg_status_to_string(s->dyncfg.status)); + } + buffer_json_object_close(wb); // health + +} + static inline void rrdhost_health_to_json_v2(BUFFER *wb, const char *key, RRDHOST_STATUS *s) { buffer_json_member_add_object(wb, key); { @@ -838,6 +938,8 @@ static void rrdcontext_to_json_v2_rrdhost(BUFFER *wb, RRDHOST *host, struct rrdc host_functions2json(host, wb); // functions agent_capabilities_to_json(wb, host, "capabilities"); + + host_dyncfg_to_json_v2(wb, "dyncfg", &s); } buffer_json_object_close(wb); // this instance buffer_json_array_close(wb); // instances @@ -913,8 +1015,11 @@ static ssize_t rrdcontext_to_json_v2_add_host(void *data, RRDHOST *host, bool qu .size = 1, .node_ids = &ctl->nodes.ni, .help = NULL, + .tags = NULL, + .access = HTTP_ACCESS_ALL, + .priority = RRDFUNCTIONS_PRIORITY_DEFAULT, }; - host_functions_to_dict(host, ctl->functions.dict, &t, sizeof(t), &t.help); + host_functions_to_dict(host, ctl->functions.dict, &t, sizeof(t), &t.help, &t.tags, &t.access, &t.priority); } if(ctl->mode & CONTEXTS_V2_NODES) { @@ -1013,10 +1118,10 @@ void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now STORAGE_ENGINE *eng = localhost->db[tier].eng; if (!eng) continue; - uint64_t max = storage_engine_disk_space_max(eng->backend, localhost->db[tier].instance); - uint64_t used = storage_engine_disk_space_used(eng->backend, localhost->db[tier].instance); - time_t first_time_s = storage_engine_global_first_time_s(eng->backend, localhost->db[tier].instance); - size_t currently_collected_metrics = storage_engine_collected_metrics(eng->backend, localhost->db[tier].instance); + uint64_t max = storage_engine_disk_space_max(eng->seb, localhost->db[tier].si); + uint64_t used = storage_engine_disk_space_used(eng->seb, localhost->db[tier].si); + time_t first_time_s = storage_engine_global_first_time_s(eng->seb, localhost->db[tier].si); + size_t currently_collected_metrics = storage_engine_collected_metrics(eng->seb, localhost->db[tier].si); NETDATA_DOUBLE percent; if (used && max) @@ -1026,6 +1131,8 @@ void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now buffer_json_add_array_item_object(wb); buffer_json_member_add_uint64(wb, "tier", tier); + buffer_json_member_add_uint64(wb, "metrics", storage_engine_metrics(eng->seb, localhost->db[tier].si)); + buffer_json_member_add_uint64(wb, "samples", storage_engine_samples(eng->seb, localhost->db[tier].si)); if(used || max) { buffer_json_member_add_uint64(wb, "disk_used", used); @@ -1212,14 +1319,9 @@ static void contexts_v2_alert_config_to_json_from_sql_alert_config_data(struct s buffer_json_member_add_string(wb, "type", is_template ? "template" : "alarm"); buffer_json_member_add_string(wb, "on", is_template ? t->selectors.on_template : t->selectors.on_key); - buffer_json_member_add_string(wb, "os", t->selectors.os); - buffer_json_member_add_string(wb, "hosts", t->selectors.hosts); buffer_json_member_add_string(wb, "families", t->selectors.families); - buffer_json_member_add_string(wb, "plugin", t->selectors.plugin); - buffer_json_member_add_string(wb, "module", t->selectors.module); buffer_json_member_add_string(wb, "host_labels", t->selectors.host_labels); buffer_json_member_add_string(wb, "chart_labels", t->selectors.chart_labels); - buffer_json_member_add_string(wb, "charts", t->selectors.charts); } buffer_json_object_close(wb); // selectors @@ -1236,9 +1338,13 @@ static void contexts_v2_alert_config_to_json_from_sql_alert_config_data(struct s buffer_json_member_add_time_t(wb, "after", t->value.db.after); buffer_json_member_add_time_t(wb, "before", t->value.db.before); + buffer_json_member_add_string(wb, "time_group_condition", alerts_group_conditions_id2txt(t->value.db.time_group_condition)); + buffer_json_member_add_double(wb, "time_group_value", t->value.db.time_group_value); + buffer_json_member_add_string(wb, "dims_group", alerts_dims_grouping_id2group(t->value.db.dims_group)); + buffer_json_member_add_string(wb, "data_source", alerts_data_source_id2source(t->value.db.data_source)); buffer_json_member_add_string(wb, "method", t->value.db.method); buffer_json_member_add_string(wb, "dimensions", t->value.db.dimensions); - web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options",(RRDR_OPTIONS) t->value.db.options); + rrdr_options_to_buffer_json_array(wb, "options", (RRDR_OPTIONS)t->value.db.options); } buffer_json_object_close(wb); // db } @@ -1378,6 +1484,41 @@ static int contexts_v2_alert_instance_to_json_callback(const DICTIONARY_ITEM *it return 1; } +static void contexts_v2_alerts_by_x_update_prototypes(void *data, STRING *type, STRING *component, STRING *classification, STRING *recipient) { + struct rrdcontext_to_json_v2_data *ctl = data; + + dictionary_set_advanced(ctl->alerts.by_type, string2str(type), (ssize_t)string_strlen(type), NULL, sizeof(struct alert_by_x_entry), NULL); + dictionary_set_advanced(ctl->alerts.by_component, string2str(component), (ssize_t)string_strlen(component), NULL, sizeof(struct alert_by_x_entry), NULL); + dictionary_set_advanced(ctl->alerts.by_classification, string2str(classification), (ssize_t)string_strlen(classification), NULL, sizeof(struct alert_by_x_entry), NULL); + dictionary_set_advanced(ctl->alerts.by_recipient, string2str(recipient), (ssize_t)string_strlen(recipient), NULL, sizeof(struct alert_by_x_entry), NULL); +} + +static void contexts_v2_alerts_by_x_to_json(BUFFER *wb, DICTIONARY *dict, const char *key) { + buffer_json_member_add_array(wb, key); + { + struct alert_by_x_entry *b; + dfe_start_read(dict, b) { + buffer_json_add_array_item_object(wb); + { + buffer_json_member_add_string(wb, "name", b_dfe.name); + buffer_json_member_add_uint64(wb, "cr", b->running.counts.critical); + buffer_json_member_add_uint64(wb, "wr", b->running.counts.warning); + buffer_json_member_add_uint64(wb, "cl", b->running.counts.clear); + buffer_json_member_add_uint64(wb, "er", b->running.counts.error); + buffer_json_member_add_uint64(wb, "running", b->running.total); + + buffer_json_member_add_uint64(wb, "running_silent", b->running.silent); + + if(b->prototypes.available) + buffer_json_member_add_uint64(wb, "available", b->prototypes.available); + } + buffer_json_object_close(wb); + } + dfe_done(b); + } + buffer_json_array_close(wb); +} + static void contexts_v2_alert_instances_to_json(BUFFER *wb, const char *key, struct rrdcontext_to_json_v2_data *ctl, bool debug) { buffer_json_member_add_array(wb, key); { @@ -1397,7 +1538,7 @@ static void contexts_v2_alerts_to_json(BUFFER *wb, struct rrdcontext_to_json_v2_ buffer_json_member_add_array(wb, "alerts"); { struct alert_v2_entry *t; - dfe_start_read(ctl->alerts.alerts, t) + dfe_start_read(ctl->alerts.summary, t) { buffer_json_add_array_item_object(wb); { @@ -1405,10 +1546,10 @@ static void contexts_v2_alerts_to_json(BUFFER *wb, struct rrdcontext_to_json_v2_ buffer_json_member_add_string(wb, "nm", string2str(t->name)); buffer_json_member_add_string(wb, "sum", string2str(t->summary)); - buffer_json_member_add_uint64(wb, "cr", t->critical); - buffer_json_member_add_uint64(wb, "wr", t->warning); - buffer_json_member_add_uint64(wb, "cl", t->clear); - buffer_json_member_add_uint64(wb, "er", t->error); + buffer_json_member_add_uint64(wb, "cr", t->counts.critical); + buffer_json_member_add_uint64(wb, "wr", t->counts.warning); + buffer_json_member_add_uint64(wb, "cl", t->counts.clear); + buffer_json_member_add_uint64(wb, "er", t->counts.error); buffer_json_member_add_uint64(wb, "in", t->instances); buffer_json_member_add_uint64(wb, "nd", dictionary_entries(t->nodes)); @@ -1419,6 +1560,13 @@ static void contexts_v2_alerts_to_json(BUFFER *wb, struct rrdcontext_to_json_v2_ dfe_done(t); } buffer_json_array_close(wb); // alerts + + health_prototype_metadata_foreach(ctl, contexts_v2_alerts_by_x_update_prototypes); + contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_type, "alerts_by_type"); + contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_component, "alerts_by_component"); + contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_classification, "alerts_by_classification"); + contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_recipient, "alerts_by_recipient"); + contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_module, "alerts_by_module"); } if(ctl->request->options & (CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES|CONTEXT_V2_OPTION_ALERTS_WITH_VALUES)) { @@ -1926,12 +2074,42 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE } } - ctl.alerts.alerts = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, + ctl.alerts.summary = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct alert_v2_entry)); - dictionary_register_insert_callback(ctl.alerts.alerts, alerts_v2_insert_callback, &ctl); - dictionary_register_conflict_callback(ctl.alerts.alerts, alerts_v2_conflict_callback, &ctl); - dictionary_register_delete_callback(ctl.alerts.alerts, alerts_v2_delete_callback, &ctl); + dictionary_register_insert_callback(ctl.alerts.summary, alerts_v2_insert_callback, &ctl); + dictionary_register_conflict_callback(ctl.alerts.summary, alerts_v2_conflict_callback, &ctl); + dictionary_register_delete_callback(ctl.alerts.summary, alerts_v2_delete_callback, &ctl); + + ctl.alerts.by_type = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, + NULL, sizeof(struct alert_by_x_entry)); + + dictionary_register_insert_callback(ctl.alerts.by_type, alerts_by_x_insert_callback, NULL); + dictionary_register_conflict_callback(ctl.alerts.by_type, alerts_by_x_conflict_callback, NULL); + + ctl.alerts.by_component = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, + NULL, sizeof(struct alert_by_x_entry)); + + dictionary_register_insert_callback(ctl.alerts.by_component, alerts_by_x_insert_callback, NULL); + dictionary_register_conflict_callback(ctl.alerts.by_component, alerts_by_x_conflict_callback, NULL); + + ctl.alerts.by_classification = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, + NULL, sizeof(struct alert_by_x_entry)); + + dictionary_register_insert_callback(ctl.alerts.by_classification, alerts_by_x_insert_callback, NULL); + dictionary_register_conflict_callback(ctl.alerts.by_classification, alerts_by_x_conflict_callback, NULL); + + ctl.alerts.by_recipient = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, + NULL, sizeof(struct alert_by_x_entry)); + + dictionary_register_insert_callback(ctl.alerts.by_recipient, alerts_by_x_insert_callback, NULL); + dictionary_register_conflict_callback(ctl.alerts.by_recipient, alerts_by_x_conflict_callback, NULL); + + ctl.alerts.by_module = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, + NULL, sizeof(struct alert_by_x_entry)); + + dictionary_register_insert_callback(ctl.alerts.by_module, alerts_by_x_insert_callback, NULL); + dictionary_register_conflict_callback(ctl.alerts.by_module, alerts_by_x_conflict_callback, NULL); if(ctl.options & (CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES | CONTEXT_V2_OPTION_ALERTS_WITH_VALUES)) { ctl.alerts.alert_instances = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, @@ -2062,12 +2240,19 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE struct function_v2_entry *t; dfe_start_read(ctl.functions.dict, t) { buffer_json_add_array_item_object(wb); - buffer_json_member_add_string(wb, "name", t_dfe.name); - buffer_json_member_add_string(wb, "help", string2str(t->help)); - buffer_json_member_add_array(wb, "ni"); - for (size_t i = 0; i < t->used; i++) - buffer_json_add_array_item_uint64(wb, t->node_ids[i]); - buffer_json_array_close(wb); + { + buffer_json_member_add_string(wb, "name", t_dfe.name); + buffer_json_member_add_string(wb, "help", string2str(t->help)); + buffer_json_member_add_array(wb, "ni"); + { + for (size_t i = 0; i < t->used; i++) + buffer_json_add_array_item_uint64(wb, t->node_ids[i]); + } + buffer_json_array_close(wb); + buffer_json_member_add_string(wb, "tags", string2str(t->tags)); + http_access2buffer_json_array(wb, "access", t->access); + buffer_json_member_add_uint64(wb, "priority", t->priority); + } buffer_json_object_close(wb); } dfe_done(t); @@ -2127,8 +2312,13 @@ cleanup: dictionary_destroy(ctl.nodes.dict); dictionary_destroy(ctl.contexts.dict); dictionary_destroy(ctl.functions.dict); - dictionary_destroy(ctl.alerts.alerts); + dictionary_destroy(ctl.alerts.summary); dictionary_destroy(ctl.alerts.alert_instances); + dictionary_destroy(ctl.alerts.by_type); + dictionary_destroy(ctl.alerts.by_component); + dictionary_destroy(ctl.alerts.by_classification); + dictionary_destroy(ctl.alerts.by_recipient); + dictionary_destroy(ctl.alerts.by_module); simple_pattern_free(ctl.nodes.scope_pattern); simple_pattern_free(ctl.nodes.pattern); simple_pattern_free(ctl.contexts.pattern); diff --git a/database/contexts/context.c b/src/database/contexts/context.c index 5613c63cf..5613c63cf 100644 --- a/database/contexts/context.c +++ b/src/database/contexts/context.c diff --git a/database/contexts/instance.c b/src/database/contexts/instance.c index 39837dbf6..117953d38 100644 --- a/database/contexts/instance.c +++ b/src/database/contexts/instance.c @@ -404,7 +404,7 @@ inline void rrdinstance_from_rrdset(RRDSET *st) { fatal("RRDCONTEXT: cannot switch rrdcontext without switching rrdinstance too"); } -#define rrdset_get_rrdinstance(st) rrdset_get_rrdinstance_with_trace(st, __FUNCTION__); +#define rrdset_get_rrdinstance(st) rrdset_get_rrdinstance_with_trace(st, __FUNCTION__) static inline RRDINSTANCE *rrdset_get_rrdinstance_with_trace(RRDSET *st, const char *function) { if(unlikely(!st->rrdcontexts.rrdinstance)) { netdata_log_error("RRDINSTANCE: RRDSET '%s' is not linked to an RRDINSTANCE at %s()", rrdset_id(st), function); diff --git a/database/contexts/internal.h b/src/database/contexts/internal.h index 293659fdd..293659fdd 100644 --- a/database/contexts/internal.h +++ b/src/database/contexts/internal.h diff --git a/database/contexts/metric.c b/src/database/contexts/metric.c index 0f0785972..0f0785972 100644 --- a/database/contexts/metric.c +++ b/src/database/contexts/metric.c diff --git a/database/contexts/query_scope.c b/src/database/contexts/query_scope.c index f3bcd0b3f..f3bcd0b3f 100644 --- a/database/contexts/query_scope.c +++ b/src/database/contexts/query_scope.c diff --git a/database/contexts/query_target.c b/src/database/contexts/query_target.c index 95abc3e65..29a9c3e59 100644 --- a/database/contexts/query_target.c +++ b/src/database/contexts/query_target.c @@ -4,7 +4,7 @@ #define QUERY_TARGET_MAX_REALLOC_INCREASE 500 #define query_target_realloc_size(size, start) \ - (size) ? ((size) < QUERY_TARGET_MAX_REALLOC_INCREASE ? (size) * 2 : (size) + QUERY_TARGET_MAX_REALLOC_INCREASE) : (start); + (size) ? ((size) < QUERY_TARGET_MAX_REALLOC_INCREASE ? (size) * 2 : (size) + QUERY_TARGET_MAX_REALLOC_INCREASE) : (start) static void query_metric_release(QUERY_TARGET *qt, QUERY_METRIC *qm); static void query_dimension_release(QUERY_DIMENSION *qd); @@ -82,7 +82,6 @@ void query_target_release(QUERY_TARGET *qt) { simple_pattern_free(qt->instances.labels_pattern); qt->instances.labels_pattern = NULL; - simple_pattern_free(qt->query.pattern); qt->query.pattern = NULL; @@ -221,10 +220,10 @@ static inline void query_metric_release(QUERY_TARGET *qt, QUERY_METRIC *qm) { // reset the tiers for(size_t tier = 0; tier < storage_tiers ;tier++) { - if(qm->tiers[tier].db_metric_handle) { + if(qm->tiers[tier].smh) { STORAGE_ENGINE *eng = query_metric_storage_engine(qt, qm, tier); - eng->api.metric_release(qm->tiers[tier].db_metric_handle); - qm->tiers[tier].db_metric_handle = NULL; + eng->api.metric_release(qm->tiers[tier].smh); + qm->tiers[tier].smh = NULL; } } } @@ -241,7 +240,7 @@ static bool query_metric_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CON struct { STORAGE_ENGINE *eng; - STORAGE_METRIC_HANDLE *db_metric_handle; + STORAGE_METRIC_HANDLE *smh; time_t db_first_time_s; time_t db_last_time_s; time_t db_update_every_s; @@ -252,14 +251,14 @@ static bool query_metric_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CON tier_retention[tier].eng = eng; tier_retention[tier].db_update_every_s = (time_t) (qn->rrdhost->db[tier].tier_grouping * ri->update_every_s); - if(rm->rrddim && rm->rrddim->tiers[tier].db_metric_handle) - tier_retention[tier].db_metric_handle = eng->api.metric_dup(rm->rrddim->tiers[tier].db_metric_handle); + if(rm->rrddim && rm->rrddim->tiers[tier].smh) + tier_retention[tier].smh = eng->api.metric_dup(rm->rrddim->tiers[tier].smh); else - tier_retention[tier].db_metric_handle = eng->api.metric_get(qn->rrdhost->db[tier].instance, &rm->uuid); + tier_retention[tier].smh = eng->api.metric_get(qn->rrdhost->db[tier].si, &rm->uuid); - if(tier_retention[tier].db_metric_handle) { - tier_retention[tier].db_first_time_s = storage_engine_oldest_time_s(tier_retention[tier].eng->backend, tier_retention[tier].db_metric_handle); - tier_retention[tier].db_last_time_s = storage_engine_latest_time_s(tier_retention[tier].eng->backend, tier_retention[tier].db_metric_handle); + if(tier_retention[tier].smh) { + tier_retention[tier].db_first_time_s = storage_engine_oldest_time_s(tier_retention[tier].eng->seb, tier_retention[tier].smh); + tier_retention[tier].db_last_time_s = storage_engine_latest_time_s(tier_retention[tier].eng->seb, tier_retention[tier].smh); if(!common_first_time_s) common_first_time_s = tier_retention[tier].db_first_time_s; @@ -331,7 +330,7 @@ static bool query_metric_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CON for (size_t tier = 0; tier < storage_tiers; tier++) { internal_fatal(tier_retention[tier].eng != query_metric_storage_engine(qt, qm, tier), "QUERY TARGET: storage engine mismatch"); - qm->tiers[tier].db_metric_handle = tier_retention[tier].db_metric_handle; + qm->tiers[tier].smh = tier_retention[tier].smh; qm->tiers[tier].db_first_time_s = tier_retention[tier].db_first_time_s; qm->tiers[tier].db_last_time_s = tier_retention[tier].db_last_time_s; qm->tiers[tier].db_update_every_s = tier_retention[tier].db_update_every_s; @@ -342,8 +341,10 @@ static bool query_metric_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CON // cleanup anything we allocated to the retention we will not use for(size_t tier = 0; tier < storage_tiers ;tier++) { - if (tier_retention[tier].db_metric_handle) - tier_retention[tier].eng->api.metric_release(tier_retention[tier].db_metric_handle); + if (tier_retention[tier].smh) { + tier_retention[tier].eng->api.metric_release(tier_retention[tier].smh); + tier_retention[tier].smh = NULL; + } } return false; @@ -627,7 +628,7 @@ static bool query_target_match_alert_pattern(RRDINSTANCE_ACQUIRED *ria, SIMPLE_P rw_spinlock_read_lock(&st->alerts.spinlock); if (st->alerts.base) { for (RRDCALC *rc = st->alerts.base; rc; rc = rc->next) { - SIMPLE_PATTERN_RESULT ret = simple_pattern_matches_string_extract(pattern, rc->name, NULL, 0); + SIMPLE_PATTERN_RESULT ret = simple_pattern_matches_string_extract(pattern, rc->config.name, NULL, 0); if(ret == SP_MATCHED_POSITIVE) { matched = true; @@ -641,7 +642,7 @@ static bool query_target_match_alert_pattern(RRDINSTANCE_ACQUIRED *ria, SIMPLE_P else buffer_flush(wb); - buffer_fast_strcat(wb, string2str(rc->name), string_strlen(rc->name)); + buffer_fast_strcat(wb, string2str(rc->config.name), string_strlen(rc->config.name)); buffer_fast_strcat(wb, ":", 1); buffer_strcat(wb, rrdcalc_status2string(rc->status)); @@ -725,13 +726,22 @@ static inline SIMPLE_PATTERN_RESULT query_instance_matches(QUERY_INSTANCE *qi, return ret; } -static inline bool query_instance_matches_labels(RRDINSTANCE *ri, SIMPLE_PATTERN *chart_label_key_sp, SIMPLE_PATTERN *labels_sp) { - if ((chart_label_key_sp && !rrdlabels_match_simple_pattern_parsed( - ri->rrdlabels, chart_label_key_sp, '\0', NULL)) || - (labels_sp && !rrdlabels_match_simple_pattern_parsed( - ri->rrdlabels, labels_sp, ':', NULL))) +static inline bool query_instance_matches_labels( + RRDINSTANCE *ri, + SIMPLE_PATTERN *chart_label_key_sp, + SIMPLE_PATTERN *labels_sp) +{ + + if (chart_label_key_sp && rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, chart_label_key_sp, '\0', NULL) != SP_MATCHED_POSITIVE) return false; + if (labels_sp) { + struct pattern_array *pa = pattern_array_add_simple_pattern(NULL, labels_sp, ':'); + bool found = pattern_array_label_match(pa, ri->rrdlabels, ':', NULL); + pattern_array_free(pa); + return found; + } + return true; } @@ -752,7 +762,10 @@ static bool query_instance_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_C qi, ri, qt->instances.pattern, qtl->match_ids, qtl->match_names, qt->request.version, qtl->host_node_id_str)); if(queryable_instance) - queryable_instance = query_instance_matches_labels(ri, qt->instances.chart_label_key_pattern, qt->instances.labels_pattern); + queryable_instance = query_instance_matches_labels( + ri, + qt->instances.chart_label_key_pattern, + qt->instances.labels_pattern); if(queryable_instance) { if(qt->instances.alerts_pattern && !query_target_match_alert_pattern(ria, qt->instances.alerts_pattern)) @@ -1216,6 +1229,5 @@ ssize_t weights_foreach_rrdmetric_in_context(RRDCONTEXT_ACQUIRED *rca, break; } dfe_done(ri); - return count; } diff --git a/database/contexts/rrdcontext.c b/src/database/contexts/rrdcontext.c index 9dee39be2..9dee39be2 100644 --- a/database/contexts/rrdcontext.c +++ b/src/database/contexts/rrdcontext.c diff --git a/database/contexts/rrdcontext.h b/src/database/contexts/rrdcontext.h index 9c497a5a5..08a5760b5 100644 --- a/database/contexts/rrdcontext.h +++ b/src/database/contexts/rrdcontext.h @@ -210,7 +210,7 @@ typedef struct query_metric { RRDR_DIMENSION_FLAGS status; struct query_metric_tier { - STORAGE_METRIC_HANDLE *db_metric_handle; + STORAGE_METRIC_HANDLE *smh; time_t db_first_time_s; // the oldest timestamp available for this tier time_t db_last_time_s; // the latest timestamp available for this tier time_t db_update_every_s; // latest update every for this tier @@ -299,6 +299,8 @@ typedef struct query_target_request { qt_interrupt_callback_t interrupt_callback; void *interrupt_callback_data; + + uuid_t *transaction; } QUERY_TARGET_REQUEST; #define GROUP_BY_MAX_LABEL_KEYS 10 @@ -459,14 +461,9 @@ struct sql_alert_config_data { const char *on_template; const char *on_key; - const char *os; - const char *hosts; const char *families; - const char *plugin; - const char *module; const char *host_labels; const char *chart_labels; - const char *charts; } selectors; const char *info; @@ -479,6 +476,10 @@ struct sql_alert_config_data { struct { const char *dimensions; const char *method; + ALERT_LOOKUP_TIME_GROUP_CONDITION time_group_condition; + NETDATA_DOUBLE time_group_value; + ALERT_LOOKUP_DIMS_GROUPING dims_group; + ALERT_LOOKUP_DATA_SOURCE data_source; uint32_t options; int32_t after; diff --git a/database/contexts/worker.c b/src/database/contexts/worker.c index 9d7c18863..2aae8363d 100644 --- a/database/contexts/worker.c +++ b/src/database/contexts/worker.c @@ -239,7 +239,7 @@ bool rrdmetric_update_retention(RRDMETRIC *rm) { STORAGE_ENGINE *eng = rrdhost->db[tier].eng; time_t first_time_t, last_time_t; - if (eng->api.metric_retention_by_uuid(rrdhost->db[tier].instance, &rm->uuid, &first_time_t, &last_time_t)) { + if (eng->api.metric_retention_by_uuid(rrdhost->db[tier].si, &rm->uuid, &first_time_t, &last_time_t)) { if (first_time_t < min_first_time_t) min_first_time_t = first_time_t; diff --git a/database/engine/README.md b/src/database/engine/README.md index 890018642..890018642 100644 --- a/database/engine/README.md +++ b/src/database/engine/README.md diff --git a/database/engine/cache.c b/src/database/engine/cache.c index eb1c35298..49a9b6b96 100644 --- a/database/engine/cache.c +++ b/src/database/engine/cache.c @@ -1325,21 +1325,8 @@ static PGC_PAGE *page_add(PGC *cache, PGC_ENTRY *entry, bool *added) { return page; } -static PGC_PAGE *page_find_and_acquire(PGC *cache, Word_t section, Word_t metric_id, time_t start_time_s, PGC_SEARCH method) { - __atomic_add_fetch(&cache->stats.workers_search, 1, __ATOMIC_RELAXED); - - size_t *stats_hit_ptr, *stats_miss_ptr; - - if(method == PGC_SEARCH_CLOSEST) { - __atomic_add_fetch(&cache->stats.searches_closest, 1, __ATOMIC_RELAXED); - stats_hit_ptr = &cache->stats.searches_closest_hits; - stats_miss_ptr = &cache->stats.searches_closest_misses; - } - else { - __atomic_add_fetch(&cache->stats.searches_exact, 1, __ATOMIC_RELAXED); - stats_hit_ptr = &cache->stats.searches_exact_hits; - stats_miss_ptr = &cache->stats.searches_exact_misses; - } +static PGC_PAGE *page_find_and_acquire_once(PGC *cache, Word_t section, Word_t metric_id, time_t start_time_s, PGC_SEARCH method, bool *retry) { + *retry = false; PGC_PAGE *page = NULL; size_t partition = pgc_indexing_partition(cache, metric_id); @@ -1462,22 +1449,13 @@ static PGC_PAGE *page_find_and_acquire(PGC *cache, Word_t section, Word_t metric if(!page_acquire(cache, page)) { // this page is not good to use + *retry = true; page = NULL; } } cleanup: pgc_index_read_unlock(cache, partition); - - if(page) { - __atomic_add_fetch(stats_hit_ptr, 1, __ATOMIC_RELAXED); - page_has_been_accessed(cache, page); - } - else - __atomic_add_fetch(stats_miss_ptr, 1, __ATOMIC_RELAXED); - - __atomic_sub_fetch(&cache->stats.workers_search, 1, __ATOMIC_RELAXED); - return page; } @@ -1882,7 +1860,7 @@ void pgc_page_release(PGC *cache, PGC_PAGE *page) { page_release(cache, page, is_page_clean(page)); } -void pgc_page_hot_to_dirty_and_release(PGC *cache, PGC_PAGE *page) { +void pgc_page_hot_to_dirty_and_release(PGC *cache, PGC_PAGE *page, bool never_flush) { __atomic_add_fetch(&cache->stats.workers_hot2dirty, 1, __ATOMIC_RELAXED); //#ifdef NETDATA_INTERNAL_CHECKS @@ -1901,10 +1879,8 @@ void pgc_page_hot_to_dirty_and_release(PGC *cache, PGC_PAGE *page) { __atomic_sub_fetch(&cache->stats.workers_hot2dirty, 1, __ATOMIC_RELAXED); // flush, if we have to - if((cache->config.options & PGC_OPTIONS_FLUSH_PAGES_INLINE) || flushing_critical(cache)) { - flush_pages(cache, cache->config.max_flushes_inline, PGC_SECTION_ALL, - false, false); - } + if(!never_flush && ((cache->config.options & PGC_OPTIONS_FLUSH_PAGES_INLINE) || flushing_critical(cache))) + flush_pages(cache, cache->config.max_flushes_inline, PGC_SECTION_ALL, false, false); } bool pgc_page_to_clean_evict_or_release(PGC *cache, PGC_PAGE *page) { @@ -1949,13 +1925,13 @@ time_t pgc_page_end_time_s(PGC_PAGE *page) { return page->end_time_s; } -time_t pgc_page_update_every_s(PGC_PAGE *page) { +uint32_t pgc_page_update_every_s(PGC_PAGE *page) { return page->update_every_s; } -time_t pgc_page_fix_update_every(PGC_PAGE *page, time_t update_every_s) { +uint32_t pgc_page_fix_update_every(PGC_PAGE *page, uint32_t update_every_s) { if(page->update_every_s == 0) - page->update_every_s = (uint32_t) update_every_s; + page->update_every_s = update_every_s; return page->update_every_s; } @@ -2050,7 +2026,46 @@ void pgc_page_hot_set_end_time_s(PGC *cache __maybe_unused, PGC_PAGE *page, time } PGC_PAGE *pgc_page_get_and_acquire(PGC *cache, Word_t section, Word_t metric_id, time_t start_time_s, PGC_SEARCH method) { - return page_find_and_acquire(cache, section, metric_id, start_time_s, method); + static const struct timespec ns = { .tv_sec = 0, .tv_nsec = 1 }; + + PGC_PAGE *page = NULL; + + __atomic_add_fetch(&cache->stats.workers_search, 1, __ATOMIC_RELAXED); + + size_t *stats_hit_ptr, *stats_miss_ptr; + + if(method == PGC_SEARCH_CLOSEST) { + __atomic_add_fetch(&cache->stats.searches_closest, 1, __ATOMIC_RELAXED); + stats_hit_ptr = &cache->stats.searches_closest_hits; + stats_miss_ptr = &cache->stats.searches_closest_misses; + } + else { + __atomic_add_fetch(&cache->stats.searches_exact, 1, __ATOMIC_RELAXED); + stats_hit_ptr = &cache->stats.searches_exact_hits; + stats_miss_ptr = &cache->stats.searches_exact_misses; + } + + while(1) { + bool retry = false; + + page = page_find_and_acquire_once(cache, section, metric_id, start_time_s, method, &retry); + + if(page || !retry) + break; + + nanosleep(&ns, NULL); + } + + if(page) { + __atomic_add_fetch(stats_hit_ptr, 1, __ATOMIC_RELAXED); + page_has_been_accessed(cache, page); + } + else + __atomic_add_fetch(stats_miss_ptr, 1, __ATOMIC_RELAXED); + + __atomic_sub_fetch(&cache->stats.workers_search, 1, __ATOMIC_RELAXED); + + return page; } struct pgc_statistics pgc_get_statistics(PGC *cache) { @@ -2224,7 +2239,7 @@ void pgc_open_cache_to_journal_v2(PGC *cache, Word_t section, unsigned datafile_ while ((PValue2 = JudyLFirstThenNext(mi->JudyL_pages_by_start_time, &start_time, &start_time_first))) { struct jv2_page_info *pi = *PValue2; page_transition_unlock(cache, pi->page); - pgc_page_hot_to_dirty_and_release(cache, pi->page); + pgc_page_hot_to_dirty_and_release(cache, pi->page, true); // make_acquired_page_clean_and_evict_or_page_release(cache, pi->page); aral_freez(ar_pi, pi); } @@ -2251,6 +2266,8 @@ void pgc_open_cache_to_journal_v2(PGC *cache, Word_t section, unsigned datafile_ aral_by_size_release(ar_mi); __atomic_sub_fetch(&cache->stats.workers_jv2_flush, 1, __ATOMIC_RELAXED); + + flush_pages(cache, cache->config.max_flushes_inline, PGC_SECTION_ALL, false, false); } static bool match_page_data(PGC_PAGE *page, void *data) { @@ -2396,7 +2413,7 @@ void *unittest_stress_test_collector(void *ptr) { if(i % 10 == 0) pgc_page_to_clean_evict_or_release(pgc_uts.cache, pgc_uts.metrics[i]); else - pgc_page_hot_to_dirty_and_release(pgc_uts.cache, pgc_uts.metrics[i]); + pgc_page_hot_to_dirty_and_release(pgc_uts.cache, pgc_uts.metrics[i], false); } } @@ -2721,7 +2738,7 @@ int pgc_unittest(void) { }, NULL); pgc_page_hot_set_end_time_s(cache, page2, 2001); - pgc_page_hot_to_dirty_and_release(cache, page2); + pgc_page_hot_to_dirty_and_release(cache, page2, false); PGC_PAGE *page3 = pgc_page_add_and_acquire(cache, (PGC_ENTRY){ .section = 3, @@ -2734,7 +2751,7 @@ int pgc_unittest(void) { }, NULL); pgc_page_hot_set_end_time_s(cache, page3, 2001); - pgc_page_hot_to_dirty_and_release(cache, page3); + pgc_page_hot_to_dirty_and_release(cache, page3, false); pgc_destroy(cache); diff --git a/database/engine/cache.h b/src/database/engine/cache.h index 7cd7c0636..b6f81bcc2 100644 --- a/database/engine/cache.h +++ b/src/database/engine/cache.h @@ -2,6 +2,7 @@ #ifndef DBENGINE_CACHE_H #define DBENGINE_CACHE_H +#include "datafile.h" #include "../rrd.h" // CACHE COMPILE TIME CONFIGURATION @@ -27,7 +28,7 @@ typedef struct pgc_entry { time_t end_time_s; // the end time of the page size_t size; // the size in bytes of the allocation, outside the cache void *data; // a pointer to data outside the cache - uint32_t update_every_s; // the update every of the page + uint32_t update_every_s; // the update every of the page bool hot; // true if this entry is currently being collected uint8_t *custom_data; } PGC_ENTRY; @@ -191,7 +192,7 @@ PGC_PAGE *pgc_page_dup(PGC *cache, PGC_PAGE *page); void pgc_page_release(PGC *cache, PGC_PAGE *page); // mark a hot page dirty, and release it -void pgc_page_hot_to_dirty_and_release(PGC *cache, PGC_PAGE *page); +void pgc_page_hot_to_dirty_and_release(PGC *cache, PGC_PAGE *page, bool never_flush); // find a page from the cache typedef enum { @@ -210,8 +211,8 @@ Word_t pgc_page_section(PGC_PAGE *page); Word_t pgc_page_metric(PGC_PAGE *page); time_t pgc_page_start_time_s(PGC_PAGE *page); time_t pgc_page_end_time_s(PGC_PAGE *page); -time_t pgc_page_update_every_s(PGC_PAGE *page); -time_t pgc_page_fix_update_every(PGC_PAGE *page, time_t update_every_s); +uint32_t pgc_page_update_every_s(PGC_PAGE *page); +uint32_t pgc_page_fix_update_every(PGC_PAGE *page, uint32_t update_every_s); time_t pgc_page_fix_end_time_s(PGC_PAGE *page, time_t end_time_s); void *pgc_page_data(PGC_PAGE *page); void *pgc_page_custom_data(PGC *cache, PGC_PAGE *page); diff --git a/database/engine/datafile.c b/src/database/engine/datafile.c index 7322039cd..1ec2dea79 100644 --- a/database/engine/datafile.c +++ b/src/database/engine/datafile.c @@ -557,7 +557,9 @@ void finalize_data_files(struct rrdengine_instance *ctx) { bool logged = false; - logged = false; + if (!ctx->datafiles.first) + return; + while(__atomic_load_n(&ctx->atomic.extents_currently_being_flushed, __ATOMIC_RELAXED)) { if(!logged) { netdata_log_info("Waiting for inflight flush to finish on tier %d...", ctx->config.tier); diff --git a/database/engine/datafile.h b/src/database/engine/datafile.h index 569f1b0a2..569f1b0a2 100644 --- a/database/engine/datafile.h +++ b/src/database/engine/datafile.h diff --git a/database/engine/dbengine-diagram.xml b/src/database/engine/dbengine-diagram.xml index 793e8a355..793e8a355 100644 --- a/database/engine/dbengine-diagram.xml +++ b/src/database/engine/dbengine-diagram.xml diff --git a/database/engine/journalfile.c b/src/database/engine/journalfile.c index 9005b81ca..8099d017f 100644 --- a/database/engine/journalfile.c +++ b/src/database/engine/journalfile.c @@ -637,9 +637,12 @@ static int journalfile_check_superblock(uv_file file) fatal_assert(req.result >= 0); uv_fs_req_cleanup(&req); - if (strncmp(superblock->magic_number, RRDENG_JF_MAGIC, RRDENG_MAGIC_SZ) || - strncmp(superblock->version, RRDENG_JF_VER, RRDENG_VER_SZ)) { - netdata_log_error("DBENGINE: File has invalid superblock."); + + char jf_magic[RRDENG_MAGIC_SZ] = RRDENG_JF_MAGIC; + char jf_ver[RRDENG_VER_SZ] = RRDENG_JF_VER; + if (strncmp(superblock->magic_number, jf_magic, RRDENG_MAGIC_SZ) != 0 || + strncmp(superblock->version, jf_ver, RRDENG_VER_SZ) != 0) { + nd_log(NDLS_DAEMON, NDLP_ERR, "DBENGINE: File has invalid superblock."); ret = UV_EINVAL; } else { ret = 0; @@ -669,7 +672,7 @@ static void journalfile_restore_extent_metadata(struct rrdengine_instance *ctx, uuid_t *temp_id; uint8_t page_type = jf_metric_data->descr[i].type; - if (page_type > PAGE_TYPE_MAX) { + if (page_type > RRDENG_PAGE_TYPE_MAX) { if (!bitmap256_get_bit(&page_error_map, page_type)) { netdata_log_error("DBENGINE: unknown page type %d encountered.", page_type); bitmap256_set_bit(&page_error_map, page_type, 1); @@ -700,13 +703,19 @@ static void journalfile_restore_extent_metadata(struct rrdengine_instance *ctx, .section = (Word_t)ctx, .first_time_s = vd.start_time_s, .last_time_s = vd.end_time_s, - .latest_update_every_s = (uint32_t) vd.update_every_s, + .latest_update_every_s = vd.update_every_s, }; bool added; metric = mrg_metric_add_and_acquire(main_mrg, entry, &added); - if(added) + if(added) { + __atomic_add_fetch(&ctx->atomic.metrics, 1, __ATOMIC_RELAXED); update_metric_time = false; + } + if (vd.update_every_s) { + uint64_t samples = (vd.end_time_s - vd.start_time_s) / vd.update_every_s; + __atomic_add_fetch(&ctx->atomic.samples, samples, __ATOMIC_RELAXED); + } } Word_t metric_id = mrg_metric_id(main_mrg, metric); @@ -1005,7 +1014,7 @@ void journalfile_v2_populate_retention_to_mrg(struct rrdengine_instance *ctx, st time_t end_time_s = header_start_time_s + metric->delta_end_s; mrg_update_metric_retention_and_granularity_by_uuid( - main_mrg, (Word_t)ctx, &metric->uuid, start_time_s, end_time_s, (time_t) metric->update_every_s, now_s); + main_mrg, (Word_t)ctx, &metric->uuid, start_time_s, end_time_s, metric->update_every_s, now_s); metric++; } @@ -1042,7 +1051,7 @@ int journalfile_v2_load(struct rrdengine_instance *ctx, struct rrdengine_journal journal_v1_file_size = (uint32_t)statbuf.st_size; journalfile_v2_generate_path(datafile, path_v2, sizeof(path_v2)); - fd = open(path_v2, O_RDONLY); + fd = open(path_v2, O_RDONLY | O_CLOEXEC); if (fd < 0) { if (errno == ENOENT) return 1; @@ -1226,7 +1235,7 @@ void *journalfile_v2_write_data_page(struct journal_v2_header *j2_header, void * data_page->delta_end_s = (uint32_t) (page_info->end_time_s - (time_t) (j2_header->start_time_ut) / USEC_PER_SEC); data_page->extent_index = page_info->extent_index; - data_page->update_every_s = (uint32_t) page_info->update_every_s; + data_page->update_every_s = page_info->update_every_s; data_page->page_length = (uint16_t) (ei ? ei->page_length : page_info->page_length); data_page->type = 0; @@ -1252,7 +1261,7 @@ static void *journalfile_v2_write_descriptors(struct journal_v2_header *j2_heade page_info = *PValue; // Write one descriptor and return the next data page location data_page = journalfile_v2_write_data_page(j2_header, (void *) data_page, page_info); - update_every_s = (uint32_t) page_info->update_every_s; + update_every_s = page_info->update_every_s; if (NULL == data_page) break; } diff --git a/database/engine/journalfile.h b/src/database/engine/journalfile.h index 5cdf72b9d..3f881ee16 100644 --- a/database/engine/journalfile.h +++ b/src/database/engine/journalfile.h @@ -7,7 +7,6 @@ /* Forward declarations */ struct rrdengine_instance; -struct rrdengine_worker_config; struct rrdengine_datafile; struct rrdengine_journalfile; diff --git a/database/engine/metric.c b/src/database/engine/metric.c index 2e132612e..01eb22fbc 100644 --- a/database/engine/metric.c +++ b/src/database/engine/metric.c @@ -1,5 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "metric.h" +#include "cache.h" +#include "libnetdata/locks/locks.h" +#include "rrddiskprotocol.h" typedef int32_t REFCOUNT; #define REFCOUNT_DELETING (-100) @@ -104,8 +107,11 @@ static inline void mrg_stats_size_judyhs_removed_uuid(MRG *mrg, size_t partition static inline size_t uuid_partition(MRG *mrg __maybe_unused, uuid_t *uuid) { uint8_t *u = (uint8_t *)uuid; - size_t *n = (size_t *)&u[UUID_SZ - sizeof(size_t)]; - return *n % mrg->partitions; + + size_t n; + memcpy(&n, &u[UUID_SZ - sizeof(size_t)], sizeof(size_t)); + + return n % mrg->partitions; } static inline time_t mrg_metric_get_first_time_s_smart(MRG *mrg __maybe_unused, METRIC *metric) { @@ -125,87 +131,174 @@ static inline time_t mrg_metric_get_first_time_s_smart(MRG *mrg __maybe_unused, return first_time_s; } -static inline REFCOUNT metric_acquire(MRG *mrg __maybe_unused, METRIC *metric) { +static void metric_log(MRG *mrg __maybe_unused, METRIC *metric, const char *msg) { + struct rrdengine_instance *ctx = (struct rrdengine_instance *)metric->section; + + char uuid[UUID_STR_LEN]; + uuid_unparse_lower(metric->uuid, uuid); + nd_log(NDLS_DAEMON, NDLP_ERR, + "METRIC: %s on %s at tier %d, refcount %d, partition %u, " + "retention [%ld - %ld (hot), %ld (clean)], update every %"PRIu32", " + "writer pid %d " + "--- PLEASE OPEN A GITHUB ISSUE TO REPORT THIS LOG LINE TO NETDATA --- ", + msg, + uuid, + ctx->config.tier, + metric->refcount, + metric->partition, + metric->first_time_s, + metric->latest_time_s_hot, + metric->latest_time_s_clean, + metric->latest_update_every_s, + (int)metric->writer + ); +} + +static inline bool acquired_metric_has_retention(MRG *mrg, METRIC *metric) { + time_t first, last; + mrg_metric_get_retention(mrg, metric, &first, &last, NULL); + return (!first || !last || first > last); +} + +static inline void acquired_for_deletion_metric_delete(MRG *mrg, METRIC *metric) { size_t partition = metric->partition; - REFCOUNT expected = __atomic_load_n(&metric->refcount, __ATOMIC_RELAXED); - REFCOUNT refcount; + + size_t mem_before_judyl, mem_after_judyl; + + mrg_index_write_lock(mrg, partition); + + Pvoid_t *sections_judy_pptr = JudyHSGet(mrg->index[partition].uuid_judy, &metric->uuid, sizeof(uuid_t)); + if(unlikely(!sections_judy_pptr || !*sections_judy_pptr)) { + MRG_STATS_DELETE_MISS(mrg, partition); + mrg_index_write_unlock(mrg, partition); + return; + } + + mem_before_judyl = JudyLMemUsed(*sections_judy_pptr); + int rc = JudyLDel(sections_judy_pptr, metric->section, PJE0); + mem_after_judyl = JudyLMemUsed(*sections_judy_pptr); + mrg_stats_size_judyl_change(mrg, mem_before_judyl, mem_after_judyl, partition); + + if(unlikely(!rc)) { + MRG_STATS_DELETE_MISS(mrg, partition); + mrg_index_write_unlock(mrg, partition); + return; + } + + if(!*sections_judy_pptr) { + rc = JudyHSDel(&mrg->index[partition].uuid_judy, &metric->uuid, sizeof(uuid_t), PJE0); + if(unlikely(!rc)) + fatal("DBENGINE METRIC: cannot delete UUID from JudyHS"); + mrg_stats_size_judyhs_removed_uuid(mrg, partition); + } + + MRG_STATS_DELETED_METRIC(mrg, partition); + + mrg_index_write_unlock(mrg, partition); + + aral_freez(mrg->index[partition].aral, metric); +} + +static inline bool metric_acquire(MRG *mrg, METRIC *metric) { + REFCOUNT expected, desired; + + expected = __atomic_load_n(&metric->refcount, __ATOMIC_RELAXED); do { - if(expected < 0) - fatal("METRIC: refcount is %d (negative) during acquire", metric->refcount); + if(unlikely(expected < 0)) + return false; + + desired = expected + 1; - refcount = expected + 1; - } while(!__atomic_compare_exchange_n(&metric->refcount, &expected, refcount, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)); + } while(!__atomic_compare_exchange_n(&metric->refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)); + + size_t partition = metric->partition; - if(refcount == 1) + if(desired == 1) __atomic_add_fetch(&mrg->index[partition].stats.entries_referenced, 1, __ATOMIC_RELAXED); __atomic_add_fetch(&mrg->index[partition].stats.current_references, 1, __ATOMIC_RELAXED); - return refcount; + return true; } -static inline bool metric_release_and_can_be_deleted(MRG *mrg __maybe_unused, METRIC *metric) { +static inline bool metric_release(MRG *mrg, METRIC *metric, bool delete_if_last_without_retention) { size_t partition = metric->partition; - REFCOUNT expected = __atomic_load_n(&metric->refcount, __ATOMIC_RELAXED); - REFCOUNT refcount; + REFCOUNT expected, desired; + + expected = __atomic_load_n(&metric->refcount, __ATOMIC_RELAXED); do { - if(expected <= 0) - fatal("METRIC: refcount is %d (zero or negative) during release", metric->refcount); + if(expected <= 0) { + metric_log(mrg, metric, "refcount is zero or negative during release"); + fatal("METRIC: refcount is %d (zero or negative) during release", expected); + } - refcount = expected - 1; - } while(!__atomic_compare_exchange_n(&metric->refcount, &expected, refcount, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)); + if(expected == 1 && delete_if_last_without_retention && !acquired_metric_has_retention(mrg, metric)) + desired = REFCOUNT_DELETING; + else + desired = expected - 1; + + } while(!__atomic_compare_exchange_n(&metric->refcount, &expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED)); - if(unlikely(!refcount)) + if(desired == 0 || desired == REFCOUNT_DELETING) { __atomic_sub_fetch(&mrg->index[partition].stats.entries_referenced, 1, __ATOMIC_RELAXED); + if(desired == REFCOUNT_DELETING) + acquired_for_deletion_metric_delete(mrg, metric); + } + __atomic_sub_fetch(&mrg->index[partition].stats.current_references, 1, __ATOMIC_RELAXED); - time_t first, last, ue; - mrg_metric_get_retention(mrg, metric, &first, &last, &ue); - return (!first || !last || first > last); + return desired == REFCOUNT_DELETING; } static inline METRIC *metric_add_and_acquire(MRG *mrg, MRG_ENTRY *entry, bool *ret) { size_t partition = uuid_partition(mrg, entry->uuid); METRIC *allocation = aral_mallocz(mrg->index[partition].aral); + Pvoid_t *PValue; - mrg_index_write_lock(mrg, partition); + while(1) { + mrg_index_write_lock(mrg, partition); - size_t mem_before_judyl, mem_after_judyl; + size_t mem_before_judyl, mem_after_judyl; - Pvoid_t *sections_judy_pptr = JudyHSIns(&mrg->index[partition].uuid_judy, entry->uuid, sizeof(uuid_t), PJE0); - if(unlikely(!sections_judy_pptr || sections_judy_pptr == PJERR)) - fatal("DBENGINE METRIC: corrupted UUIDs JudyHS array"); + Pvoid_t *sections_judy_pptr = JudyHSIns(&mrg->index[partition].uuid_judy, entry->uuid, sizeof(uuid_t), PJE0); + if (unlikely(!sections_judy_pptr || sections_judy_pptr == PJERR)) + fatal("DBENGINE METRIC: corrupted UUIDs JudyHS array"); - if(unlikely(!*sections_judy_pptr)) - mrg_stats_size_judyhs_added_uuid(mrg, partition); + if (unlikely(!*sections_judy_pptr)) + mrg_stats_size_judyhs_added_uuid(mrg, partition); - mem_before_judyl = JudyLMemUsed(*sections_judy_pptr); - Pvoid_t *PValue = JudyLIns(sections_judy_pptr, entry->section, PJE0); - mem_after_judyl = JudyLMemUsed(*sections_judy_pptr); - mrg_stats_size_judyl_change(mrg, mem_before_judyl, mem_after_judyl, partition); + mem_before_judyl = JudyLMemUsed(*sections_judy_pptr); + PValue = JudyLIns(sections_judy_pptr, entry->section, PJE0); + mem_after_judyl = JudyLMemUsed(*sections_judy_pptr); + mrg_stats_size_judyl_change(mrg, mem_before_judyl, mem_after_judyl, partition); - if(unlikely(!PValue || PValue == PJERR)) - fatal("DBENGINE METRIC: corrupted section JudyL array"); + if (unlikely(!PValue || PValue == PJERR)) + fatal("DBENGINE METRIC: corrupted section JudyL array"); - if(unlikely(*PValue != NULL)) { - METRIC *metric = *PValue; + if (unlikely(*PValue != NULL)) { + METRIC *metric = *PValue; - metric_acquire(mrg, metric); + if(!metric_acquire(mrg, metric)) { + mrg_index_write_unlock(mrg, partition); + continue; + } - MRG_STATS_DUPLICATE_ADD(mrg, partition); + MRG_STATS_DUPLICATE_ADD(mrg, partition); + mrg_index_write_unlock(mrg, partition); - mrg_index_write_unlock(mrg, partition); + if (ret) + *ret = false; - if(ret) - *ret = false; + aral_freez(mrg->index[partition].aral, allocation); - aral_freez(mrg->index[partition].aral, allocation); + return metric; + } - return metric; + break; } METRIC *metric = allocation; @@ -216,9 +309,8 @@ static inline METRIC *metric_add_and_acquire(MRG *mrg, MRG_ENTRY *entry, bool *r metric->latest_time_s_hot = 0; metric->latest_update_every_s = entry->latest_update_every_s; metric->writer = 0; - metric->refcount = 0; + metric->refcount = 1; metric->partition = partition; - metric_acquire(mrg, metric); *PValue = metric; MRG_STATS_ADDED_METRIC(mrg, partition); @@ -234,77 +326,35 @@ static inline METRIC *metric_add_and_acquire(MRG *mrg, MRG_ENTRY *entry, bool *r static inline METRIC *metric_get_and_acquire(MRG *mrg, uuid_t *uuid, Word_t section) { size_t partition = uuid_partition(mrg, uuid); - mrg_index_read_lock(mrg, partition); + while(1) { + mrg_index_read_lock(mrg, partition); - Pvoid_t *sections_judy_pptr = JudyHSGet(mrg->index[partition].uuid_judy, uuid, sizeof(uuid_t)); - if(unlikely(!sections_judy_pptr)) { - mrg_index_read_unlock(mrg, partition); - MRG_STATS_SEARCH_MISS(mrg, partition); - return NULL; - } - - Pvoid_t *PValue = JudyLGet(*sections_judy_pptr, section, PJE0); - if(unlikely(!PValue)) { - mrg_index_read_unlock(mrg, partition); - MRG_STATS_SEARCH_MISS(mrg, partition); - return NULL; - } - - METRIC *metric = *PValue; - - metric_acquire(mrg, metric); - - mrg_index_read_unlock(mrg, partition); - - MRG_STATS_SEARCH_HIT(mrg, partition); - return metric; -} - -static inline bool acquired_metric_del(MRG *mrg, METRIC *metric) { - size_t partition = metric->partition; - - size_t mem_before_judyl, mem_after_judyl; - - mrg_index_write_lock(mrg, partition); + Pvoid_t *sections_judy_pptr = JudyHSGet(mrg->index[partition].uuid_judy, uuid, sizeof(uuid_t)); + if (unlikely(!sections_judy_pptr)) { + mrg_index_read_unlock(mrg, partition); + MRG_STATS_SEARCH_MISS(mrg, partition); + return NULL; + } - if(!metric_release_and_can_be_deleted(mrg, metric)) { - mrg->index[partition].stats.delete_having_retention_or_referenced++; - mrg_index_write_unlock(mrg, partition); - return false; - } + Pvoid_t *PValue = JudyLGet(*sections_judy_pptr, section, PJE0); + if (unlikely(!PValue)) { + mrg_index_read_unlock(mrg, partition); + MRG_STATS_SEARCH_MISS(mrg, partition); + return NULL; + } - Pvoid_t *sections_judy_pptr = JudyHSGet(mrg->index[partition].uuid_judy, &metric->uuid, sizeof(uuid_t)); - if(unlikely(!sections_judy_pptr || !*sections_judy_pptr)) { - MRG_STATS_DELETE_MISS(mrg, partition); - mrg_index_write_unlock(mrg, partition); - return false; - } + METRIC *metric = *PValue; - mem_before_judyl = JudyLMemUsed(*sections_judy_pptr); - int rc = JudyLDel(sections_judy_pptr, metric->section, PJE0); - mem_after_judyl = JudyLMemUsed(*sections_judy_pptr); - mrg_stats_size_judyl_change(mrg, mem_before_judyl, mem_after_judyl, partition); + if(metric && !metric_acquire(mrg, metric)) + metric = NULL; - if(unlikely(!rc)) { - MRG_STATS_DELETE_MISS(mrg, partition); - mrg_index_write_unlock(mrg, partition); - return false; - } + mrg_index_read_unlock(mrg, partition); - if(!*sections_judy_pptr) { - rc = JudyHSDel(&mrg->index[partition].uuid_judy, &metric->uuid, sizeof(uuid_t), PJE0); - if(unlikely(!rc)) - fatal("DBENGINE METRIC: cannot delete UUID from JudyHS"); - mrg_stats_size_judyhs_removed_uuid(mrg, partition); + if(metric) { + MRG_STATS_SEARCH_HIT(mrg, partition); + return metric; + } } - - MRG_STATS_DELETED_METRIC(mrg, partition); - - mrg_index_write_unlock(mrg, partition); - - aral_freez(mrg->index[partition].aral, metric); - - return true; } // ---------------------------------------------------------------------------- @@ -359,7 +409,7 @@ inline METRIC *mrg_metric_get_and_acquire(MRG *mrg, uuid_t *uuid, Word_t section } inline bool mrg_metric_release_and_delete(MRG *mrg, METRIC *metric) { - return acquired_metric_del(mrg, metric); + return metric_release(mrg, metric, true); } inline METRIC *mrg_metric_dup(MRG *mrg, METRIC *metric) { @@ -367,8 +417,8 @@ inline METRIC *mrg_metric_dup(MRG *mrg, METRIC *metric) { return metric; } -inline bool mrg_metric_release(MRG *mrg, METRIC *metric) { - return metric_release_and_can_be_deleted(mrg, metric); +inline void mrg_metric_release(MRG *mrg, METRIC *metric) { + metric_release(mrg, metric, false); } inline Word_t mrg_metric_id(MRG *mrg __maybe_unused, METRIC *metric) { @@ -394,8 +444,8 @@ inline bool mrg_metric_set_first_time_s(MRG *mrg __maybe_unused, METRIC *metric, return true; } -inline void mrg_metric_expand_retention(MRG *mrg __maybe_unused, METRIC *metric, time_t first_time_s, time_t last_time_s, time_t update_every_s) { - internal_fatal(first_time_s < 0 || last_time_s < 0 || update_every_s < 0, +inline void mrg_metric_expand_retention(MRG *mrg __maybe_unused, METRIC *metric, time_t first_time_s, time_t last_time_s, uint32_t update_every_s) { + internal_fatal(first_time_s < 0 || last_time_s < 0, "DBENGINE METRIC: timestamp is negative"); internal_fatal(first_time_s > max_acceptable_collected_time(), "DBENGINE METRIC: metric first time is in the future"); @@ -425,13 +475,14 @@ inline time_t mrg_metric_get_first_time_s(MRG *mrg __maybe_unused, METRIC *metri return mrg_metric_get_first_time_s_smart(mrg, metric); } -inline void mrg_metric_get_retention(MRG *mrg __maybe_unused, METRIC *metric, time_t *first_time_s, time_t *last_time_s, time_t *update_every_s) { +inline void mrg_metric_get_retention(MRG *mrg __maybe_unused, METRIC *metric, time_t *first_time_s, time_t *last_time_s, uint32_t *update_every_s) { time_t clean = __atomic_load_n(&metric->latest_time_s_clean, __ATOMIC_RELAXED); time_t hot = __atomic_load_n(&metric->latest_time_s_hot, __ATOMIC_RELAXED); *last_time_s = MAX(clean, hot); *first_time_s = mrg_metric_get_first_time_s_smart(mrg, metric); - *update_every_s = __atomic_load_n(&metric->latest_update_every_s, __ATOMIC_RELAXED); + if (update_every_s) + *update_every_s = __atomic_load_n(&metric->latest_update_every_s, __ATOMIC_RELAXED); } inline bool mrg_metric_set_clean_latest_time_s(MRG *mrg __maybe_unused, METRIC *metric, time_t latest_time_s) { @@ -498,8 +549,8 @@ inline bool mrg_metric_zero_disk_retention(MRG *mrg __maybe_unused, METRIC *metr } } while(do_again); - time_t first, last, ue; - mrg_metric_get_retention(mrg, metric, &first, &last, &ue); + time_t first, last; + mrg_metric_get_retention(mrg, metric, &first, &last, NULL); return (first && last && first < last); } @@ -517,6 +568,11 @@ inline bool mrg_metric_set_hot_latest_time_s(MRG *mrg __maybe_unused, METRIC *me return false; } +inline time_t mrg_metric_get_latest_clean_time_s(MRG *mrg __maybe_unused, METRIC *metric) { + time_t clean = __atomic_load_n(&metric->latest_time_s_clean, __ATOMIC_RELAXED); + return clean; +} + inline time_t mrg_metric_get_latest_time_s(MRG *mrg __maybe_unused, METRIC *metric) { time_t clean = __atomic_load_n(&metric->latest_time_s_clean, __ATOMIC_RELAXED); time_t hot = __atomic_load_n(&metric->latest_time_s_hot, __ATOMIC_RELAXED); @@ -524,25 +580,21 @@ inline time_t mrg_metric_get_latest_time_s(MRG *mrg __maybe_unused, METRIC *metr return MAX(clean, hot); } -inline bool mrg_metric_set_update_every(MRG *mrg __maybe_unused, METRIC *metric, time_t update_every_s) { - internal_fatal(update_every_s < 0, "DBENGINE METRIC: timestamp is negative"); - +inline bool mrg_metric_set_update_every(MRG *mrg __maybe_unused, METRIC *metric, uint32_t update_every_s) { if(update_every_s > 0) return set_metric_field_with_condition(metric->latest_update_every_s, update_every_s, true); return false; } -inline bool mrg_metric_set_update_every_s_if_zero(MRG *mrg __maybe_unused, METRIC *metric, time_t update_every_s) { - internal_fatal(update_every_s < 0, "DBENGINE METRIC: timestamp is negative"); - +inline bool mrg_metric_set_update_every_s_if_zero(MRG *mrg __maybe_unused, METRIC *metric, uint32_t update_every_s) { if(update_every_s > 0) return set_metric_field_with_condition(metric->latest_update_every_s, update_every_s, _current <= 0); return false; } -inline time_t mrg_metric_get_update_every_s(MRG *mrg __maybe_unused, METRIC *metric) { +inline uint32_t mrg_metric_get_update_every_s(MRG *mrg __maybe_unused, METRIC *metric) { return __atomic_load_n(&metric->latest_update_every_s, __ATOMIC_RELAXED); } @@ -589,7 +641,7 @@ inline bool mrg_metric_clear_writer(MRG *mrg, METRIC *metric) { inline void mrg_update_metric_retention_and_granularity_by_uuid( MRG *mrg, Word_t section, uuid_t *uuid, time_t first_time_s, time_t last_time_s, - time_t update_every_s, time_t now_s) + uint32_t update_every_s, time_t now_s) { if(unlikely(last_time_s > now_s)) { nd_log_limit_static_global_var(erl, 1, 0); @@ -626,14 +678,35 @@ inline void mrg_update_metric_retention_and_granularity_by_uuid( .section = section, .first_time_s = first_time_s, .last_time_s = last_time_s, - .latest_update_every_s = (uint32_t) update_every_s + .latest_update_every_s = update_every_s }; metric = mrg_metric_add_and_acquire(mrg, entry, &added); } - if (likely(!added)) + struct rrdengine_instance *ctx = (struct rrdengine_instance *) section; + if (likely(!added)) { + uint64_t old_samples = 0; + + if (update_every_s && metric->latest_update_every_s && metric->latest_time_s_clean) + old_samples = (metric->latest_time_s_clean - metric->first_time_s) / metric->latest_update_every_s; + mrg_metric_expand_retention(mrg, metric, first_time_s, last_time_s, update_every_s); + uint64_t new_samples = 0; + if (update_every_s && metric->latest_update_every_s && metric->latest_time_s_clean) + new_samples = (metric->latest_time_s_clean - metric->first_time_s) / metric->latest_update_every_s; + + __atomic_add_fetch(&ctx->atomic.samples, new_samples - old_samples, __ATOMIC_RELAXED); + } + else { + // Newly added + if (update_every_s) { + uint64_t samples = (last_time_s - first_time_s) / update_every_s; + __atomic_add_fetch(&ctx->atomic.samples, samples, __ATOMIC_RELAXED); + } + __atomic_add_fetch(&ctx->atomic.metrics, 1, __ATOMIC_RELAXED); + } + mrg_metric_release(mrg, metric); } diff --git a/database/engine/metric.h b/src/database/engine/metric.h index dbb949301..3bace9057 100644 --- a/database/engine/metric.h +++ b/src/database/engine/metric.h @@ -52,7 +52,7 @@ MRG *mrg_create(ssize_t partitions); void mrg_destroy(MRG *mrg); METRIC *mrg_metric_dup(MRG *mrg, METRIC *metric); -bool mrg_metric_release(MRG *mrg, METRIC *metric); +void mrg_metric_release(MRG *mrg, METRIC *metric); METRIC *mrg_metric_add_and_acquire(MRG *mrg, MRG_ENTRY entry, bool *ret); METRIC *mrg_metric_get_and_acquire(MRG *mrg, uuid_t *uuid, Word_t section); @@ -69,13 +69,14 @@ time_t mrg_metric_get_first_time_s(MRG *mrg, METRIC *metric); bool mrg_metric_set_clean_latest_time_s(MRG *mrg, METRIC *metric, time_t latest_time_s); bool mrg_metric_set_hot_latest_time_s(MRG *mrg, METRIC *metric, time_t latest_time_s); time_t mrg_metric_get_latest_time_s(MRG *mrg, METRIC *metric); +time_t mrg_metric_get_latest_clean_time_s(MRG *mrg, METRIC *metric); -bool mrg_metric_set_update_every(MRG *mrg, METRIC *metric, time_t update_every_s); -bool mrg_metric_set_update_every_s_if_zero(MRG *mrg, METRIC *metric, time_t update_every_s); -time_t mrg_metric_get_update_every_s(MRG *mrg, METRIC *metric); +bool mrg_metric_set_update_every(MRG *mrg, METRIC *metric, uint32_t update_every_s); +bool mrg_metric_set_update_every_s_if_zero(MRG *mrg, METRIC *metric, uint32_t update_every_s); +uint32_t mrg_metric_get_update_every_s(MRG *mrg, METRIC *metric); -void mrg_metric_expand_retention(MRG *mrg, METRIC *metric, time_t first_time_s, time_t last_time_s, time_t update_every_s); -void mrg_metric_get_retention(MRG *mrg, METRIC *metric, time_t *first_time_s, time_t *last_time_s, time_t *update_every_s); +void mrg_metric_expand_retention(MRG *mrg, METRIC *metric, time_t first_time_s, time_t last_time_s, uint32_t update_every_s); +void mrg_metric_get_retention(MRG *mrg, METRIC *metric, time_t *first_time_s, time_t *last_time_s, uint32_t *update_every_s); bool mrg_metric_zero_disk_retention(MRG *mrg __maybe_unused, METRIC *metric); bool mrg_metric_set_writer(MRG *mrg, METRIC *metric); @@ -89,6 +90,6 @@ size_t mrg_aral_overhead(void); void mrg_update_metric_retention_and_granularity_by_uuid( MRG *mrg, Word_t section, uuid_t *uuid, time_t first_time_s, time_t last_time_s, - time_t update_every_s, time_t now_s); + uint32_t update_every_s, time_t now_s); #endif // DBENGINE_METRIC_H diff --git a/database/engine/page.c b/src/database/engine/page.c index b7a393483..13fe90f7f 100644 --- a/database/engine/page.c +++ b/src/database/engine/page.c @@ -111,9 +111,9 @@ void pgd_init_arals(void) // FIXME: add stats pgd_alloc_globals.aral_gorilla_buffer[i] = aral_create( buf, - GORILLA_BUFFER_SIZE, + RRDENG_GORILLA_32BIT_BUFFER_SIZE, 64, - 512 * GORILLA_BUFFER_SIZE, + 512 * RRDENG_GORILLA_32BIT_BUFFER_SIZE, pgc_aral_statistics(), NULL, NULL, false, false); } @@ -165,8 +165,8 @@ PGD *pgd_create(uint8_t type, uint32_t slots) pg->states = PGD_STATE_CREATED_FROM_COLLECTOR; switch (type) { - case PAGE_METRICS: - case PAGE_TIER: { + case RRDENG_PAGE_TYPE_ARRAY_32BIT: + case RRDENG_PAGE_TYPE_ARRAY_TIER1: { uint32_t size = slots * page_type_size[type]; internal_fatal(!size || slots == 1, @@ -176,11 +176,11 @@ PGD *pgd_create(uint8_t type, uint32_t slots) pg->raw.data = pgd_data_aral_alloc(size); break; } - case PAGE_GORILLA_METRICS: { + case RRDENG_PAGE_TYPE_GORILLA_32BIT: { internal_fatal(slots == 1, "DBENGINE: invalid number of slots (%u) or page type (%u)", slots, type); - pg->slots = 8 * GORILLA_BUFFER_SLOTS; + pg->slots = 8 * RRDENG_GORILLA_32BIT_BUFFER_SLOTS; // allocate new gorilla writer pg->gorilla.aral_index = gettid() % 4; @@ -188,16 +188,19 @@ PGD *pgd_create(uint8_t type, uint32_t slots) // allocate new gorilla buffer gorilla_buffer_t *gbuf = aral_mallocz(pgd_alloc_globals.aral_gorilla_buffer[pg->gorilla.aral_index]); - memset(gbuf, 0, GORILLA_BUFFER_SIZE); + memset(gbuf, 0, RRDENG_GORILLA_32BIT_BUFFER_SIZE); global_statistics_gorilla_buffer_add_hot(); - *pg->gorilla.writer = gorilla_writer_init(gbuf, GORILLA_BUFFER_SLOTS); + *pg->gorilla.writer = gorilla_writer_init(gbuf, RRDENG_GORILLA_32BIT_BUFFER_SLOTS); pg->gorilla.num_buffers = 1; break; } default: - fatal("Unknown page type: %uc", type); + netdata_log_error("%s() - Unknown page type: %uc", __FUNCTION__, type); + aral_freez(pgd_alloc_globals.aral_pgd, pg); + pg = PGD_EMPTY; + break; } return pg; @@ -219,8 +222,8 @@ PGD *pgd_create_from_disk_data(uint8_t type, void *base, uint32_t size) switch (type) { - case PAGE_METRICS: - case PAGE_TIER: + case RRDENG_PAGE_TYPE_ARRAY_32BIT: + case RRDENG_PAGE_TYPE_ARRAY_TIER1: pg->raw.size = size; pg->used = size / page_type_size[type]; pg->slots = pg->used; @@ -228,10 +231,11 @@ PGD *pgd_create_from_disk_data(uint8_t type, void *base, uint32_t size) pg->raw.data = pgd_data_aral_alloc(size); memcpy(pg->raw.data, base, size); break; - case PAGE_GORILLA_METRICS: + case RRDENG_PAGE_TYPE_GORILLA_32BIT: internal_fatal(size == 0, "Asked to create page with 0 data!!!"); internal_fatal(size % sizeof(uint32_t), "Unaligned gorilla buffer size"); - internal_fatal(size % GORILLA_BUFFER_SIZE, "Expected size to be a multiple of %zu-bytes", GORILLA_BUFFER_SIZE); + internal_fatal(size % RRDENG_GORILLA_32BIT_BUFFER_SIZE, "Expected size to be a multiple of %zu-bytes", + RRDENG_GORILLA_32BIT_BUFFER_SIZE); pg->raw.data = mallocz(size); pg->raw.size = size; @@ -246,7 +250,10 @@ PGD *pgd_create_from_disk_data(uint8_t type, void *base, uint32_t size) pg->slots = pg->used; break; default: - fatal("Unknown page type: %uc", type); + netdata_log_error("%s() - Unknown page type: %uc", __FUNCTION__, type); + aral_freez(pgd_alloc_globals.aral_pgd, pg); + pg = PGD_EMPTY; + break; } return pg; @@ -262,11 +269,11 @@ void pgd_free(PGD *pg) switch (pg->type) { - case PAGE_METRICS: - case PAGE_TIER: + case RRDENG_PAGE_TYPE_ARRAY_32BIT: + case RRDENG_PAGE_TYPE_ARRAY_TIER1: pgd_data_aral_free(pg->raw.data, pg->raw.size); break; - case PAGE_GORILLA_METRICS: { + case RRDENG_PAGE_TYPE_GORILLA_32BIT: { if (pg->states & PGD_STATE_CREATED_FROM_DISK) { internal_fatal(pg->raw.data == NULL, "Tried to free gorilla PGD loaded from disk with NULL data"); @@ -306,7 +313,8 @@ void pgd_free(PGD *pg) break; } default: - fatal("Unknown page type: %uc", pg->type); + netdata_log_error("%s() - Unknown page type: %uc", __FUNCTION__, pg->type); + break; } aral_freez(pgd_alloc_globals.aral_pgd, pg); @@ -358,20 +366,21 @@ uint32_t pgd_memory_footprint(PGD *pg) size_t footprint = 0; switch (pg->type) { - case PAGE_METRICS: - case PAGE_TIER: + case RRDENG_PAGE_TYPE_ARRAY_32BIT: + case RRDENG_PAGE_TYPE_ARRAY_TIER1: footprint = sizeof(PGD) + pg->raw.size; break; - case PAGE_GORILLA_METRICS: { + case RRDENG_PAGE_TYPE_GORILLA_32BIT: { if (pg->states & PGD_STATE_CREATED_FROM_DISK) footprint = sizeof(PGD) + pg->raw.size; else - footprint = sizeof(PGD) + sizeof(gorilla_writer_t) + (pg->gorilla.num_buffers * GORILLA_BUFFER_SIZE); + footprint = sizeof(PGD) + sizeof(gorilla_writer_t) + (pg->gorilla.num_buffers * RRDENG_GORILLA_32BIT_BUFFER_SIZE); break; } default: - fatal("Unknown page type: %uc", pg->type); + netdata_log_error("%s() - Unknown page type: %uc", __FUNCTION__, pg->type); + break; } return footprint; @@ -385,15 +394,15 @@ uint32_t pgd_disk_footprint(PGD *pg) size_t size = 0; switch (pg->type) { - case PAGE_METRICS: - case PAGE_TIER: { + case RRDENG_PAGE_TYPE_ARRAY_32BIT: + case RRDENG_PAGE_TYPE_ARRAY_TIER1: { uint32_t used_size = pg->used * page_type_size[pg->type]; internal_fatal(used_size > pg->raw.size, "Wrong disk footprint page size"); size = used_size; break; } - case PAGE_GORILLA_METRICS: { + case RRDENG_PAGE_TYPE_GORILLA_32BIT: { if (pg->states & PGD_STATE_CREATED_FROM_COLLECTOR || pg->states & PGD_STATE_SCHEDULED_FOR_FLUSHING || pg->states & PGD_STATE_FLUSHED_TO_DISK) @@ -404,7 +413,7 @@ uint32_t pgd_disk_footprint(PGD *pg) internal_fatal(pg->gorilla.num_buffers == 0, "Gorilla writer does not have any buffers"); - size = pg->gorilla.num_buffers * GORILLA_BUFFER_SIZE; + size = pg->gorilla.num_buffers * RRDENG_GORILLA_32BIT_BUFFER_SIZE; if (pg->states & PGD_STATE_CREATED_FROM_COLLECTOR) { global_statistics_tier0_disk_compressed_bytes(gorilla_writer_nbytes(pg->gorilla.writer)); @@ -419,7 +428,8 @@ uint32_t pgd_disk_footprint(PGD *pg) break; } default: - fatal("Unknown page type: %uc", pg->type); + netdata_log_error("%s() - Unknown page type: %uc", __FUNCTION__, pg->type); + break; } internal_fatal(pg->states & PGD_STATE_CREATED_FROM_DISK, @@ -434,11 +444,11 @@ void pgd_copy_to_extent(PGD *pg, uint8_t *dst, uint32_t dst_size) pgd_disk_footprint(pg), dst_size); switch (pg->type) { - case PAGE_METRICS: - case PAGE_TIER: + case RRDENG_PAGE_TYPE_ARRAY_32BIT: + case RRDENG_PAGE_TYPE_ARRAY_TIER1: memcpy(dst, pg->raw.data, dst_size); break; - case PAGE_GORILLA_METRICS: { + case RRDENG_PAGE_TYPE_GORILLA_32BIT: { if ((pg->states & PGD_STATE_SCHEDULED_FOR_FLUSHING) == 0) fatal("Copying to extent is supported only for PGDs that are scheduled for flushing."); @@ -456,7 +466,8 @@ void pgd_copy_to_extent(PGD *pg, uint8_t *dst, uint32_t dst_size) break; } default: - fatal("Unknown page type: %uc", pg->type); + netdata_log_error("%s() - Unknown page type: %uc", __FUNCTION__, pg->type); + break; } pg->states = PGD_STATE_FLUSHED_TO_DISK; @@ -490,7 +501,7 @@ void pgd_append_point(PGD *pg, fatal("Data collection on page already scheduled for flushing"); switch (pg->type) { - case PAGE_METRICS: { + case RRDENG_PAGE_TYPE_ARRAY_32BIT: { storage_number *tier0_metric_data = (storage_number *)pg->raw.data; storage_number t = pack_storage_number(n, flags); tier0_metric_data[pg->used++] = t; @@ -500,7 +511,7 @@ void pgd_append_point(PGD *pg, break; } - case PAGE_TIER: { + case RRDENG_PAGE_TYPE_ARRAY_TIER1: { storage_number_tier1_t *tier12_metric_data = (storage_number_tier1_t *)pg->raw.data; storage_number_tier1_t t; t.sum_value = (float) n; @@ -515,7 +526,7 @@ void pgd_append_point(PGD *pg, break; } - case PAGE_GORILLA_METRICS: { + case RRDENG_PAGE_TYPE_GORILLA_32BIT: { pg->used++; storage_number t = pack_storage_number(n, flags); @@ -525,9 +536,9 @@ void pgd_append_point(PGD *pg, bool ok = gorilla_writer_write(pg->gorilla.writer, t); if (!ok) { gorilla_buffer_t *new_buffer = aral_mallocz(pgd_alloc_globals.aral_gorilla_buffer[pg->gorilla.aral_index]); - memset(new_buffer, 0, GORILLA_BUFFER_SIZE); + memset(new_buffer, 0, RRDENG_GORILLA_32BIT_BUFFER_SIZE); - gorilla_writer_add_buffer(pg->gorilla.writer, new_buffer, GORILLA_BUFFER_SLOTS); + gorilla_writer_add_buffer(pg->gorilla.writer, new_buffer, RRDENG_GORILLA_32BIT_BUFFER_SLOTS); pg->gorilla.num_buffers += 1; global_statistics_gorilla_buffer_add_hot(); @@ -537,7 +548,7 @@ void pgd_append_point(PGD *pg, break; } default: - fatal("DBENGINE: unknown page type id %d", pg->type); + netdata_log_error("%s() - Unknown page type: %uc", __FUNCTION__, pg->type); break; } } @@ -550,11 +561,11 @@ static void pgdc_seek(PGDC *pgdc, uint32_t position) PGD *pg = pgdc->pgd; switch (pg->type) { - case PAGE_METRICS: - case PAGE_TIER: + case RRDENG_PAGE_TYPE_ARRAY_32BIT: + case RRDENG_PAGE_TYPE_ARRAY_TIER1: pgdc->slots = pgdc->pgd->used; break; - case PAGE_GORILLA_METRICS: { + case RRDENG_PAGE_TYPE_GORILLA_32BIT: { if (pg->states & PGD_STATE_CREATED_FROM_DISK) { pgdc->slots = pgdc->pgd->slots; pgdc->gr = gorilla_reader_init((void *) pg->raw.data); @@ -588,7 +599,7 @@ static void pgdc_seek(PGDC *pgdc, uint32_t position) break; } default: - fatal("DBENGINE: unknown page type id %d", pg->type); + netdata_log_error("%s() - Unknown page type: %uc", __FUNCTION__, pg->type); break; } } @@ -612,7 +623,7 @@ void pgdc_reset(PGDC *pgdc, PGD *pgd, uint32_t position) pgdc_seek(pgdc, position); } -bool pgdc_get_next_point(PGDC *pgdc, uint32_t expected_position, STORAGE_POINT *sp) +bool pgdc_get_next_point(PGDC *pgdc, uint32_t expected_position __maybe_unused, STORAGE_POINT *sp) { if (!pgdc->pgd || pgdc->pgd == PGD_EMPTY || pgdc->position >= pgdc->slots) { @@ -624,7 +635,7 @@ bool pgdc_get_next_point(PGDC *pgdc, uint32_t expected_position, STORAGE_POINT * switch (pgdc->pgd->type) { - case PAGE_METRICS: { + case RRDENG_PAGE_TYPE_ARRAY_32BIT: { storage_number *array = (storage_number *) pgdc->pgd->raw.data; storage_number n = array[pgdc->position++]; @@ -635,7 +646,7 @@ bool pgdc_get_next_point(PGDC *pgdc, uint32_t expected_position, STORAGE_POINT * return true; } - case PAGE_TIER: { + case RRDENG_PAGE_TYPE_ARRAY_TIER1: { storage_number_tier1_t *array = (storage_number_tier1_t *) pgdc->pgd->raw.data; storage_number_tier1_t n = array[pgdc->position++]; @@ -648,7 +659,7 @@ bool pgdc_get_next_point(PGDC *pgdc, uint32_t expected_position, STORAGE_POINT * return true; } - case PAGE_GORILLA_METRICS: { + case RRDENG_PAGE_TYPE_GORILLA_32BIT: { pgdc->position++; uint32_t n = 666666666; @@ -668,7 +679,8 @@ bool pgdc_get_next_point(PGDC *pgdc, uint32_t expected_position, STORAGE_POINT * static bool logged = false; if (!logged) { - netdata_log_error("DBENGINE: unknown page type %d found. Cannot decode it. Ignoring its metrics.", pgd_type(pgdc->pgd)); + netdata_log_error("DBENGINE: unknown page type %"PRIu32" found. Cannot decode it. Ignoring its metrics.", + pgd_type(pgdc->pgd)); logged = true; } diff --git a/database/engine/page.h b/src/database/engine/page.h index 32c87c580..32c87c580 100644 --- a/database/engine/page.h +++ b/src/database/engine/page.h diff --git a/database/engine/page_test.cc b/src/database/engine/page_test.cc index d61299bc4..d61299bc4 100644 --- a/database/engine/page_test.cc +++ b/src/database/engine/page_test.cc diff --git a/database/engine/page_test.h b/src/database/engine/page_test.h index 30837f0ab..30837f0ab 100644 --- a/database/engine/page_test.h +++ b/src/database/engine/page_test.h diff --git a/database/engine/pagecache.c b/src/database/engine/pagecache.c index dab9cdd0d..452fdc50b 100644 --- a/database/engine/pagecache.c +++ b/src/database/engine/pagecache.c @@ -222,7 +222,7 @@ static size_t get_page_list_from_pgc(PGC *cache, METRIC *metric, struct rrdengin Word_t metric_id = mrg_metric_id(main_mrg, metric); time_t now_s = wanted_start_time_s; - time_t dt_s = mrg_metric_get_update_every_s(main_mrg, metric); + uint32_t dt_s = mrg_metric_get_update_every_s(main_mrg, metric); if(!dt_s) dt_s = default_rrd_update_every; @@ -246,7 +246,7 @@ static size_t get_page_list_from_pgc(PGC *cache, METRIC *metric, struct rrdengin time_t page_start_time_s = pgc_page_start_time_s(page); time_t page_end_time_s = pgc_page_end_time_s(page); - time_t page_update_every_s = pgc_page_update_every_s(page); + uint32_t page_update_every_s = pgc_page_update_every_s(page); if(!page_update_every_s) page_update_every_s = dt_s; @@ -282,7 +282,7 @@ static size_t get_page_list_from_pgc(PGC *cache, METRIC *metric, struct rrdengin pd->metric_id = metric_id; pd->first_time_s = page_start_time_s; pd->last_time_s = page_end_time_s; - pd->update_every_s = (uint32_t) page_update_every_s; + pd->update_every_s = page_update_every_s; pd->page = (open_cache_mode) ? NULL : page; pd->status |= tags; @@ -332,8 +332,8 @@ static size_t get_page_list_from_pgc(PGC *cache, METRIC *metric, struct rrdengin static void pgc_inject_gap(struct rrdengine_instance *ctx, METRIC *metric, time_t start_time_s, time_t end_time_s) { - time_t db_first_time_s, db_last_time_s, db_update_every_s; - mrg_metric_get_retention(main_mrg, metric, &db_first_time_s, &db_last_time_s, &db_update_every_s); + time_t db_first_time_s, db_last_time_s; + mrg_metric_get_retention(main_mrg, metric, &db_first_time_s, &db_last_time_s, NULL); if(is_page_in_time_range(start_time_s, end_time_s, db_first_time_s, db_last_time_s) != PAGE_IS_IN_RANGE) return; @@ -547,7 +547,7 @@ static size_t get_page_list_from_journal_v2(struct rrdengine_instance *ctx, METR if(prc == PAGE_IS_IN_THE_FUTURE) break; - time_t page_update_every_s = page_entry_in_journal->update_every_s; + uint32_t page_update_every_s = page_entry_in_journal->update_every_s; size_t page_length = page_entry_in_journal->page_length; if(datafile_acquire(datafile, DATAFILE_ACQUIRE_OPEN_CACHE)) { //for open cache item @@ -567,7 +567,7 @@ static size_t get_page_list_from_journal_v2(struct rrdengine_instance *ctx, METR .metric_id = metric_id, .start_time_s = page_first_time_s, .end_time_s = page_last_time_s, - .update_every_s = (uint32_t) page_update_every_s, + .update_every_s = page_update_every_s, .data = datafile, .size = 0, .custom_data = (uint8_t *) &ei, @@ -845,7 +845,7 @@ struct pgc_page *pg_cache_lookup_next( struct rrdengine_instance *ctx, PDC *pdc, time_t now_s, - time_t last_update_every_s, + uint32_t last_update_every_s, size_t *entries ) { if (unlikely(!pdc)) @@ -905,7 +905,7 @@ struct pgc_page *pg_cache_lookup_next( time_t page_start_time_s = pgc_page_start_time_s(page); time_t page_end_time_s = pgc_page_end_time_s(page); - time_t page_update_every_s = pgc_page_update_every_s(page); + uint32_t page_update_every_s = pgc_page_update_every_s(page); if(unlikely(page_start_time_s == INVALID_TIME || page_end_time_s == INVALID_TIME)) { __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_zero_time_skipped, 1, __ATOMIC_RELAXED); @@ -918,7 +918,7 @@ struct pgc_page *pg_cache_lookup_next( if (unlikely(page_update_every_s <= 0 || page_update_every_s > 86400)) { __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_invalid_update_every_fixed, 1, __ATOMIC_RELAXED); page_update_every_s = pgc_page_fix_update_every(page, last_update_every_s); - pd->update_every_s = (uint32_t) page_update_every_s; + pd->update_every_s = page_update_every_s; } size_t entries_by_size = pgd_slots_used(pgc_page_data(page)); @@ -983,7 +983,7 @@ struct pgc_page *pg_cache_lookup_next( return page; } -void pgc_open_add_hot_page(Word_t section, Word_t metric_id, time_t start_time_s, time_t end_time_s, time_t update_every_s, +void pgc_open_add_hot_page(Word_t section, Word_t metric_id, time_t start_time_s, time_t end_time_s, uint32_t update_every_s, struct rrdengine_datafile *datafile, uint64_t extent_offset, unsigned extent_size, uint32_t page_length) { if(!datafile_acquire(datafile, DATAFILE_ACQUIRE_OPEN_CACHE)) // for open cache item @@ -1003,7 +1003,7 @@ void pgc_open_add_hot_page(Word_t section, Word_t metric_id, time_t start_time_s .metric_id = metric_id, .start_time_s = start_time_s, .end_time_s = end_time_s, - .update_every_s = (uint32_t) update_every_s, + .update_every_s = update_every_s, .size = 0, .data = datafile, .custom_data = (uint8_t *) &ext_io_data, diff --git a/database/engine/pagecache.h b/src/database/engine/pagecache.h index dbcbea53a..103d36484 100644 --- a/database/engine/pagecache.h +++ b/src/database/engine/pagecache.h @@ -14,8 +14,6 @@ extern struct pgc *extent_cache; struct rrdengine_instance; #define INVALID_TIME (0) -#define MAX_PAGE_CACHE_FETCH_RETRIES (3) -#define PAGE_CACHE_FETCH_WAIT_TIMEOUT (3) extern struct rrdeng_cache_efficiency_stats rrdeng_cache_efficiency_stats; @@ -54,9 +52,9 @@ struct page_details_control; void rrdeng_prep_wait(struct page_details_control *pdc); void rrdeng_prep_query(struct page_details_control *pdc, bool worker); void pg_cache_preload(struct rrdeng_query_handle *handle); -struct pgc_page *pg_cache_lookup_next(struct rrdengine_instance *ctx, struct page_details_control *pdc, time_t now_s, time_t last_update_every_s, size_t *entries); +struct pgc_page *pg_cache_lookup_next(struct rrdengine_instance *ctx, struct page_details_control *pdc, time_t now_s, uint32_t last_update_every_s, size_t *entries); void pgc_and_mrg_initialize(void); -void pgc_open_add_hot_page(Word_t section, Word_t metric_id, time_t start_time_s, time_t end_time_s, time_t update_every_s, struct rrdengine_datafile *datafile, uint64_t extent_offset, unsigned extent_size, uint32_t page_length); +void pgc_open_add_hot_page(Word_t section, Word_t metric_id, time_t start_time_s, time_t end_time_s, uint32_t update_every_s, struct rrdengine_datafile *datafile, uint64_t extent_offset, unsigned extent_size, uint32_t page_length); #endif /* NETDATA_PAGECACHE_H */ diff --git a/database/engine/pdc.c b/src/database/engine/pdc.c index 5fe205e64..79a424b77 100644 --- a/database/engine/pdc.c +++ b/src/database/engine/pdc.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #define NETDATA_RRD_INTERNALS #include "pdc.h" +#include "dbengine-compression.h" struct extent_page_details_list { uv_file file; @@ -628,24 +629,25 @@ void collect_page_flags_to_buffer(BUFFER *wb, RRDENG_COLLECT_PAGE_FLAGS flags) { buffer_strcat(wb, "STEP_UNALIGNED"); } -inline VALIDATED_PAGE_DESCRIPTOR validate_extent_page_descr(const struct rrdeng_extent_page_descr *descr, time_t now_s, time_t overwrite_zero_update_every_s, bool have_read_error) { +inline 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) { time_t start_time_s = (time_t) (descr->start_time_ut / USEC_PER_SEC); - time_t end_time_s; - size_t entries; + time_t end_time_s = 0; + size_t entries = 0; switch (descr->type) { - case PAGE_METRICS: - case PAGE_TIER: + case RRDENG_PAGE_TYPE_ARRAY_32BIT: + case RRDENG_PAGE_TYPE_ARRAY_TIER1: end_time_s = descr->end_time_ut / USEC_PER_SEC; entries = 0; break; - case PAGE_GORILLA_METRICS: + case RRDENG_PAGE_TYPE_GORILLA_32BIT: end_time_s = start_time_s + descr->gorilla.delta_time_s; entries = descr->gorilla.entries; break; default: - fatal("Unknown page type: %uc\n", descr->type); + // Nothing to do. Validate page will notify the user. + break; } return validate_page( @@ -666,29 +668,30 @@ VALIDATED_PAGE_DESCRIPTOR validate_page( uuid_t *uuid, time_t start_time_s, time_t end_time_s, - time_t update_every_s, // can be zero, if unknown + uint32_t update_every_s, // can be zero, if unknown size_t page_length, uint8_t page_type, size_t entries, // can be zero, if unknown time_t now_s, // can be zero, to disable future timestamp check - time_t overwrite_zero_update_every_s, // can be zero, if unknown + uint32_t overwrite_zero_update_every_s, // can be zero, if unknown bool have_read_error, const char *msg, - RRDENG_COLLECT_PAGE_FLAGS flags) { - + RRDENG_COLLECT_PAGE_FLAGS flags) +{ VALIDATED_PAGE_DESCRIPTOR vd = { .start_time_s = start_time_s, .end_time_s = end_time_s, .update_every_s = update_every_s, .page_length = page_length, + .point_size = page_type_size[page_type], .type = page_type, .is_valid = true, }; - vd.point_size = page_type_size[vd.type]; + bool known_page_type = true; switch (page_type) { - case PAGE_METRICS: - case PAGE_TIER: + case RRDENG_PAGE_TYPE_ARRAY_32BIT: + case RRDENG_PAGE_TYPE_ARRAY_TIER1: // always calculate entries by size vd.entries = page_entries_by_size(vd.page_length, vd.point_size); @@ -696,13 +699,13 @@ VALIDATED_PAGE_DESCRIPTOR validate_page( if(!entries) entries = vd.entries; break; - case PAGE_GORILLA_METRICS: + case RRDENG_PAGE_TYPE_GORILLA_32BIT: internal_fatal(entries == 0, "0 number of entries found on gorilla page"); vd.entries = entries; break; default: - // TODO: should set vd.is_valid false instead? - fatal("Unknown page type: %uc", page_type); + known_page_type = false; + break; } // allow to be called without update every (when loading pages from disk) @@ -723,16 +726,16 @@ VALIDATED_PAGE_DESCRIPTOR validate_page( // If gorilla can not compress the data we might end up needing slightly more // than 4KiB. However, gorilla pages extend the page length by increments of // 512 bytes. - max_page_length += ((page_type == PAGE_GORILLA_METRICS) * GORILLA_BUFFER_SIZE); + max_page_length += ((page_type == RRDENG_PAGE_TYPE_GORILLA_32BIT) * RRDENG_GORILLA_32BIT_BUFFER_SIZE); - if( have_read_error || + if (!known_page_type || + have_read_error || vd.page_length == 0 || vd.page_length > max_page_length || vd.start_time_s > vd.end_time_s || (now_s && vd.end_time_s > now_s) || vd.start_time_s <= 0 || vd.end_time_s <= 0 || - vd.update_every_s < 0 || (vd.start_time_s == vd.end_time_s && vd.entries > 1) || (vd.update_every_s == 0 && vd.entries > 1)) { @@ -791,13 +794,13 @@ VALIDATED_PAGE_DESCRIPTOR validate_page( nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR, #endif "DBENGINE: metric '%s' %s invalid page of type %u " - "from %ld to %ld (now %ld), update every %ld, page length %zu, entries %zu (flags: %s)", + "from %ld to %ld (now %ld), update every %u, page length %zu, entries %zu (flags: %s)", uuid_str, msg, vd.type, vd.start_time_s, vd.end_time_s, now_s, vd.update_every_s, vd.page_length, vd.entries, wb?buffer_tostring(wb):"" ); } else { - const char *err_valid = (vd.is_valid) ? "" : "found invalid, "; + const char *err_valid = ""; const char *err_start = (vd.start_time_s == start_time_s) ? "" : "start time updated, "; const char *err_end = (vd.end_time_s == end_time_s) ? "" : "end time updated, "; const char *err_update = (vd.update_every_s == update_every_s) ? "" : "update every updated, "; @@ -811,9 +814,9 @@ VALIDATED_PAGE_DESCRIPTOR validate_page( nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR, #endif "DBENGINE: metric '%s' %s page of type %u " - "from %ld to %ld (now %ld), update every %ld, page length %zu, entries %zu (flags: %s), " + "from %ld to %ld (now %ld), update every %u, page length %zu, entries %zu (flags: %s), " "found inconsistent - the right is " - "from %ld to %ld, update every %ld, page length %zu, entries %zu: " + "from %ld to %ld, update every %u, page length %zu, entries %zu: " "%s%s%s%s%s%s%s", uuid_str, msg, vd.type, start_time_s, end_time_s, now_s, update_every_s, page_length, entries, wb?buffer_tostring(wb):"", @@ -871,11 +874,11 @@ static void epdl_extent_loading_error_log(struct rrdengine_instance *ctx, EPDL * if (descr) { start_time_s = (time_t)(descr->start_time_ut / USEC_PER_SEC); switch (descr->type) { - case PAGE_METRICS: - case PAGE_TIER: + case RRDENG_PAGE_TYPE_ARRAY_32BIT: + case RRDENG_PAGE_TYPE_ARRAY_TIER1: end_time_s = (time_t)(descr->end_time_ut / USEC_PER_SEC); break; - case PAGE_GORILLA_METRICS: + case RRDENG_PAGE_TYPE_GORILLA_32BIT: end_time_s = (time_t) start_time_s + (descr->gorilla.delta_time_s); break; } @@ -938,7 +941,6 @@ static bool epdl_populate_pages_from_extent_data( PDC_PAGE_STATUS tags, bool cached_extent) { - int ret; unsigned i, count; void *uncompressed_buf = NULL; uint32_t payload_length, payload_offset, trailer_offset, uncompressed_payload_length = 0; @@ -973,18 +975,17 @@ static bool epdl_populate_pages_from_extent_data( if( !can_use_data || count < 1 || count > MAX_PAGES_PER_EXTENT || - (header->compression_algorithm != RRD_NO_COMPRESSION && header->compression_algorithm != RRD_LZ4) || + !dbengine_valid_compression_algorithm(header->compression_algorithm) || (payload_length != trailer_offset - payload_offset) || (data_length != payload_offset + payload_length + sizeof(*trailer)) - ) { + ) { epdl_extent_loading_error_log(ctx, epdl, NULL, "header is INVALID"); return false; } crc = crc32(0L, Z_NULL, 0); crc = crc32(crc, data, epdl->extent_size - sizeof(*trailer)); - ret = crc32cmp(trailer->checksum, crc); - if (unlikely(ret)) { + if (unlikely(crc32cmp(trailer->checksum, crc))) { ctx_io_error(ctx); have_read_error = true; epdl_extent_loading_error_log(ctx, epdl, NULL, "CRC32 checksum FAILED"); @@ -993,14 +994,15 @@ static bool epdl_populate_pages_from_extent_data( if(worker) worker_is_busy(UV_EVENT_DBENGINE_EXTENT_DECOMPRESSION); - if (likely(!have_read_error && RRD_NO_COMPRESSION != header->compression_algorithm)) { + if (likely(!have_read_error && RRDENG_COMPRESSION_NONE != header->compression_algorithm)) { // find the uncompressed extent size uncompressed_payload_length = 0; for (i = 0; i < count; ++i) { size_t page_length = header->descr[i].page_length; - if (page_length > RRDENG_BLOCK_SIZE && (header->descr[i].type != PAGE_GORILLA_METRICS || - (header->descr[i].type == PAGE_GORILLA_METRICS && - (page_length - RRDENG_BLOCK_SIZE) % GORILLA_BUFFER_SIZE))) { + if (page_length > RRDENG_BLOCK_SIZE && + (header->descr[i].type != RRDENG_PAGE_TYPE_GORILLA_32BIT || + (header->descr[i].type == RRDENG_PAGE_TYPE_GORILLA_32BIT && + (page_length - RRDENG_BLOCK_SIZE) % RRDENG_GORILLA_32BIT_BUFFER_SIZE))) { have_read_error = true; break; } @@ -1015,11 +1017,16 @@ static bool epdl_populate_pages_from_extent_data( eb = extent_buffer_get(uncompressed_payload_length); uncompressed_buf = eb->data; - ret = LZ4_decompress_safe(data + payload_offset, uncompressed_buf, - (int) payload_length, (int) uncompressed_payload_length); + size_t bytes = dbengine_decompress(uncompressed_buf, data + payload_offset, + uncompressed_payload_length, payload_length, + header->compression_algorithm); - __atomic_add_fetch(&ctx->stats.before_decompress_bytes, payload_length, __ATOMIC_RELAXED); - __atomic_add_fetch(&ctx->stats.after_decompress_bytes, ret, __ATOMIC_RELAXED); + if(!bytes) + have_read_error = true; + else { + __atomic_add_fetch(&ctx->stats.before_decompress_bytes, payload_length, __ATOMIC_RELAXED); + __atomic_add_fetch(&ctx->stats.after_decompress_bytes, bytes, __ATOMIC_RELAXED); + } } } @@ -1075,7 +1082,7 @@ static bool epdl_populate_pages_from_extent_data( stats_load_invalid_page++; } else { - if (RRD_NO_COMPRESSION == header->compression_algorithm) { + if (RRDENG_COMPRESSION_NONE == header->compression_algorithm) { pgd = pgd_create_from_disk_data(header->descr[i].type, data + payload_offset + page_offset, vd.page_length); diff --git a/database/engine/pdc.h b/src/database/engine/pdc.h index 9bae39ade..9bae39ade 100644 --- a/database/engine/pdc.h +++ b/src/database/engine/pdc.h diff --git a/database/engine/rrddiskprotocol.h b/src/database/engine/rrddiskprotocol.h index 86b41f0b3..dc1a4c980 100644 --- a/database/engine/rrddiskprotocol.h +++ b/src/database/engine/rrddiskprotocol.h @@ -19,13 +19,16 @@ #define UUID_SZ (16) #define CHECKSUM_SZ (4) /* CRC32 */ -#define RRD_NO_COMPRESSION (0) -#define RRD_LZ4 (1) +#define RRDENG_COMPRESSION_NONE (0) +#define RRDENG_COMPRESSION_LZ4 (1) +#define RRDENG_COMPRESSION_ZSTD (2) #define RRDENG_DF_SB_PADDING_SZ (RRDENG_BLOCK_SIZE - (RRDENG_MAGIC_SZ + RRDENG_VER_SZ + sizeof(uint8_t))) + /* * Data file persistent super-block */ + struct rrdeng_df_sb { char magic_number[RRDENG_MAGIC_SZ]; char version[RRDENG_VER_SZ]; @@ -36,10 +39,11 @@ struct rrdeng_df_sb { /* * Page types */ -#define PAGE_METRICS (0) -#define PAGE_TIER (1) -#define PAGE_GORILLA_METRICS (2) -#define PAGE_TYPE_MAX 2 // Maximum page type (inclusive) + +#define RRDENG_PAGE_TYPE_ARRAY_32BIT (0) +#define RRDENG_PAGE_TYPE_ARRAY_TIER1 (1) +#define RRDENG_PAGE_TYPE_GORILLA_32BIT (2) +#define RRDENG_PAGE_TYPE_MAX (2) // Maximum page type (inclusive) /* * Data file page descriptor diff --git a/database/engine/rrdengine.c b/src/database/engine/rrdengine.c index b82cc1ad1..7b2137436 100644 --- a/database/engine/rrdengine.c +++ b/src/database/engine/rrdengine.c @@ -3,6 +3,7 @@ #include "rrdengine.h" #include "pdc.h" +#include "dbengine-compression.h" rrdeng_stats_t global_io_errors = 0; rrdeng_stats_t global_fs_errors = 0; @@ -229,7 +230,7 @@ static void after_work_standard_callback(uv_work_t* req, int status) { worker_is_idle(); } -static bool work_dispatch(struct rrdengine_instance *ctx, void *data, struct completion *completion, enum rrdeng_opcode opcode, work_cb work_cb, after_work_cb after_work_cb) { +static bool work_dispatch(struct rrdengine_instance *ctx, void *data, struct completion *completion, enum rrdeng_opcode opcode, work_cb do_work_cb, after_work_cb do_after_work_cb) { struct rrdeng_work *work_request = NULL; internal_fatal(rrdeng_main.tid != gettid(), "work_dispatch() can only be run from the event loop thread"); @@ -240,8 +241,8 @@ static bool work_dispatch(struct rrdengine_instance *ctx, void *data, struct com work_request->ctx = ctx; work_request->data = data; work_request->completion = completion; - work_request->work_cb = work_cb; - work_request->after_work_cb = after_work_cb; + work_request->work_cb = do_work_cb; + work_request->after_work_cb = do_after_work_cb; work_request->opcode = opcode; if(uv_queue_work(&rrdeng_main.loop, &work_request->req, work_standard_worker, after_work_standard_callback)) { @@ -772,13 +773,10 @@ static struct rrdengine_datafile *get_datafile_to_write_extent(struct rrdengine_ */ static struct extent_io_descriptor *datafile_extent_build(struct rrdengine_instance *ctx, struct page_descr_with_data *base, struct completion *completion) { int ret; - int compressed_size, max_compressed_size = 0; unsigned i, count, size_bytes, pos, real_io_size; - uint32_t uncompressed_payload_length, payload_offset; + uint32_t uncompressed_payload_length, max_compressed_size, payload_offset; struct page_descr_with_data *descr, *eligible_pages[MAX_PAGES_PER_EXTENT]; struct extent_io_descriptor *xt_io_descr; - struct extent_buffer *eb = NULL; - void *compressed_buf = NULL; Word_t Index; uint8_t compression_algorithm = ctx->config.global_compress_alg; struct rrdengine_datafile *datafile; @@ -807,20 +805,8 @@ static struct extent_io_descriptor *datafile_extent_build(struct rrdengine_insta xt_io_descr = extent_io_descriptor_get(); xt_io_descr->ctx = ctx; payload_offset = sizeof(*header) + count * sizeof(header->descr[0]); - switch (compression_algorithm) { - case RRD_NO_COMPRESSION: - size_bytes = payload_offset + uncompressed_payload_length + sizeof(*trailer); - break; - - default: /* Compress */ - fatal_assert(uncompressed_payload_length < LZ4_MAX_INPUT_SIZE); - max_compressed_size = LZ4_compressBound(uncompressed_payload_length); - eb = extent_buffer_get(max_compressed_size); - compressed_buf = eb->data; - size_bytes = payload_offset + MAX(uncompressed_payload_length, (unsigned)max_compressed_size) + sizeof(*trailer); - break; - } - + max_compressed_size = dbengine_max_compressed_size(uncompressed_payload_length, compression_algorithm); + size_bytes = payload_offset + MAX(uncompressed_payload_length, max_compressed_size) + sizeof(*trailer); ret = posix_memalign((void *)&xt_io_descr->buf, RRDFILE_ALIGNMENT, ALIGN_BYTES_CEILING(size_bytes)); if (unlikely(ret)) { fatal("DBENGINE: posix_memalign:%s", strerror(ret)); @@ -832,7 +818,6 @@ static struct extent_io_descriptor *datafile_extent_build(struct rrdengine_insta pos = 0; header = xt_io_descr->buf; - header->compression_algorithm = compression_algorithm; header->number_of_pages = count; pos += sizeof(*header); @@ -844,11 +829,11 @@ static struct extent_io_descriptor *datafile_extent_build(struct rrdengine_insta header->descr[i].start_time_ut = descr->start_time_ut; switch (descr->type) { - case PAGE_METRICS: - case PAGE_TIER: + case RRDENG_PAGE_TYPE_ARRAY_32BIT: + case RRDENG_PAGE_TYPE_ARRAY_TIER1: header->descr[i].end_time_ut = descr->end_time_ut; break; - case PAGE_GORILLA_METRICS: + case RRDENG_PAGE_TYPE_GORILLA_32BIT: header->descr[i].gorilla.delta_time_s = (uint32_t) ((descr->end_time_ut - descr->start_time_ut) / USEC_PER_SEC); header->descr[i].gorilla.entries = pgd_slots_used(descr->pgd); break; @@ -858,29 +843,40 @@ static struct extent_io_descriptor *datafile_extent_build(struct rrdengine_insta pos += sizeof(header->descr[i]); } + + // build the extent payload for (i = 0 ; i < count ; ++i) { descr = xt_io_descr->descr_array[i]; pgd_copy_to_extent(descr->pgd, xt_io_descr->buf + pos, descr->page_length); pos += descr->page_length; } - if(likely(compression_algorithm == RRD_LZ4)) { - compressed_size = LZ4_compress_default( - xt_io_descr->buf + payload_offset, - compressed_buf, - (int)uncompressed_payload_length, - max_compressed_size); + // compress the payload + size_t compressed_size = + (int)dbengine_compress(xt_io_descr->buf + payload_offset, + uncompressed_payload_length, + compression_algorithm); - __atomic_add_fetch(&ctx->stats.before_compress_bytes, uncompressed_payload_length, __ATOMIC_RELAXED); - __atomic_add_fetch(&ctx->stats.after_compress_bytes, compressed_size, __ATOMIC_RELAXED); + internal_fatal(compressed_size > max_compressed_size, "DBENGINE: compression returned more data than the max allowed"); + internal_fatal(compressed_size > uncompressed_payload_length, "DBENGINE: compression returned more data than the uncompressed extent"); - (void) memcpy(xt_io_descr->buf + payload_offset, compressed_buf, compressed_size); - extent_buffer_release(eb); - size_bytes = payload_offset + compressed_size + sizeof(*trailer); + if(compressed_size) { + header->compression_algorithm = compression_algorithm; header->payload_length = compressed_size; } - else { // RRD_NO_COMPRESSION - header->payload_length = uncompressed_payload_length; + else { + // compression failed, or generated bigger pages + // so it didn't touch our uncompressed buffer + header->compression_algorithm = RRDENG_COMPRESSION_NONE; + header->payload_length = compressed_size = uncompressed_payload_length; + } + + // set the correct size + size_bytes = payload_offset + compressed_size + sizeof(*trailer); + + if(compression_algorithm != RRDENG_COMPRESSION_NONE) { + __atomic_add_fetch(&ctx->stats.before_compress_bytes, uncompressed_payload_length, __ATOMIC_RELAXED); + __atomic_add_fetch(&ctx->stats.after_compress_bytes, compressed_size, __ATOMIC_RELAXED); } real_io_size = ALIGN_BYTES_CEILING(size_bytes); @@ -1171,7 +1167,17 @@ static void update_metrics_first_time_s(struct rrdengine_instance *ctx, struct r for (size_t index = 0; index < added; ++index) { uuid_first_t_entry = &uuid_first_entry_list[index]; if (likely(uuid_first_t_entry->first_time_s != LONG_MAX)) { - mrg_metric_set_first_time_s_if_bigger(main_mrg, uuid_first_t_entry->metric, uuid_first_t_entry->first_time_s); + + time_t old_first_time_s = mrg_metric_get_first_time_s(main_mrg, uuid_first_t_entry->metric); + + bool changed = mrg_metric_set_first_time_s_if_bigger(main_mrg, uuid_first_t_entry->metric, uuid_first_t_entry->first_time_s); + if (changed) { + uint32_t update_every_s = mrg_metric_get_update_every_s(main_mrg, uuid_first_t_entry->metric); + if (update_every_s && old_first_time_s && uuid_first_t_entry->first_time_s > old_first_time_s) { + uint64_t remove_samples = (uuid_first_t_entry->first_time_s - old_first_time_s) / update_every_s; + __atomic_sub_fetch(&ctx->atomic.samples, remove_samples, __ATOMIC_RELAXED); + } + } mrg_metric_release(main_mrg, uuid_first_t_entry->metric); } else { @@ -1180,6 +1186,14 @@ static void update_metrics_first_time_s(struct rrdengine_instance *ctx, struct r // there is no retention for this metric bool has_retention = mrg_metric_zero_disk_retention(main_mrg, uuid_first_t_entry->metric); if (!has_retention) { + time_t first_time_s = mrg_metric_get_first_time_s(main_mrg, uuid_first_t_entry->metric); + time_t last_time_s = mrg_metric_get_latest_time_s(main_mrg, uuid_first_t_entry->metric); + time_t update_every_s = mrg_metric_get_update_every_s(main_mrg, uuid_first_t_entry->metric); + if (update_every_s && first_time_s && last_time_s) { + uint64_t remove_samples = (first_time_s - last_time_s) / update_every_s; + __atomic_sub_fetch(&ctx->atomic.samples, remove_samples, __ATOMIC_RELAXED); + } + bool deleted = mrg_metric_release_and_delete(main_mrg, uuid_first_t_entry->metric); if(deleted) deleted_metrics++; diff --git a/database/engine/rrdengine.h b/src/database/engine/rrdengine.h index cd3352f12..3047e0c6a 100644 --- a/database/engine/rrdengine.h +++ b/src/database/engine/rrdengine.h @@ -153,9 +153,9 @@ struct jv2_metrics_info { struct jv2_page_info { time_t start_time_s; time_t end_time_s; - time_t update_every_s; - size_t page_length; + uint32_t update_every_s; uint32_t extent_index; + size_t page_length; void *custom_data; // private @@ -217,7 +217,7 @@ struct rrdeng_query_handle { // internal data time_t now_s; - time_t dt_s; + uint32_t dt_s; unsigned position; unsigned entries; @@ -387,6 +387,8 @@ struct rrdengine_instance { unsigned extents_currently_being_flushed; // non-zero until we commit data to disk (both datafile and journal file) time_t first_time_s; + uint64_t metrics; + uint64_t samples; } atomic; struct { @@ -482,7 +484,7 @@ struct page_descr_with_data *page_descriptor_get(void); typedef struct validated_page_descriptor { time_t start_time_s; time_t end_time_s; - time_t update_every_s; + uint32_t update_every_s; size_t page_length; size_t point_size; size_t entries; @@ -499,16 +501,16 @@ typedef struct validated_page_descriptor { VALIDATED_PAGE_DESCRIPTOR validate_page(uuid_t *uuid, time_t start_time_s, time_t end_time_s, - time_t update_every_s, + uint32_t update_every_s, size_t page_length, uint8_t page_type, size_t entries, time_t now_s, - time_t overwrite_zero_update_every_s, + uint32_t overwrite_zero_update_every_s, bool have_read_error, const char *msg, RRDENG_COLLECT_PAGE_FLAGS flags); -VALIDATED_PAGE_DESCRIPTOR validate_extent_page_descr(const struct rrdeng_extent_page_descr *descr, time_t now_s, time_t overwrite_zero_update_every_s, bool have_read_error); +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); void collect_page_flags_to_buffer(BUFFER *wb, RRDENG_COLLECT_PAGE_FLAGS flags); typedef enum { diff --git a/database/engine/rrdengineapi.c b/src/database/engine/rrdengineapi.c index 1ddce5243..43fed492b 100755 --- a/database/engine/rrdengineapi.c +++ b/src/database/engine/rrdengineapi.c @@ -2,6 +2,7 @@ #include "database/engine/rrddiskprotocol.h" #include "rrdengine.h" +#include "dbengine-compression.h" /* Default global database instance */ struct rrdengine_instance multidb_ctx_storage_tier0; @@ -16,7 +17,12 @@ struct rrdengine_instance multidb_ctx_storage_tier4; #error RRD_STORAGE_TIERS is not 5 - you need to add allocations here #endif struct rrdengine_instance *multidb_ctx[RRD_STORAGE_TIERS]; -uint8_t tier_page_type[RRD_STORAGE_TIERS] = {PAGE_METRICS, PAGE_TIER, PAGE_TIER, PAGE_TIER, PAGE_TIER}; +uint8_t tier_page_type[RRD_STORAGE_TIERS] = { + RRDENG_PAGE_TYPE_GORILLA_32BIT, + RRDENG_PAGE_TYPE_ARRAY_TIER1, + RRDENG_PAGE_TYPE_ARRAY_TIER1, + RRDENG_PAGE_TYPE_ARRAY_TIER1, + RRDENG_PAGE_TYPE_ARRAY_TIER1}; #if defined(ENV32BIT) size_t tier_page_size[RRD_STORAGE_TIERS] = {2048, 1024, 192, 192, 192}; @@ -24,14 +30,14 @@ size_t tier_page_size[RRD_STORAGE_TIERS] = {2048, 1024, 192, 192, 192}; size_t tier_page_size[RRD_STORAGE_TIERS] = {4096, 2048, 384, 384, 384}; #endif -#if PAGE_TYPE_MAX != 2 +#if RRDENG_PAGE_TYPE_MAX != 2 #error PAGE_TYPE_MAX is not 2 - you need to add allocations here #endif size_t page_type_size[256] = { - [PAGE_METRICS] = sizeof(storage_number), - [PAGE_TIER] = sizeof(storage_number_tier1_t), - [PAGE_GORILLA_METRICS] = sizeof(storage_number) + [RRDENG_PAGE_TYPE_ARRAY_32BIT] = sizeof(storage_number), + [RRDENG_PAGE_TYPE_ARRAY_TIER1] = sizeof(storage_number_tier1_t), + [RRDENG_PAGE_TYPE_GORILLA_32BIT] = sizeof(storage_number) }; __attribute__((constructor)) void initialize_multidb_ctx(void) { @@ -74,14 +80,14 @@ static inline bool rrdeng_page_alignment_release(struct pg_alignment *pa) { } // charts call this -STORAGE_METRICS_GROUP *rrdeng_metrics_group_get(STORAGE_INSTANCE *db_instance __maybe_unused, uuid_t *uuid __maybe_unused) { +STORAGE_METRICS_GROUP *rrdeng_metrics_group_get(STORAGE_INSTANCE *si __maybe_unused, uuid_t *uuid __maybe_unused) { struct pg_alignment *pa = callocz(1, sizeof(struct pg_alignment)); rrdeng_page_alignment_acquire(pa); return (STORAGE_METRICS_GROUP *)pa; } // charts call this -void rrdeng_metrics_group_release(STORAGE_INSTANCE *db_instance __maybe_unused, STORAGE_METRICS_GROUP *smg) { +void rrdeng_metrics_group_release(STORAGE_INSTANCE *si __maybe_unused, STORAGE_METRICS_GROUP *smg) { if(unlikely(!smg)) return; struct pg_alignment *pa = (struct pg_alignment *)smg; @@ -108,8 +114,8 @@ void rrdeng_generate_legacy_uuid(const char *dim_id, const char *chart_id, uuid_ memcpy(ret_uuid, hash_value, sizeof(uuid_t)); } -static METRIC *rrdeng_metric_get_legacy(STORAGE_INSTANCE *db_instance, const char *rd_id, const char *st_id) { - struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; +static METRIC *rrdeng_metric_get_legacy(STORAGE_INSTANCE *si, const char *rd_id, const char *st_id) { + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si; uuid_t legacy_uuid; rrdeng_generate_legacy_uuid(rd_id, st_id, &legacy_uuid); return mrg_metric_get_and_acquire(main_mrg, &legacy_uuid, (Word_t) ctx); @@ -118,25 +124,25 @@ static METRIC *rrdeng_metric_get_legacy(STORAGE_INSTANCE *db_instance, const cha // ---------------------------------------------------------------------------- // metric handle -void rrdeng_metric_release(STORAGE_METRIC_HANDLE *db_metric_handle) { - METRIC *metric = (METRIC *)db_metric_handle; +void rrdeng_metric_release(STORAGE_METRIC_HANDLE *smh) { + METRIC *metric = (METRIC *)smh; mrg_metric_release(main_mrg, metric); } -STORAGE_METRIC_HANDLE *rrdeng_metric_dup(STORAGE_METRIC_HANDLE *db_metric_handle) { - METRIC *metric = (METRIC *)db_metric_handle; +STORAGE_METRIC_HANDLE *rrdeng_metric_dup(STORAGE_METRIC_HANDLE *smh) { + METRIC *metric = (METRIC *)smh; return (STORAGE_METRIC_HANDLE *) mrg_metric_dup(main_mrg, metric); } -STORAGE_METRIC_HANDLE *rrdeng_metric_get(STORAGE_INSTANCE *db_instance, uuid_t *uuid) { - struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; +STORAGE_METRIC_HANDLE *rrdeng_metric_get(STORAGE_INSTANCE *si, uuid_t *uuid) { + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si; return (STORAGE_METRIC_HANDLE *) mrg_metric_get_and_acquire(main_mrg, uuid, (Word_t) ctx); } -static METRIC *rrdeng_metric_create(STORAGE_INSTANCE *db_instance, uuid_t *uuid) { - internal_fatal(!db_instance, "DBENGINE: db_instance is NULL"); +static METRIC *rrdeng_metric_create(STORAGE_INSTANCE *si, uuid_t *uuid) { + internal_fatal(!si, "DBENGINE: STORAGE_INSTANCE is NULL"); - struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si; MRG_ENTRY entry = { .uuid = uuid, .section = (Word_t)ctx, @@ -145,12 +151,15 @@ static METRIC *rrdeng_metric_create(STORAGE_INSTANCE *db_instance, uuid_t *uuid) .latest_update_every_s = 0, }; - METRIC *metric = mrg_metric_add_and_acquire(main_mrg, entry, NULL); + bool added; + METRIC *metric = mrg_metric_add_and_acquire(main_mrg, entry, &added); + if (added) + __atomic_add_fetch(&ctx->atomic.metrics, 1, __ATOMIC_RELAXED); return metric; } -STORAGE_METRIC_HANDLE *rrdeng_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *db_instance) { - struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; +STORAGE_METRIC_HANDLE *rrdeng_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *si) { + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si; METRIC *metric; metric = mrg_metric_get_and_acquire(main_mrg, &rd->metric_uuid, (Word_t) ctx); @@ -160,13 +169,13 @@ STORAGE_METRIC_HANDLE *rrdeng_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE // this is a single host database // generate uuid from the chart and dimensions ids // and overwrite the one supplied by rrddim - metric = rrdeng_metric_get_legacy(db_instance, rrddim_id(rd), rrdset_id(rd->rrdset)); + metric = rrdeng_metric_get_legacy(si, rrddim_id(rd), rrdset_id(rd->rrdset)); if (metric) uuid_copy(rd->metric_uuid, *mrg_metric_uuid(main_mrg, metric)); } if(likely(!metric)) - metric = rrdeng_metric_create(db_instance, &rd->metric_uuid); + metric = rrdeng_metric_create(si, &rd->metric_uuid); } #ifdef NETDATA_INTERNAL_CHECKS @@ -192,14 +201,14 @@ STORAGE_METRIC_HANDLE *rrdeng_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE // collect ops static inline void check_and_fix_mrg_update_every(struct rrdeng_collect_handle *handle) { - if(unlikely((time_t)(handle->update_every_ut / USEC_PER_SEC) != mrg_metric_get_update_every_s(main_mrg, handle->metric))) { - internal_error(true, "DBENGINE: collection handle has update every %ld, but the metric registry has %ld. Fixing it.", - (time_t)(handle->update_every_ut / USEC_PER_SEC), mrg_metric_get_update_every_s(main_mrg, handle->metric)); + if(unlikely((uint32_t)(handle->update_every_ut / USEC_PER_SEC) != mrg_metric_get_update_every_s(main_mrg, handle->metric))) { + internal_error(true, "DBENGINE: collection handle has update every %u, but the metric registry has %u. Fixing it.", + (uint32_t)(handle->update_every_ut / USEC_PER_SEC), mrg_metric_get_update_every_s(main_mrg, handle->metric)); if(unlikely(!handle->update_every_ut)) handle->update_every_ut = (usec_t)mrg_metric_get_update_every_s(main_mrg, handle->metric) * USEC_PER_SEC; else - mrg_metric_set_update_every(main_mrg, handle->metric, (time_t)(handle->update_every_ut / USEC_PER_SEC)); + mrg_metric_set_update_every(main_mrg, handle->metric, (uint32_t)(handle->update_every_ut / USEC_PER_SEC)); } } @@ -213,7 +222,7 @@ static inline bool check_completed_page_consistency(struct rrdeng_collect_handle uuid_t *uuid = mrg_metric_uuid(main_mrg, handle->metric); time_t start_time_s = pgc_page_start_time_s(handle->pgc_page); time_t end_time_s = pgc_page_end_time_s(handle->pgc_page); - time_t update_every_s = pgc_page_update_every_s(handle->pgc_page); + uint32_t update_every_s = pgc_page_update_every_s(handle->pgc_page); size_t page_length = handle->page_position * CTX_POINT_SIZE_BYTES(ctx); size_t entries = handle->page_position; time_t overwrite_zero_update_every_s = (time_t)(handle->update_every_ut / USEC_PER_SEC); @@ -245,8 +254,8 @@ static inline bool check_completed_page_consistency(struct rrdeng_collect_handle * Gets a handle for storing metrics to the database. * The handle must be released with rrdeng_store_metric_final(). */ -STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every, STORAGE_METRICS_GROUP *smg) { - METRIC *metric = (METRIC *)db_metric_handle; +STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *smh, uint32_t update_every, STORAGE_METRICS_GROUP *smg) { + METRIC *metric = (METRIC *)smh; struct rrdengine_instance *ctx = mrg_metric_ctx(metric); bool is_1st_metric_writer = true; @@ -262,7 +271,7 @@ STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metri struct rrdeng_collect_handle *handle; handle = callocz(1, sizeof(struct rrdeng_collect_handle)); - handle->common.backend = STORAGE_ENGINE_BACKEND_DBENGINE; + handle->common.seb = STORAGE_ENGINE_BACKEND_DBENGINE; handle->metric = metric; handle->pgc_page = NULL; @@ -288,15 +297,15 @@ STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metri // data collection may be able to go back in time and during the addition of new pages // clean pages may be found matching ours! - time_t db_first_time_s, db_last_time_s, db_update_every_s; - mrg_metric_get_retention(main_mrg, metric, &db_first_time_s, &db_last_time_s, &db_update_every_s); + time_t db_first_time_s, db_last_time_s; + mrg_metric_get_retention(main_mrg, metric, &db_first_time_s, &db_last_time_s, NULL); handle->page_end_time_ut = (usec_t)db_last_time_s * USEC_PER_SEC; return (STORAGE_COLLECT_HANDLE *)handle; } -void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_handle) { - struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle; +void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *sch) { + struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)sch; if (unlikely(!handle->pgc_page)) return; @@ -307,7 +316,17 @@ void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_h else { check_completed_page_consistency(handle); mrg_metric_set_clean_latest_time_s(main_mrg, handle->metric, pgc_page_end_time_s(handle->pgc_page)); - pgc_page_hot_to_dirty_and_release(main_cache, handle->pgc_page); + + struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric); + time_t start_time_s = pgc_page_start_time_s(handle->pgc_page); + time_t end_time_s = pgc_page_end_time_s(handle->pgc_page); + uint32_t update_every_s = mrg_metric_get_update_every_s(main_mrg, handle->metric); + if (end_time_s && start_time_s && end_time_s > start_time_s && update_every_s) { + uint64_t add_samples = (end_time_s - start_time_s) / update_every_s; + __atomic_add_fetch(&ctx->atomic.samples, add_samples, __ATOMIC_RELAXED); + } + + pgc_page_hot_to_dirty_and_release(main_cache, handle->pgc_page, false); } mrg_metric_set_hot_latest_time_s(main_mrg, handle->metric, 0); @@ -336,7 +355,7 @@ static void rrdeng_store_metric_create_new_page(struct rrdeng_collect_handle *ha PGD *data, size_t data_size) { time_t point_in_time_s = (time_t)(point_in_time_ut / USEC_PER_SEC); - const time_t update_every_s = (time_t)(handle->update_every_ut / USEC_PER_SEC); + const uint32_t update_every_s = (uint32_t)(handle->update_every_ut / USEC_PER_SEC); PGC_ENTRY page_entry = { .section = (Word_t) ctx, @@ -345,7 +364,7 @@ static void rrdeng_store_metric_create_new_page(struct rrdeng_collect_handle *ha .end_time_s = point_in_time_s, .size = data_size, .data = data, - .update_every_s = (uint32_t) update_every_s, + .update_every_s = update_every_s, .hot = true }; @@ -364,11 +383,11 @@ static void rrdeng_store_metric_create_new_page(struct rrdeng_collect_handle *ha nd_log_limit_static_global_var(erl, 1, 0); nd_log_limit(&erl, NDLS_DAEMON, NDLP_WARNING, #endif - "DBENGINE: metric '%s' new page from %ld to %ld, update every %ld, has a conflict in main cache " - "with existing %s%s page from %ld to %ld, update every %ld - " + "DBENGINE: metric '%s' new page from %ld to %ld, update every %u, has a conflict in main cache " + "with existing %s%s page from %ld to %ld, update every %u - " "is it collected more than once?", uuid, - page_entry.start_time_s, page_entry.end_time_s, (time_t)page_entry.update_every_s, + page_entry.start_time_s, page_entry.end_time_s, page_entry.update_every_s, pgc_is_page_hot(pgc_page) ? "hot" : "not-hot", pgc_page_data(pgc_page) == PGD_EMPTY ? " gap" : "", pgc_page_start_time_s(pgc_page), pgc_page_end_time_s(pgc_page), pgc_page_update_every_s(pgc_page) @@ -444,14 +463,14 @@ static PGD *rrdeng_alloc_new_page_data(struct rrdeng_collect_handle *handle, siz *data_size = size; switch (ctx->config.page_type) { - case PAGE_METRICS: - case PAGE_TIER: + case RRDENG_PAGE_TYPE_ARRAY_32BIT: + case RRDENG_PAGE_TYPE_ARRAY_TIER1: d = pgd_create(ctx->config.page_type, slots); break; - case PAGE_GORILLA_METRICS: + case RRDENG_PAGE_TYPE_GORILLA_32BIT: // ignore slots, and use the fixed number of slots per gorilla buffer. // gorilla will automatically add more buffers if needed. - d = pgd_create(ctx->config.page_type, GORILLA_BUFFER_SLOTS); + d = pgd_create(ctx->config.page_type, RRDENG_GORILLA_32BIT_BUFFER_SLOTS); break; default: fatal("Unknown page type: %uc\n", ctx->config.page_type); @@ -461,7 +480,7 @@ static PGD *rrdeng_alloc_new_page_data(struct rrdeng_collect_handle *handle, siz return d; } -static void rrdeng_store_metric_append_point(STORAGE_COLLECT_HANDLE *collection_handle, +static void rrdeng_store_metric_append_point(STORAGE_COLLECT_HANDLE *sch, const usec_t point_in_time_ut, const NETDATA_DOUBLE n, const NETDATA_DOUBLE min_value, @@ -470,7 +489,7 @@ static void rrdeng_store_metric_append_point(STORAGE_COLLECT_HANDLE *collection_ const uint16_t anomaly_count, const SN_FLAGS flags) { - struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle; + struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)sch; struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric); if(unlikely(!handle->page_data)) @@ -497,7 +516,7 @@ static void rrdeng_store_metric_append_point(STORAGE_COLLECT_HANDLE *collection_ if(unlikely(++handle->page_position >= handle->page_entries_max)) { internal_fatal(handle->page_position > handle->page_entries_max, "DBENGINE: exceeded page max number of points"); handle->page_flags |= RRDENG_PAGE_FULL; - rrdeng_store_metric_flush_current_page(collection_handle); + rrdeng_store_metric_flush_current_page(sch); } } @@ -543,7 +562,7 @@ static void store_metric_next_error_log(struct rrdeng_collect_handle *handle __m #endif } -void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, +void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *sch, const usec_t point_in_time_ut, const NETDATA_DOUBLE n, const NETDATA_DOUBLE min_value, @@ -554,7 +573,7 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, { timing_step(TIMING_STEP_RRDSET_STORE_METRIC); - struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle; + struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)sch; #ifdef NETDATA_INTERNAL_CHECKS if(unlikely(point_in_time_ut > (usec_t)max_acceptable_collected_time() * USEC_PER_SEC)) @@ -571,11 +590,11 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, if(handle->pgc_page) { if (unlikely(delta_ut < handle->update_every_ut)) { handle->page_flags |= RRDENG_PAGE_STEP_TOO_SMALL; - rrdeng_store_metric_flush_current_page(collection_handle); + rrdeng_store_metric_flush_current_page(sch); } else if (unlikely(delta_ut % handle->update_every_ut)) { handle->page_flags |= RRDENG_PAGE_STEP_UNALIGNED; - rrdeng_store_metric_flush_current_page(collection_handle); + rrdeng_store_metric_flush_current_page(sch); } else { size_t points_gap = delta_ut / handle->update_every_ut; @@ -583,7 +602,7 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, if (points_gap >= page_remaining_points) { handle->page_flags |= RRDENG_PAGE_BIG_GAP; - rrdeng_store_metric_flush_current_page(collection_handle); + rrdeng_store_metric_flush_current_page(sch); } else { // loop to fill the gap @@ -594,7 +613,7 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, this_ut <= stop_ut; this_ut = handle->page_end_time_ut + handle->update_every_ut) { rrdeng_store_metric_append_point( - collection_handle, + sch, this_ut, NAN, NAN, NAN, 1, 0, @@ -618,7 +637,7 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, timing_step(TIMING_STEP_DBENGINE_FIRST_CHECK); - rrdeng_store_metric_append_point(collection_handle, + rrdeng_store_metric_append_point(sch, point_in_time_ut, n, min_value, max_value, count, anomaly_count, @@ -629,12 +648,12 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, * Releases the database reference from the handle for storing metrics. * Returns 1 if it's safe to delete the dimension. */ -int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle) { - struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle; +int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *sch) { + struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)sch; struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric); handle->page_flags |= RRDENG_PAGE_COLLECT_FINALIZE; - rrdeng_store_metric_flush_current_page(collection_handle); + rrdeng_store_metric_flush_current_page(sch); rrdeng_page_alignment_release(handle->alignment); __atomic_sub_fetch(&ctx->atomic.collectors_running, 1, __ATOMIC_RELAXED); @@ -644,8 +663,8 @@ int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle) { if((handle->options & RRDENG_1ST_METRIC_WRITER) && !mrg_metric_clear_writer(main_mrg, handle->metric)) internal_fatal(true, "DBENGINE: metric is already released"); - time_t first_time_s, last_time_s, update_every_s; - mrg_metric_get_retention(main_mrg, handle->metric, &first_time_s, &last_time_s, &update_every_s); + time_t first_time_s, last_time_s; + mrg_metric_get_retention(main_mrg, handle->metric, &first_time_s, &last_time_s, NULL); mrg_metric_release(main_mrg, handle->metric); freez(handle); @@ -656,8 +675,8 @@ int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle) { return 0; } -void rrdeng_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *collection_handle, int update_every) { - struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle; +void rrdeng_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *sch, int update_every) { + struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)sch; check_and_fix_mrg_update_every(handle); METRIC *metric = handle->metric; @@ -667,7 +686,7 @@ void rrdeng_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *col return; handle->page_flags |= RRDENG_PAGE_UPDATE_EVERY_CHANGE; - rrdeng_store_metric_flush_current_page(collection_handle); + rrdeng_store_metric_flush_current_page(sch); mrg_metric_set_update_every(main_mrg, metric, update_every); handle->update_every_ut = update_every_ut; } @@ -704,8 +723,8 @@ static void unregister_query_handle(struct rrdeng_query_handle *handle __maybe_u * Gets a handle for loading metrics from the database. * The handle must be released with rrdeng_load_metric_final(). */ -void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, - struct storage_engine_query_handle *rrddim_handle, +void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *smh, + struct storage_engine_query_handle *seqh, time_t start_time_s, time_t end_time_s, STORAGE_PRIORITY priority) @@ -714,7 +733,7 @@ void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, netdata_thread_disable_cancelability(); - METRIC *metric = (METRIC *)db_metric_handle; + METRIC *metric = (METRIC *)smh; struct rrdengine_instance *ctx = mrg_metric_ctx(metric); struct rrdeng_query_handle *handle; @@ -736,7 +755,8 @@ void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, // is inserted into the main cache, to avoid scanning the journals // again for pages matching the gap. - time_t db_first_time_s, db_last_time_s, db_update_every_s; + time_t db_first_time_s, db_last_time_s; + uint32_t db_update_every_s; mrg_metric_get_retention(main_mrg, metric, &db_first_time_s, &db_last_time_s, &db_update_every_s); if(is_page_in_time_range(start_time_s, end_time_s, db_first_time_s, db_last_time_s) == PAGE_IS_IN_RANGE) { @@ -750,11 +770,11 @@ void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, mrg_metric_set_update_every_s_if_zero(main_mrg, metric, default_rrd_update_every); } - rrddim_handle->handle = (STORAGE_QUERY_HANDLE *) handle; - rrddim_handle->start_time_s = handle->start_time_s; - rrddim_handle->end_time_s = handle->end_time_s; - rrddim_handle->priority = priority; - rrddim_handle->backend = STORAGE_ENGINE_BACKEND_DBENGINE; + seqh->handle = (STORAGE_QUERY_HANDLE *) handle; + seqh->start_time_s = handle->start_time_s; + seqh->end_time_s = handle->end_time_s; + seqh->priority = priority; + seqh->seb = STORAGE_ENGINE_BACKEND_DBENGINE; pg_cache_preload(handle); @@ -766,16 +786,16 @@ void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, handle->now_s = start_time_s; handle->dt_s = db_update_every_s; - rrddim_handle->handle = (STORAGE_QUERY_HANDLE *) handle; - rrddim_handle->start_time_s = handle->start_time_s; - rrddim_handle->end_time_s = 0; - rrddim_handle->priority = priority; - rrddim_handle->backend = STORAGE_ENGINE_BACKEND_DBENGINE; + seqh->handle = (STORAGE_QUERY_HANDLE *) handle; + seqh->start_time_s = handle->start_time_s; + seqh->end_time_s = 0; + seqh->priority = priority; + seqh->seb = STORAGE_ENGINE_BACKEND_DBENGINE; } } -static bool rrdeng_load_page_next(struct storage_engine_query_handle *rrddim_handle, bool debug_this __maybe_unused) { - struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)rrddim_handle->handle; +static bool rrdeng_load_page_next(struct storage_engine_query_handle *seqh, bool debug_this __maybe_unused) { + struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)seqh->handle; struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric); if (likely(handle->page)) { @@ -785,7 +805,7 @@ static bool rrdeng_load_page_next(struct storage_engine_query_handle *rrddim_han pgdc_reset(&handle->pgdc, NULL, UINT32_MAX); } - if (unlikely(handle->now_s > rrddim_handle->end_time_s)) + if (unlikely(handle->now_s > seqh->end_time_s)) return false; size_t entries = 0; @@ -799,7 +819,7 @@ static bool rrdeng_load_page_next(struct storage_engine_query_handle *rrddim_han time_t page_start_time_s = pgc_page_start_time_s(handle->page); time_t page_end_time_s = pgc_page_end_time_s(handle->page); - time_t page_update_every_s = pgc_page_update_every_s(handle->page); + uint32_t page_update_every_s = pgc_page_update_every_s(handle->page); unsigned position; if(likely(handle->now_s >= page_start_time_s && handle->now_s <= page_end_time_s)) { @@ -810,13 +830,13 @@ static bool rrdeng_load_page_next(struct storage_engine_query_handle *rrddim_han } else { position = (handle->now_s - page_start_time_s) * (entries - 1) / (page_end_time_s - page_start_time_s); - time_t point_end_time_s = page_start_time_s + position * page_update_every_s; + time_t point_end_time_s = page_start_time_s + position * (time_t) page_update_every_s; while(point_end_time_s < handle->now_s && position + 1 < entries) { // https://github.com/netdata/netdata/issues/14411 // we really need a while() here, because the delta may be // 2 points at higher tiers position++; - point_end_time_s = page_start_time_s + position * page_update_every_s; + point_end_time_s = page_start_time_s + position * (time_t) page_update_every_s; } handle->now_s = point_end_time_s; } @@ -845,11 +865,11 @@ static bool rrdeng_load_page_next(struct storage_engine_query_handle *rrddim_han // Returns the metric and sets its timestamp into current_time // IT IS REQUIRED TO **ALWAYS** SET ALL RETURN VALUES (current_time, end_time, flags) // IT IS REQUIRED TO **ALWAYS** KEEP TRACK OF TIME, EVEN OUTSIDE THE DATABASE BOUNDARIES -STORAGE_POINT rrdeng_load_metric_next(struct storage_engine_query_handle *rrddim_handle) { - struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)rrddim_handle->handle; +STORAGE_POINT rrdeng_load_metric_next(struct storage_engine_query_handle *seqh) { + struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)seqh->handle; STORAGE_POINT sp; - if (unlikely(handle->now_s > rrddim_handle->end_time_s)) { + if (unlikely(handle->now_s > seqh->end_time_s)) { storage_point_empty(sp, handle->now_s - handle->dt_s, handle->now_s); goto prepare_for_next_iteration; } @@ -857,8 +877,8 @@ STORAGE_POINT rrdeng_load_metric_next(struct storage_engine_query_handle *rrddim if (unlikely(!handle->page || handle->position >= handle->entries)) { // We need to get a new page - if (!rrdeng_load_page_next(rrddim_handle, false)) { - handle->now_s = rrddim_handle->end_time_s; + if (!rrdeng_load_page_next(seqh, false)) { + handle->now_s = seqh->end_time_s; storage_point_empty(sp, handle->now_s - handle->dt_s, handle->now_s); goto prepare_for_next_iteration; } @@ -870,7 +890,7 @@ STORAGE_POINT rrdeng_load_metric_next(struct storage_engine_query_handle *rrddim pgdc_get_next_point(&handle->pgdc, handle->position, &sp); prepare_for_next_iteration: - internal_fatal(sp.end_time_s < rrddim_handle->start_time_s, "DBENGINE: this point is too old for this query"); + internal_fatal(sp.end_time_s < seqh->start_time_s, "DBENGINE: this point is too old for this query"); internal_fatal(sp.end_time_s < handle->now_s, "DBENGINE: this point is too old for this point in time"); handle->now_s += handle->dt_s; @@ -879,17 +899,17 @@ prepare_for_next_iteration: return sp; } -int rrdeng_load_metric_is_finished(struct storage_engine_query_handle *rrddim_handle) { - struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)rrddim_handle->handle; - return (handle->now_s > rrddim_handle->end_time_s); +int rrdeng_load_metric_is_finished(struct storage_engine_query_handle *seqh) { + struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)seqh->handle; + return (handle->now_s > seqh->end_time_s); } /* * Releases the database reference from the handle for loading metrics. */ -void rrdeng_load_metric_finalize(struct storage_engine_query_handle *rrddim_handle) +void rrdeng_load_metric_finalize(struct storage_engine_query_handle *seqh) { - struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)rrddim_handle->handle; + struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)seqh->handle; if (handle->page) { pgc_page_release(main_cache, handle->page); @@ -901,24 +921,24 @@ void rrdeng_load_metric_finalize(struct storage_engine_query_handle *rrddim_hand unregister_query_handle(handle); rrdeng_query_handle_release(handle); - rrddim_handle->handle = NULL; + seqh->handle = NULL; netdata_thread_enable_cancelability(); } -time_t rrdeng_load_align_to_optimal_before(struct storage_engine_query_handle *rrddim_handle) { - struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)rrddim_handle->handle; +time_t rrdeng_load_align_to_optimal_before(struct storage_engine_query_handle *seqh) { + struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)seqh->handle; if(handle->pdc) { rrdeng_prep_wait(handle->pdc); - if (handle->pdc->optimal_end_time_s > rrddim_handle->end_time_s) - rrddim_handle->end_time_s = handle->pdc->optimal_end_time_s; + if (handle->pdc->optimal_end_time_s > seqh->end_time_s) + seqh->end_time_s = handle->pdc->optimal_end_time_s; } - return rrddim_handle->end_time_s; + return seqh->end_time_s; } -time_t rrdeng_metric_latest_time(STORAGE_METRIC_HANDLE *db_metric_handle) { - METRIC *metric = (METRIC *)db_metric_handle; +time_t rrdeng_metric_latest_time(STORAGE_METRIC_HANDLE *smh) { + METRIC *metric = (METRIC *)smh; time_t latest_time_s = 0; if (metric) @@ -927,8 +947,8 @@ time_t rrdeng_metric_latest_time(STORAGE_METRIC_HANDLE *db_metric_handle) { return latest_time_s; } -time_t rrdeng_metric_oldest_time(STORAGE_METRIC_HANDLE *db_metric_handle) { - METRIC *metric = (METRIC *)db_metric_handle; +time_t rrdeng_metric_oldest_time(STORAGE_METRIC_HANDLE *smh) { + METRIC *metric = (METRIC *)smh; time_t oldest_time_s = 0; if (metric) @@ -937,9 +957,9 @@ time_t rrdeng_metric_oldest_time(STORAGE_METRIC_HANDLE *db_metric_handle) { return oldest_time_s; } -bool rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *db_instance, uuid_t *dim_uuid, time_t *first_entry_s, time_t *last_entry_s) +bool rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *si, uuid_t *dim_uuid, time_t *first_entry_s, time_t *last_entry_s) { - struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si; if (unlikely(!ctx)) { netdata_log_error("DBENGINE: invalid STORAGE INSTANCE to %s()", __FUNCTION__); return false; @@ -949,26 +969,35 @@ bool rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *db_instance, uuid_t *dim_ if (unlikely(!metric)) return false; - time_t update_every_s; - mrg_metric_get_retention(main_mrg, metric, first_entry_s, last_entry_s, &update_every_s); + mrg_metric_get_retention(main_mrg, metric, first_entry_s, last_entry_s, NULL); mrg_metric_release(main_mrg, metric); return true; } -uint64_t rrdeng_disk_space_max(STORAGE_INSTANCE *db_instance) { - struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; +uint64_t rrdeng_disk_space_max(STORAGE_INSTANCE *si) { + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si; return ctx->config.max_disk_space; } -uint64_t rrdeng_disk_space_used(STORAGE_INSTANCE *db_instance) { - struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; +uint64_t rrdeng_disk_space_used(STORAGE_INSTANCE *si) { + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si; return __atomic_load_n(&ctx->atomic.current_disk_space, __ATOMIC_RELAXED); } -time_t rrdeng_global_first_time_s(STORAGE_INSTANCE *db_instance) { - struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; +uint64_t rrdeng_metrics(STORAGE_INSTANCE *si) { + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si; + return __atomic_load_n(&ctx->atomic.metrics, __ATOMIC_RELAXED); +} + +uint64_t rrdeng_samples(STORAGE_INSTANCE *si) { + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si; + return __atomic_load_n(&ctx->atomic.samples, __ATOMIC_RELAXED); +} + +time_t rrdeng_global_first_time_s(STORAGE_INSTANCE *si) { + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si; time_t t = __atomic_load_n(&ctx->atomic.first_time_s, __ATOMIC_RELAXED); if(t == LONG_MAX || t < 0) @@ -977,8 +1006,8 @@ time_t rrdeng_global_first_time_s(STORAGE_INSTANCE *db_instance) { return t; } -size_t rrdeng_currently_collected_metrics(STORAGE_INSTANCE *db_instance) { - struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; +size_t rrdeng_currently_collected_metrics(STORAGE_INSTANCE *si) { + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si; return __atomic_load_n(&ctx->atomic.collectors_running, __ATOMIC_RELAXED); } @@ -1099,8 +1128,8 @@ void rrdeng_readiness_wait(struct rrdengine_instance *ctx) { netdata_log_info("DBENGINE: tier %d is ready for data collection and queries", ctx->config.tier); } -bool rrdeng_is_legacy(STORAGE_INSTANCE *db_instance) { - struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; +bool rrdeng_is_legacy(STORAGE_INSTANCE *si) { + struct rrdengine_instance *ctx = (struct rrdengine_instance *)si; return ctx->config.legacy; } @@ -1142,7 +1171,7 @@ int rrdeng_init(struct rrdengine_instance **ctxp, const char *dbfiles_path, ctx->config.tier = (int)tier; ctx->config.page_type = tier_page_type[tier]; - ctx->config.global_compress_alg = RRD_LZ4; + ctx->config.global_compress_alg = dbengine_default_compression(); if (disk_space_mb < RRDENG_MIN_DISK_SPACE_MB) disk_space_mb = RRDENG_MIN_DISK_SPACE_MB; ctx->config.max_disk_space = disk_space_mb * 1048576LLU; @@ -1154,6 +1183,8 @@ int rrdeng_init(struct rrdengine_instance **ctxp, const char *dbfiles_path, rw_spinlock_init(&ctx->njfv2idx.spinlock); ctx->atomic.first_time_s = LONG_MAX; + ctx->atomic.metrics = 0; + ctx->atomic.samples = 0; if (rrdeng_dbengine_spawn(ctx) && !init_rrd_files(ctx)) { // success - we run this ctx too diff --git a/database/engine/rrdengineapi.h b/src/database/engine/rrdengineapi.h index 7ae0e7079..fb449cd9b 100644 --- a/database/engine/rrdengineapi.h +++ b/src/database/engine/rrdengineapi.h @@ -26,32 +26,32 @@ extern uint8_t tier_page_type[]; void rrdeng_generate_legacy_uuid(const char *dim_id, const char *chart_id, uuid_t *ret_uuid); -STORAGE_METRIC_HANDLE *rrdeng_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *db_instance); -STORAGE_METRIC_HANDLE *rrdeng_metric_get(STORAGE_INSTANCE *db_instance, uuid_t *uuid); -void rrdeng_metric_release(STORAGE_METRIC_HANDLE *db_metric_handle); -STORAGE_METRIC_HANDLE *rrdeng_metric_dup(STORAGE_METRIC_HANDLE *db_metric_handle); - -STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every, STORAGE_METRICS_GROUP *smg); -void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_handle); -void rrdeng_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *collection_handle, int update_every); -void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, usec_t point_in_time_ut, NETDATA_DOUBLE n, +STORAGE_METRIC_HANDLE *rrdeng_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *si); +STORAGE_METRIC_HANDLE *rrdeng_metric_get(STORAGE_INSTANCE *si, uuid_t *uuid); +void rrdeng_metric_release(STORAGE_METRIC_HANDLE *smh); +STORAGE_METRIC_HANDLE *rrdeng_metric_dup(STORAGE_METRIC_HANDLE *smh); + +STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *smh, uint32_t update_every, STORAGE_METRICS_GROUP *smg); +void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *sch); +void rrdeng_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *sch, int update_every); +void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *sch, usec_t point_in_time_ut, NETDATA_DOUBLE n, NETDATA_DOUBLE min_value, NETDATA_DOUBLE max_value, uint16_t count, uint16_t anomaly_count, SN_FLAGS flags); -int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle); +int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *sch); -void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct storage_engine_query_handle *rrddim_handle, +void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *smh, struct storage_engine_query_handle *seqh, time_t start_time_s, time_t end_time_s, STORAGE_PRIORITY priority); -STORAGE_POINT rrdeng_load_metric_next(struct storage_engine_query_handle *rrddim_handle); +STORAGE_POINT rrdeng_load_metric_next(struct storage_engine_query_handle *seqh); -int rrdeng_load_metric_is_finished(struct storage_engine_query_handle *rrddim_handle); -void rrdeng_load_metric_finalize(struct storage_engine_query_handle *rrddim_handle); -time_t rrdeng_metric_latest_time(STORAGE_METRIC_HANDLE *db_metric_handle); -time_t rrdeng_metric_oldest_time(STORAGE_METRIC_HANDLE *db_metric_handle); -time_t rrdeng_load_align_to_optimal_before(struct storage_engine_query_handle *rrddim_handle); +int rrdeng_load_metric_is_finished(struct storage_engine_query_handle *seqh); +void rrdeng_load_metric_finalize(struct storage_engine_query_handle *seqh); +time_t rrdeng_metric_latest_time(STORAGE_METRIC_HANDLE *smh); +time_t rrdeng_metric_oldest_time(STORAGE_METRIC_HANDLE *smh); +time_t rrdeng_load_align_to_optimal_before(struct storage_engine_query_handle *seqh); void rrdeng_get_37_statistics(struct rrdengine_instance *ctx, unsigned long long *array); @@ -64,10 +64,10 @@ void rrdeng_exit_mode(struct rrdengine_instance *ctx); int rrdeng_exit(struct rrdengine_instance *ctx); void rrdeng_prepare_exit(struct rrdengine_instance *ctx); -bool rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *db_instance, uuid_t *dim_uuid, time_t *first_entry_s, time_t *last_entry_s); +bool rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *si, uuid_t *dim_uuid, time_t *first_entry_s, time_t *last_entry_s); -extern STORAGE_METRICS_GROUP *rrdeng_metrics_group_get(STORAGE_INSTANCE *db_instance, uuid_t *uuid); -extern void rrdeng_metrics_group_release(STORAGE_INSTANCE *db_instance, STORAGE_METRICS_GROUP *smg); +extern STORAGE_METRICS_GROUP *rrdeng_metrics_group_get(STORAGE_INSTANCE *si, uuid_t *uuid); +extern void rrdeng_metrics_group_release(STORAGE_INSTANCE *si, STORAGE_METRICS_GROUP *smg); typedef struct rrdengine_size_statistics { size_t default_granularity_secs; @@ -221,9 +221,6 @@ struct rrdeng_cache_efficiency_stats rrdeng_get_cache_efficiency_stats(void); RRDENG_SIZE_STATS rrdeng_size_statistics(struct rrdengine_instance *ctx); size_t rrdeng_collectors_running(struct rrdengine_instance *ctx); -bool rrdeng_is_legacy(STORAGE_INSTANCE *db_instance); - -uint64_t rrdeng_disk_space_max(STORAGE_INSTANCE *db_instance); -uint64_t rrdeng_disk_space_used(STORAGE_INSTANCE *db_instance); +bool rrdeng_is_legacy(STORAGE_INSTANCE *si); #endif /* NETDATA_RRDENGINEAPI_H */ diff --git a/database/engine/rrdenginelib.c b/src/database/engine/rrdenginelib.c index dc581d98d..dc581d98d 100644 --- a/database/engine/rrdenginelib.c +++ b/src/database/engine/rrdenginelib.c diff --git a/database/engine/rrdenginelib.h b/src/database/engine/rrdenginelib.h index a0febd4f4..a0febd4f4 100644 --- a/database/engine/rrdenginelib.h +++ b/src/database/engine/rrdenginelib.h diff --git a/database/rrd.c b/src/database/rrd.c index 5b7752a5e..b664ad3ae 100644 --- a/database/rrd.c +++ b/src/database/rrd.c @@ -19,7 +19,7 @@ int default_rrd_history_entries = RRD_DEFAULT_HISTORY_ENTRIES; #ifdef ENABLE_DBENGINE RRD_MEMORY_MODE default_rrd_memory_mode = RRD_MEMORY_MODE_DBENGINE; #else -RRD_MEMORY_MODE default_rrd_memory_mode = RRD_MEMORY_MODE_SAVE; +RRD_MEMORY_MODE default_rrd_memory_mode = RRD_MEMORY_MODE_RAM; #endif int gap_when_lost_iterations_above = 1; @@ -32,15 +32,9 @@ inline const char *rrd_memory_mode_name(RRD_MEMORY_MODE id) { case RRD_MEMORY_MODE_RAM: return RRD_MEMORY_MODE_RAM_NAME; - case RRD_MEMORY_MODE_MAP: - return RRD_MEMORY_MODE_MAP_NAME; - case RRD_MEMORY_MODE_NONE: return RRD_MEMORY_MODE_NONE_NAME; - case RRD_MEMORY_MODE_SAVE: - return RRD_MEMORY_MODE_SAVE_NAME; - case RRD_MEMORY_MODE_ALLOC: return RRD_MEMORY_MODE_ALLOC_NAME; @@ -53,7 +47,7 @@ inline const char *rrd_memory_mode_name(RRD_MEMORY_MODE id) { return eng->name; } - return RRD_MEMORY_MODE_SAVE_NAME; + return RRD_MEMORY_MODE_RAM_NAME; } RRD_MEMORY_MODE rrd_memory_mode_id(const char *name) { @@ -62,7 +56,7 @@ RRD_MEMORY_MODE rrd_memory_mode_id(const char *name) { return eng->id; } - return RRD_MEMORY_MODE_SAVE; + return RRD_MEMORY_MODE_RAM; } @@ -133,28 +127,6 @@ const char *rrdset_type_name(RRDSET_TYPE chart_type) { } // ---------------------------------------------------------------------------- -// RRD - cache directory - -char *rrdhost_cache_dir_for_rrdset_alloc(RRDHOST *host, const char *id) { - char *ret = NULL; - - char b[FILENAME_MAX + 1]; - char n[FILENAME_MAX + 1]; - rrdset_strncpyz_name(b, id, FILENAME_MAX); - - snprintfz(n, FILENAME_MAX, "%s/%s", host->cache_dir, b); - ret = strdupz(n); - - if(host->rrd_memory_mode == RRD_MEMORY_MODE_MAP || host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE) { - int r = mkdir(ret, 0775); - if(r != 0 && errno != EEXIST) - netdata_log_error("Cannot create directory '%s'", ret); - } - - return ret; -} - -// ---------------------------------------------------------------------------- // RRD - string management STRING *rrd_string_strdupz(const char *s) { diff --git a/database/rrd.h b/src/database/rrd.h index 5f4bee037..3295f036c 100644 --- a/database/rrd.h +++ b/src/database/rrd.h @@ -20,14 +20,11 @@ typedef struct rrdhost RRDHOST; typedef struct rrddim RRDDIM; typedef struct rrdset RRDSET; typedef struct rrdcalc RRDCALC; -typedef struct rrdcalctemplate RRDCALCTEMPLATE; typedef struct alarm_entry ALARM_ENTRY; typedef struct rrdlabels RRDLABELS; -typedef struct rrdfamily_acquired RRDFAMILY_ACQUIRED; typedef struct rrdvar_acquired RRDVAR_ACQUIRED; -typedef struct rrdsetvar_acquired RRDSETVAR_ACQUIRED; typedef struct rrdcalc_acquired RRDCALC_ACQUIRED; typedef struct rrdhost_acquired RRDHOST_ACQUIRED; @@ -68,9 +65,7 @@ typedef enum __attribute__ ((__packed__)) storage_priority { struct rrddim_tier; #ifdef ENABLE_DBENGINE -struct rrdeng_page_descr; struct rrdengine_instance; -struct pg_cache_page_index; #endif // ---------------------------------------------------------------------------- @@ -79,8 +74,6 @@ struct pg_cache_page_index; typedef enum __attribute__ ((__packed__)) rrd_memory_mode { RRD_MEMORY_MODE_NONE = 0, RRD_MEMORY_MODE_RAM = 1, - RRD_MEMORY_MODE_MAP = 2, - RRD_MEMORY_MODE_SAVE = 3, RRD_MEMORY_MODE_ALLOC = 4, RRD_MEMORY_MODE_DBENGINE = 5, @@ -89,8 +82,6 @@ typedef enum __attribute__ ((__packed__)) rrd_memory_mode { #define RRD_MEMORY_MODE_NONE_NAME "none" #define RRD_MEMORY_MODE_RAM_NAME "ram" -#define RRD_MEMORY_MODE_MAP_NAME "map" -#define RRD_MEMORY_MODE_SAVE_NAME "save" #define RRD_MEMORY_MODE_ALLOC_NAME "alloc" #define RRD_MEMORY_MODE_DBENGINE_NAME "dbengine" @@ -110,11 +101,8 @@ struct ml_metrics_statistics { #include "daemon/common.h" #include "web/api/queries/query.h" #include "web/api/queries/rrdr.h" -#include "rrdvar.h" -#include "rrdsetvar.h" -#include "rrddimvar.h" -#include "rrdcalc.h" -#include "rrdcalctemplate.h" +#include "health/rrdvar.h" +#include "health/rrdcalc.h" #include "rrdlabels.h" #include "streaming/rrdpush.h" #include "aclk/aclk_rrdhost_state.h" @@ -134,7 +122,7 @@ struct storage_engine_query_handle { time_t start_time_s; time_t end_time_s; STORAGE_PRIORITY priority; - STORAGE_ENGINE_BACKEND backend; + STORAGE_ENGINE_BACKEND seb; STORAGE_QUERY_HANDLE *handle; }; @@ -156,7 +144,6 @@ const char *rrdset_type_name(RRDSET_TYPE chart_type); #include "contexts/rrdcontext.h" -extern bool unittest_running; extern bool dbengine_enabled; extern size_t storage_tiers; extern bool use_direct_io; @@ -194,10 +181,9 @@ extern time_t rrdset_free_obsolete_time_s; extern int libuv_worker_threads; extern bool ieee754_doubles; -#define RRD_ID_LENGTH_MAX 1000 +#define RRD_ID_LENGTH_MAX 1200 typedef long long total_number; -#define TOTAL_NUMBER_FORMAT "%lld" // ---------------------------------------------------------------------------- // algorithms types @@ -220,16 +206,6 @@ RRD_ALGORITHM rrd_algorithm_id(const char *name); const char *rrd_algorithm_name(RRD_ALGORITHM algorithm); // ---------------------------------------------------------------------------- -// RRD FAMILY - -const RRDFAMILY_ACQUIRED *rrdfamily_add_and_acquire(RRDHOST *host, const char *id); -void rrdfamily_release(RRDHOST *host, const RRDFAMILY_ACQUIRED *rfa); -void rrdfamily_index_init(RRDHOST *host); -void rrdfamily_index_destroy(RRDHOST *host); -DICTIONARY *rrdfamily_rrdvars_dict(const RRDFAMILY_ACQUIRED *rf); - - -// ---------------------------------------------------------------------------- // flags & options // options are permanent configuration options (no atomics to alter/access them) @@ -272,7 +248,7 @@ typedef enum __attribute__ ((__packed__)) rrddim_flags { // ---------------------------------------------------------------------------- // engine-specific iterator state for dimension data collection typedef struct storage_collect_handle { - STORAGE_ENGINE_BACKEND backend; + STORAGE_ENGINE_BACKEND seb; } STORAGE_COLLECT_HANDLE; // ---------------------------------------------------------------------------- @@ -280,11 +256,12 @@ typedef struct storage_collect_handle { struct rrddim_tier { STORAGE_POINT virtual_point; - STORAGE_ENGINE_BACKEND backend; + STORAGE_ENGINE_BACKEND seb; + SPINLOCK spinlock; uint32_t tier_grouping; time_t next_point_end_time_s; - STORAGE_METRIC_HANDLE *db_metric_handle; // the metric handle inside the database - STORAGE_COLLECT_HANDLE *db_collection_handle; // the data collection handle + STORAGE_METRIC_HANDLE *smh; // the metric handle inside the database + STORAGE_COLLECT_HANDLE *sch; // the data collection handle }; void rrdr_fill_tier_gap_from_smaller_tiers(RRDDIM *rd, size_t tier, time_t now_s); @@ -326,13 +303,12 @@ struct rrddim { #endif // ------------------------------------------------------------------------ - // db mode RAM, SAVE, MAP, ALLOC, NONE specifics + // db mode RAM, ALLOC, NONE specifics // TODO - they should be managed by storage engine // (RRDDIM_DB_STATE ptr to an undefined structure, and a call to clean this up during destruction) struct { size_t memsize; // the memory allocated for this dimension (without RRDDIM) - void *rd_on_file; // pointer to the header written on disk storage_number *data; // the array of values } db; @@ -382,287 +358,292 @@ size_t rrddim_size(void); #define rrddim_set_updated(rd) (rd)->collector.options |= RRDDIM_OPTION_UPDATED #define rrddim_clear_updated(rd) (rd)->collector.options &= ~RRDDIM_OPTION_UPDATED -// returns the RRDDIM cache filename, or NULL if it does not exist -const char *rrddim_cache_filename(RRDDIM *rd); - -// updated the header with the latest RRDDIM value, for memory mode MAP and SAVE -void rrddim_memory_file_update(RRDDIM *rd); - -// free the memory file structures for memory mode MAP and SAVE -void rrddim_memory_file_free(RRDDIM *rd); - -bool rrddim_memory_load_or_create_map_save(RRDSET *st, RRDDIM *rd, RRD_MEMORY_MODE memory_mode); - -// return the v019 header size of RRDDIM files -size_t rrddim_memory_file_header_size(void); - -void rrddim_memory_file_save(RRDDIM *rd); - // ------------------------------------------------------------------------ // DATA COLLECTION STORAGE OPS -STORAGE_METRICS_GROUP *rrdeng_metrics_group_get(STORAGE_INSTANCE *db_instance, uuid_t *uuid); -STORAGE_METRICS_GROUP *rrddim_metrics_group_get(STORAGE_INSTANCE *db_instance, uuid_t *uuid); -static inline STORAGE_METRICS_GROUP *storage_engine_metrics_group_get(STORAGE_ENGINE_BACKEND backend __maybe_unused, STORAGE_INSTANCE *db_instance, uuid_t *uuid) { - internal_fatal(!is_valid_backend(backend), "STORAGE: invalid backend"); +STORAGE_METRICS_GROUP *rrdeng_metrics_group_get(STORAGE_INSTANCE *si, uuid_t *uuid); +STORAGE_METRICS_GROUP *rrddim_metrics_group_get(STORAGE_INSTANCE *si, uuid_t *uuid); +static inline STORAGE_METRICS_GROUP *storage_engine_metrics_group_get(STORAGE_ENGINE_BACKEND seb __maybe_unused, STORAGE_INSTANCE *si, uuid_t *uuid) { + internal_fatal(!is_valid_backend(seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_metrics_group_get(db_instance, uuid); + if(likely(seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_metrics_group_get(si, uuid); #endif - return rrddim_metrics_group_get(db_instance, uuid); + return rrddim_metrics_group_get(si, uuid); } -void rrdeng_metrics_group_release(STORAGE_INSTANCE *db_instance, STORAGE_METRICS_GROUP *smg); -void rrddim_metrics_group_release(STORAGE_INSTANCE *db_instance, STORAGE_METRICS_GROUP *smg); -static inline void storage_engine_metrics_group_release(STORAGE_ENGINE_BACKEND backend __maybe_unused, STORAGE_INSTANCE *db_instance, STORAGE_METRICS_GROUP *smg) { - internal_fatal(!is_valid_backend(backend), "STORAGE: invalid backend"); +void rrdeng_metrics_group_release(STORAGE_INSTANCE *si, STORAGE_METRICS_GROUP *smg); +void rrddim_metrics_group_release(STORAGE_INSTANCE *si, STORAGE_METRICS_GROUP *smg); +static inline void storage_engine_metrics_group_release(STORAGE_ENGINE_BACKEND seb __maybe_unused, STORAGE_INSTANCE *si, STORAGE_METRICS_GROUP *smg) { + internal_fatal(!is_valid_backend(seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - rrdeng_metrics_group_release(db_instance, smg); + if(likely(seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + rrdeng_metrics_group_release(si, smg); else #endif - rrddim_metrics_group_release(db_instance, smg); + rrddim_metrics_group_release(si, smg); } -STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every, STORAGE_METRICS_GROUP *smg); -STORAGE_COLLECT_HANDLE *rrddim_collect_init(STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every, STORAGE_METRICS_GROUP *smg); -static inline STORAGE_COLLECT_HANDLE *storage_metric_store_init(STORAGE_ENGINE_BACKEND backend __maybe_unused, STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every, STORAGE_METRICS_GROUP *smg) { - internal_fatal(!is_valid_backend(backend), "STORAGE: invalid backend"); +STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *smh, uint32_t update_every, STORAGE_METRICS_GROUP *smg); +STORAGE_COLLECT_HANDLE *rrddim_collect_init(STORAGE_METRIC_HANDLE *smh, uint32_t update_every, STORAGE_METRICS_GROUP *smg); +static inline STORAGE_COLLECT_HANDLE *storage_metric_store_init(STORAGE_ENGINE_BACKEND seb __maybe_unused, STORAGE_METRIC_HANDLE *smh, uint32_t update_every, STORAGE_METRICS_GROUP *smg) { + internal_fatal(!is_valid_backend(seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_store_metric_init(db_metric_handle, update_every, smg); + if(likely(seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_store_metric_init(smh, update_every, smg); #endif - return rrddim_collect_init(db_metric_handle, update_every, smg); + return rrddim_collect_init(smh, update_every, smg); } void rrdeng_store_metric_next( - STORAGE_COLLECT_HANDLE *collection_handle, usec_t point_in_time_ut, + STORAGE_COLLECT_HANDLE *sch, usec_t point_in_time_ut, NETDATA_DOUBLE n, NETDATA_DOUBLE min_value, NETDATA_DOUBLE max_value, uint16_t count, uint16_t anomaly_count, SN_FLAGS flags); void rrddim_collect_store_metric( - STORAGE_COLLECT_HANDLE *collection_handle, usec_t point_in_time_ut, + STORAGE_COLLECT_HANDLE *sch, usec_t point_in_time_ut, NETDATA_DOUBLE n, NETDATA_DOUBLE min_value, NETDATA_DOUBLE max_value, uint16_t count, uint16_t anomaly_count, SN_FLAGS flags); static inline void storage_engine_store_metric( - STORAGE_COLLECT_HANDLE *collection_handle, usec_t point_in_time_ut, + STORAGE_COLLECT_HANDLE *sch, usec_t point_in_time_ut, NETDATA_DOUBLE n, NETDATA_DOUBLE min_value, NETDATA_DOUBLE max_value, uint16_t count, uint16_t anomaly_count, SN_FLAGS flags) { - internal_fatal(!is_valid_backend(collection_handle->backend), "STORAGE: invalid backend"); + internal_fatal(!is_valid_backend(sch->seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(collection_handle->backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_store_metric_next(collection_handle, point_in_time_ut, + if(likely(sch->seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_store_metric_next(sch, point_in_time_ut, n, min_value, max_value, count, anomaly_count, flags); #endif - return rrddim_collect_store_metric(collection_handle, point_in_time_ut, + return rrddim_collect_store_metric(sch, point_in_time_ut, n, min_value, max_value, count, anomaly_count, flags); } -uint64_t rrdeng_disk_space_max(STORAGE_INSTANCE *db_instance); -static inline uint64_t storage_engine_disk_space_max(STORAGE_ENGINE_BACKEND backend __maybe_unused, STORAGE_INSTANCE *db_instance __maybe_unused) { +uint64_t rrdeng_disk_space_max(STORAGE_INSTANCE *si); +static inline uint64_t storage_engine_disk_space_max(STORAGE_ENGINE_BACKEND seb __maybe_unused, STORAGE_INSTANCE *si __maybe_unused) { +#ifdef ENABLE_DBENGINE + if(likely(seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_disk_space_max(si); +#endif + + return 0; +} + +uint64_t rrdeng_disk_space_used(STORAGE_INSTANCE *si); +static inline uint64_t storage_engine_disk_space_used(STORAGE_ENGINE_BACKEND seb __maybe_unused, STORAGE_INSTANCE *si __maybe_unused) { #ifdef ENABLE_DBENGINE - if(likely(backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_disk_space_max(db_instance); + if(likely(seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_disk_space_used(si); #endif + // TODO - calculate the total host disk space for memory mode save and map return 0; } -uint64_t rrdeng_disk_space_used(STORAGE_INSTANCE *db_instance); -static inline uint64_t storage_engine_disk_space_used(STORAGE_ENGINE_BACKEND backend __maybe_unused, STORAGE_INSTANCE *db_instance __maybe_unused) { +uint64_t rrdeng_metrics(STORAGE_INSTANCE *si); +static inline uint64_t storage_engine_metrics(STORAGE_ENGINE_BACKEND seb __maybe_unused, STORAGE_INSTANCE *si __maybe_unused) { #ifdef ENABLE_DBENGINE - if(likely(backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_disk_space_used(db_instance); + if(likely(seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_metrics(si); #endif // TODO - calculate the total host disk space for memory mode save and map return 0; } -time_t rrdeng_global_first_time_s(STORAGE_INSTANCE *db_instance); -static inline time_t storage_engine_global_first_time_s(STORAGE_ENGINE_BACKEND backend __maybe_unused, STORAGE_INSTANCE *db_instance __maybe_unused) { +uint64_t rrdeng_samples(STORAGE_INSTANCE *si); +static inline uint64_t storage_engine_samples(STORAGE_ENGINE_BACKEND seb __maybe_unused, STORAGE_INSTANCE *si __maybe_unused) { +#ifdef ENABLE_DBENGINE + if(likely(seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_samples(si); +#endif + return 0; +} + + +time_t rrdeng_global_first_time_s(STORAGE_INSTANCE *si); +static inline time_t storage_engine_global_first_time_s(STORAGE_ENGINE_BACKEND seb __maybe_unused, STORAGE_INSTANCE *si __maybe_unused) { #ifdef ENABLE_DBENGINE - if(likely(backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_global_first_time_s(db_instance); + if(likely(seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_global_first_time_s(si); #endif return now_realtime_sec() - (time_t)(default_rrd_history_entries * default_rrd_update_every); } -size_t rrdeng_currently_collected_metrics(STORAGE_INSTANCE *db_instance); -static inline size_t storage_engine_collected_metrics(STORAGE_ENGINE_BACKEND backend __maybe_unused, STORAGE_INSTANCE *db_instance __maybe_unused) { +size_t rrdeng_currently_collected_metrics(STORAGE_INSTANCE *si); +static inline size_t storage_engine_collected_metrics(STORAGE_ENGINE_BACKEND seb __maybe_unused, STORAGE_INSTANCE *si __maybe_unused) { #ifdef ENABLE_DBENGINE - if(likely(backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_currently_collected_metrics(db_instance); + if(likely(seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_currently_collected_metrics(si); #endif // TODO - calculate the total host disk space for memory mode save and map return 0; } -void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_handle); -void rrddim_store_metric_flush(STORAGE_COLLECT_HANDLE *collection_handle); -static inline void storage_engine_store_flush(STORAGE_COLLECT_HANDLE *collection_handle) { - if(unlikely(!collection_handle)) +void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *sch); +void rrddim_store_metric_flush(STORAGE_COLLECT_HANDLE *sch); +static inline void storage_engine_store_flush(STORAGE_COLLECT_HANDLE *sch) { + if(unlikely(!sch)) return; - internal_fatal(!is_valid_backend(collection_handle->backend), "STORAGE: invalid backend"); + internal_fatal(!is_valid_backend(sch->seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(collection_handle->backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - rrdeng_store_metric_flush_current_page(collection_handle); + if(likely(sch->seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + rrdeng_store_metric_flush_current_page(sch); else #endif - rrddim_store_metric_flush(collection_handle); + rrddim_store_metric_flush(sch); } -int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle); -int rrddim_collect_finalize(STORAGE_COLLECT_HANDLE *collection_handle); +int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *sch); +int rrddim_collect_finalize(STORAGE_COLLECT_HANDLE *sch); // a finalization function to run after collection is over // returns 1 if it's safe to delete the dimension -static inline int storage_engine_store_finalize(STORAGE_COLLECT_HANDLE *collection_handle) { - internal_fatal(!is_valid_backend(collection_handle->backend), "STORAGE: invalid backend"); +static inline int storage_engine_store_finalize(STORAGE_COLLECT_HANDLE *sch) { + internal_fatal(!is_valid_backend(sch->seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(collection_handle->backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_store_metric_finalize(collection_handle); + if(likely(sch->seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_store_metric_finalize(sch); #endif - return rrddim_collect_finalize(collection_handle); + return rrddim_collect_finalize(sch); } -void rrdeng_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *collection_handle, int update_every); -void rrddim_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *collection_handle, int update_every); -static inline void storage_engine_store_change_collection_frequency(STORAGE_COLLECT_HANDLE *collection_handle, int update_every) { - internal_fatal(!is_valid_backend(collection_handle->backend), "STORAGE: invalid backend"); +void rrdeng_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *sch, int update_every); +void rrddim_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *sch, int update_every); +static inline void storage_engine_store_change_collection_frequency(STORAGE_COLLECT_HANDLE *sch, int update_every) { + internal_fatal(!is_valid_backend(sch->seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(collection_handle->backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - rrdeng_store_metric_change_collection_frequency(collection_handle, update_every); + if(likely(sch->seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + rrdeng_store_metric_change_collection_frequency(sch, update_every); else #endif - rrddim_store_metric_change_collection_frequency(collection_handle, update_every); + rrddim_store_metric_change_collection_frequency(sch, update_every); } // ---------------------------------------------------------------------------- // STORAGE ENGINE QUERY OPS -time_t rrdeng_metric_oldest_time(STORAGE_METRIC_HANDLE *db_metric_handle); -time_t rrddim_query_oldest_time_s(STORAGE_METRIC_HANDLE *db_metric_handle); -static inline time_t storage_engine_oldest_time_s(STORAGE_ENGINE_BACKEND backend __maybe_unused, STORAGE_METRIC_HANDLE *db_metric_handle) { - internal_fatal(!is_valid_backend(backend), "STORAGE: invalid backend"); +time_t rrdeng_metric_oldest_time(STORAGE_METRIC_HANDLE *smh); +time_t rrddim_query_oldest_time_s(STORAGE_METRIC_HANDLE *smh); +static inline time_t storage_engine_oldest_time_s(STORAGE_ENGINE_BACKEND seb __maybe_unused, STORAGE_METRIC_HANDLE *smh) { + internal_fatal(!is_valid_backend(seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_metric_oldest_time(db_metric_handle); + if(likely(seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_metric_oldest_time(smh); #endif - return rrddim_query_oldest_time_s(db_metric_handle); + return rrddim_query_oldest_time_s(smh); } -time_t rrdeng_metric_latest_time(STORAGE_METRIC_HANDLE *db_metric_handle); -time_t rrddim_query_latest_time_s(STORAGE_METRIC_HANDLE *db_metric_handle); -static inline time_t storage_engine_latest_time_s(STORAGE_ENGINE_BACKEND backend __maybe_unused, STORAGE_METRIC_HANDLE *db_metric_handle) { - internal_fatal(!is_valid_backend(backend), "STORAGE: invalid backend"); +time_t rrdeng_metric_latest_time(STORAGE_METRIC_HANDLE *smh); +time_t rrddim_query_latest_time_s(STORAGE_METRIC_HANDLE *smh); +static inline time_t storage_engine_latest_time_s(STORAGE_ENGINE_BACKEND seb __maybe_unused, STORAGE_METRIC_HANDLE *smh) { + internal_fatal(!is_valid_backend(seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_metric_latest_time(db_metric_handle); + if(likely(seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_metric_latest_time(smh); #endif - return rrddim_query_latest_time_s(db_metric_handle); + return rrddim_query_latest_time_s(smh); } void rrdeng_load_metric_init( - STORAGE_METRIC_HANDLE *db_metric_handle, struct storage_engine_query_handle *rrddim_handle, + STORAGE_METRIC_HANDLE *smh, struct storage_engine_query_handle *seqh, time_t start_time_s, time_t end_time_s, STORAGE_PRIORITY priority); void rrddim_query_init( - STORAGE_METRIC_HANDLE *db_metric_handle, struct storage_engine_query_handle *handle, + STORAGE_METRIC_HANDLE *smh, struct storage_engine_query_handle *seqh, time_t start_time_s, time_t end_time_s, STORAGE_PRIORITY priority); static inline void storage_engine_query_init( - STORAGE_ENGINE_BACKEND backend __maybe_unused, - STORAGE_METRIC_HANDLE *db_metric_handle, struct storage_engine_query_handle *handle, + STORAGE_ENGINE_BACKEND seb __maybe_unused, + STORAGE_METRIC_HANDLE *smh, struct storage_engine_query_handle *seqh, time_t start_time_s, time_t end_time_s, STORAGE_PRIORITY priority) { - internal_fatal(!is_valid_backend(backend), "STORAGE: invalid backend"); + internal_fatal(!is_valid_backend(seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - rrdeng_load_metric_init(db_metric_handle, handle, start_time_s, end_time_s, priority); + if(likely(seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + rrdeng_load_metric_init(smh, seqh, start_time_s, end_time_s, priority); else #endif - rrddim_query_init(db_metric_handle, handle, start_time_s, end_time_s, priority); + rrddim_query_init(smh, seqh, start_time_s, end_time_s, priority); } -STORAGE_POINT rrdeng_load_metric_next(struct storage_engine_query_handle *rrddim_handle); -STORAGE_POINT rrddim_query_next_metric(struct storage_engine_query_handle *handle); -static inline STORAGE_POINT storage_engine_query_next_metric(struct storage_engine_query_handle *handle) { - internal_fatal(!is_valid_backend(handle->backend), "STORAGE: invalid backend"); +STORAGE_POINT rrdeng_load_metric_next(struct storage_engine_query_handle *seqh); +STORAGE_POINT rrddim_query_next_metric(struct storage_engine_query_handle *seqh); +static inline STORAGE_POINT storage_engine_query_next_metric(struct storage_engine_query_handle *seqh) { + internal_fatal(!is_valid_backend(seqh->seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(handle->backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_load_metric_next(handle); + if(likely(seqh->seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_load_metric_next(seqh); #endif - return rrddim_query_next_metric(handle); + return rrddim_query_next_metric(seqh); } -int rrdeng_load_metric_is_finished(struct storage_engine_query_handle *rrddim_handle); -int rrddim_query_is_finished(struct storage_engine_query_handle *handle); -static inline int storage_engine_query_is_finished(struct storage_engine_query_handle *handle) { - internal_fatal(!is_valid_backend(handle->backend), "STORAGE: invalid backend"); +int rrdeng_load_metric_is_finished(struct storage_engine_query_handle *seqh); +int rrddim_query_is_finished(struct storage_engine_query_handle *seqh); +static inline int storage_engine_query_is_finished(struct storage_engine_query_handle *seqh) { + internal_fatal(!is_valid_backend(seqh->seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(handle->backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_load_metric_is_finished(handle); + if(likely(seqh->seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_load_metric_is_finished(seqh); #endif - return rrddim_query_is_finished(handle); + return rrddim_query_is_finished(seqh); } -void rrdeng_load_metric_finalize(struct storage_engine_query_handle *rrddim_handle); -void rrddim_query_finalize(struct storage_engine_query_handle *handle); -static inline void storage_engine_query_finalize(struct storage_engine_query_handle *handle) { - internal_fatal(!is_valid_backend(handle->backend), "STORAGE: invalid backend"); +void rrdeng_load_metric_finalize(struct storage_engine_query_handle *seqh); +void rrddim_query_finalize(struct storage_engine_query_handle *seqh); +static inline void storage_engine_query_finalize(struct storage_engine_query_handle *seqh) { + internal_fatal(!is_valid_backend(seqh->seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(handle->backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - rrdeng_load_metric_finalize(handle); + if(likely(seqh->seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + rrdeng_load_metric_finalize(seqh); else #endif - rrddim_query_finalize(handle); + rrddim_query_finalize(seqh); } -time_t rrdeng_load_align_to_optimal_before(struct storage_engine_query_handle *rrddim_handle); -time_t rrddim_query_align_to_optimal_before(struct storage_engine_query_handle *rrddim_handle); -static inline time_t storage_engine_align_to_optimal_before(struct storage_engine_query_handle *handle) { - internal_fatal(!is_valid_backend(handle->backend), "STORAGE: invalid backend"); +time_t rrdeng_load_align_to_optimal_before(struct storage_engine_query_handle *seqh); +time_t rrddim_query_align_to_optimal_before(struct storage_engine_query_handle *seqh); +static inline time_t storage_engine_align_to_optimal_before(struct storage_engine_query_handle *seqh) { + internal_fatal(!is_valid_backend(seqh->seb), "STORAGE: invalid backend"); #ifdef ENABLE_DBENGINE - if(likely(handle->backend == STORAGE_ENGINE_BACKEND_DBENGINE)) - return rrdeng_load_align_to_optimal_before(handle); + if(likely(seqh->seb == STORAGE_ENGINE_BACKEND_DBENGINE)) + return rrdeng_load_align_to_optimal_before(seqh); #endif - return rrddim_query_align_to_optimal_before(handle); + return rrddim_query_align_to_optimal_before(seqh); } // ------------------------------------------------------------------------ // function pointers for all APIs provided by a storage engine typedef struct storage_engine_api { // metric management - STORAGE_METRIC_HANDLE *(*metric_get)(STORAGE_INSTANCE *instance, uuid_t *uuid); - STORAGE_METRIC_HANDLE *(*metric_get_or_create)(RRDDIM *rd, STORAGE_INSTANCE *instance); + STORAGE_METRIC_HANDLE *(*metric_get)(STORAGE_INSTANCE *si, uuid_t *uuid); + STORAGE_METRIC_HANDLE *(*metric_get_or_create)(RRDDIM *rd, STORAGE_INSTANCE *si); void (*metric_release)(STORAGE_METRIC_HANDLE *); STORAGE_METRIC_HANDLE *(*metric_dup)(STORAGE_METRIC_HANDLE *); - bool (*metric_retention_by_uuid)(STORAGE_INSTANCE *db_instance, uuid_t *uuid, time_t *first_entry_s, time_t *last_entry_s); + bool (*metric_retention_by_uuid)(STORAGE_INSTANCE *si, uuid_t *uuid, time_t *first_entry_s, time_t *last_entry_s); } STORAGE_ENGINE_API; typedef struct storage_engine { - STORAGE_ENGINE_BACKEND backend; + STORAGE_ENGINE_BACKEND seb; RRD_MEMORY_MODE id; const char* name; STORAGE_ENGINE_API api; @@ -762,9 +743,6 @@ struct rrdset { int32_t update_every; // data collection frequency RRDLABELS *rrdlabels; // chart labels - DICTIONARY *rrdsetvar_root_index; // chart variables - DICTIONARY *rrddimvar_root_index; // dimension variables - // we use this dictionary to manage their allocation uint32_t version; // the metadata version (auto-increment) @@ -780,7 +758,7 @@ struct rrdset { rrd_ml_chart_t *ml_chart; - STORAGE_METRICS_GROUP *storage_metrics_groups[RRD_STORAGE_TIERS]; + STORAGE_METRICS_GROUP *smg[RRD_STORAGE_TIERS]; // ------------------------------------------------------------------------ // linking to siblings and parents @@ -830,11 +808,7 @@ struct rrdset { // (RRDSET_DB_STATE ptr to an undefined structure, and a call to clean this up during destruction) struct { - char *cache_dir; // the directory to store dimensions - void *st_on_file; // compatibility with V019 RRDSET files - int32_t entries; // total number of entries in the data set - int32_t current_entry; // the entry that is currently being updated // it goes around in a round-robin fashion } db; @@ -855,7 +829,6 @@ struct rrdset { NETDATA_DOUBLE red; // red threshold for this chart DICTIONARY *rrdvars; // RRDVAR index for this chart - const RRDFAMILY_ACQUIRED *rrdfamily; // pointer to RRDFAMILY dictionary item, this chart belongs to struct { RW_SPINLOCK spinlock; // protection for RRDCALC *base @@ -961,12 +934,7 @@ STRING *rrd_string_strdupz(const char *s); #define rrdset_number_of_dimensions(st) \ dictionary_entries((st)->rrddim_root_index) -void rrdset_memory_file_save(RRDSET *st); -void rrdset_memory_file_free(RRDSET *st); -void rrdset_memory_file_update(RRDSET *st); -const char *rrdset_cache_filename(RRDSET *st); -bool rrdset_memory_load_or_create_map_save(RRDSET *st_on_file, RRD_MEMORY_MODE memory_mode); - +#include "rrdcollector.h" #include "rrdfunctions.h" // ---------------------------------------------------------------------------- @@ -1002,19 +970,18 @@ typedef enum __attribute__ ((__packed__)) rrdhost_flags { // ACLK RRDHOST_FLAG_ACLK_STREAM_CONTEXTS = (1 << 21), // when set, we should send ACLK stream context updates - RRDHOST_FLAG_ACLK_STREAM_ALERTS = (1 << 22), // set when the receiver part is disconnected + RRDHOST_FLAG_ACLK_STREAM_ALERTS = (1 << 22), // Host should stream alerts // Metadata RRDHOST_FLAG_METADATA_UPDATE = (1 << 23), // metadata needs to be stored in the database RRDHOST_FLAG_METADATA_LABELS = (1 << 24), // metadata needs to be stored in the database RRDHOST_FLAG_METADATA_INFO = (1 << 25), // metadata needs to be stored in the database - RRDHOST_FLAG_PENDING_CONTEXT_LOAD = (1 << 26), // metadata needs to be stored in the database - RRDHOST_FLAG_CONTEXT_LOAD_IN_PROGRESS = (1 << 27), // metadata needs to be stored in the database + RRDHOST_FLAG_PENDING_CONTEXT_LOAD = (1 << 26), // Context needs to be loaded - RRDHOST_FLAG_METADATA_CLAIMID = (1 << 28), // metadata needs to be stored in the database - RRDHOST_FLAG_RRDPUSH_RECEIVER_DISCONNECTED = (1 << 29), // set when the receiver part is disconnected + RRDHOST_FLAG_METADATA_CLAIMID = (1 << 27), // metadata needs to be stored in the database + RRDHOST_FLAG_RRDPUSH_RECEIVER_DISCONNECTED = (1 << 28), // set when the receiver part is disconnected - RRDHOST_FLAG_GLOBAL_FUNCTIONS_UPDATED = (1 << 30), // set when the host has updated global functions + RRDHOST_FLAG_GLOBAL_FUNCTIONS_UPDATED = (1 << 29), // set when the host has updated global functions } RRDHOST_FLAGS; #define rrdhost_flag_check(host, flag) (__atomic_load_n(&((host)->flags), __ATOMIC_SEQ_CST) & (flag)) @@ -1035,15 +1002,11 @@ typedef enum __attribute__ ((__packed__)) { // Streaming configuration RRDHOST_OPTION_SENDER_ENABLED = (1 << 2), // set when the host is configured to send metrics to a parent + RRDHOST_OPTION_REPLICATION = (1 << 3), // when set, we support replication for this host - // Configuration options - RRDHOST_OPTION_DELETE_OBSOLETE_CHARTS = (1 << 3), // delete files of obsolete charts - RRDHOST_OPTION_DELETE_ORPHAN_HOST = (1 << 4), // delete the entire host when orphan - - RRDHOST_OPTION_REPLICATION = (1 << 5), // when set, we support replication for this host - - RRDHOST_OPTION_VIRTUAL_HOST = (1 << 6), // when set, this host is a virtual one - RRDHOST_OPTION_EPHEMERAL_HOST = (1 << 7), // when set, this host is an ephemeral one + // Other options + RRDHOST_OPTION_VIRTUAL_HOST = (1 << 4), // when set, this host is a virtual one + RRDHOST_OPTION_EPHEMERAL_HOST = (1 << 5), // when set, this host is an ephemeral one } RRDHOST_OPTIONS; #define rrdhost_option_check(host, flag) ((host)->options & (flag)) @@ -1141,8 +1104,6 @@ typedef struct health { time_t health_delay_up_to; // a timestamp to delay alarms processing up to STRING *health_default_exec; // the full path of the alarms notifications program STRING *health_default_recipient; // the default recipient for all alarms - uint32_t health_default_warn_repeat_every; // the default value for the interval between repeating warning notifications - uint32_t health_default_crit_repeat_every; // the default value for the interval between repeating critical notifications unsigned int health_enabled; // 1 when this host has health enabled bool use_summary_for_notifications; // whether or not to use the summary field as a subject for notifications } HEALTH; @@ -1199,7 +1160,6 @@ struct rrdhost { STRING *hostname; // the hostname of this host STRING *registry_hostname; // the registry hostname for this host STRING *os; // the O/S type of the host - STRING *tags; // tags for this host STRING *timezone; // the timezone of the host STRING *abbrev_timezone; // the abbriviated timezone of the host STRING *program_name; // the program name that collects metrics for this host @@ -1222,7 +1182,7 @@ struct rrdhost { struct { RRD_MEMORY_MODE mode; // the db mode for this tier STORAGE_ENGINE *eng; // the storage engine API for this tier - STORAGE_INSTANCE *instance; // the db instance for this tier + STORAGE_INSTANCE *si; // the db instance for this tier uint32_t tier_grouping; // tier 0 iterations aggregated on this tier } db[RRD_STORAGE_TIERS]; @@ -1301,9 +1261,6 @@ struct rrdhost { // all RRDCALCs are primarily allocated and linked here DICTIONARY *rrdcalc_root_index; - // templates of alarms - DICTIONARY *rrdcalctemplate_root_index; - ALARM_LOG health_log; // alarms historical events (event log) uint32_t health_last_processed_id; // the last processed health id from the log uint32_t health_max_unique_id; // the max alarm log unique id given for the host @@ -1333,7 +1290,6 @@ struct rrdhost { DICTIONARY *rrdset_root_index; // the host's charts index (by id) DICTIONARY *rrdset_root_index_name; // the host's charts index (by name) - DICTIONARY *rrdfamily_root_index; // the host's chart families index DICTIONARY *rrdvars; // the host's chart variables index // this includes custom host variables @@ -1357,8 +1313,6 @@ struct rrdhost { netdata_mutex_t aclk_state_lock; aclk_rrdhost_state aclk_state; - DICTIONARY *configurable_plugins; // configurable plugins for this host - struct rrdhost *next; struct rrdhost *prev; }; @@ -1367,7 +1321,6 @@ extern RRDHOST *localhost; #define rrdhost_hostname(host) string2str((host)->hostname) #define rrdhost_registry_hostname(host) string2str((host)->registry_hostname) #define rrdhost_os(host) string2str((host)->os) -#define rrdhost_tags(host) string2str((host)->tags) #define rrdhost_timezone(host) string2str((host)->timezone) #define rrdhost_abbrev_timezone(host) string2str((host)->abbrev_timezone) #define rrdhost_program_name(host) string2str((host)->program_name) @@ -1416,7 +1369,7 @@ extern netdata_rwlock_t rrd_rwlock; // ---------------------------------------------------------------------------- -bool is_storage_engine_shared(STORAGE_INSTANCE *engine); +bool is_storage_engine_shared(STORAGE_INSTANCE *si); void rrdset_index_init(RRDHOST *host); void rrdset_index_destroy(RRDHOST *host); @@ -1442,9 +1395,8 @@ RRDHOST *rrdhost_find_or_create( const char *timezone, const char *abbrev_timezone, int32_t utc_offset, - const char *tags, - const char *program_name, - const char *program_version, + const char *prog_name, + const char *prog_version, int update_every, long history, RRD_MEMORY_MODE mode, @@ -1489,13 +1441,9 @@ RRDSET *rrdset_create_custom(RRDHOST *host rrdset_create(localhost, type, id, name, family, context, title, units, plugin, module, priority, update_every, chart_type) void rrdhost_free_all(void); -void rrdhost_save_all(void); -void rrdhost_cleanup_all(void); void rrdhost_system_info_free(struct rrdhost_system_info *system_info); void rrdhost_free___while_having_rrd_wrlock(RRDHOST *host, bool force); -void rrdhost_save_charts(RRDHOST *host); -void rrdhost_delete_charts(RRDHOST *host); int rrdhost_should_be_removed(RRDHOST *host, RRDHOST *protected_host, time_t now_s); @@ -1624,8 +1572,6 @@ void rrdhost_set_is_parent_label(void); // ---------------------------------------------------------------------------- // RRD internal functions -void rrdset_delete_files(RRDSET *st); -void rrdset_save(RRDSET *st); void rrdset_free(RRDSET *st); void rrddim_free(RRDSET *st, RRDDIM *rd); @@ -1633,17 +1579,15 @@ void rrddim_free(RRDSET *st, RRDDIM *rd); #ifdef NETDATA_RRD_INTERNALS char *rrdhost_cache_dir_for_rrdset_alloc(RRDHOST *host, const char *id); -const char *rrdset_cache_dir(RRDSET *st); void rrdset_reset(RRDSET *st); -void rrdset_delete_obsolete_dimensions(RRDSET *st); #endif /* NETDATA_RRD_INTERNALS */ void set_host_properties( RRDHOST *host, int update_every, RRD_MEMORY_MODE memory_mode, const char *registry_hostname, - const char *os, const char *tags, const char *tzone, const char *abbrev_tzone, int32_t utc_offset, - const char *program_name, const char *program_version); + const char *os, const char *tzone, const char *abbrev_tzone, int32_t utc_offset, + const char *prog_name, const char *prog_version); size_t get_tier_grouping(size_t tier); void store_metric_collection_completed(void); diff --git a/database/rrdhost.c b/src/database/rrdhost.c index a3c272153..9c818618f 100644 --- a/database/rrdhost.c +++ b/src/database/rrdhost.c @@ -33,9 +33,9 @@ time_t rrdset_free_obsolete_time_s = 3600; time_t rrdhost_free_orphan_time_s = 3600; time_t rrdhost_free_ephemeral_time_s = 86400; -bool is_storage_engine_shared(STORAGE_INSTANCE *engine __maybe_unused) { +bool is_storage_engine_shared(STORAGE_INSTANCE *si __maybe_unused) { #ifdef ENABLE_DBENGINE - if(!rrdeng_is_legacy(engine)) + if(!rrdeng_is_legacy(si)) return true; #endif @@ -178,15 +178,6 @@ static inline RRDHOST *rrdhost_index_add_hostname(RRDHOST *host) { // ---------------------------------------------------------------------------- // RRDHOST - internal helpers -static inline void rrdhost_init_tags(RRDHOST *host, const char *tags) { - if(host->tags && tags && !strcmp(rrdhost_tags(host), tags)) - return; - - STRING *old = host->tags; - host->tags = string_strdupz((tags && *tags)?tags:NULL); - string_freez(old); -} - static inline void rrdhost_init_hostname(RRDHOST *host, const char *hostname, bool add_to_index) { if(unlikely(hostname && !*hostname)) hostname = NULL; @@ -229,9 +220,9 @@ static inline void rrdhost_init_timezone(RRDHOST *host, const char *timezone, co } void set_host_properties(RRDHOST *host, int update_every, RRD_MEMORY_MODE memory_mode, - const char *registry_hostname, const char *os, const char *tags, - const char *tzone, const char *abbrev_tzone, int32_t utc_offset, const char *program_name, - const char *program_version) + const char *registry_hostname, const char *os, const char *tzone, + const char *abbrev_tzone, int32_t utc_offset, const char *prog_name, + const char *prog_version) { host->rrd_update_every = update_every; @@ -239,10 +230,9 @@ void set_host_properties(RRDHOST *host, int update_every, RRD_MEMORY_MODE memory rrdhost_init_os(host, os); rrdhost_init_timezone(host, tzone, abbrev_tzone, utc_offset); - rrdhost_init_tags(host, tags); - host->program_name = string_strdupz((program_name && *program_name) ? program_name : "unknown"); - host->program_version = string_strdupz((program_version && *program_version) ? program_version : "unknown"); + host->program_name = string_strdupz((prog_name && *prog_name) ? prog_name : "unknown"); + host->program_version = string_strdupz((prog_version && *prog_version) ? prog_version : "unknown"); host->registry_hostname = string_strdupz((registry_hostname && *registry_hostname) ? registry_hostname : rrdhost_hostname(host)); } @@ -287,9 +277,8 @@ static RRDHOST *rrdhost_create( const char *timezone, const char *abbrev_timezone, int32_t utc_offset, - const char *tags, - const char *program_name, - const char *program_version, + const char *prog_name, + const char *prog_version, int update_every, long entries, RRD_MEMORY_MODE memory_mode, @@ -326,7 +315,9 @@ int is_legacy = 1; strncpyz(host->machine_guid, guid, GUID_LEN + 1); set_host_properties(host, (update_every > 0)?update_every:1, memory_mode, registry_hostname, os, - tags, timezone, abbrev_timezone, utc_offset, program_name, program_version); + timezone, abbrev_timezone, utc_offset, + prog_name, + prog_version); rrdhost_init_hostname(host, hostname, false); @@ -337,7 +328,7 @@ int is_legacy = 1; netdata_mutex_init(&host->receiver_lock); if (likely(!archived)) { - rrdfunctions_host_init(host); + rrd_functions_host_init(host); host->last_connected = now_realtime_sec(); host->rrdlabels = rrdlabels_create(); rrdhost_initialize_rrdpush_sender( @@ -356,8 +347,6 @@ int is_legacy = 1; switch(memory_mode) { default: case RRD_MEMORY_MODE_ALLOC: - case RRD_MEMORY_MODE_MAP: - case RRD_MEMORY_MODE_SAVE: case RRD_MEMORY_MODE_RAM: if(host->rrdpush_seconds_to_replicate > (time_t) host->rrd_history_entries * (time_t) host->rrd_update_every) host->rrdpush_seconds_to_replicate = (time_t) host->rrd_history_entries * (time_t) host->rrd_update_every; @@ -371,12 +360,6 @@ int is_legacy = 1; rrdset_index_init(host); - if(config_get_boolean(CONFIG_SECTION_DB, "delete obsolete charts files", 1)) - rrdhost_option_set(host, RRDHOST_OPTION_DELETE_OBSOLETE_CHARTS); - - if(config_get_boolean(CONFIG_SECTION_DB, "delete orphan hosts files", 1) && !is_localhost) - rrdhost_option_set(host, RRDHOST_OPTION_DELETE_ORPHAN_HOST); - char filename[FILENAME_MAX + 1]; if(is_localhost) host->cache_dir = strdupz(netdata_configured_cache_dir); @@ -390,8 +373,8 @@ int is_legacy = 1; host->cache_dir = strdupz(filename); } - if((host->rrd_memory_mode == RRD_MEMORY_MODE_MAP || host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || - (host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_legacy))) { + if(host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_legacy) + { int r = mkdir(host->cache_dir, 0775); if(r != 0 && errno != EEXIST) nd_log(NDLS_DAEMON, NDLP_CRIT, @@ -409,8 +392,6 @@ int is_legacy = 1; else error_report("Host machine GUID %s is not valid", host->machine_guid); - rrdfamily_index_init(host); - rrdcalctemplate_index_init(host); rrdcalc_rrdhost_index_init(host); if (host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) { @@ -436,13 +417,13 @@ int is_legacy = 1; host->db[0].tier_grouping = get_tier_grouping(0); ret = rrdeng_init( - (struct rrdengine_instance **)&host->db[0].instance, + (struct rrdengine_instance **)&host->db[0].si, dbenginepath, default_rrdeng_disk_quota_mb, 0); // may fail here for legacy dbengine initialization if(ret == 0) { - rrdeng_readiness_wait((struct rrdengine_instance *)host->db[0].instance); + rrdeng_readiness_wait((struct rrdengine_instance *)host->db[0].si); // assign the rest of the shared storage instances to it // to allow them collect its metrics too @@ -450,7 +431,7 @@ int is_legacy = 1; for(size_t tier = 1; tier < storage_tiers ; tier++) { host->db[tier].mode = RRD_MEMORY_MODE_DBENGINE; host->db[tier].eng = storage_engine_get(host->db[tier].mode); - host->db[tier].instance = (STORAGE_INSTANCE *) multidb_ctx[tier]; + host->db[tier].si = (STORAGE_INSTANCE *) multidb_ctx[tier]; host->db[tier].tier_grouping = get_tier_grouping(tier); } } @@ -459,7 +440,7 @@ int is_legacy = 1; for(size_t tier = 0; tier < storage_tiers ; tier++) { host->db[tier].mode = RRD_MEMORY_MODE_DBENGINE; host->db[tier].eng = storage_engine_get(host->db[tier].mode); - host->db[tier].instance = (STORAGE_INSTANCE *)multidb_ctx[tier]; + host->db[tier].si = (STORAGE_INSTANCE *)multidb_ctx[tier]; host->db[tier].tier_grouping = get_tier_grouping(tier); } } @@ -483,7 +464,7 @@ int is_legacy = 1; else { host->db[0].mode = host->rrd_memory_mode; host->db[0].eng = storage_engine_get(host->db[0].mode); - host->db[0].instance = NULL; + host->db[0].si = NULL; host->db[0].tier_grouping = get_tier_grouping(0); #ifdef ENABLE_DBENGINE @@ -491,7 +472,7 @@ int is_legacy = 1; for(size_t tier = 1; tier < storage_tiers ; tier++) { host->db[tier].mode = RRD_MEMORY_MODE_DBENGINE; host->db[tier].eng = storage_engine_get(host->db[tier].mode); - host->db[tier].instance = (STORAGE_INSTANCE *) multidb_ctx[tier]; + host->db[tier].si = (STORAGE_INSTANCE *) multidb_ctx[tier]; host->db[tier].tier_grouping = get_tier_grouping(tier); } #endif @@ -541,7 +522,6 @@ int is_legacy = 1; "Host '%s' (at registry as '%s') with guid '%s' initialized" ", os '%s'" ", timezone '%s'" - ", tags '%s'" ", program_name '%s'" ", program_version '%s'" ", update every %d" @@ -558,7 +538,6 @@ int is_legacy = 1; , host->machine_guid , rrdhost_os(host) , rrdhost_timezone(host) - , rrdhost_tags(host) , rrdhost_program_name(host) , rrdhost_program_version(host) , host->rrd_update_every @@ -573,9 +552,6 @@ int is_legacy = 1; , string2str(host->health.health_default_recipient) ); - host->configurable_plugins = dyncfg_dictionary_create(); - dictionary_register_delete_callback(host->configurable_plugins, plugin_del_cb, NULL); - if(!archived) { metaqueue_host_update_info(host); rrdhost_load_rrdcontext_data(host); @@ -588,28 +564,27 @@ int is_legacy = 1; } static void rrdhost_update(RRDHOST *host - , const char *hostname - , const char *registry_hostname - , const char *guid - , const char *os - , const char *timezone - , const char *abbrev_timezone - , int32_t utc_offset - , const char *tags - , const char *program_name - , const char *program_version - , int update_every - , long history - , RRD_MEMORY_MODE mode - , unsigned int health_enabled - , unsigned int rrdpush_enabled - , char *rrdpush_destination - , char *rrdpush_api_key - , char *rrdpush_send_charts_matching - , bool rrdpush_enable_replication - , time_t rrdpush_seconds_to_replicate - , time_t rrdpush_replication_step - , struct rrdhost_system_info *system_info + , const char *hostname + , const char *registry_hostname + , const char *guid + , const char *os + , const char *timezone + , const char *abbrev_timezone + , int32_t utc_offset + , const char *prog_name + , const char *prog_version + , int update_every + , long history + , RRD_MEMORY_MODE mode + , unsigned int health_enabled + , unsigned int rrdpush_enabled + , char *rrdpush_destination + , char *rrdpush_api_key + , char *rrdpush_send_charts_matching + , bool rrdpush_enable_replication + , time_t rrdpush_seconds_to_replicate + , time_t rrdpush_replication_step + , struct rrdhost_system_info *system_info ) { UNUSED(guid); @@ -641,23 +616,25 @@ static void rrdhost_update(RRDHOST *host rrdhost_index_add_hostname(host); } - if(strcmp(rrdhost_program_name(host), program_name) != 0) { + if(strcmp(rrdhost_program_name(host), prog_name) != 0) { nd_log(NDLS_DAEMON, NDLP_NOTICE, "Host '%s' switched program name from '%s' to '%s'", - rrdhost_hostname(host), rrdhost_program_name(host), program_name); + rrdhost_hostname(host), rrdhost_program_name(host), + prog_name); STRING *t = host->program_name; - host->program_name = string_strdupz(program_name); + host->program_name = string_strdupz(prog_name); string_freez(t); } - if(strcmp(rrdhost_program_version(host), program_version) != 0) { + if(strcmp(rrdhost_program_version(host), prog_version) != 0) { nd_log(NDLS_DAEMON, NDLP_NOTICE, "Host '%s' switched program version from '%s' to '%s'", - rrdhost_hostname(host), rrdhost_program_version(host), program_version); + rrdhost_hostname(host), rrdhost_program_version(host), + prog_version); STRING *t = host->program_version; - host->program_version = string_strdupz(program_version); + host->program_version = string_strdupz(prog_version); string_freez(t); } @@ -683,9 +660,6 @@ static void rrdhost_update(RRDHOST *host host->rrd_history_entries, history); - // update host tags - rrdhost_init_tags(host, tags); - if(!host->rrdvars) host->rrdvars = rrdvariables_create(); @@ -694,7 +668,7 @@ static void rrdhost_update(RRDHOST *host if (rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED)) { rrdhost_flag_clear(host, RRDHOST_FLAG_ARCHIVED); - rrdfunctions_host_init(host); + rrd_functions_host_init(host); if(!host->rrdlabels) host->rrdlabels = rrdlabels_create(); @@ -708,8 +682,6 @@ static void rrdhost_update(RRDHOST *host rrdpush_api_key, rrdpush_send_charts_matching); - rrdfamily_index_init(host); - rrdcalctemplate_index_init(host); rrdcalc_rrdhost_index_init(host); if(rrdpush_enable_replication) @@ -732,29 +704,28 @@ static void rrdhost_update(RRDHOST *host } RRDHOST *rrdhost_find_or_create( - const char *hostname - , const char *registry_hostname - , const char *guid - , const char *os - , const char *timezone - , const char *abbrev_timezone - , int32_t utc_offset - , const char *tags - , const char *program_name - , const char *program_version - , int update_every - , long history - , RRD_MEMORY_MODE mode - , unsigned int health_enabled - , unsigned int rrdpush_enabled - , char *rrdpush_destination - , char *rrdpush_api_key - , char *rrdpush_send_charts_matching - , bool rrdpush_enable_replication - , time_t rrdpush_seconds_to_replicate - , time_t rrdpush_replication_step - , struct rrdhost_system_info *system_info - , bool archived + const char *hostname + , const char *registry_hostname + , const char *guid + , const char *os + , const char *timezone + , const char *abbrev_timezone + , int32_t utc_offset + , const char *prog_name + , const char *prog_version + , int update_every + , long history + , RRD_MEMORY_MODE mode + , unsigned int health_enabled + , unsigned int rrdpush_enabled + , char *rrdpush_destination + , char *rrdpush_api_key + , char *rrdpush_send_charts_matching + , bool rrdpush_enable_replication + , time_t rrdpush_seconds_to_replicate + , time_t rrdpush_replication_step + , struct rrdhost_system_info *system_info + , bool archived ) { RRDHOST *host = rrdhost_find_by_guid(guid); if (unlikely(host && host->rrd_memory_mode != mode && rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED))) { @@ -784,9 +755,8 @@ RRDHOST *rrdhost_find_or_create( , timezone , abbrev_timezone , utc_offset - , tags - , program_name - , program_version + , prog_name + , prog_version , update_every , history , mode @@ -813,9 +783,8 @@ RRDHOST *rrdhost_find_or_create( , timezone , abbrev_timezone , utc_offset - , tags - , program_name - , program_version + , prog_name + , prog_version , update_every , history , mode @@ -904,23 +873,12 @@ void dbengine_init(char *hostname) { struct dbengine_initialization tiers_init[RRD_STORAGE_TIERS] = {}; + bool tiers_adjusted = false; size_t created_tiers = 0; char dbenginepath[FILENAME_MAX + 1]; char dbengineconfig[200 + 1]; int divisor = 1; for(size_t tier = 0; tier < storage_tiers ;tier++) { - if(tier == 0) - snprintfz(dbenginepath, FILENAME_MAX, "%s/dbengine", netdata_configured_cache_dir); - else - snprintfz(dbenginepath, FILENAME_MAX, "%s/dbengine-tier%zu", netdata_configured_cache_dir, tier); - - int ret = mkdir(dbenginepath, 0775); - if (ret != 0 && errno != EEXIST) { - nd_log(NDLS_DAEMON, NDLP_CRIT, - "DBENGINE on '%s': cannot create directory '%s'", - hostname, dbenginepath); - break; - } if(tier > 0) divisor *= 2; @@ -949,10 +907,7 @@ void dbengine_init(char *hostname) { else if(strcmp(bf, "full") == 0) backfill = RRD_BACKFILL_FULL; else if(strcmp(bf, "none") == 0) backfill = RRD_BACKFILL_NONE; else { - nd_log(NDLS_DAEMON, NDLP_WARNING, - "DBENGINE: unknown backfill value '%s', assuming 'new'", - bf); - + nd_log(NDLS_DAEMON, NDLP_WARNING, "DBENGINE: unknown backfill value '%s', assuming 'new'", bf); config_set(CONFIG_SECTION_DB, dbengineconfig, "new"); backfill = RRD_BACKFILL_NEW; } @@ -965,8 +920,21 @@ void dbengine_init(char *hostname) { storage_tiers_grouping_iterations[tier] = 1; nd_log(NDLS_DAEMON, NDLP_WARNING, "DBENGINE on '%s': dbengine tier %zu gives aggregation of more than 65535 points of tier 0. " - "Disabling tiers above %zu", + "Disabling tiers %zu and above", hostname, tier, tier); + storage_tiers = tier; + tiers_adjusted = true; + break; + } + + if(tier == 0) + snprintfz(dbenginepath, FILENAME_MAX, "%s/dbengine", netdata_configured_cache_dir); + else + snprintfz(dbenginepath, FILENAME_MAX, "%s/dbengine-tier%zu", netdata_configured_cache_dir, tier); + + int ret = mkdir(dbenginepath, 0775); + if (ret != 0 && errno != EEXIST) { + nd_log(NDLS_DAEMON, NDLP_CRIT, "DBENGINE on '%s': cannot create directory '%s'", hostname, dbenginepath); break; } @@ -986,6 +954,8 @@ void dbengine_init(char *hostname) { else dbengine_tier_init(&tiers_init[tier]); } + if (tiers_adjusted) + config_set_number(CONFIG_SECTION_DB, "storage tiers", storage_tiers); for(size_t tier = 0; tier < storage_tiers ;tier++) { void *ptr; @@ -1033,7 +1003,7 @@ void dbengine_init(char *hostname) { int rrd_init(char *hostname, struct rrdhost_system_info *system_info, bool unittest) { rrdhost_init(); - if (unlikely(sql_init_database(DB_CHECK_NONE, system_info ? 0 : 1))) { + if (unlikely(sql_init_meta_database(DB_CHECK_NONE, system_info ? 0 : 1))) { if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) { set_late_global_environment(system_info); fatal("Failed to initialize SQLite"); @@ -1051,7 +1021,6 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info, bool unitt dbengine_enabled = true; } else { - health_init(); rrdpush_init(); if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE || rrdpush_receiver_needs_dbengine()) { @@ -1093,13 +1062,12 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info, bool unitt , netdata_configured_timezone , netdata_configured_abbrev_timezone , netdata_configured_utc_offset - , "" , program_name , program_version , default_rrd_update_every , default_rrd_history_entries , default_rrd_memory_mode - , default_health_enabled + , health_plugin_enabled() , default_rrdpush_enabled , default_rrdpush_destination , default_rrdpush_api_key @@ -1112,23 +1080,34 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info, bool unitt , 0 ); - if (unlikely(!localhost)) { + if (unlikely(!localhost)) return 1; + + dyncfg_host_init(localhost); + + if(!unittest) { + health_plugin_init(); } // we register this only on localhost // for the other nodes, the origin server should register it - rrd_collector_started(); // this creates a collector that runs for as long as netdata runs - rrd_function_add(localhost, NULL, "streaming", 10, - RRDFUNCTIONS_STREAMING_HELP, true, - rrdhost_function_streaming, NULL); + rrd_function_add_inline(localhost, NULL, "streaming", 10, + RRDFUNCTIONS_PRIORITY_DEFAULT + 1, RRDFUNCTIONS_STREAMING_HELP, "top", + HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA, + rrdhost_function_streaming); + + rrd_function_add_inline(localhost, NULL, "netdata-api-calls", 10, + RRDFUNCTIONS_PRIORITY_DEFAULT + 2, RRDFUNCTIONS_PROGRESS_HELP, "top", + HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA, + rrdhost_function_progress); if (likely(system_info)) { - migrate_localhost(&localhost->host_uuid); + detect_machine_guid_change(&localhost->host_uuid); sql_aclk_sync_init(); web_client_api_v1_management_init(); } - return localhost==NULL; + + return 0; } // ---------------------------------------------------------------------------- @@ -1261,16 +1240,15 @@ void rrdhost_free___while_having_rrd_wrlock(RRDHOST *host, bool force) { #ifdef ENABLE_DBENGINE for(size_t tier = 0; tier < storage_tiers ;tier++) { if(host->db[tier].mode == RRD_MEMORY_MODE_DBENGINE - && host->db[tier].instance - && !is_storage_engine_shared(host->db[tier].instance)) - rrdeng_prepare_exit((struct rrdengine_instance *)host->db[tier].instance); + && host->db[tier].si + && !is_storage_engine_shared(host->db[tier].si)) + rrdeng_prepare_exit((struct rrdengine_instance *)host->db[tier].si); } #endif // delete all the RRDSETs of the host rrdset_index_destroy(host); rrdcalc_rrdhost_index_destroy(host); - rrdcalctemplate_index_destroy(host); // cleanup ML resources ml_host_delete(host); @@ -1282,9 +1260,9 @@ void rrdhost_free___while_having_rrd_wrlock(RRDHOST *host, bool force) { #ifdef ENABLE_DBENGINE for(size_t tier = 0; tier < storage_tiers ;tier++) { if(host->db[tier].mode == RRD_MEMORY_MODE_DBENGINE - && host->db[tier].instance - && !is_storage_engine_shared(host->db[tier].instance)) - rrdeng_exit((struct rrdengine_instance *)host->db[tier].instance); + && host->db[tier].si + && !is_storage_engine_shared(host->db[tier].si)) + rrdeng_exit((struct rrdengine_instance *)host->db[tier].si); } #endif @@ -1303,7 +1281,6 @@ void rrdhost_free___while_having_rrd_wrlock(RRDHOST *host, bool force) { pthread_mutex_destroy(&host->aclk_state_lock); freez(host->aclk_state.claimed_id); freez(host->aclk_state.prev_claimed_id); - string_freez(host->tags); rrdlabels_destroy(host->rrdlabels); string_freez(host->os); string_freez(host->timezone); @@ -1321,11 +1298,10 @@ void rrdhost_free___while_having_rrd_wrlock(RRDHOST *host, bool force) { simple_pattern_free(host->rrdpush_send_charts_matching); freez(host->node_id); - rrdfamily_index_destroy(host); - rrdfunctions_host_destroy(host); + rrd_functions_host_destroy(host); rrdvariables_destroy(host->rrdvars); if (host == localhost) - rrdvariables_destroy(health_rrdvars); + health_plugin_destroy(); rrdhost_destroy_rrdcontexts(host); @@ -1356,26 +1332,6 @@ void rrd_finalize_collection_for_all_hosts(void) { dfe_done(host); } -// ---------------------------------------------------------------------------- -// RRDHOST - save host files - -void rrdhost_save_charts(RRDHOST *host) { - if(!host) return; - - nd_log(NDLS_DAEMON, NDLP_DEBUG, - "RRD: 'host:%s' saving / closing database...", - rrdhost_hostname(host)); - - RRDSET *st; - - // we get a write lock - // to ensure only one thread is saving the database - rrdset_foreach_write(st, host) { - rrdset_save(st); - } - rrdset_foreach_done(st); -} - struct rrdhost_system_info *rrdhost_labels_to_system_info(RRDLABELS *labels) { struct rrdhost_system_info *info = callocz(1, sizeof(struct rrdhost_system_info)); info->hops = 1; @@ -1476,6 +1432,9 @@ static void rrdhost_load_auto_labels(void) { rrdlabels_add(labels, "_is_parent", (localhost->connected_children_count > 0) ? "true" : "false", RRDLABEL_SRC_AUTO); + rrdlabels_add(labels, "_hostname", string2str(localhost->hostname), RRDLABEL_SRC_AUTO); + rrdlabels_add(labels, "_os", string2str(localhost->os), RRDLABEL_SRC_AUTO); + if (localhost->rrdpush_send_destination) rrdlabels_add(labels, "_streams_to", localhost->rrdpush_send_destination, RRDLABEL_SRC_AUTO); } @@ -1564,6 +1523,12 @@ void reload_host_labels(void) { } void rrdhost_finalize_collection(RRDHOST *host) { + ND_LOG_STACK lgs[] = { + ND_LOG_FIELD_TXT(NDF_NIDL_NODE, rrdhost_hostname(host)), + ND_LOG_FIELD_END(), + }; + ND_LOG_STACK_PUSH(lgs); + nd_log(NDLS_DAEMON, NDLP_DEBUG, "RRD: 'host:%s' stopping data collection...", rrdhost_hostname(host)); @@ -1575,103 +1540,6 @@ void rrdhost_finalize_collection(RRDHOST *host) { } // ---------------------------------------------------------------------------- -// RRDHOST - delete host files - -void rrdhost_delete_charts(RRDHOST *host) { - if(!host) return; - - nd_log(NDLS_DAEMON, NDLP_DEBUG, - "RRD: 'host:%s' deleting disk files...", - rrdhost_hostname(host)); - - RRDSET *st; - - if(host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || host->rrd_memory_mode == RRD_MEMORY_MODE_MAP) { - // we get a write lock - // to ensure only one thread is saving the database - rrdset_foreach_write(st, host){ - rrdset_delete_files(st); - } - rrdset_foreach_done(st); - } - - recursively_delete_dir(host->cache_dir, "left over host"); -} - -// ---------------------------------------------------------------------------- -// RRDHOST - cleanup host files - -void rrdhost_cleanup_charts(RRDHOST *host) { - if(!host) return; - - nd_log(NDLS_DAEMON, NDLP_DEBUG, - "RRD: 'host:%s' cleaning up disk files...", - rrdhost_hostname(host)); - - RRDSET *st; - uint32_t rrdhost_delete_obsolete_charts = rrdhost_option_check(host, RRDHOST_OPTION_DELETE_OBSOLETE_CHARTS); - - // we get a write lock - // to ensure only one thread is saving the database - rrdset_foreach_write(st, host) { - - if(rrdhost_delete_obsolete_charts && rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE)) - rrdset_delete_files(st); - - else if(rrdhost_delete_obsolete_charts && rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE_DIMENSIONS)) - rrdset_delete_obsolete_dimensions(st); - - else - rrdset_save(st); - - } - rrdset_foreach_done(st); -} - - -// ---------------------------------------------------------------------------- -// RRDHOST - save all hosts to disk - -void rrdhost_save_all(void) { - nd_log(NDLS_DAEMON, NDLP_DEBUG, - "RRD: saving databases [%zu hosts(s)]...", - rrdhost_hosts_available()); - - rrd_rdlock(); - - RRDHOST *host; - rrdhost_foreach_read(host) - rrdhost_save_charts(host); - - rrd_unlock(); -} - -// ---------------------------------------------------------------------------- -// RRDHOST - save or delete all hosts from disk - -void rrdhost_cleanup_all(void) { - nd_log(NDLS_DAEMON, NDLP_DEBUG, - "RRD: cleaning up database [%zu hosts(s)]...", - rrdhost_hosts_available()); - - rrd_rdlock(); - - RRDHOST *host; - rrdhost_foreach_read(host) { - if (host != localhost && rrdhost_option_check(host, RRDHOST_OPTION_DELETE_ORPHAN_HOST) && !host->receiver - /* don't delete multi-host DB host files */ - && !(host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_storage_engine_shared(host->db[0].instance)) - ) - rrdhost_delete_charts(host); - else - rrdhost_cleanup_charts(host); - } - - rrd_unlock(); -} - - -// ---------------------------------------------------------------------------- // RRDHOST - set system info from environment variables // system_info fields must be heap allocated or NULL int rrdhost_set_system_info_variable(struct rrdhost_system_info *system_info, char *name, char *value) { @@ -1843,6 +1711,10 @@ void rrdhost_status(RRDHOST *host, time_t now, RRDHOST_STATUS *s) { RRDHOST_FLAGS flags = __atomic_load_n(&host->flags, __ATOMIC_RELAXED); + // --- dyncfg --- + + s->dyncfg.status = dyncfg_available_for_rrdhost(host) ? RRDHOST_DYNCFG_STATUS_AVAILABLE : RRDHOST_DYNCFG_STATUS_UNAVAILABLE; + // --- db --- bool online = rrdhost_is_online(host); @@ -1852,7 +1724,7 @@ void rrdhost_status(RRDHOST *host, time_t now, RRDHOST_STATUS *s) { s->db.instances = host->rrdctx.instances; s->db.contexts = dictionary_entries(host->rrdctx.contexts); if(!s->db.first_time_s || !s->db.last_time_s || !s->db.metrics || !s->db.instances || !s->db.contexts || - (flags & (RRDHOST_FLAG_PENDING_CONTEXT_LOAD|RRDHOST_FLAG_CONTEXT_LOAD_IN_PROGRESS))) + (flags & (RRDHOST_FLAG_PENDING_CONTEXT_LOAD))) s->db.status = RRDHOST_DB_STATUS_INITIALIZING; else s->db.status = RRDHOST_DB_STATUS_QUERYABLE; diff --git a/database/rrdlabels.c b/src/database/rrdlabels.c index 69ee55526..9ea1d7c58 100644 --- a/database/rrdlabels.c +++ b/src/database/rrdlabels.c @@ -448,7 +448,7 @@ __attribute__((constructor)) void initialize_labels_keys_char_map(void) { label_names_char_map[' '] = '_'; label_names_char_map['\\'] = '/'; - // create the spaces map + // create the space map for(i = 0; i < 256 ;i++) label_spaces_char_map[i] = (isspace(i) || iscntrl(i) || !isprint(i))?1:0; @@ -460,8 +460,8 @@ __attribute__((constructor)) void initialize_label_stats(void) { dictionary_stats_category_rrdlabels.memory.values = 0; } -size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_size, unsigned char *char_map, bool utf, const char *empty, size_t *multibyte_length) { - if(unlikely(!dst_size)) return 0; +size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_size, const unsigned char *char_map, bool utf, const char *empty, size_t *multibyte_length) { + if(unlikely(!src || !dst_size)) return 0; if(unlikely(!src || !*src)) { strncpyz((char *)dst, empty, dst_size); @@ -476,7 +476,7 @@ size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_si // make room for the final string termination unsigned char *end = &d[dst_size - 1]; - // copy while converting, but keep only one white space + // copy while converting, but keep only one space // we start wil last_is_space = 1 to skip leading spaces int last_is_space = 1; @@ -671,8 +671,11 @@ void rrdlabels_destroy(RRDLABELS *labels) freez(labels); } +// // Check in labels to see if we have the key specified in label -static RRDLABEL *rrdlabels_find_label_with_key_unsafe(RRDLABELS *labels, RRDLABEL *label) +// same_value indicates if the value should also be matched +// +static RRDLABEL *rrdlabels_find_label_with_key_unsafe(RRDLABELS *labels, RRDLABEL *label, bool same_value) { if (unlikely(!labels)) return NULL; @@ -683,7 +686,7 @@ static RRDLABEL *rrdlabels_find_label_with_key_unsafe(RRDLABELS *labels, RRDLABE RRDLABEL *found = NULL; while ((PValue = JudyLFirstThenNext(labels->JudyL, &Index, &first_then_next))) { RRDLABEL *lb = (RRDLABEL *)Index; - if (lb->index.key == label->index.key && lb != label) { + if (lb->index.key == label->index.key && ((lb == label) == same_value)) { found = (RRDLABEL *)Index; break; } @@ -718,7 +721,7 @@ static void labels_add_already_sanitized(RRDLABELS *labels, const char *key, con new_ls |= RRDLABEL_FLAG_NEW; *((RRDLABEL_SRC *)PValue) = new_ls; - RRDLABEL *old_label_with_same_key = rrdlabels_find_label_with_key_unsafe(labels, new_label); + RRDLABEL *old_label_with_same_key = rrdlabels_find_label_with_key_unsafe(labels, new_label, false); if (old_label_with_same_key) { (void) JudyLDel(&labels->JudyL, (Word_t) old_label_with_same_key, PJE0); delete_label(old_label_with_same_key); @@ -982,6 +985,25 @@ int rrdlabels_walkthrough_read(RRDLABELS *labels, int (*callback)(const char *na return ret; } +static SIMPLE_PATTERN_RESULT rrdlabels_walkthrough_read_sp(RRDLABELS *labels, SIMPLE_PATTERN_RESULT (*callback)(const char *name, const char *value, RRDLABEL_SRC ls, void *data), void *data) +{ + SIMPLE_PATTERN_RESULT ret = SP_NOT_MATCHED; + + if(unlikely(!labels || !callback)) return 0; + + RRDLABEL *lb; + RRDLABEL_SRC ls; + lfe_start_read(labels, lb, ls) + { + ret = callback(string2str(lb->index.key), string2str(lb->index.value), ls, data); + if (ret != SP_NOT_MATCHED) + break; + } + lfe_done(labels); + + return ret; +} + // ---------------------------------------------------------------------------- // rrdlabels_migrate_to_these() // migrate an existing label list to a new list @@ -1027,6 +1049,39 @@ void rrdlabels_migrate_to_these(RRDLABELS *dst, RRDLABELS *src) { spinlock_unlock(&dst->spinlock); } +// +// +// Return the common labels count in labels1, labels2 +// +size_t rrdlabels_common_count(RRDLABELS *labels1, RRDLABELS *labels2) +{ + if (!labels1 || !labels2) + return 0; + + if (labels1 == labels2) + return rrdlabels_entries(labels1); + + RRDLABEL *label; + RRDLABEL_SRC ls; + + spinlock_lock(&labels1->spinlock); + spinlock_lock(&labels2->spinlock); + + size_t count = 0; + lfe_start_nolock(labels2, label, ls) + { + RRDLABEL *old_label_with_key = rrdlabels_find_label_with_key_unsafe(labels1, label, true); + if (old_label_with_key) + count++; + } + lfe_done_nolock(); + + spinlock_unlock(&labels2->spinlock); + spinlock_unlock(&labels1->spinlock); + return count; +} + + void rrdlabels_copy(RRDLABELS *dst, RRDLABELS *src) { if (!dst || !src || (dst == src)) @@ -1042,7 +1097,7 @@ void rrdlabels_copy(RRDLABELS *dst, RRDLABELS *src) bool update_statistics = false; lfe_start_nolock(src, label, ls) { - RRDLABEL *old_label_with_key = rrdlabels_find_label_with_key_unsafe(dst, label); + RRDLABEL *old_label_with_key = rrdlabels_find_label_with_key_unsafe(dst, label, false); Pvoid_t *PValue = JudyLIns(&dst->JudyL, (Word_t)label, PJE0); if(unlikely(!PValue || PValue == PJERR)) fatal("RRDLABELS: corrupted labels array"); @@ -1083,18 +1138,16 @@ struct simple_pattern_match_name_value { char equal; }; -static int simple_pattern_match_name_only_callback(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) { +static SIMPLE_PATTERN_RESULT simple_pattern_match_name_only_callback(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) { struct simple_pattern_match_name_value *t = (struct simple_pattern_match_name_value *)data; (void)value; // we return -1 to stop the walkthrough on first match t->searches++; - if(simple_pattern_matches(t->pattern, name)) return -1; - - return 0; + return simple_pattern_matches_extract(t->pattern, name, NULL, 0); } -static int simple_pattern_match_name_and_value_callback(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) { +static SIMPLE_PATTERN_RESULT simple_pattern_match_name_and_value_callback(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) { struct simple_pattern_match_name_value *t = (struct simple_pattern_match_name_value *)data; // we return -1 to stop the walkthrough on first match @@ -1118,13 +1171,10 @@ static int simple_pattern_match_name_and_value_callback(const char *name, const *dst = '\0'; t->searches++; - if(simple_pattern_matches_length_extract(t->pattern, tmp, dst - tmp, NULL, 0) == SP_MATCHED_POSITIVE) - return -1; - - return 0; + return simple_pattern_matches_length_extract(t->pattern, tmp, dst - tmp, NULL, 0); } -bool rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pattern, char equal, size_t *searches) { +SIMPLE_PATTERN_RESULT rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pattern, char equal, size_t *searches) { if (!labels) return false; struct simple_pattern_match_name_value t = { @@ -1133,12 +1183,12 @@ bool rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pa .equal = equal }; - int ret = rrdlabels_walkthrough_read(labels, equal?simple_pattern_match_name_and_value_callback:simple_pattern_match_name_only_callback, &t); + SIMPLE_PATTERN_RESULT ret = rrdlabels_walkthrough_read_sp(labels, equal?simple_pattern_match_name_and_value_callback:simple_pattern_match_name_only_callback, &t); if(searches) *searches = t.searches; - return (ret == -1)?true:false; + return ret; } bool rrdlabels_match_simple_pattern(RRDLABELS *labels, const char *simple_pattern_txt) { @@ -1155,11 +1205,11 @@ bool rrdlabels_match_simple_pattern(RRDLABELS *labels, const char *simple_patter } } - bool ret = rrdlabels_match_simple_pattern_parsed(labels, pattern, equal, NULL); + SIMPLE_PATTERN_RESULT ret = rrdlabels_match_simple_pattern_parsed(labels, pattern, equal, NULL); simple_pattern_free(pattern); - return ret; + return ret == SP_MATCHED_POSITIVE; } @@ -1311,6 +1361,153 @@ void rrdset_update_rrdlabels(RRDSET *st, RRDLABELS *new_rrdlabels) { rrdset_metadata_updated(st); } +struct pattern_array *pattern_array_allocate() +{ + struct pattern_array *pa = callocz(1, sizeof(*pa)); + return pa; +} + +static void pattern_array_add_lblkey_with_sp(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *sp) +{ + if (!pa || !key || !sp) + return; + + STRING *string_key = string_strdupz(key); + Pvoid_t *Pvalue = JudyLIns(&pa->JudyL, (Word_t) string_key, PJE0); + if (!Pvalue) { + string_freez(string_key); + simple_pattern_free(sp); + return; + } + + struct pattern_array_item *pai; + if (*Pvalue) { + pai = *Pvalue; + } else { + *Pvalue = pai = callocz(1, sizeof(*pai)); + pa->key_count++; + } + + pai->size++; + Pvalue = JudyLIns(&pai->JudyL, (Word_t) pai->size, PJE0); + if (!Pvalue) { + simple_pattern_free(sp); + return; + } + + *Pvalue = sp; +} + +bool pattern_array_label_match( + struct pattern_array *pa, + RRDLABELS *labels, + char eq, + size_t *searches) +{ + if (!pa || !labels) + return true; + + Pvoid_t *Pvalue; + Word_t Index = 0; + bool first_then_next = true; + while ((Pvalue = JudyLFirstThenNext(pa->JudyL, &Index, &first_then_next))) { + // for each label key in the patterns array + + struct pattern_array_item *pai = *Pvalue; + SIMPLE_PATTERN_RESULT match = SP_NOT_MATCHED ; + for (Word_t i = 1; i <= pai->size; i++) { + // for each pattern in the label key pattern list + + if (!(Pvalue = JudyLGet(pai->JudyL, i, PJE0)) || !*Pvalue) + continue; + + match = rrdlabels_match_simple_pattern_parsed(labels, (SIMPLE_PATTERN *)(*Pvalue), eq, searches); + + if(match != SP_NOT_MATCHED) + break; + } + + if (match != SP_MATCHED_POSITIVE) + return false; + } + return true; +} + +struct pattern_array *pattern_array_add_key_simple_pattern(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *pattern) +{ + if (unlikely(!pattern || !key)) + return pa; + + if (!pa) + pa = pattern_array_allocate(); + + pattern_array_add_lblkey_with_sp(pa, key, pattern); + return pa; +} + +struct pattern_array *pattern_array_add_simple_pattern(struct pattern_array *pa, SIMPLE_PATTERN *pattern, char sep) +{ + if (unlikely(!pattern)) + return pa; + + if (!pa) + pa = pattern_array_allocate(); + + char *label_key; + while (pattern && (label_key = simple_pattern_iterate(&pattern))) { + char key[RRDLABELS_MAX_NAME_LENGTH + 1], *key_sep; + + if (unlikely(!label_key || !(key_sep = strchr(label_key, sep)))) + return pa; + + *key_sep = '\0'; + strncpyz(key, label_key, RRDLABELS_MAX_NAME_LENGTH); + *key_sep = sep; + + pattern_array_add_lblkey_with_sp(pa, key, string_to_simple_pattern(label_key)); + } + return pa; +} + +struct pattern_array *pattern_array_add_key_value(struct pattern_array *pa, const char *key, const char *value, char sep) +{ + if (unlikely(!key || !value)) + return pa; + + if (!pa) + pa = pattern_array_allocate(); + + char label_key[RRDLABELS_MAX_NAME_LENGTH + RRDLABELS_MAX_VALUE_LENGTH + 2]; + snprintfz(label_key, sizeof(label_key) - 1, "%s%c%s", key, sep, value); + pattern_array_add_lblkey_with_sp( + pa, key, simple_pattern_create(label_key, SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true)); + return pa; +} + +void pattern_array_free(struct pattern_array *pa) +{ + if (!pa) + return; + + Pvoid_t *Pvalue; + Word_t Index = 0; + while ((Pvalue = JudyLFirst(pa->JudyL, &Index, PJE0))) { + struct pattern_array_item *pai = *Pvalue; + + for (Word_t i = 1; i <= pai->size; i++) { + if (!(Pvalue = JudyLGet(pai->JudyL, i, PJE0))) + continue; + simple_pattern_free((SIMPLE_PATTERN *) (*Pvalue)); + } + JudyLFreeArray(&(pai->JudyL), PJE0); + + string_freez((STRING *)Index); + (void) JudyLDel(&(pa->JudyL), Index, PJE0); + freez(pai); + Index = 0; + } + freez(pa); +} // ---------------------------------------------------------------------------- // rrdlabels unit test @@ -1497,6 +1694,7 @@ static int rrdlabels_walkthrough_index_read(RRDLABELS *labels, int (*callback)(c ret = callback(string2str(lb->index.key), string2str(lb->index.value), ls, index, data); if (ret < 0) break; + index++; } lfe_done(labels); @@ -1513,6 +1711,70 @@ static int unittest_dump_labels(const char *name, const char *value, RRDLABEL_SR return 1; } +static int rrdlabels_unittest_pattern_check() +{ + fprintf(stderr, "\n%s() tests\n", __FUNCTION__); + int rc = 0; + + RRDLABELS *labels = NULL; + + labels = rrdlabels_create(); + + rrdlabels_add(labels, "_module", "disk_detection", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "_plugin", "super_plugin", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "key1", "value1", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "key2", "caterpillar", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "key3", "elephant", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "key4", "value4", RRDLABEL_SRC_CONFIG); + + bool match; + struct pattern_array *pa = pattern_array_add_key_value(NULL, "_module", "wrong_module", '='); + match = pattern_array_label_match(pa, labels, '=', NULL); + // This should not match: _module in ("wrong_module") + if (match) + rc++; + + pattern_array_add_key_value(pa, "_module", "disk_detection", '='); + match = pattern_array_label_match(pa, labels, '=', NULL); + // This should match: _module in ("wrong_module","disk_detection") + if (!match) + rc++; + + pattern_array_add_key_value(pa, "key1", "wrong_key1_value", '='); + match = pattern_array_label_match(pa, labels, '=', NULL); + // This should not match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value") + if (match) + rc++; + + pattern_array_add_key_value(pa, "key1", "value1", '='); + match = pattern_array_label_match(pa, labels, '=', NULL); + // This should match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value", "value1") + if (!match) + rc++; + + SIMPLE_PATTERN *sp = simple_pattern_create("key2=cat*,!d*", SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true); + pattern_array_add_lblkey_with_sp(pa, "key2", sp); + + sp = simple_pattern_create("key3=*phant", SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true); + pattern_array_add_lblkey_with_sp(pa, "key3", sp); + + match = pattern_array_label_match(pa, labels, '=', NULL); + // This should match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value", "value1") AND key2 in ("cat* !d*") AND key3 in ("*phant") + if (!match) + rc++; + + rrdlabels_add(labels, "key3", "now_fail", RRDLABEL_SRC_CONFIG); + match = pattern_array_label_match(pa, labels, '=', NULL); + // This should not match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value", "value1") AND key2 in ("cat* !d*") AND key3 in ("*phant") + if (match) + rc++; + + pattern_array_free(pa); + rrdlabels_destroy(labels); + + return rc; +} + static int rrdlabels_unittest_migrate_check() { fprintf(stderr, "\n%s() tests\n", __FUNCTION__); @@ -1594,6 +1856,69 @@ static int rrdlabels_unittest_migrate_check() return rc; } +struct pattern_array *trim_and_add_key_to_values(struct pattern_array *pa, const char *key, STRING *input); +static int rrdlabels_unittest_check_pattern_list(RRDLABELS *labels, const char *pattern, bool expected) { + fprintf(stderr, "rrdlabels_match_simple_pattern(labels, \"%s\") ... ", pattern); + + STRING *str = string_strdupz(pattern); + struct pattern_array *pa = trim_and_add_key_to_values(NULL, NULL, str); + + bool ret = pattern_array_label_match(pa, labels, '=', NULL); + + fprintf(stderr, "%s, got %s expected %s\n", (ret == expected)?"OK":"FAILED", ret?"true":"false", expected?"true":"false"); + + string_freez(str); + pattern_array_free(pa); + + return (ret == expected)?0:1; +} + +static int rrdlabels_unittest_host_chart_labels() { + fprintf(stderr, "\n%s() tests\n", __FUNCTION__); + + int errors = 0; + + RRDLABELS *labels = rrdlabels_create(); + rrdlabels_add(labels, "_hostname", "hostname1", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "_os", "linux", RRDLABEL_SRC_CONFIG); + rrdlabels_add(labels, "_distro", "ubuntu", RRDLABEL_SRC_CONFIG); + + // match a single key + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*", false); + + // conflicting keys (some positive, some negative) + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=* _os=!*", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!* _os=*", false); + + // the user uses a key that is not there + errors += rrdlabels_unittest_check_pattern_list(labels, "_not_a_key=*", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_not_a_key=!*", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_not_a_key=* _hostname=* _os=*", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_not_a_key=!* _hostname=* _os=*", false); + + // positive and negative matches on the same key + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*invalid* !*bad* *name*", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*name* !*invalid* !*bad*", true); + + // positive and negative matches on the same key with catch all + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*invalid* !*bad* *", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=* !*invalid* !*bad*", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*invalid* !*name* *", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=* !*invalid* !*name*", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*name* !*", true); + + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*name* _os=l*", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_os=l* hostname=!*name*", false); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*name* _hostname=*", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*name* _os=l*", true); + errors += rrdlabels_unittest_check_pattern_list(labels, "_os=l* _hostname=*name*", true); + + rrdlabels_destroy(labels); + + return errors; +} + static int rrdlabels_unittest_check_simple_pattern(RRDLABELS *labels, const char *pattern, bool expected) { fprintf(stderr, "rrdlabels_match_simple_pattern(labels, \"%s\") ... ", pattern); @@ -1686,9 +2011,12 @@ int rrdlabels_unittest(void) { errors += rrdlabels_unittest_sanitization(); errors += rrdlabels_unittest_add_pairs(); errors += rrdlabels_unittest_simple_pattern(); + errors += rrdlabels_unittest_host_chart_labels(); errors += rrdlabels_unittest_double_check(); errors += rrdlabels_unittest_migrate_check(); + errors += rrdlabels_unittest_pattern_check(); fprintf(stderr, "%d errors found\n", errors); return errors; } + diff --git a/database/rrdlabels.h b/src/database/rrdlabels.h index 64a0e2384..88b35cf92 100644 --- a/database/rrdlabels.h +++ b/src/database/rrdlabels.h @@ -5,6 +5,16 @@ #include "rrd.h" +struct pattern_array_item { + Word_t size; + Pvoid_t JudyL; +}; + +struct pattern_array { + Word_t key_count; + Pvoid_t JudyL; +}; + typedef enum __attribute__ ((__packed__)) rrdlabel_source { RRDLABEL_SRC_AUTO = (1 << 0), // set when Netdata found the label by some automation RRDLABEL_SRC_CONFIG = (1 << 1), // set when the user configured the label @@ -20,7 +30,7 @@ typedef enum __attribute__ ((__packed__)) rrdlabel_source { #define RRDLABEL_FLAG_INTERNAL (RRDLABEL_FLAG_OLD | RRDLABEL_FLAG_NEW | RRDLABEL_FLAG_DONT_DELETE) -size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_size, unsigned char *char_map, bool utf, const char *empty, size_t *multibyte_length); +size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_size, const unsigned char *char_map, bool utf, const char *empty, size_t *multibyte_length); RRDLABELS *rrdlabels_create(void); void rrdlabels_destroy(RRDLABELS *labels_dict); @@ -41,7 +51,7 @@ int rrdlabels_walkthrough_read(RRDLABELS *labels, int (*callback)(const char *na void rrdlabels_log_to_buffer(RRDLABELS *labels, BUFFER *wb); bool rrdlabels_match_simple_pattern(RRDLABELS *labels, const char *simple_pattern_txt); -bool rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pattern, char equal, size_t *searches); +SIMPLE_PATTERN_RESULT rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pattern, char equal, size_t *searches); int rrdlabels_to_buffer(RRDLABELS *labels, BUFFER *wb, const char *before_each, const char *equal, const char *quote, const char *between_them, bool (*filter_callback)(const char *name, const char *value, RRDLABEL_SRC ls, void *data), void *filter_data, void (*name_sanitizer)(char *dst, const char *src, size_t dst_size), @@ -50,6 +60,20 @@ void rrdlabels_to_buffer_json_members(RRDLABELS *labels, BUFFER *wb); void rrdlabels_migrate_to_these(RRDLABELS *dst, RRDLABELS *src); void rrdlabels_copy(RRDLABELS *dst, RRDLABELS *src); +size_t rrdlabels_common_count(RRDLABELS *labels1, RRDLABELS *labels2); + +struct pattern_array *pattern_array_allocate(); +struct pattern_array * +pattern_array_add_key_value(struct pattern_array *pa, const char *key, const char *value, char sep); +bool pattern_array_label_match( + struct pattern_array *pa, + RRDLABELS *labels, + char eq, + size_t *searches); +struct pattern_array *pattern_array_add_simple_pattern(struct pattern_array *pa, SIMPLE_PATTERN *pattern, char sep); +struct pattern_array * +pattern_array_add_key_simple_pattern(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *pattern); +void pattern_array_free(struct pattern_array *pa); int rrdlabels_unittest(void); diff --git a/database/rrdset.c b/src/database/rrdset.c index f4bb48aa7..fc206585d 100644 --- a/database/rrdset.c +++ b/src/database/rrdset.c @@ -2,7 +2,6 @@ #define NETDATA_RRD_INTERNALS #include "rrd.h" -#include <sched.h> #include "storage_engine.h" @@ -270,34 +269,19 @@ static void rrdset_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v rw_spinlock_init(&st->alerts.spinlock); - if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) { - if(!rrdset_memory_load_or_create_map_save(st, st->rrd_memory_mode)) { - netdata_log_info("Failed to use db mode %s for chart '%s', falling back to ram mode.", (st->rrd_memory_mode == RRD_MEMORY_MODE_MAP)?"map":"save", rrdset_name(st)); - st->rrd_memory_mode = RRD_MEMORY_MODE_RAM; - } - } - // initialize the db tiers { for(size_t tier = 0; tier < storage_tiers ; tier++) { STORAGE_ENGINE *eng = st->rrdhost->db[tier].eng; if(!eng) continue; - st->storage_metrics_groups[tier] = storage_engine_metrics_group_get(eng->backend, host->db[tier].instance, &st->chart_uuid); + st->smg[tier] = storage_engine_metrics_group_get(eng->seb, host->db[tier].si, &st->chart_uuid); } } rrddim_index_init(st); - // chart variables - we need this for data collection to work (collector given chart variables) - not only health - rrdsetvar_index_init(st); - - if (host->health.health_enabled) { - st->rrdfamily = rrdfamily_add_and_acquire(host, rrdset_family(st)); - st->rrdvars = rrdvariables_create(); - rrddimvar_index_init(st); - } - + st->rrdvars = rrdvariables_create(); st->rrdlabels = rrdlabels_create(); rrdset_update_permanent_labels(st); @@ -312,6 +296,14 @@ static void rrdset_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v } void rrdset_finalize_collection(RRDSET *st, bool dimensions_too) { + ND_LOG_STACK lgs[] = { + ND_LOG_FIELD_TXT(NDF_NIDL_NODE, rrdhost_hostname(st->rrdhost)), + ND_LOG_FIELD_TXT(NDF_NIDL_CONTEXT, rrdset_context(st)), + ND_LOG_FIELD_TXT(NDF_NIDL_INSTANCE, rrdset_name(st)), + ND_LOG_FIELD_END(), + }; + ND_LOG_STACK_PUSH(lgs); + RRDHOST *host = st->rrdhost; rrdset_flag_set(st, RRDSET_FLAG_COLLECTION_FINISHED); @@ -327,9 +319,9 @@ void rrdset_finalize_collection(RRDSET *st, bool dimensions_too) { STORAGE_ENGINE *eng = st->rrdhost->db[tier].eng; if(!eng) continue; - if(st->storage_metrics_groups[tier]) { - storage_engine_metrics_group_release(eng->backend, host->db[tier].instance, st->storage_metrics_groups[tier]); - st->storage_metrics_groups[tier] = NULL; + if(st->smg[tier]) { + storage_engine_metrics_group_release(eng->seb, host->db[tier].si, st->smg[tier]); + st->smg[tier] = NULL; } } @@ -353,40 +345,26 @@ static void rrdset_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, v // release the collector info dictionary_destroy(st->functions_view); - rrdcalc_unlink_all_rrdset_alerts(st); + rrdcalc_unlink_and_delete_all_rrdset_alerts(st); // ------------------------------------------------------------------------ // the order of destruction is important here - // 1. delete RRDDIMVAR index - this will speed up the destruction of RRDDIMs - // because each dimension loops to find its own variables in this index. - // There are no references to the items on this index from the dimensions. - // To find their own, they have to walk-through the dictionary. - rrddimvar_index_destroy(st); // destroy the rrddimvar index - - // 2. delete RRDSETVAR index - rrdsetvar_index_destroy(st); // destroy the rrdsetvar index - - // 3. delete RRDVAR index after the above, to avoid triggering its garbage collector (they have references on this) + // 1. delete RRDVAR index after the above, to avoid triggering its garbage collector (they have references on this) rrdvariables_destroy(st->rrdvars); // free all variables and destroy the rrdvar dictionary - // 4. delete RRDFAMILY - this has to be last, because RRDDIMVAR and RRDSETVAR need the reference counter - rrdfamily_release(host, st->rrdfamily); // release the acquired rrdfamily -- has to be after all variables - - // 5. delete RRDDIMs, now their variables are not existing, so this is fast + // 2. delete RRDDIMs, now their variables are not existing, so this is fast rrddim_index_destroy(st); // free all the dimensions and destroy the dimensions index - // 6. this has to be after the dimensions are freed, but before labels are freed (contexts need the labels) + // 3. this has to be after the dimensions are freed, but before labels are freed (contexts need the labels) rrdcontext_removed_rrdset(st); // let contexts know - // 7. destroy the chart labels + // 4. destroy the chart labels rrdlabels_destroy(st->rrdlabels); // destroy the labels, after letting the contexts know - // 8. destroy the ml handle + // 5. destroy the ml handle ml_chart_delete(st); - rrdset_memory_file_free(st); // remove files of db mode save and map - // ------------------------------------------------------------------------ // free it @@ -403,7 +381,6 @@ static void rrdset_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, v string_freez(st->module_name); freez(st->exporting_flags); - freez(st->db.cache_dir); } // the item to be inserted, is already in the dictionary @@ -470,8 +447,6 @@ static bool rrdset_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, if(old_family != st->family) ctr->react_action |= RRDSET_REACT_UPDATED; string_freez(old_family); - - // TODO - we should rename RRDFAMILY variables } if(ctr->context && *ctr->context) { @@ -652,16 +627,10 @@ int rrdset_reset_name(RRDSET *st, const char *name) { rrdset_index_del_name(host, st); string_freez(st->name); st->name = name_string; - rrdsetvar_rename_all(st); } else st->name = name_string; - RRDDIM *rd; - rrddim_foreach_read(rd, st) - rrddimvar_rename_all(rd); - rrddim_foreach_done(rd); - rrdset_index_add_name(host, st); rrdset_flag_clear(st, RRDSET_FLAG_EXPORTING_SEND); @@ -888,7 +857,7 @@ void rrdset_reset(RRDSET *st) { if(!rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) { for(size_t tier = 0; tier < storage_tiers ;tier++) - storage_engine_store_flush(rd->tiers[tier].db_collection_handle); + storage_engine_store_flush(rd->tiers[tier].sch); } } rrddim_foreach_done(rd); @@ -904,12 +873,9 @@ inline long align_entries_to_pagesize(RRD_MEMORY_MODE mode, long entries) { if(entries < 5) entries = 5; if(entries > RRD_HISTORY_ENTRIES_MAX) entries = RRD_HISTORY_ENTRIES_MAX; - if(mode == RRD_MEMORY_MODE_MAP || mode == RRD_MEMORY_MODE_SAVE || mode == RRD_MEMORY_MODE_RAM) { + if(mode == RRD_MEMORY_MODE_RAM) { long header_size = 0; - if(mode == RRD_MEMORY_MODE_MAP || mode == RRD_MEMORY_MODE_SAVE) - header_size = (long)rrddim_memory_file_header_size(); - long page = (long)sysconf(_SC_PAGESIZE); long size = (long)(header_size + entries * sizeof(storage_number)); if (unlikely(size % page)) { @@ -946,62 +912,6 @@ void rrdset_free(RRDSET *st) { rrdset_index_del(st->rrdhost, st); } -void rrdset_save(RRDSET *st) { - rrdset_memory_file_save(st); - - RRDDIM *rd; - rrddim_foreach_read(rd, st) - rrddim_memory_file_save(rd); - rrddim_foreach_done(rd); -} - -void rrdset_delete_files(RRDSET *st) { - RRDDIM *rd; - - netdata_log_info("Deleting chart '%s' ('%s') from disk...", rrdset_id(st), rrdset_name(st)); - - if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) { - const char *cache_filename = rrdset_cache_filename(st); - if(cache_filename) { - netdata_log_info("Deleting chart header file '%s'.", cache_filename); - if (unlikely(unlink(cache_filename) == -1)) - netdata_log_error("Cannot delete chart header file '%s'", cache_filename); - } - else - netdata_log_error("Cannot find the cache filename of chart '%s'", rrdset_id(st)); - } - - rrddim_foreach_read(rd, st) { - const char *cache_filename = rrddim_cache_filename(rd); - if(!cache_filename) continue; - - netdata_log_info("Deleting dimension file '%s'.", cache_filename); - if(unlikely(unlink(cache_filename) == -1)) - netdata_log_error("Cannot delete dimension file '%s'", cache_filename); - } - rrddim_foreach_done(rd); - - if(st->db.cache_dir) - recursively_delete_dir(st->db.cache_dir, "left-over chart"); -} - -void rrdset_delete_obsolete_dimensions(RRDSET *st) { - RRDDIM *rd; - - netdata_log_info("Deleting dimensions of chart '%s' ('%s') from disk...", rrdset_id(st), rrdset_name(st)); - - rrddim_foreach_read(rd, st) { - if(rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)) { - const char *cache_filename = rrddim_cache_filename(rd); - if(!cache_filename) continue; - netdata_log_info("Deleting dimension file '%s'.", cache_filename); - if(unlikely(unlink(cache_filename) == -1)) - netdata_log_error("Cannot delete dimension file '%s'", cache_filename); - } - } - rrddim_foreach_done(rd); -} - // ---------------------------------------------------------------------------- // RRDSET - create a chart @@ -1267,7 +1177,7 @@ void store_metric_at_tier(RRDDIM *rd, size_t tier, struct rrddim_tier *t, STORAG if (likely(!storage_point_is_unset(t->virtual_point))) { storage_engine_store_metric( - t->db_collection_handle, + t->sch, t->next_point_end_time_s * USEC_PER_SEC, t->virtual_point.sum, t->virtual_point.min, @@ -1278,7 +1188,7 @@ void store_metric_at_tier(RRDDIM *rd, size_t tier, struct rrddim_tier *t, STORAG } else { storage_engine_store_metric( - t->db_collection_handle, + t->sch, t->next_point_end_time_s * USEC_PER_SEC, NAN, NAN, @@ -1357,7 +1267,7 @@ void rrddim_store_metric(RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n, #endif // NETDATA_LOG_COLLECTION_ERRORS // store the metric on tier 0 - storage_engine_store_metric(rd->tiers[0].db_collection_handle, point_end_time_ut, + storage_engine_store_metric(rd->tiers[0].sch, point_end_time_ut, n, 0, 0, 1, 0, flags); @@ -1377,7 +1287,7 @@ void rrddim_store_metric(RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n, }; for(size_t tier = 1; tier < storage_tiers ;tier++) { - if(unlikely(!rd->tiers[tier].db_metric_handle)) continue; + if(unlikely(!rd->tiers[tier].smh)) continue; struct rrddim_tier *t = &rd->tiers[tier]; @@ -1718,8 +1628,7 @@ void rrdset_timed_done(RRDSET *st, struct timeval now, bool pending_rrdset_next) // check if we will re-write the entire data set if(unlikely(dt_usec(&st->last_collected_time, &st->last_updated) > st->db.entries * update_every_ut && st->rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE)) { - netdata_log_info( - "'%s': too old data (last updated at %"PRId64".%"PRId64", last collected at %"PRId64".%"PRId64"). " + nd_log_daemon(NDLP_DEBUG, "'%s': too old data (last updated at %" PRId64 ".%" PRId64 ", last collected at %" PRId64 ".%" PRId64 "). " "Resetting it. Will not store the next entry.", rrdset_id(st), (int64_t)st->last_updated.tv_sec, @@ -2101,18 +2010,6 @@ void rrdset_timed_done(RRDSET *st, struct timeval now, bool pending_rrdset_next) // ALL DONE ABOUT THE DATA UPDATE // -------------------------------------------------------------------- - if(unlikely(st->rrd_memory_mode == RRD_MEMORY_MODE_MAP)) { - // update the memory mapped files with the latest values - - rrdset_memory_file_update(st); - - for(dim_id = 0, rda = rda_base; dim_id < rda_slots ; ++dim_id, ++rda) { - rd = rda->rd; - if(unlikely(!rd)) continue; - rrddim_memory_file_update(rd); - } - } - for(dim_id = 0, rda = rda_base; dim_id < rda_slots ; ++dim_id, ++rda) { rd = rda->rd; if(unlikely(!rd)) continue; @@ -2141,9 +2038,9 @@ time_t rrdset_set_update_every_s(RRDSET *st, time_t update_every_s) { RRDDIM *rd; rrddim_foreach_read(rd, st) { for (size_t tier = 0; tier < storage_tiers; tier++) { - if (rd->tiers[tier].db_collection_handle) + if (rd->tiers[tier].sch) storage_engine_store_change_collection_frequency( - rd->tiers[tier].db_collection_handle, + rd->tiers[tier].sch, (int)(st->rrdhost->db[tier].tier_grouping * st->update_every)); } } @@ -2151,211 +2048,3 @@ time_t rrdset_set_update_every_s(RRDSET *st, time_t update_every_s) { return prev_update_every_s; } - -// ---------------------------------------------------------------------------- -// compatibility layer for RRDSET files v019 - -#define RRDSET_MAGIC_V019 "NETDATA RRD SET FILE V019" -#define RRD_ID_LENGTH_MAX_V019 200 - -struct avl_element_v019 { - void *avl_link[2]; - signed char avl_balance; -}; -struct avl_tree_type_v019 { - void *root; - int (*compar)(void *a, void *b); -}; -struct avl_tree_lock_v019 { - struct avl_tree_type_v019 avl_tree; - pthread_rwlock_t rwlock; -}; -struct rrdset_map_save_v019 { - struct avl_element_v019 avl; // ignored - struct avl_element_v019 avlname; // ignored - char id[RRD_ID_LENGTH_MAX_V019 + 1]; // check to reset all - update on load - void *name; // ignored - void *unused_ptr; // ignored - void *type; // ignored - void *family; // ignored - void *title; // ignored - void *units; // ignored - void *context; // ignored - uint32_t hash_context; // ignored - uint32_t chart_type; // ignored - int update_every; // check to reset all - update on load - long entries; // check to reset all - update on load - long current_entry; // NEEDS TO BE UPDATED - FIXED ON LOAD - uint32_t flags; // ignored - void *exporting_flags; // ignored - int gap_when_lost_iterations_above; // ignored - long priority; // ignored - uint32_t rrd_memory_mode; // ignored - void *cache_dir; // ignored - char cache_filename[FILENAME_MAX+1]; // ignored - update on load - pthread_rwlock_t rrdset_rwlock; // ignored - size_t counter; // NEEDS TO BE UPDATED - maintained on load - size_t counter_done; // ignored - union { // - time_t last_accessed_time_s; // ignored - time_t last_entry_s; // ignored - }; // - time_t upstream_resync_time; // ignored - void *plugin_name; // ignored - void *module_name; // ignored - void *chart_uuid; // ignored - void *state; // ignored - size_t unused[3]; // ignored - size_t rrddim_page_alignment; // ignored - uint32_t hash; // ignored - uint32_t hash_name; // ignored - usec_t usec_since_last_update; // NEEDS TO BE UPDATED - maintained on load - struct timeval last_updated; // NEEDS TO BE UPDATED - check to reset all - fixed on load - struct timeval last_collected_time; // ignored - long long collected_total; // ignored - long long last_collected_total; // ignored - void *rrdfamily; // ignored - void *rrdhost; // ignored - void *next; // ignored - long double green; // ignored - long double red; // ignored - struct avl_tree_lock_v019 rrdvar_root_index; // ignored - void *variables; // ignored - void *alarms; // ignored - unsigned long memsize; // check to reset all - update on load - char magic[sizeof(RRDSET_MAGIC_V019) + 1]; // check to reset all - update on load - struct avl_tree_lock_v019 dimensions_index; // ignored - void *dimensions; // ignored -}; - -void rrdset_memory_file_update(RRDSET *st) { - if(!st->db.st_on_file) return; - struct rrdset_map_save_v019 *st_on_file = st->db.st_on_file; - - st_on_file->current_entry = st->db.current_entry; - st_on_file->counter = st->counter; - st_on_file->usec_since_last_update = st->usec_since_last_update; - st_on_file->last_updated.tv_sec = st->last_updated.tv_sec; - st_on_file->last_updated.tv_usec = st->last_updated.tv_usec; -} - -const char *rrdset_cache_filename(RRDSET *st) { - if(!st->db.st_on_file) return NULL; - struct rrdset_map_save_v019 *st_on_file = st->db.st_on_file; - return st_on_file->cache_filename; -} - -const char *rrdset_cache_dir(RRDSET *st) { - if(!st->db.cache_dir) - st->db.cache_dir = rrdhost_cache_dir_for_rrdset_alloc(st->rrdhost, rrdset_id(st)); - - return st->db.cache_dir; -} - -void rrdset_memory_file_free(RRDSET *st) { - if(!st->db.st_on_file) return; - - // needed for memory mode map, to save the latest state - rrdset_memory_file_update(st); - - struct rrdset_map_save_v019 *st_on_file = st->db.st_on_file; - __atomic_sub_fetch(&rrddim_db_memory_size, st_on_file->memsize, __ATOMIC_RELAXED); - netdata_munmap(st_on_file, st_on_file->memsize); - - // remove the pointers from the RRDDIM - st->db.st_on_file = NULL; -} - -void rrdset_memory_file_save(RRDSET *st) { - if(!st->db.st_on_file) return; - - rrdset_memory_file_update(st); - - struct rrdset_map_save_v019 *st_on_file = st->db.st_on_file; - if(st_on_file->rrd_memory_mode != RRD_MEMORY_MODE_SAVE) return; - - memory_file_save(st_on_file->cache_filename, st->db.st_on_file, st_on_file->memsize); -} - -bool rrdset_memory_load_or_create_map_save(RRDSET *st, RRD_MEMORY_MODE memory_mode) { - if(memory_mode != RRD_MEMORY_MODE_SAVE && memory_mode != RRD_MEMORY_MODE_MAP) - return false; - - char fullfilename[FILENAME_MAX + 1]; - snprintfz(fullfilename, FILENAME_MAX, "%s/main.db", rrdset_cache_dir(st)); - - unsigned long size = sizeof(struct rrdset_map_save_v019); - struct rrdset_map_save_v019 *st_on_file = (struct rrdset_map_save_v019 *)netdata_mmap( - fullfilename, size, ((memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 0, false, NULL); - - if(!st_on_file) return false; - - time_t now_s = now_realtime_sec(); - - st_on_file->magic[sizeof(RRDSET_MAGIC_V019)] = '\0'; - if(strcmp(st_on_file->magic, RRDSET_MAGIC_V019) != 0) { - netdata_log_info("Initializing file '%s'.", fullfilename); - memset(st_on_file, 0, size); - } - else if(strncmp(st_on_file->id, rrdset_id(st), RRD_ID_LENGTH_MAX_V019) != 0) { - netdata_log_error("File '%s' contents are not for chart '%s'. Clearing it.", fullfilename, rrdset_id(st)); - memset(st_on_file, 0, size); - } - else if(st_on_file->memsize != size || st_on_file->entries != st->db.entries) { - netdata_log_error("File '%s' does not have the desired size. Clearing it.", fullfilename); - memset(st_on_file, 0, size); - } - else if(st_on_file->update_every != st->update_every) { - netdata_log_error("File '%s' does not have the desired granularity. Clearing it.", fullfilename); - memset(st_on_file, 0, size); - } - else if((now_s - st_on_file->last_updated.tv_sec) > (long)st->update_every * (long)st->db.entries) { - netdata_log_info("File '%s' is too old. Clearing it.", fullfilename); - memset(st_on_file, 0, size); - } - else if(st_on_file->last_updated.tv_sec > now_s + st->update_every) { - netdata_log_error("File '%s' refers to the future by %zd secs. Resetting it to now.", fullfilename, (ssize_t)(st_on_file->last_updated.tv_sec - now_s)); - st_on_file->last_updated.tv_sec = now_s; - } - - if(st_on_file->current_entry >= st_on_file->entries) - st_on_file->current_entry = 0; - - // make sure the database is aligned - bool align_last_updated = false; - if(st_on_file->last_updated.tv_sec) { - st_on_file->update_every = st->update_every; - align_last_updated = true; - } - - // copy the useful values to st - st->db.current_entry = st_on_file->current_entry; - st->counter = st_on_file->counter; - st->usec_since_last_update = st_on_file->usec_since_last_update; - st->last_updated.tv_sec = st_on_file->last_updated.tv_sec; - st->last_updated.tv_usec = st_on_file->last_updated.tv_usec; - - // link it to st - st->db.st_on_file = st_on_file; - - // clear everything - memset(st_on_file, 0, size); - - // set the values we need - strncpyz(st_on_file->id, rrdset_id(st), RRD_ID_LENGTH_MAX_V019); - strcpy(st_on_file->cache_filename, fullfilename); - strcpy(st_on_file->magic, RRDSET_MAGIC_V019); - st_on_file->memsize = size; - st_on_file->entries = st->db.entries; - st_on_file->update_every = st->update_every; - st_on_file->rrd_memory_mode = memory_mode; - - if(align_last_updated) - last_updated_time_align(st); - - // copy the useful values back to st_on_file - rrdset_memory_file_update(st); - - __atomic_add_fetch(&rrddim_db_memory_size, st_on_file->memsize, __ATOMIC_RELAXED); - return true; -} diff --git a/database/sqlite/dbdata.c b/src/database/sqlite/dbdata.c index 1ad742e04..1ad742e04 100644 --- a/database/sqlite/dbdata.c +++ b/src/database/sqlite/dbdata.c diff --git a/database/sqlite/sqlite3.c b/src/database/sqlite/sqlite3.c index da8c38d09..da8c38d09 100644 --- a/database/sqlite/sqlite3.c +++ b/src/database/sqlite/sqlite3.c diff --git a/database/sqlite/sqlite3.h b/src/database/sqlite/sqlite3.h index 48effe202..48effe202 100644 --- a/database/sqlite/sqlite3.h +++ b/src/database/sqlite/sqlite3.h diff --git a/database/sqlite/sqlite3recover.c b/src/database/sqlite/sqlite3recover.c index 3dae0b7a9..3dae0b7a9 100644 --- a/database/sqlite/sqlite3recover.c +++ b/src/database/sqlite/sqlite3recover.c diff --git a/database/sqlite/sqlite3recover.h b/src/database/sqlite/sqlite3recover.h index 7a1cd1cd8..7a1cd1cd8 100644 --- a/database/sqlite/sqlite3recover.h +++ b/src/database/sqlite/sqlite3recover.h diff --git a/database/sqlite/sqlite_aclk.c b/src/database/sqlite/sqlite_aclk.c index ac574879c..c410406b2 100644 --- a/database/sqlite/sqlite_aclk.c +++ b/src/database/sqlite/sqlite_aclk.c @@ -61,7 +61,6 @@ enum { IDX_UPDATE_EVERY, IDX_OS, IDX_TIMEZONE, - IDX_TAGS, IDX_HOPS, IDX_MEMORY_MODE, IDX_ABBREV_TIMEZONE, @@ -72,6 +71,7 @@ enum { IDX_HEALTH_ENABLED, IDX_LAST_CONNECTED, IDX_IS_EPHEMERAL, + IDX_IS_REGISTERED, }; static int create_host_callback(void *data, int argc, char **argv, char **column) @@ -88,21 +88,27 @@ static int create_host_callback(void *data, int argc, char **argv, char **column time_t age = now_realtime_sec() - last_connected; int is_ephemeral = 0; + int is_registered = 0; if (argv[IDX_IS_EPHEMERAL]) is_ephemeral = str2i(argv[IDX_IS_EPHEMERAL]); + if (argv[IDX_IS_REGISTERED]) + is_registered = str2i(argv[IDX_IS_REGISTERED]); + char guid[UUID_STR_LEN]; uuid_unparse_lower(*(uuid_t *)argv[IDX_HOST_ID], guid); if (is_ephemeral && age > rrdhost_free_ephemeral_time_s) { netdata_log_info( - "Skipping ephemeral hostname \"%s\" with GUID \"%s\", age = %ld seconds (limit %ld seconds)", + "%s ephemeral hostname \"%s\" with GUID \"%s\", age = %ld seconds (limit %ld seconds)", + is_registered ? "Loading registered" : "Skipping unregistered", (const char *)argv[IDX_HOSTNAME], guid, age, rrdhost_free_ephemeral_time_s); - return 0; + if (!is_registered) + return 0; } struct rrdhost_system_info *system_info = callocz(1, sizeof(struct rrdhost_system_info)); @@ -120,7 +126,6 @@ static int create_host_callback(void *data, int argc, char **argv, char **column (const char *)argv[IDX_TIMEZONE], (const char *)argv[IDX_ABBREV_TIMEZONE], (int32_t)(argv[IDX_UTC_OFFSET] ? str2uint32_t(argv[IDX_UTC_OFFSET], NULL) : 0), - (const char *)argv[IDX_TAGS], (const char *)(argv[IDX_PROGRAM_NAME] ? argv[IDX_PROGRAM_NAME] : "unknown"), (const char *)(argv[IDX_PROGRAM_VERSION] ? argv[IDX_PROGRAM_VERSION] : "unknown"), argv[IDX_UPDATE_EVERY] ? str2i(argv[IDX_UPDATE_EVERY]) : 1, @@ -557,11 +562,12 @@ void sql_create_aclk_table(RRDHOST *host __maybe_unused, uuid_t *host_uuid __may #define SQL_FETCH_ALL_HOSTS \ "SELECT host_id, hostname, registry_hostname, update_every, os, " \ - "timezone, tags, hops, memory_mode, abbrev_timezone, utc_offset, program_name, " \ + "timezone, hops, memory_mode, abbrev_timezone, utc_offset, program_name, " \ "program_version, entries, health_enabled, last_connected, " \ "(SELECT CASE WHEN hl.label_value = 'true' THEN 1 ELSE 0 END FROM " \ - "host_label hl WHERE hl.host_id = h.host_id AND hl.label_key = '_is_ephemeral') " \ - "FROM host h WHERE hops > 0" + "host_label hl WHERE hl.host_id = h.host_id AND hl.label_key = '_is_ephemeral'), " \ + "(SELECT CASE WHEN ni.node_id is NULL THEN 0 ELSE 1 END FROM " \ + "node_instance ni WHERE ni.host_id = h.host_id) FROM host h WHERE hops > 0" #define SQL_FETCH_ALL_INSTANCES \ "SELECT ni.host_id, ni.node_id FROM host h, node_instance ni " \ @@ -675,4 +681,4 @@ void unregister_node(const char *machine_guid) cmd.completion = NULL; aclk_database_enq_cmd(&cmd); } -#endif
\ No newline at end of file +#endif diff --git a/database/sqlite/sqlite_aclk.h b/src/database/sqlite/sqlite_aclk.h index 0db2647bf..0db2647bf 100644 --- a/database/sqlite/sqlite_aclk.h +++ b/src/database/sqlite/sqlite_aclk.h diff --git a/database/sqlite/sqlite_aclk_alert.c b/src/database/sqlite/sqlite_aclk_alert.c index 9bd060f96..c96f0eef8 100644 --- a/database/sqlite/sqlite_aclk_alert.c +++ b/src/database/sqlite/sqlite_aclk_alert.c @@ -201,7 +201,7 @@ void sql_queue_alarm_to_aclk(RRDHOST *host, ALARM_ENTRY *ae, bool skip_filter) ae->flags |= HEALTH_ENTRY_FLAG_ACLK_QUEUED; rrdhost_flag_set(host, RRDHOST_FLAG_ACLK_STREAM_ALERTS); } else - error_report("Failed to store alert event %"PRId64", rc = %d", ae->unique_id, rc); + error_report("Failed to store alert event %"PRIu32", rc = %d", ae->unique_id, rc); done: if (unlikely(sqlite3_finalize(res_alert) != SQLITE_OK)) @@ -351,7 +351,8 @@ static void aclk_push_alert_event(struct aclk_sync_cfg_t *wc __maybe_unused) strdupz("UNKNOWN=0=UNKNOWN"); alarm_log.command = strdupz(edit_command); - alarm_log.duration = (time_t) sqlite3_column_int64(res, 6); + time_t duration = (time_t) sqlite3_column_int64(res, 6); + alarm_log.duration = (duration > 0) ? duration : 0; alarm_log.non_clear_duration = (time_t) sqlite3_column_int64(res, 7); alarm_log.status = rrdcalc_status_to_proto_enum((RRDCALC_STATUS) sqlite3_column_int(res, 19)); alarm_log.old_status = rrdcalc_status_to_proto_enum((RRDCALC_STATUS) sqlite3_column_int(res, 20)); @@ -609,7 +610,7 @@ void aclk_push_alert_config_event(char *node_id __maybe_unused, char *config_has netdata_log_error("aclk_push_alert_config_event: Unexpected param number %d", param); BUFFER *tmp_buf = buffer_create(1024, &netdata_buffers_statistics.buffers_sqlite); - buffer_data_options2string(tmp_buf, sqlite3_column_int(res, 28)); + rrdr_options_to_buffer(tmp_buf, sqlite3_column_int(res, 28)); alarm_config.p_db_lookup_options = strdupz((char *)buffer_tostring(tmp_buf)); buffer_free(tmp_buf); @@ -821,7 +822,7 @@ void health_alarm_entry2proto_nolock(struct alarm_log_entry *alarm_log, ALARM_EN #endif #ifdef ENABLE_ACLK -static bool have_recent_alarm(RRDHOST *host, int64_t alarm_id, int64_t mark) +static bool have_recent_alarm_unsafe(RRDHOST *host, int64_t alarm_id, int64_t mark) { ALARM_ENTRY *ae = host->health_log.alarms; @@ -882,7 +883,7 @@ void aclk_push_alert_snapshot_event(char *node_id __maybe_unused) if (unlikely(ae->new_status == RRDCALC_STATUS_UNINITIALIZED)) continue; - if (have_recent_alarm(host, ae->alarm_id, ae->unique_id)) + if (have_recent_alarm_unsafe(host, ae->alarm_id, ae->unique_id)) continue; if (is_event_from_alert_variable_config(ae->unique_id, &host->host_uuid)) @@ -911,7 +912,7 @@ void aclk_push_alert_snapshot_event(char *node_id __maybe_unused) if (likely(ae->updated_by_id) || unlikely(ae->new_status == RRDCALC_STATUS_UNINITIALIZED)) continue; - if (have_recent_alarm(host, ae->alarm_id, ae->unique_id)) + if (have_recent_alarm_unsafe(host, ae->alarm_id, ae->unique_id)) continue; if (is_event_from_alert_variable_config(ae->unique_id, &host->host_uuid)) @@ -1090,7 +1091,7 @@ void aclk_push_alarm_checkpoint(RRDHOST *host __maybe_unused) } active_alerts[cnt].name = (char *)rrdcalc_name(rc); - len += string_strlen(rc->name); + len += string_strlen(rc->config.name); active_alerts[cnt].chart = (char *)rrdcalc_chart_name(rc); len += string_strlen(rc->chart); active_alerts[cnt].status = rc->status; diff --git a/database/sqlite/sqlite_aclk_alert.h b/src/database/sqlite/sqlite_aclk_alert.h index cfb3468b9..cfb3468b9 100644 --- a/database/sqlite/sqlite_aclk_alert.h +++ b/src/database/sqlite/sqlite_aclk_alert.h diff --git a/database/sqlite/sqlite_aclk_node.c b/src/database/sqlite/sqlite_aclk_node.c index dcc8c375c..dcc8c375c 100644 --- a/database/sqlite/sqlite_aclk_node.c +++ b/src/database/sqlite/sqlite_aclk_node.c diff --git a/database/sqlite/sqlite_aclk_node.h b/src/database/sqlite/sqlite_aclk_node.h index 6afdf8d78..6afdf8d78 100644 --- a/database/sqlite/sqlite_aclk_node.h +++ b/src/database/sqlite/sqlite_aclk_node.h diff --git a/database/sqlite/sqlite_context.c b/src/database/sqlite/sqlite_context.c index 26ed8a96a..ad76a1ee2 100644 --- a/database/sqlite/sqlite_context.c +++ b/src/database/sqlite/sqlite_context.c @@ -52,7 +52,7 @@ int sql_init_context_database(int memory) if (likely(!memory)) target_version = perform_context_database_migration(db_context_meta, DB_CONTEXT_METADATA_VERSION); - if (configure_sqlite_database(db_context_meta, target_version)) + if (configure_sqlite_database(db_context_meta, target_version, "context_config")) return 1; if (likely(!memory)) @@ -60,34 +60,17 @@ int sql_init_context_database(int memory) else snprintfz(buf, sizeof(buf) - 1, "ATTACH DATABASE ':memory:' as meta"); - if(init_database_batch(db_context_meta, list)) return 1; + if(init_database_batch(db_context_meta, list, "context")) return 1; - if (init_database_batch(db_context_meta, &database_context_config[0])) + if (init_database_batch(db_context_meta, &database_context_config[0], "context_init")) return 1; - if (init_database_batch(db_context_meta, &database_context_cleanup[0])) + if (init_database_batch(db_context_meta, &database_context_cleanup[0], "context_cleanup")) return 1; return 0; } -/* - * Close the sqlite database - */ - -void sql_close_context_database(void) -{ - int rc; - if (unlikely(!db_context_meta)) - return; - - netdata_log_info("Closing context SQLite database"); - - rc = sqlite3_close_v2(db_context_meta); - if (unlikely(rc != SQLITE_OK)) - error_report("Error %d while closing the context SQLite database, %s", rc, sqlite3_errstr(rc)); -} - // // Fetching data // @@ -421,6 +404,12 @@ int sql_context_cache_stats(int op) return count; } + +uint64_t sqlite_get_context_space(void) +{ + return sqlite_get_db_space(db_context_meta); +} + // // TESTING FUNCTIONS // @@ -455,7 +444,8 @@ int ctx_unittest(void) uuid_t host_uuid; uuid_generate(host_uuid); - initialize_thread_key_pool(); + if (sqlite_library_init()) + return 1; int rc = sql_init_context_database(1); @@ -531,7 +521,8 @@ int ctx_unittest(void) ctx_get_context_list(&host_uuid, dict_ctx_get_context_list_cb, NULL); netdata_log_info("List context end after delete"); - sql_close_context_database(); + sql_close_database(db_context_meta, "CONTEXT"); + sqlite_library_shutdown(); return 0; } diff --git a/database/sqlite/sqlite_context.h b/src/database/sqlite/sqlite_context.h index 2586916ea..92d02fdd2 100644 --- a/database/sqlite/sqlite_context.h +++ b/src/database/sqlite/sqlite_context.h @@ -65,6 +65,7 @@ int ctx_store_context(uuid_t *host_uuid, VERSIONED_CONTEXT_DATA *context_data); int ctx_delete_context(uuid_t *host_id, VERSIONED_CONTEXT_DATA *context_data); int sql_init_context_database(int memory); +uint64_t sqlite_get_context_space(void); void sql_close_context_database(void); int ctx_unittest(void); #endif //NETDATA_SQLITE_CONTEXT_H diff --git a/database/sqlite/sqlite_db_migration.c b/src/database/sqlite/sqlite_db_migration.c index 29da6c249..0131c4bf6 100644 --- a/database/sqlite/sqlite_db_migration.c +++ b/src/database/sqlite/sqlite_db_migration.c @@ -153,17 +153,36 @@ const char *database_migrate_v13_v14[] = { NULL }; +const char *database_migrate_v16_v17[] = { + "ALTER TABLE alert_hash ADD time_group_condition INT", + "ALTER TABLE alert_hash ADD time_group_value DOUBLE", + "ALTER TABLE alert_hash ADD dims_group INT", + "ALTER TABLE alert_hash ADD data_source INT", + NULL +}; + +// Note: Same as database_migrate_v16_v17. This is not wrong +// Do additional migration to handle agents that created wrong alert_hash table +const char *database_migrate_v17_v18[] = { + "ALTER TABLE alert_hash ADD time_group_condition INT", + "ALTER TABLE alert_hash ADD time_group_value DOUBLE", + "ALTER TABLE alert_hash ADD dims_group INT", + "ALTER TABLE alert_hash ADD data_source INT", + NULL +}; + + static int do_migration_v1_v2(sqlite3 *database) { if (table_exists_in_database(database, "host") && !column_exists_in_table(database, "host", "hops")) - return init_database_batch(database, &database_migrate_v1_v2[0]); + return init_database_batch(database, &database_migrate_v1_v2[0], "meta_migrate"); return 0; } static int do_migration_v2_v3(sqlite3 *database) { if (table_exists_in_database(database, "host") && !column_exists_in_table(database, "host", "memory_mode")) - return init_database_batch(database, &database_migrate_v2_v3[0]); + return init_database_batch(database, &database_migrate_v2_v3[0], "meta_migrate"); return 0; } @@ -198,12 +217,12 @@ static int do_migration_v3_v4(sqlite3 *database) static int do_migration_v4_v5(sqlite3 *database) { - return init_database_batch(database, &database_migrate_v4_v5[0]); + return init_database_batch(database, &database_migrate_v4_v5[0], "meta_migrate"); } static int do_migration_v5_v6(sqlite3 *database) { - return init_database_batch(database, &database_migrate_v5_v6[0]); + return init_database_batch(database, &database_migrate_v5_v6[0], "meta_migrate"); } static int do_migration_v6_v7(sqlite3 *database) @@ -341,14 +360,14 @@ static int do_migration_v8_v9(sqlite3 *database) static int do_migration_v9_v10(sqlite3 *database) { if (table_exists_in_database(database, "alert_hash") && !column_exists_in_table(database, "alert_hash", "chart_labels")) - return init_database_batch(database, &database_migrate_v9_v10[0]); + return init_database_batch(database, &database_migrate_v9_v10[0], "meta_migrate"); return 0; } static int do_migration_v10_v11(sqlite3 *database) { if (table_exists_in_database(database, "health_log") && !column_exists_in_table(database, "health_log", "chart_name")) - return init_database_batch(database, &database_migrate_v10_v11[0]); + return init_database_batch(database, &database_migrate_v10_v11[0], "meta_migrate"); return 0; } @@ -360,7 +379,7 @@ static int do_migration_v11_v12(sqlite3 *database) if (table_exists_in_database(database, "health_log_detail") && !column_exists_in_table(database, "health_log_detail", "summary") && table_exists_in_database(database, "alert_hash") && !column_exists_in_table(database, "alert_hash", "summary")) - rc = init_database_batch(database, &database_migrate_v11_v12[0]); + rc = init_database_batch(database, &database_migrate_v11_v12[0], "meta_migrate"); if (!rc) sqlite3_exec_monitored(database, MIGR_11_12_UPD_HEALTH_LOG_DETAIL, 0, 0, NULL); @@ -430,17 +449,34 @@ static int do_migration_v15_v16(sqlite3 *database) return 0; } +static int do_migration_v16_v17(sqlite3 *database) +{ + if (table_exists_in_database(database, "alert_hash") && !column_exists_in_table(database, "alert_hash", "time_group_condition")) + return init_database_batch(database, &database_migrate_v16_v17[0], "meta_migrate"); + + return 0; +} + +static int do_migration_v17_v18(sqlite3 *database) +{ + if (table_exists_in_database(database, "alert_hash") && !column_exists_in_table(database, "alert_hash", "time_group_condition")) + return init_database_batch(database, &database_migrate_v17_v18[0], "meta_migrate"); + + return 0; +} + + static int do_migration_v12_v13(sqlite3 *database) { int rc = 0; if (table_exists_in_database(database, "health_log_detail") && !column_exists_in_table(database, "health_log_detail", "summary")) { - rc = init_database_batch(database, &database_migrate_v12_v13_detail[0]); + rc = init_database_batch(database, &database_migrate_v12_v13_detail[0], "meta_migrate"); sqlite3_exec_monitored(database, MIGR_11_12_UPD_HEALTH_LOG_DETAIL, 0, 0, NULL); } if (table_exists_in_database(database, "alert_hash") && !column_exists_in_table(database, "alert_hash", "summary")) - rc = init_database_batch(database, &database_migrate_v12_v13_hash[0]); + rc = init_database_batch(database, &database_migrate_v12_v13_hash[0], "meta_migrate"); return rc; } @@ -448,7 +484,7 @@ static int do_migration_v12_v13(sqlite3 *database) static int do_migration_v13_v14(sqlite3 *database) { if (table_exists_in_database(database, "host") && !column_exists_in_table(database, "host", "last_connected")) - return init_database_batch(database, &database_migrate_v13_v14[0]); + return init_database_batch(database, &database_migrate_v13_v14[0], "meta_migrate"); return 0; } @@ -466,7 +502,7 @@ const char *database_ml_migrate_v1_v2[] = { static int do_ml_migration_v1_v2(sqlite3 *database) { if (get_auto_vaccum(database) != 2) - return init_database_batch(database, &database_ml_migrate_v1_v2[0]); + return init_database_batch(database, &database_ml_migrate_v1_v2[0], "ml_migrate"); return 0; } @@ -527,6 +563,8 @@ DATABASE_FUNC_MIGRATION_LIST migration_action[] = { {.name = "v13 to v14", .func = do_migration_v13_v14}, {.name = "v14 to v15", .func = do_migration_v14_v15}, {.name = "v15 to v16", .func = do_migration_v15_v16}, + {.name = "v16 to v17", .func = do_migration_v16_v17}, + {.name = "v17 to v18", .func = do_migration_v17_v18}, // the terminator of this array {.name = NULL, .func = NULL} }; diff --git a/database/sqlite/sqlite_db_migration.h b/src/database/sqlite/sqlite_db_migration.h index e3c1be84f..e3c1be84f 100644 --- a/database/sqlite/sqlite_db_migration.h +++ b/src/database/sqlite/sqlite_db_migration.h diff --git a/database/sqlite/sqlite_health.c b/src/database/sqlite/sqlite_health.c index 7d79ff70b..ea883c51b 100644 --- a/database/sqlite/sqlite_health.c +++ b/src/database/sqlite/sqlite_health.c @@ -3,6 +3,7 @@ #include "sqlite_health.h" #include "sqlite_functions.h" #include "sqlite_db_migration.h" +#include "health/health_internals.h" #define MAX_HEALTH_SQL_SIZE 2048 #define SQLITE3_BIND_STRING_OR_NULL(res, key, param) \ @@ -101,7 +102,8 @@ failed: "config_hash_id, name, chart, exec, recipient, units, chart_context, last_transition_id, chart_name) " \ "VALUES (@host_id,@alarm_id, @config_hash_id,@name,@chart,@exec,@recipient,@units,@chart_context," \ "@last_transition_id,@chart_name) ON CONFLICT (host_id, alarm_id) DO UPDATE " \ - "SET last_transition_id = excluded.last_transition_id, chart_name = excluded.chart_name RETURNING health_log_id" + "SET last_transition_id = excluded.last_transition_id, chart_name = excluded.chart_name, " \ + "config_hash_id=excluded.config_hash_id RETURNING health_log_id" #define SQL_INSERT_HEALTH_LOG_DETAIL \ "INSERT INTO health_log_detail (health_log_id, unique_id, alarm_id, alarm_event_id, " \ @@ -893,18 +895,21 @@ void sql_health_alarm_log_load(RRDHOST *host) /* * Store an alert config hash in the database */ -#define SQL_STORE_ALERT_CONFIG_HASH \ - "insert or replace into alert_hash (hash_id, date_updated, alarm, template, " \ - "on_key, class, component, type, os, hosts, lookup, every, units, calc, plugin, module, " \ - "charts, green, red, warn, crit, exec, to_key, info, delay, options, repeat, host_labels, " \ - "p_db_lookup_dimensions, p_db_lookup_method, p_db_lookup_options, p_db_lookup_after, " \ - "p_db_lookup_before, p_update_every, source, chart_labels, summary) values (@hash_id,UNIXEPOCH(),@alarm,@template," \ - "@on_key,@class,@component,@type,@os,@hosts,@lookup,@every,@units,@calc,@plugin,@module," \ - "@charts,@green,@red,@warn,@crit,@exec,@to_key,@info,@delay,@options,@repeat,@host_labels," \ - "@p_db_lookup_dimensions,@p_db_lookup_method,@p_db_lookup_options,@p_db_lookup_after," \ - "@p_db_lookup_before,@p_update_every,@source,@chart_labels,@summary)" - -int sql_store_alert_config_hash(uuid_t *hash_id, struct alert_config *cfg) +#define SQL_STORE_ALERT_CONFIG_HASH \ + "insert or replace into alert_hash (hash_id, date_updated, alarm, template, " \ + "on_key, class, component, type, lookup, every, units, calc, " \ + "green, red, warn, crit, exec, to_key, info, delay, options, repeat, host_labels, " \ + "p_db_lookup_dimensions, p_db_lookup_method, p_db_lookup_options, p_db_lookup_after, " \ + "p_db_lookup_before, p_update_every, source, chart_labels, summary, time_group_condition, " \ + "time_group_value, dims_group, data_source) " \ + "values (@hash_id,UNIXEPOCH(),@alarm,@template," \ + "@on_key,@class,@component,@type,@lookup,@every,@units,@calc," \ + "@green,@red,@warn,@crit,@exec,@to_key,@info,@delay,@options,@repeat,@host_labels," \ + "@p_db_lookup_dimensions,@p_db_lookup_method,@p_db_lookup_options,@p_db_lookup_after," \ + "@p_db_lookup_before,@p_update_every,@source,@chart_labels,@summary, @time_group_condition, " \ + "@time_group_value, @dims_group, @data_source)" + +int sql_alert_store_config(RRD_ALERT_PROTOTYPE *ap __maybe_unused) { static __thread sqlite3_stmt *res = NULL; int rc, param = 0; @@ -923,133 +928,153 @@ int sql_store_alert_config_hash(uuid_t *hash_id, struct alert_config *cfg) return 1; } } + BUFFER *buf = buffer_create(128, NULL); - rc = sqlite3_bind_blob(res, ++param, hash_id, sizeof(*hash_id), SQLITE_STATIC); + rc = sqlite3_bind_blob(res, ++param, &ap->config.hash_id, sizeof(ap->config.hash_id), SQLITE_STATIC); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->alarm, ++param); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; + if (ap->match.is_template) + rc = SQLITE3_BIND_STRING_OR_NULL(res, NULL, ++param); + else + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.name, ++param); - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->template_key, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->on, ++param); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; + if (ap->match.is_template) + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.name, ++param); + else + rc = SQLITE3_BIND_STRING_OR_NULL(res, NULL, ++param); - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->classification, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->component, ++param); + if (ap->match.is_template) + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.on.context, ++param); + else + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.on.chart, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->type, ++param); + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.classification, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->os, ++param); + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.component, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->host, ++param); + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.type, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->lookup, ++param); + // Rebuild lookup + rc = SQLITE3_BIND_STRING_OR_NULL(res, NULL, ++param); // lookup line if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->every, ++param); + rc = sqlite3_bind_int(res, ++param, ap->config.update_every); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->units, ++param); + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.units, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->calc, ++param); + if (ap->config.calculation) + rc = sqlite3_bind_text(res, ++param, expression_source(ap->config.calculation), -1, SQLITE_STATIC); + else + rc = sqlite3_bind_null(res, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->plugin, ++param); + NETDATA_DOUBLE green = NAN; + rc = sqlite3_bind_double(res, ++param, green); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->module, ++param); + NETDATA_DOUBLE red = NAN; + rc = sqlite3_bind_double(res, ++param, red); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->charts, ++param); + if (ap->config.warning) + rc = sqlite3_bind_text(res, ++param, expression_source(ap->config.warning), -1, SQLITE_STATIC); + else + rc = sqlite3_bind_null(res, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->green, ++param); + if (ap->config.critical) + rc = sqlite3_bind_text(res, ++param, expression_source(ap->config.critical), -1, SQLITE_STATIC); + else + rc = sqlite3_bind_null(res, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->red, ++param); + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.exec, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->warn, ++param); + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.recipient, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->crit, ++param); + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.info, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->exec, ++param); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; + if (ap->config.delay_up_duration) + buffer_sprintf(buf, "up %ds ", ap->config.delay_up_duration); - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->to, ++param); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; + if (ap->config.delay_down_duration) + buffer_sprintf(buf, "down %ds ", ap->config.delay_down_duration); - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->info, ++param); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; + if (ap->config.delay_multiplier) + buffer_sprintf(buf, "multiplier %.1f ", ap->config.delay_multiplier); + + if (ap->config.delay_max_duration) + buffer_sprintf(buf, "max %ds", ap->config.delay_max_duration); - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->delay, ++param); + // delay + rc = sqlite3_bind_text(res, ++param, buffer_tostring(buf), -1, SQLITE_STATIC); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->options, ++param); + if (ap->config.alert_action_options & ALERT_ACTION_OPTION_NO_CLEAR_NOTIFICATION) + rc = sqlite3_bind_text(res, ++param, "no-clear-notification", -1, SQLITE_STATIC); + else + rc = sqlite3_bind_null(res, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->repeat, ++param); + rc = sqlite3_bind_int(res, ++param, ap->config.update_every); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->host_labels, ++param); + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.host_labels, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - if (cfg->p_db_lookup_after) { - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->p_db_lookup_dimensions, ++param); + if (ap->config.after) { + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.dimensions, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->p_db_lookup_method, ++param); + rc = sqlite3_bind_text(res, ++param, time_grouping_id2txt(ap->config.time_group), -1, SQLITE_STATIC); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = sqlite3_bind_int(res, ++param, (int) cfg->p_db_lookup_options); + rc = sqlite3_bind_int(res, ++param, (int) RRDR_OPTIONS_REMOVE_OVERLAPPING(ap->config.options)); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = sqlite3_bind_int(res, ++param, (int) cfg->p_db_lookup_after); + rc = sqlite3_bind_int64(res, ++param, (int) ap->config.after); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = sqlite3_bind_int(res, ++param, (int) cfg->p_db_lookup_before); + rc = sqlite3_bind_int64(res, ++param, (int) ap->config.before); if (unlikely(rc != SQLITE_OK)) goto bind_fail; } else { @@ -1074,19 +1099,35 @@ int sql_store_alert_config_hash(uuid_t *hash_id, struct alert_config *cfg) goto bind_fail; } - rc = sqlite3_bind_int(res, ++param, cfg->p_update_every); + rc = sqlite3_bind_int(res, ++param, ap->config.update_every); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->source, ++param); + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.source, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->chart_labels, ++param); + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.chart_labels, ++param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->summary, ++param); + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.summary, ++param); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_int(res, ++param, ap->config.time_group_condition); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_double(res, ++param, ap->config.time_group_value); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_int(res, ++param, ap->config.dims_group); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_int(res, ++param, ap->config.data_source); if (unlikely(rc != SQLITE_OK)) goto bind_fail; @@ -1098,9 +1139,11 @@ int sql_store_alert_config_hash(uuid_t *hash_id, struct alert_config *cfg) if (unlikely(rc != SQLITE_OK)) error_report("Failed to reset statement in alert hash_id store function, rc = %d", rc); + buffer_free(buf); return 0; bind_fail: + buffer_free(buf); error_report("Failed to bind parameter %d to store alert hash_id, rc = %d", param, rc); rc = sqlite3_reset(res); if (unlikely(rc != SQLITE_OK)) @@ -1108,75 +1151,6 @@ bind_fail: return 1; } -/* - alert hashes are used for cloud communication. - if cloud is disabled or openssl is not available (which will prevent cloud connectivity) - skip hash calculations -*/ -#if defined ENABLE_HTTPS -#define DIGEST_ALERT_CONFIG_VAL(v) ((v) ? EVP_DigestUpdate(evpctx, (string2str(v)), string_strlen((v))) : EVP_DigestUpdate(evpctx, "", 1)) -#endif -int alert_hash_and_store_config( - uuid_t hash_id, - struct alert_config *cfg, - int store_hash) -{ -#if defined ENABLE_HTTPS - EVP_MD_CTX *evpctx; - unsigned char hash_value[EVP_MAX_MD_SIZE]; - unsigned int hash_len; - evpctx = EVP_MD_CTX_create(); - EVP_DigestInit_ex(evpctx, EVP_sha256(), NULL); - - DIGEST_ALERT_CONFIG_VAL(cfg->alarm); - DIGEST_ALERT_CONFIG_VAL(cfg->template_key); - DIGEST_ALERT_CONFIG_VAL(cfg->os); - DIGEST_ALERT_CONFIG_VAL(cfg->host); - DIGEST_ALERT_CONFIG_VAL(cfg->on); - DIGEST_ALERT_CONFIG_VAL(cfg->plugin); - DIGEST_ALERT_CONFIG_VAL(cfg->module); - DIGEST_ALERT_CONFIG_VAL(cfg->charts); - DIGEST_ALERT_CONFIG_VAL(cfg->lookup); - DIGEST_ALERT_CONFIG_VAL(cfg->calc); - DIGEST_ALERT_CONFIG_VAL(cfg->every); - DIGEST_ALERT_CONFIG_VAL(cfg->green); - DIGEST_ALERT_CONFIG_VAL(cfg->red); - DIGEST_ALERT_CONFIG_VAL(cfg->warn); - DIGEST_ALERT_CONFIG_VAL(cfg->crit); - DIGEST_ALERT_CONFIG_VAL(cfg->exec); - DIGEST_ALERT_CONFIG_VAL(cfg->to); - DIGEST_ALERT_CONFIG_VAL(cfg->units); - DIGEST_ALERT_CONFIG_VAL(cfg->info); - DIGEST_ALERT_CONFIG_VAL(cfg->classification); - DIGEST_ALERT_CONFIG_VAL(cfg->component); - DIGEST_ALERT_CONFIG_VAL(cfg->type); - DIGEST_ALERT_CONFIG_VAL(cfg->delay); - DIGEST_ALERT_CONFIG_VAL(cfg->options); - DIGEST_ALERT_CONFIG_VAL(cfg->repeat); - DIGEST_ALERT_CONFIG_VAL(cfg->host_labels); - DIGEST_ALERT_CONFIG_VAL(cfg->chart_labels); - DIGEST_ALERT_CONFIG_VAL(cfg->summary); - - EVP_DigestFinal_ex(evpctx, hash_value, &hash_len); - EVP_MD_CTX_destroy(evpctx); - fatal_assert(hash_len > sizeof(uuid_t)); - - char uuid_str[UUID_STR_LEN]; - uuid_unparse_lower(*((uuid_t *)&hash_value), uuid_str); - uuid_copy(hash_id, *((uuid_t *)&hash_value)); - - /* store everything, so it can be recreated when not in memory or just a subset ? */ - if (store_hash) - (void)sql_store_alert_config_hash( (uuid_t *)&hash_value, cfg); -#else - UNUSED(hash_id); - UNUSED(cfg); - UNUSED(store_hash); -#endif - - return 1; -} - #define SQL_SELECT_HEALTH_LAST_EXECUTED_EVENT \ "SELECT hld.new_status FROM health_log hl, health_log_detail hld " \ "WHERE hl.host_id = @host_id AND hl.alarm_id = @alarm_id AND hld.unique_id != @unique_id AND hld.flags & @flags " \ @@ -1580,10 +1554,9 @@ static uint32_t get_next_alarm_event_id(uint64_t health_log_id, uint32_t alarm_i } #define SQL_GET_ALARM_ID \ - "SELECT alarm_id, health_log_id FROM health_log WHERE host_id = @host_id AND chart = @chart " \ - "AND name = @name AND config_hash_id = @config_hash_id" + "SELECT alarm_id, health_log_id FROM health_log WHERE host_id = @host_id AND chart = @chart AND name = @name" -uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id) +uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id) { int rc = 0; sqlite3_stmt *res = NULL; @@ -1617,13 +1590,6 @@ uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t * return alarm_id; } - rc = sqlite3_bind_blob(res, 4, config_hash_id, sizeof(*config_hash_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind config_hash_id parameter for SQL_GET_ALARM_ID."); - sqlite3_finalize(res); - return alarm_id; - } - while (sqlite3_step_monitored(res) == SQLITE_ROW) { alarm_id = (uint32_t) sqlite3_column_int64(res, 0); health_log_id = (uint64_t) sqlite3_column_int64(res, 1); @@ -1639,111 +1605,6 @@ uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t * return alarm_id; } -#define SQL_UPDATE_ALARM_ID_WITH_CONFIG_HASH \ - "UPDATE health_log SET config_hash_id = @config_hash_id WHERE host_id = @host_id AND alarm_id = @alarm_id " \ - "AND health_log_id = @health_log_id" - -void sql_update_alarm_with_config_hash(RRDHOST *host, uint32_t alarm_id, uint64_t health_log_id, uuid_t *config_hash_id) -{ - int rc = 0; - sqlite3_stmt *res = NULL; - - rc = sqlite3_prepare_v2(db_meta, SQL_UPDATE_ALARM_ID_WITH_CONFIG_HASH, -1, &res, 0); - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement when trying to update an alarm id with a config hash."); - return; - } - - rc = sqlite3_bind_blob(res, 1, config_hash_id, sizeof(*config_hash_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind config_hash_id parameter for SQL_UPDATE_ALARM_ID_WITH_CONFIG_HASH."); - goto done; - } - - rc = sqlite3_bind_blob(res, 2, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host_id parameter for SQL_UPDATE_ALARM_ID_WITH_CONFIG_HASH."); - goto done; - } - - rc = sqlite3_bind_int64(res, 3, (sqlite3_int64) alarm_id); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind alarm_id parameter for SQL_GET_ALARM_ID."); - goto done; - } - - rc = sqlite3_bind_int64(res, 4, (sqlite3_int64) health_log_id); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind alarm_id parameter for SQL_GET_ALARM_ID."); - goto done; - } - - rc = execute_insert(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to execute SQL_UPDATE_ALARM_ID_WITH_CONFIG_HASH, rc = %d", rc); - -done: - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement to update health log detail table with config hash ids, rc = %d", rc); - -} - -#define SQL_GET_ALARM_ID_CHECK_ZERO_HASH \ - "SELECT alarm_id, health_log_id FROM health_log WHERE host_id = @host_id AND chart = @chart " \ - "AND name = @name AND (config_hash_id IS NULL OR config_hash_id = ZEROBLOB(16))" - -uint32_t sql_get_alarm_id_check_zero_hash(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id) -{ - int rc = 0; - sqlite3_stmt *res = NULL; - uint32_t alarm_id = 0; - uint64_t health_log_id = 0; - - rc = sqlite3_prepare_v2(db_meta, SQL_GET_ALARM_ID_CHECK_ZERO_HASH, -1, &res, 0); - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement when trying to get an alarm id with zero hash"); - return alarm_id; - } - - rc = sqlite3_bind_blob(res, 1, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host_id parameter for SQL_GET_ALARM_ID_CHECK_ZERO_HASH."); - sqlite3_finalize(res); - return alarm_id; - } - - rc = SQLITE3_BIND_STRING_OR_NULL(res, chart, 2); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind char parameter for SQL_GET_ALARM_ID_CHECK_ZERO_HASH."); - sqlite3_finalize(res); - return alarm_id; - } - - rc = SQLITE3_BIND_STRING_OR_NULL(res, name, 3); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind name parameter for SQL_GET_ALARM_ID_CHECK_ZERO_HASH."); - sqlite3_finalize(res); - return alarm_id; - } - - while (sqlite3_step_monitored(res) == SQLITE_ROW) { - alarm_id = (uint32_t) sqlite3_column_int64(res, 0); - health_log_id = (uint64_t) sqlite3_column_int64(res, 1); - } - - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize the statement while getting an alarm id."); - - if (alarm_id) { - sql_update_alarm_with_config_hash(host, alarm_id, health_log_id, config_hash_id); - *next_event_id = get_next_alarm_event_id(health_log_id, alarm_id); - } - - return alarm_id; -} - #define SQL_GET_ALARM_ID_FROM_TRANSITION_ID \ "SELECT hld.alarm_id, hl.host_id, hl.chart_context FROM health_log_detail hld, health_log hl " \ "WHERE hld.transition_id = @transition_id " \ @@ -1996,10 +1857,11 @@ done_only_drop: #define SQL_POPULATE_TEMP_CONFIG_TARGET_TABLE "INSERT INTO c_%p (hash_id) VALUES (@hash_id)" #define SQL_SEARCH_CONFIG_LIST \ - "SELECT ah.hash_id, alarm, template, on_key, class, component, type, os, hosts, lookup, every, " \ - " units, calc, families, plugin, module, charts, green, red, warn, crit, " \ + "SELECT ah.hash_id, alarm, template, on_key, class, component, type, lookup, every, " \ + " units, calc, families, green, red, warn, crit, " \ " exec, to_key, info, delay, options, repeat, host_labels, p_db_lookup_dimensions, p_db_lookup_method, " \ - " p_db_lookup_options, p_db_lookup_after, p_db_lookup_before, p_update_every, source, chart_labels, summary " \ + " p_db_lookup_options, p_db_lookup_after, p_db_lookup_before, p_update_every, source, chart_labels, summary, " \ + " time_group_condition, time_group_value, dims_group, data_source " \ " FROM alert_hash ah, c_%p t where ah.hash_id = t.hash_id" int sql_get_alert_configuration( @@ -2079,16 +1941,11 @@ int sql_get_alert_configuration( acd.classification = (const char *) sqlite3_column_text(res, param++); acd.component = (const char *) sqlite3_column_text(res, param++); acd.type = (const char *) sqlite3_column_text(res, param++); - acd.selectors.os = (const char *) sqlite3_column_text(res, param++); - acd.selectors.hosts = (const char *) sqlite3_column_text(res, param++); acd.value.db.lookup = (const char *) sqlite3_column_text(res, param++); acd.value.every = (const char *) sqlite3_column_text(res, param++); acd.value.units = (const char *) sqlite3_column_text(res, param++); acd.value.calc = (const char *) sqlite3_column_text(res, param++); acd.selectors.families = (const char *) sqlite3_column_text(res, param++); - acd.selectors.plugin = (const char *) sqlite3_column_text(res, param++); - acd.selectors.module = (const char *) sqlite3_column_text(res, param++); - acd.selectors.charts = (const char *) sqlite3_column_text(res, param++); acd.status.green = (const char *) sqlite3_column_text(res, param++); acd.status.red = (const char *) sqlite3_column_text(res, param++); acd.status.warn = (const char *) sqlite3_column_text(res, param++); @@ -2109,6 +1966,10 @@ int sql_get_alert_configuration( acd.source = (const char *) sqlite3_column_text(res, param++); acd.selectors.chart_labels = (const char *) sqlite3_column_text(res, param++); acd.summary = (const char *) sqlite3_column_text(res, param++); + acd.value.db.time_group_condition =(int32_t) sqlite3_column_int(res, param++); + acd.value.db.time_group_value = sqlite3_column_double(res, param++); + acd.value.db.dims_group = (int32_t) sqlite3_column_int(res, param++); + acd.value.db.data_source = (int32_t) sqlite3_column_int(res, param++); cb(&acd, data); added++; diff --git a/database/sqlite/sqlite_health.h b/src/database/sqlite/sqlite_health.h index 5549b7525..1b889436e 100644 --- a/database/sqlite/sqlite_health.h +++ b/src/database/sqlite/sqlite_health.h @@ -2,21 +2,22 @@ #ifndef NETDATA_SQLITE_HEALTH_H #define NETDATA_SQLITE_HEALTH_H -#include "../../daemon/common.h" + +#include "daemon/common.h" #include "sqlite3.h" struct sql_alert_transition_data; struct sql_alert_config_data; +struct rrd_alert_prototype; void sql_health_alarm_log_load(RRDHOST *host); void sql_health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae); void sql_health_alarm_log_cleanup(RRDHOST *host, bool claimed); -int alert_hash_and_store_config(uuid_t hash_id, struct alert_config *cfg, int store_hash); +int sql_alert_store_config(struct rrd_alert_prototype *ap); void sql_aclk_alert_clean_dead_entries(RRDHOST *host); int sql_health_get_last_executed_event(RRDHOST *host, ALARM_ENTRY *ae, RRDCALC_STATUS *last_executed_status); void sql_health_alarm_log2json(RRDHOST *host, BUFFER *wb, time_t after, const char *chart); int health_migrate_old_health_log_table(char *table); -uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id); -uint32_t sql_get_alarm_id_check_zero_hash(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id); +uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id); void sql_alert_transitions( DICTIONARY *nodes, time_t after, diff --git a/database/storage_engine.h b/src/database/storage_engine.h index b7fb7383a..b7fb7383a 100644 --- a/database/storage_engine.h +++ b/src/database/storage_engine.h diff --git a/database/engine/metadata_log/README.md b/src/go/collectors/go.d.plugin/agent/testdata/agent-empty.conf index e69de29bb..e69de29bb 100644 --- a/database/engine/metadata_log/README.md +++ b/src/go/collectors/go.d.plugin/agent/testdata/agent-empty.conf |