@cryptotaxi247 / netdata-1 / commits / bc840a799

DBENGINE: pgc tuning, replication tuning (#19237)

* evict, a page at a time * 4 replication ahead requests per replication thread * added per job average timings for workers and dbengine query router * debug statement to find what is slow * yield the processor to avoid monopolizing the cache * test more page sizes in aral * more polite journal v2 indexing * pulse macros for atomics * added profile so that users can control the defaults of the agent * fix windows warnings; journal v2 generation yields the processor for every page * replication threads are 1/3 of the cores and they are synchronous * removed the default from the list of profiles * turn pgc locks into macros to have tracing on the functions that called them * log the size of madvise() when failing * more work on profiles * restore batch cache evictions, but lower the batch size significantly * do not spin while searching for pages in the cache - handle currently being deleted pages within the search logic itself * remove bottleneck in epdl processing while merging extents * allocate outside the lock * rw spinlock implemented without spinlocks; both spinlocks and r/w spinlocks now support exponential backoff while waiting * apply max sleep to spinlocks * tune replication * r/w spinlock prefers writers again, but this time recursive readers are bypassing the writer wait * tuning of rw spinlock * more tuning of the rw spinlock * configure glibc arenas based on profile * moving global variables into nd_profile * do not accept sockets that have not received any data; once sockets with data have been accepted, check they are not closed already before processing them * poll_events is now using nd_poll(), resulting in vast simplification of the code; static web files are now served inline resulting in another simplification of the web server logic (was required because epoll does not support normal files) * startup fixes * added debug info to poll_fd_close() * closed sockets are automatically removed from epoll(), by the kernel * fix for mrg acquired and referenced going negative * fixed bug in mrg cleanup, not deleting metrics that do not have retention * monitor strings index size * strings memory chart is now stacked * replication: do not lock data collection when running in batches * explicitly set socket flags for sender and receiver * normalize the event loop for sending data (receiver and sender) * normalize the event loop for receiving data (receiver and sender) * check all sender nodes every half a second * fix bug on sender, not enabling sending * first cleanup then destroy * normalize nd_poll() to handle all possible events * cleanup * normalize socket helper functions * fixed warnings on alpine * fix for POLLRDHUP missing * fix cleanup on shutdown * added detailed replication summary * moved logs to INFO * prevent crash when sender is not there * madvise _dontfork() should not be used with aral, madvise_dontdump() is only used for file backed maps * fix wording * fix log wording * split replication receiver and sender; add logs to find missing replication requests * fix compilation * fixed bug in backfilling, having garbage for counters - malloc instead of calloc * backfilling logs if it misses callbacks * log replication rcv and replication snd in node info * remove contention from aral_page_free_lock(), but having 2 free lists per page, one for incoming and another for available items and moving incoming to available when the available is empty - this allows aral_mallocz() and aral_freez() to operate concurrently on the same page * fix internal checks * log errors for all replication receiver exceptions * removed wrong error log * prevent health crashing * cleanup logs that are irrelevant with the missing replication events * replication tracking: added replication tracking to figure out how replication missed requests * fix compilation and fix bug on spawn server cleanup calling uv_shutdown at exit * merged receiver initialization * prevent compilation warnings * fix race condition in nd_poll() returning events for deleted fds * for user queries, prepare as many queries as half the processors * fix log * add option dont_dump to netdata_mmap and aral_create * add logging missing receiver and sender charts * reviewed judy memory accounting; adbstracted flags handling to ensure they all work the same way; introduced atomic_flags_set_and_clear() to set and clear atomic flags with a single atomic operation * improvement(go.d/nats): add server_id label (#19280) * Regenerate integrations docs (#19281) Co-authored-by: ilyam8 <22274335+ilyam8@users.noreply.github.com> * [ci skip] Update changelog and version for nightly build: v2.1.0-30-nightly. * docs: improve on-prem troubleshooting readability (#19279) * docs: improve on-prem troubleshooting readability * Apply suggestions from code review --------- Co-authored-by: Fotis Voutsas <fotis@netdata.cloud> * improvement(go.d/nats): add leafz metrics (#19282) * Regenerate integrations docs (#19283) Co-authored-by: ilyam8 <22274335+ilyam8@users.noreply.github.com> * [ci skip] Update changelog and version for nightly build: v2.1.0-34-nightly. * fix go.d/nats tests (#19284) * improvement(go.d/nats): add basic jetstream metrics (#19285) * Regenerate integrations docs (#19286) Co-authored-by: ilyam8 <22274335+ilyam8@users.noreply.github.com> * [ci skip] Update changelog and version for nightly build: v2.1.0-38-nightly. * bump dag req jinja version (#19287) * more strict control on replication counters * do not flush the log files - to cope with the rate * [ci skip] Update changelog and version for nightly build: v2.1.0-40-nightly. * fix aral on windows * add waiting queue to sender commit, to allow the streaming thread go fast and put replication threads in order * use the receiver tid * fix(netdata-updater.sh): remove commit_check_file directory (#19288) * receiver now has periodic checks too (like the senders have) * fixed logs * replication periodic checks: resending of chart definitions * strict checking on rrdhost state id * replication periodic checks: added for receivers * shorter replication status messages * do not log about ieee754 * receiver logs replication traffic without RSET * object state: rrdhost_state_id has become object_state in libnetdata so that it can be reused * fixed metadata; added journal message id for netdata fatal messages * replication: undo bypassing the pipeline * receiver cleanup: free all structures at the end, to ensure there are not crashes while cleaning up * replication periodic checks: do not run it on receivers, when there is replication in progress * nd_log: prevent fatal statements from recursing * replication tracking: disabled (compile time) * fix priority and log * disconnect on stale replication - detected on both sender and receiver * update our tagline * when sending data from within opcode handling do not remove the receiver/sender * improve interactivity of streaming sockets * log the replication cmd counters on disconnect and reset them on reconnect * rrdhost object state activate/deactivate should happen in set/clear receiver * remove writer preference from rw spinlocks * show the value in health logs * move counter to the right place to avoid double counting replication commands * do not run opcodes when running inline * fix replication log messages * make IoT harmless for the moment --------- Co-authored-by: Ilya Mashchenko <ilya@netdata.cloud> Co-authored-by: Netdata bot <43409846+netdatabot@users.noreply.github.com> Co-authored-by: ilyam8 <22274335+ilyam8@users.noreply.github.com> Co-authored-by: netdatabot <bot@netdata.cloud> Co-authored-by: Fotis Voutsas <fotis@netdata.cloud>

Costa Tsaousis committed Dec 29, 2024 at 18:22 UTC bc840a79943aeb7f570f1a21d9e0792e902ffc5b
170 files changed +5201 -3381
CMakeLists.txt
+14 -4
@@ -946,6 +946,12 @@ set(LIBNETDATA_FILES
946 src/libnetdata/locks/spinlock.h
947 src/libnetdata/locks/rw-spinlock.c
948 src/libnetdata/locks/rw-spinlock.h
949 + src/libnetdata/atomics/atomic_flags.h
950 + src/libnetdata/atomics/atomics.h
951 + src/libnetdata/waiting-queue/waiting-queue.c
952 + src/libnetdata/waiting-queue/waiting-queue.h
953 + src/libnetdata/object-state/object-state.c
954 + src/libnetdata/object-state/object-state.h
955 )
956
957 set(LIBH2O_FILES
@@ -1122,6 +1128,8 @@ set(DAEMON_FILES
1128 src/daemon/pulse/pulse-db-rrd.h
1129 src/daemon/config/netdata-conf-cloud.c
1130 src/daemon/config/netdata-conf-cloud.h
1131 + src/daemon/config/netdata-conf-profile.c
1132 + src/daemon/config/netdata-conf-profile.h
1133 )
1134
1135 set(H2O_FILES
@@ -1439,8 +1447,6 @@ set(RRD_PLUGIN_FILES
1447 src/database/rrdcollector-internals.h
1448 src/database/rrd-database-mode.h
1449 src/database/rrd-database-mode.c
1442 - src/database/rrdhost-state-id.c
1443 - src/database/rrdhost-state-id.h
1450 src/database/rrdhost-system-info.c
1451 src/database/rrdhost-system-info.h
1452 )
@@ -1523,8 +1529,8 @@ set(STREAMING_PLUGIN_FILES
1529 src/streaming/stream-compression/zstd.h
1530 src/streaming/stream-receiver.c
1531 src/streaming/stream-sender.c
1526 - src/streaming/replication.c
1527 - src/streaming/replication.h
1532 + src/streaming/stream-replication-sender.c
1533 + src/streaming/stream-replication-sender.h
1534 src/streaming/h2o-common.h
1535 src/streaming/protocol/command-nodeid.c
1536 src/streaming/protocol/commands.c
@@ -1565,6 +1571,10 @@ set(STREAMING_PLUGIN_FILES
1571 src/streaming/stream-control.h
1572 src/streaming/stream-waiting-list.c
1573 src/streaming/stream-waiting-list.h
1574 + src/streaming/stream-replication-receiver.c
1575 + src/streaming/stream-replication-receiver.h
1576 + src/streaming/stream-replication-tracking.c
1577 + src/streaming/stream-replication-tracking.h
1578 )
1579
1580 set(WEB_PLUGIN_FILES
docs/diagrams/data_structures/web.svg
+1 -1
@@ -1,2 +1,2 @@
1 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
2 -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1805px" height="765px" viewBox="-0.5 -0.5 1805 765" style="background-color: rgb(255, 255, 255);"><defs><clipPath id="mx-clip-7-61-322-26-0"><rect x="7" y="61" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-87-322-26-0"><rect x="7" y="87" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-113-322-26-0"><rect x="7" y="113" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-139-322-26-0"><rect x="7" y="139" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-165-322-26-0"><rect x="7" y="165" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-191-322-26-0"><rect x="7" y="191" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-217-322-26-0"><rect x="7" y="217" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-243-322-26-0"><rect x="7" y="243" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-269-322-26-0"><rect x="7" y="269" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-295-322-26-0"><rect x="7" y="295" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-321-322-26-0"><rect x="7" y="321" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-347-322-26-0"><rect x="7" y="347" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-373-322-26-0"><rect x="7" y="373" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-399-322-26-0"><rect x="7" y="399" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-425-322-26-0"><rect x="7" y="425" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-451-322-26-0"><rect x="7" y="451" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-477-322-26-0"><rect x="7" y="477" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-503-322-26-0"><rect x="7" y="503" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-529-322-26-0"><rect x="7" y="529" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-555-322-26-0"><rect x="7" y="555" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-581-322-26-0"><rect x="7" y="581" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-607-322-26-0"><rect x="7" y="607" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-633-322-26-0"><rect x="7" y="633" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-659-322-26-0"><rect x="7" y="659" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-685-322-26-0"><rect x="7" y="685" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-711-322-26-0"><rect x="7" y="711" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-737-322-26-0"><rect x="7" y="737" width="322" height="26"/></clipPath><clipPath id="mx-clip-477-165-252-26-0"><rect x="477" y="165" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-191-252-26-0"><rect x="477" y="191" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-217-252-26-0"><rect x="477" y="217" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-243-252-26-0"><rect x="477" y="243" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-269-252-26-0"><rect x="477" y="269" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-295-252-26-0"><rect x="477" y="295" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-321-252-26-0"><rect x="477" y="321" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-347-252-26-0"><rect x="477" y="347" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-373-252-26-0"><rect x="477" y="373" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-399-252-26-0"><rect x="477" y="399" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-425-252-26-0"><rect x="477" y="425" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-451-252-26-0"><rect x="477" y="451" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-577-252-26-0"><rect x="477" y="577" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-603-252-26-0"><rect x="477" y="603" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-629-252-26-0"><rect x="477" y="629" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-655-252-26-0"><rect x="477" y="655" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-681-252-26-0"><rect x="477" y="681" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-707-252-26-0"><rect x="477" y="707" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-733-252-26-0"><rect x="477" y="733" width="252" height="26"/></clipPath><clipPath id="mx-clip-837-113-232-26-0"><rect x="837" y="113" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-139-232-26-0"><rect x="837" y="139" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-165-232-26-0"><rect x="837" y="165" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-191-232-26-0"><rect x="837" y="191" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-217-232-26-0"><rect x="837" y="217" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-243-232-26-0"><rect x="837" y="243" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-269-232-26-0"><rect x="837" y="269" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-295-232-26-0"><rect x="837" y="295" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-321-232-26-0"><rect x="837" y="321" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-347-232-26-0"><rect x="837" y="347" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-373-232-26-0"><rect x="837" y="373" width="232" height="26"/></clipPath><clipPath id="mx-clip-1177-61-222-26-0"><rect x="1177" y="61" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-87-222-26-0"><rect x="1177" y="87" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-113-222-26-0"><rect x="1177" y="113" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-139-222-26-0"><rect x="1177" y="139" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-165-222-26-0"><rect x="1177" y="165" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-191-222-26-0"><rect x="1177" y="191" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-217-222-26-0"><rect x="1177" y="217" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-243-222-26-0"><rect x="1177" y="243" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-269-222-26-0"><rect x="1177" y="269" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-295-222-26-0"><rect x="1177" y="295" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-321-222-26-0"><rect x="1177" y="321" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-347-222-26-0"><rect x="1177" y="347" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-373-222-26-0"><rect x="1177" y="373" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-399-222-26-0"><rect x="1177" y="399" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-425-222-26-0"><rect x="1177" y="425" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-451-222-26-0"><rect x="1177" y="451" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-477-222-26-0"><rect x="1177" y="477" width="222" height="26"/></clipPath><clipPath id="mx-clip-1527-126-242-26-0"><rect x="1527" y="126" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-152-242-26-0"><rect x="1527" y="152" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-178-242-26-0"><rect x="1527" y="178" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-204-242-26-0"><rect x="1527" y="204" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-230-242-26-0"><rect x="1527" y="230" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-256-242-26-0"><rect x="1527" y="256" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-282-242-26-0"><rect x="1527" y="282" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-308-242-26-0"><rect x="1527" y="308" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-334-242-26-0"><rect x="1527" y="334" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-360-242-26-0"><rect x="1527" y="360" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-386-242-26-0"><rect x="1527" y="386" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-412-242-26-0"><rect x="1527" y="412" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-438-242-26-0"><rect x="1527" y="438" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-464-242-26-0"><rect x="1527" y="464" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-490-242-26-0"><rect x="1527" y="490" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-516-242-26-0"><rect x="1527" y="516" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-542-242-26-0"><rect x="1527" y="542" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-568-242-26-0"><rect x="1527" y="568" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-594-242-26-0"><rect x="1527" y="594" width="242" height="26"/></clipPath></defs><path d="M 3 56 L 3 30 L 333 30 L 333 56" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 3 56 L 3 758 L 333 758 L 333 56" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 3 56 L 333 56" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="167.5" y="47.5">web_client</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-61-322-26-0)" font-size="12px"><text x="8.5" y="73.5">unsigned long long id</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-87-322-26-0)" font-size="12px"><text x="8.5" y="99.5">WEB_CLIENT_FLAGS flags</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-113-322-26-0)" font-size="12px"><text x="8.5" y="125.5">WEB_CLIENT_MODE mode</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-139-322-26-0)" font-size="12px"><text x="8.5" y="151.5">WEB_CLIENT_ACL acl</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-165-322-26-0)" font-size="12px"><text x="8.5" y="177.5">size_t header_parse_tries</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-191-322-26-0)" font-size="12px"><text x="8.5" y="203.5">size_t header_parse_last_size</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-217-322-26-0)" font-size="12px"><text x="8.5" y="229.5">int tcp_cork</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-243-322-26-0)" font-size="12px"><text x="8.5" y="255.5">int ifd</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-269-322-26-0)" font-size="12px"><text x="8.5" y="281.5">int ofd</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-295-322-26-0)" font-size="12px"><text x="8.5" y="307.5">char client_ip[NI_MAXHOST+1}</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-321-322-26-0)" font-size="12px"><text x="8.5" y="333.5">char client_port[NI_MAXSERV+1]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-347-322-26-0)" font-size="12px"><text x="8.5" y="359.5">char decoded_url[NETDATA_WEB_REQUEST_URL_SIZE+1</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-373-322-26-0)" font-size="12px"><text x="8.5" y="385.5">char last_url[NETDATA_WEB_REQUEST_URL_SIZE+1]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-399-322-26-0)" font-size="12px"><text x="8.5" y="411.5">struct timeval tv_in, tv_ready</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-425-322-26-0)" font-size="12px"><text x="8.5" y="437.5">char cookie1[NETDATA_WEB_REQUEST_COOKIE_SIZE+1]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-451-322-26-0)" font-size="12px"><text x="8.5" y="463.5">char cookie2[NETDATA_WEB_REQUEST_COOKIE_SIZE+1]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-477-322-26-0)" font-size="12px"><text x="8.5" y="489.5">char origin[NETDATA_WEB_REQUEST_ORIGIN_HEADER_SIZE+1]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-503-322-26-0)" font-size="12px"><text x="8.5" y="515.5">char *user_agent</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-529-322-26-0)" font-size="12px"><text x="8.5" y="541.5">struct response response</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-555-322-26-0)" font-size="12px"><text x="8.5" y="567.5">size_t stats_received_bytes</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-581-322-26-0)" font-size="12px"><text x="8.5" y="593.5">size_t stats_sent_bytes</text></g><path d="M 333 615 L 353 615 L 353 10 L 168 10 L 168 23.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 168 28.88 L 164.5 21.88 L 168 23.63 L 171.5 21.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-607-322-26-0)" font-size="12px"><text x="8.5" y="619.5">struct web_client *prev</text></g><path d="M 333 641 L 353 641 L 353 10 L 168 10 L 168 23.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 168 28.88 L 164.5 21.88 L 168 23.63 L 171.5 21.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(304.5,299.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="102" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; white-space: nowrap; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;background-color:#ffffff;">double linked list of</div></div></foreignObject><text x="51" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">double linked list of</text></switch></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-633-322-26-0)" font-size="12px"><text x="8.5" y="645.5">struct web_client *next</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-659-322-26-0)" font-size="12px"><text x="8.5" y="671.5">netdata_thread_t thread</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-685-322-26-0)" font-size="12px"><text x="8.5" y="697.5">volatile int running</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-711-322-26-0)" font-size="12px"><text x="8.5" y="723.5">size_t pollinfo_slot</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-737-322-26-0)" font-size="12px"><text x="8.5" y="749.5">size_t pollinfo_filecopy_slot</text></g><path d="M 473 160 L 473 134 L 733 134 L 733 160" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 473 160 L 473 472 L 733 472 L 733 160" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 473 160 L 733 160" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="602.5" y="151.5">response</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-165-252-26-0)" font-size="12px"><text x="478.5" y="177.5">BUFFER *header</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-191-252-26-0)" font-size="12px"><text x="478.5" y="203.5">BUFFER *header_output</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-217-252-26-0)" font-size="12px"><text x="478.5" y="229.5">BUFFER *data</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-243-252-26-0)" font-size="12px"><text x="478.5" y="255.5">int code</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-269-252-26-0)" font-size="12px"><text x="478.5" y="281.5">size_t rlen</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-295-252-26-0)" font-size="12px"><text x="478.5" y="307.5">size_t sent</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-321-252-26-0)" font-size="12px"><text x="478.5" y="333.5">int zoutput</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-347-252-26-0)" font-size="12px"><text x="478.5" y="359.5">z_stream zstream</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-373-252-26-0)" font-size="12px"><text x="478.5" y="385.5">Bytef zbuffer[NETDATA_WEB_RESPONSE_ZLIB_CHUNK_SIZE]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-399-252-26-0)" font-size="12px"><text x="478.5" y="411.5">size_t zsent</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-425-252-26-0)" font-size="12px"><text x="478.5" y="437.5">size_t zhave</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-451-252-26-0)" font-size="12px"><text x="478.5" y="463.5">unsigned int zinitialized</text></g><path d="M 333 537 L 403 537 L 403 114 L 603 114 L 603 127.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 603 132.88 L 599.5 125.88 L 603 127.63 L 606.5 125.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 473 572 L 473 546 L 733 546 L 733 572" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 473 572 L 473 754 L 733 754 L 733 572" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 473 572 L 733 572" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="602.5" y="563.5">clients_cache</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-577-252-26-0)" font-size="12px"><text x="478.5" y="589.5">pid_t pid</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-603-252-26-0)" font-size="12px"><text x="478.5" y="615.5">struct web_client *used</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-629-252-26-0)" font-size="12px"><text x="478.5" y="641.5">size_t used_count</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-655-252-26-0)" font-size="12px"><text x="478.5" y="667.5">struct web_client *avail</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-681-252-26-0)" font-size="12px"><text x="478.5" y="693.5">size_t avail_count</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-707-252-26-0)" font-size="12px"><text x="478.5" y="719.5">size_t reused</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-733-252-26-0)" font-size="12px"><text x="478.5" y="745.5">size_t allocated</text></g><path d="M 733 611 L 753 611 L 753 10 L 168 10 L 168 23.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 168 28.88 L 164.5 21.88 L 168 23.63 L 171.5 21.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(724.5,12.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="57" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; white-space: nowrap; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;background-color:#ffffff;">linked list of</div></div></foreignObject><text x="29" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">linked list of</text></switch></g><path d="M 733 663 L 753 663 L 753 10 L 168 10 L 168 23.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 168 28.88 L 164.5 21.88 L 168 23.63 L 171.5 21.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(724.5,38.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="57" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; white-space: nowrap; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;background-color:#ffffff;">linked list of</div></div></foreignObject><text x="29" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">linked list of</text></switch></g><path d="M 833 108 L 833 82 L 1073 82 L 1073 108" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 833 108 L 833 394 L 1073 394 L 1073 108" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 833 108 L 1073 108" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="952.5" y="99.5">listen_sockets</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-113-232-26-0)" font-size="12px"><text x="838.5" y="125.5">struct config *config</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-139-232-26-0)" font-size="12px"><text x="838.5" y="151.5">const char *config_section</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-165-232-26-0)" font-size="12px"><text x="838.5" y="177.5">const char *default_bind_to</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-191-232-26-0)" font-size="12px"><text x="838.5" y="203.5">uint16_t default_port</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-217-232-26-0)" font-size="12px"><text x="838.5" y="229.5">int backlog</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-243-232-26-0)" font-size="12px"><text x="838.5" y="255.5">size_t opened</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-269-232-26-0)" font-size="12px"><text x="838.5" y="281.5">size_t failed</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-295-232-26-0)" font-size="12px"><text x="838.5" y="307.5">int fds[MAX_LISTEN_FDS]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-321-232-26-0)" font-size="12px"><text x="838.5" y="333.5">int *fds_names[MAX_LISTEN_FDS]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-347-232-26-0)" font-size="12px"><text x="838.5" y="359.5">int fds_types[MAX_LISTEN_FDS]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-373-232-26-0)" font-size="12px"><text x="838.5" y="385.5">int fds_families[MAX_LISTEN_FDS]</text></g><path d="M 1173 56 L 1173 30 L 1403 30 L 1403 56" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1173 56 L 1173 498 L 1403 498 L 1403 56" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1173 56 L 1403 56" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="1287.5" y="47.5">POLLINFO</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-61-222-26-0)" font-size="12px"><text x="1178.5" y="73.5">POLLJOB *p</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-87-222-26-0)" font-size="12px"><text x="1178.5" y="99.5">size_t slot</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-113-222-26-0)" font-size="12px"><text x="1178.5" y="125.5">int fd</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-139-222-26-0)" font-size="12px"><text x="1178.5" y="151.5">int socktype</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-165-222-26-0)" font-size="12px"><text x="1178.5" y="177.5">char *client_ip</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-191-222-26-0)" font-size="12px"><text x="1178.5" y="203.5">char *client_port</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-217-222-26-0)" font-size="12px"><text x="1178.5" y="229.5">time_t connected_t</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-243-222-26-0)" font-size="12px"><text x="1178.5" y="255.5">time_t last_received_t</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-269-222-26-0)" font-size="12px"><text x="1178.5" y="281.5">time_t last_sent_t</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-295-222-26-0)" font-size="12px"><text x="1178.5" y="307.5">size_t recv_count</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-321-222-26-0)" font-size="12px"><text x="1178.5" y="333.5">size_t send_count</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-347-222-26-0)" font-size="12px"><text x="1178.5" y="359.5">uint32_t flags</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-373-222-26-0)" font-size="12px"><text x="1178.5" y="385.5">void (*del_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-399-222-26-0)" font-size="12px"><text x="1178.5" y="411.5">int (*rcv_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-425-222-26-0)" font-size="12px"><text x="1178.5" y="437.5">int (*snd_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-451-222-26-0)" font-size="12px"><text x="1178.5" y="463.5">void *data</text></g><path d="M 1403 485 L 1423 485 L 1423 10 L 1279 10 L 1279 22.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1279 27.88 L 1275.5 20.88 L 1279 22.63 L 1282.5 20.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-477-222-26-0)" font-size="12px"><text x="1178.5" y="489.5">struct pollinfo *next</text></g><path d="M 1523 121 L 1523 95 L 1773 95 L 1773 121" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1523 121 L 1523 615 L 1773 615 L 1773 121" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1523 121 L 1773 121" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="1647.5" y="112.5">POLLJOB</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-126-242-26-0)" font-size="12px"><text x="1528.5" y="138.5">size_t slots</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-152-242-26-0)" font-size="12px"><text x="1528.5" y="164.5">size_t used</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-178-242-26-0)" font-size="12px"><text x="1528.5" y="190.5">size_t min</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-204-242-26-0)" font-size="12px"><text x="1528.5" y="216.5">size_t max</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-230-242-26-0)" font-size="12px"><text x="1528.5" y="242.5">size_t limit</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-256-242-26-0)" font-size="12px"><text x="1528.5" y="268.5">time_t complete_request_timeout</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-282-242-26-0)" font-size="12px"><text x="1528.5" y="294.5">time_t idle_timeout</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-308-242-26-0)" font-size="12px"><text x="1528.5" y="320.5">time_t check_every</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-334-242-26-0)" font-size="12px"><text x="1528.5" y="346.5">time_t timer_milliseconds</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-360-242-26-0)" font-size="12px"><text x="1528.5" y="372.5">void *timer_data</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-386-242-26-0)" font-size="12px"><text x="1528.5" y="398.5">struct run *fds</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-412-242-26-0)" font-size="12px"><text x="1528.5" y="424.5">struct pollinfo *inf</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-438-242-26-0)" font-size="12px"><text x="1528.5" y="450.5">struct pollinfo *first_free</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-464-242-26-0)" font-size="12px"><text x="1528.5" y="476.5">SIMPLE_PATTERN *access_list</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-490-242-26-0)" font-size="12px"><text x="1528.5" y="502.5">void *(*add_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-516-242-26-0)" font-size="12px"><text x="1528.5" y="528.5">void (*dell_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-542-242-26-0)" font-size="12px"><text x="1528.5" y="554.5">int (*rcv_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-568-242-26-0)" font-size="12px"><text x="1528.5" y="580.5">int (*snd_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-594-242-26-0)" font-size="12px"><text x="1528.5" y="606.5">void (*tmr_callback)</text></g><path d="M 1403 69 L 1648 69 L 1648 88.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1648 93.88 L 1644.5 86.88 L 1648 88.63 L 1651.5 86.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1773 420 L 1793 420 L 1793 10 L 1279 10 L 1279 22.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1279 27.88 L 1275.5 20.88 L 1279 22.63 L 1282.5 20.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1773 446 L 1793 446 L 1793 10 L 1278 10 L 1278 22.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1278 27.88 L 1274.5 20.88 L 1278 22.63 L 1281.5 20.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/></svg>
\ No newline at end of file
2 +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1805px" height="765px" viewBox="-0.5 -0.5 1805 765" style="background-color: rgb(255, 255, 255);"><defs><clipPath id="mx-clip-7-61-322-26-0"><rect x="7" y="61" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-87-322-26-0"><rect x="7" y="87" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-113-322-26-0"><rect x="7" y="113" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-139-322-26-0"><rect x="7" y="139" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-165-322-26-0"><rect x="7" y="165" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-191-322-26-0"><rect x="7" y="191" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-217-322-26-0"><rect x="7" y="217" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-243-322-26-0"><rect x="7" y="243" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-269-322-26-0"><rect x="7" y="269" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-295-322-26-0"><rect x="7" y="295" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-321-322-26-0"><rect x="7" y="321" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-347-322-26-0"><rect x="7" y="347" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-373-322-26-0"><rect x="7" y="373" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-399-322-26-0"><rect x="7" y="399" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-425-322-26-0"><rect x="7" y="425" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-451-322-26-0"><rect x="7" y="451" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-477-322-26-0"><rect x="7" y="477" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-503-322-26-0"><rect x="7" y="503" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-529-322-26-0"><rect x="7" y="529" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-555-322-26-0"><rect x="7" y="555" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-581-322-26-0"><rect x="7" y="581" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-607-322-26-0"><rect x="7" y="607" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-633-322-26-0"><rect x="7" y="633" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-659-322-26-0"><rect x="7" y="659" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-685-322-26-0"><rect x="7" y="685" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-711-322-26-0"><rect x="7" y="711" width="322" height="26"/></clipPath><clipPath id="mx-clip-7-737-322-26-0"><rect x="7" y="737" width="322" height="26"/></clipPath><clipPath id="mx-clip-477-165-252-26-0"><rect x="477" y="165" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-191-252-26-0"><rect x="477" y="191" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-217-252-26-0"><rect x="477" y="217" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-243-252-26-0"><rect x="477" y="243" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-269-252-26-0"><rect x="477" y="269" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-295-252-26-0"><rect x="477" y="295" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-321-252-26-0"><rect x="477" y="321" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-347-252-26-0"><rect x="477" y="347" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-373-252-26-0"><rect x="477" y="373" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-399-252-26-0"><rect x="477" y="399" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-425-252-26-0"><rect x="477" y="425" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-451-252-26-0"><rect x="477" y="451" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-577-252-26-0"><rect x="477" y="577" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-603-252-26-0"><rect x="477" y="603" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-629-252-26-0"><rect x="477" y="629" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-655-252-26-0"><rect x="477" y="655" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-681-252-26-0"><rect x="477" y="681" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-707-252-26-0"><rect x="477" y="707" width="252" height="26"/></clipPath><clipPath id="mx-clip-477-733-252-26-0"><rect x="477" y="733" width="252" height="26"/></clipPath><clipPath id="mx-clip-837-113-232-26-0"><rect x="837" y="113" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-139-232-26-0"><rect x="837" y="139" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-165-232-26-0"><rect x="837" y="165" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-191-232-26-0"><rect x="837" y="191" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-217-232-26-0"><rect x="837" y="217" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-243-232-26-0"><rect x="837" y="243" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-269-232-26-0"><rect x="837" y="269" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-295-232-26-0"><rect x="837" y="295" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-321-232-26-0"><rect x="837" y="321" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-347-232-26-0"><rect x="837" y="347" width="232" height="26"/></clipPath><clipPath id="mx-clip-837-373-232-26-0"><rect x="837" y="373" width="232" height="26"/></clipPath><clipPath id="mx-clip-1177-61-222-26-0"><rect x="1177" y="61" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-87-222-26-0"><rect x="1177" y="87" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-113-222-26-0"><rect x="1177" y="113" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-139-222-26-0"><rect x="1177" y="139" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-165-222-26-0"><rect x="1177" y="165" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-191-222-26-0"><rect x="1177" y="191" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-217-222-26-0"><rect x="1177" y="217" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-243-222-26-0"><rect x="1177" y="243" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-269-222-26-0"><rect x="1177" y="269" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-295-222-26-0"><rect x="1177" y="295" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-321-222-26-0"><rect x="1177" y="321" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-347-222-26-0"><rect x="1177" y="347" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-373-222-26-0"><rect x="1177" y="373" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-399-222-26-0"><rect x="1177" y="399" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-425-222-26-0"><rect x="1177" y="425" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-451-222-26-0"><rect x="1177" y="451" width="222" height="26"/></clipPath><clipPath id="mx-clip-1177-477-222-26-0"><rect x="1177" y="477" width="222" height="26"/></clipPath><clipPath id="mx-clip-1527-126-242-26-0"><rect x="1527" y="126" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-152-242-26-0"><rect x="1527" y="152" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-178-242-26-0"><rect x="1527" y="178" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-204-242-26-0"><rect x="1527" y="204" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-230-242-26-0"><rect x="1527" y="230" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-256-242-26-0"><rect x="1527" y="256" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-282-242-26-0"><rect x="1527" y="282" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-308-242-26-0"><rect x="1527" y="308" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-334-242-26-0"><rect x="1527" y="334" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-360-242-26-0"><rect x="1527" y="360" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-386-242-26-0"><rect x="1527" y="386" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-412-242-26-0"><rect x="1527" y="412" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-438-242-26-0"><rect x="1527" y="438" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-464-242-26-0"><rect x="1527" y="464" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-490-242-26-0"><rect x="1527" y="490" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-516-242-26-0"><rect x="1527" y="516" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-542-242-26-0"><rect x="1527" y="542" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-568-242-26-0"><rect x="1527" y="568" width="242" height="26"/></clipPath><clipPath id="mx-clip-1527-594-242-26-0"><rect x="1527" y="594" width="242" height="26"/></clipPath></defs><path d="M 3 56 L 3 30 L 333 30 L 333 56" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 3 56 L 3 758 L 333 758 L 333 56" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 3 56 L 333 56" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="167.5" y="47.5">web_client</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-61-322-26-0)" font-size="12px"><text x="8.5" y="73.5">unsigned long long id</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-87-322-26-0)" font-size="12px"><text x="8.5" y="99.5">WEB_CLIENT_FLAGS flags</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-113-322-26-0)" font-size="12px"><text x="8.5" y="125.5">WEB_CLIENT_MODE mode</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-139-322-26-0)" font-size="12px"><text x="8.5" y="151.5">WEB_CLIENT_ACL acl</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-165-322-26-0)" font-size="12px"><text x="8.5" y="177.5">size_t header_parse_tries</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-191-322-26-0)" font-size="12px"><text x="8.5" y="203.5">size_t header_parse_last_size</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-217-322-26-0)" font-size="12px"><text x="8.5" y="229.5">int tcp_cork</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-243-322-26-0)" font-size="12px"><text x="8.5" y="255.5">int fd</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-269-322-26-0)" font-size="12px"><text x="8.5" y="281.5">int fd</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-295-322-26-0)" font-size="12px"><text x="8.5" y="307.5">char remote_ip[NI_MAXHOST+1}</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-321-322-26-0)" font-size="12px"><text x="8.5" y="333.5">char remote_port[NI_MAXSERV+1]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-347-322-26-0)" font-size="12px"><text x="8.5" y="359.5">char decoded_url[NETDATA_WEB_REQUEST_URL_SIZE+1</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-373-322-26-0)" font-size="12px"><text x="8.5" y="385.5">char last_url[NETDATA_WEB_REQUEST_URL_SIZE+1]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-399-322-26-0)" font-size="12px"><text x="8.5" y="411.5">struct timeval tv_in, tv_ready</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-425-322-26-0)" font-size="12px"><text x="8.5" y="437.5">char cookie1[NETDATA_WEB_REQUEST_COOKIE_SIZE+1]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-451-322-26-0)" font-size="12px"><text x="8.5" y="463.5">char cookie2[NETDATA_WEB_REQUEST_COOKIE_SIZE+1]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-477-322-26-0)" font-size="12px"><text x="8.5" y="489.5">char origin[NETDATA_WEB_REQUEST_ORIGIN_HEADER_SIZE+1]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-503-322-26-0)" font-size="12px"><text x="8.5" y="515.5">char *user_agent</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-529-322-26-0)" font-size="12px"><text x="8.5" y="541.5">struct response response</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-555-322-26-0)" font-size="12px"><text x="8.5" y="567.5">size_t stats_received_bytes</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-581-322-26-0)" font-size="12px"><text x="8.5" y="593.5">size_t stats_sent_bytes</text></g><path d="M 333 615 L 353 615 L 353 10 L 168 10 L 168 23.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 168 28.88 L 164.5 21.88 L 168 23.63 L 171.5 21.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-607-322-26-0)" font-size="12px"><text x="8.5" y="619.5">struct web_client *prev</text></g><path d="M 333 641 L 353 641 L 353 10 L 168 10 L 168 23.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 168 28.88 L 164.5 21.88 L 168 23.63 L 171.5 21.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(304.5,299.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="102" height="12" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; white-space: nowrap; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;background-color:#ffffff;">double linked list of</div></div></foreignObject><text x="51" y="12" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">double linked list of</text></switch></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-633-322-26-0)" font-size="12px"><text x="8.5" y="645.5">struct web_client *next</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-659-322-26-0)" font-size="12px"><text x="8.5" y="671.5">netdata_thread_t thread</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-685-322-26-0)" font-size="12px"><text x="8.5" y="697.5">volatile int running</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-711-322-26-0)" font-size="12px"><text x="8.5" y="723.5">size_t pollinfo_slot</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-7-737-322-26-0)" font-size="12px"><text x="8.5" y="749.5">size_t pollinfo_filecopy_slot</text></g><path d="M 473 160 L 473 134 L 733 134 L 733 160" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 473 160 L 473 472 L 733 472 L 733 160" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 473 160 L 733 160" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="602.5" y="151.5">response</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-165-252-26-0)" font-size="12px"><text x="478.5" y="177.5">BUFFER *header</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-191-252-26-0)" font-size="12px"><text x="478.5" y="203.5">BUFFER *header_output</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-217-252-26-0)" font-size="12px"><text x="478.5" y="229.5">BUFFER *data</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-243-252-26-0)" font-size="12px"><text x="478.5" y="255.5">int code</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-269-252-26-0)" font-size="12px"><text x="478.5" y="281.5">size_t rlen</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-295-252-26-0)" font-size="12px"><text x="478.5" y="307.5">size_t sent</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-321-252-26-0)" font-size="12px"><text x="478.5" y="333.5">int zoutput</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-347-252-26-0)" font-size="12px"><text x="478.5" y="359.5">z_stream zstream</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-373-252-26-0)" font-size="12px"><text x="478.5" y="385.5">Bytef zbuffer[NETDATA_WEB_RESPONSE_ZLIB_CHUNK_SIZE]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-399-252-26-0)" font-size="12px"><text x="478.5" y="411.5">size_t zsent</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-425-252-26-0)" font-size="12px"><text x="478.5" y="437.5">size_t zhave</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-451-252-26-0)" font-size="12px"><text x="478.5" y="463.5">unsigned int zinitialized</text></g><path d="M 333 537 L 403 537 L 403 114 L 603 114 L 603 127.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 603 132.88 L 599.5 125.88 L 603 127.63 L 606.5 125.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 473 572 L 473 546 L 733 546 L 733 572" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 473 572 L 473 754 L 733 754 L 733 572" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 473 572 L 733 572" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="602.5" y="563.5">clients_cache</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-577-252-26-0)" font-size="12px"><text x="478.5" y="589.5">pid_t pid</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-603-252-26-0)" font-size="12px"><text x="478.5" y="615.5">struct web_client *used</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-629-252-26-0)" font-size="12px"><text x="478.5" y="641.5">size_t used_count</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-655-252-26-0)" font-size="12px"><text x="478.5" y="667.5">struct web_client *avail</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-681-252-26-0)" font-size="12px"><text x="478.5" y="693.5">size_t avail_count</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-707-252-26-0)" font-size="12px"><text x="478.5" y="719.5">size_t reused</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-477-733-252-26-0)" font-size="12px"><text x="478.5" y="745.5">size_t allocated</text></g><path d="M 733 611 L 753 611 L 753 10 L 168 10 L 168 23.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 168 28.88 L 164.5 21.88 L 168 23.63 L 171.5 21.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(724.5,12.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="57" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; white-space: nowrap; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;background-color:#ffffff;">linked list of</div></div></foreignObject><text x="29" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">linked list of</text></switch></g><path d="M 733 663 L 753 663 L 753 10 L 168 10 L 168 23.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 168 28.88 L 164.5 21.88 L 168 23.63 L 171.5 21.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(724.5,38.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="57" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; white-space: nowrap; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;background-color:#ffffff;">linked list of</div></div></foreignObject><text x="29" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">linked list of</text></switch></g><path d="M 833 108 L 833 82 L 1073 82 L 1073 108" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 833 108 L 833 394 L 1073 394 L 1073 108" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 833 108 L 1073 108" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="952.5" y="99.5">listen_sockets</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-113-232-26-0)" font-size="12px"><text x="838.5" y="125.5">struct config *config</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-139-232-26-0)" font-size="12px"><text x="838.5" y="151.5">const char *config_section</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-165-232-26-0)" font-size="12px"><text x="838.5" y="177.5">const char *default_bind_to</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-191-232-26-0)" font-size="12px"><text x="838.5" y="203.5">uint16_t default_port</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-217-232-26-0)" font-size="12px"><text x="838.5" y="229.5">int backlog</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-243-232-26-0)" font-size="12px"><text x="838.5" y="255.5">size_t opened</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-269-232-26-0)" font-size="12px"><text x="838.5" y="281.5">size_t failed</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-295-232-26-0)" font-size="12px"><text x="838.5" y="307.5">int fds[MAX_LISTEN_FDS]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-321-232-26-0)" font-size="12px"><text x="838.5" y="333.5">int *fds_names[MAX_LISTEN_FDS]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-347-232-26-0)" font-size="12px"><text x="838.5" y="359.5">int fds_types[MAX_LISTEN_FDS]</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-837-373-232-26-0)" font-size="12px"><text x="838.5" y="385.5">int fds_families[MAX_LISTEN_FDS]</text></g><path d="M 1173 56 L 1173 30 L 1403 30 L 1403 56" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1173 56 L 1173 498 L 1403 498 L 1403 56" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1173 56 L 1403 56" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="1287.5" y="47.5">POLLINFO</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-61-222-26-0)" font-size="12px"><text x="1178.5" y="73.5">POLLJOB *p</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-87-222-26-0)" font-size="12px"><text x="1178.5" y="99.5">size_t slot</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-113-222-26-0)" font-size="12px"><text x="1178.5" y="125.5">int fd</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-139-222-26-0)" font-size="12px"><text x="1178.5" y="151.5">int socktype</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-165-222-26-0)" font-size="12px"><text x="1178.5" y="177.5">char *remote_ip</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-191-222-26-0)" font-size="12px"><text x="1178.5" y="203.5">char *remote_port</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-217-222-26-0)" font-size="12px"><text x="1178.5" y="229.5">time_t connected_t</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-243-222-26-0)" font-size="12px"><text x="1178.5" y="255.5">time_t last_received_t</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-269-222-26-0)" font-size="12px"><text x="1178.5" y="281.5">time_t last_sent_t</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-295-222-26-0)" font-size="12px"><text x="1178.5" y="307.5">size_t recv_count</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-321-222-26-0)" font-size="12px"><text x="1178.5" y="333.5">size_t send_count</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-347-222-26-0)" font-size="12px"><text x="1178.5" y="359.5">uint32_t flags</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-373-222-26-0)" font-size="12px"><text x="1178.5" y="385.5">void (*del_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-399-222-26-0)" font-size="12px"><text x="1178.5" y="411.5">int (*rcv_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-425-222-26-0)" font-size="12px"><text x="1178.5" y="437.5">int (*snd_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-451-222-26-0)" font-size="12px"><text x="1178.5" y="463.5">void *data</text></g><path d="M 1403 485 L 1423 485 L 1423 10 L 1279 10 L 1279 22.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1279 27.88 L 1275.5 20.88 L 1279 22.63 L 1282.5 20.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1177-477-222-26-0)" font-size="12px"><text x="1178.5" y="489.5">struct pollinfo *next</text></g><path d="M 1523 121 L 1523 95 L 1773 95 L 1773 121" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1523 121 L 1523 615 L 1773 615 L 1773 121" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1523 121 L 1773 121" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="1647.5" y="112.5">POLLJOB</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-126-242-26-0)" font-size="12px"><text x="1528.5" y="138.5">size_t slots</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-152-242-26-0)" font-size="12px"><text x="1528.5" y="164.5">size_t used</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-178-242-26-0)" font-size="12px"><text x="1528.5" y="190.5">size_t min</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-204-242-26-0)" font-size="12px"><text x="1528.5" y="216.5">size_t max</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-230-242-26-0)" font-size="12px"><text x="1528.5" y="242.5">size_t limit</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-256-242-26-0)" font-size="12px"><text x="1528.5" y="268.5">time_t complete_request_timeout</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-282-242-26-0)" font-size="12px"><text x="1528.5" y="294.5">time_t idle_timeout</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-308-242-26-0)" font-size="12px"><text x="1528.5" y="320.5">time_t check_every</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-334-242-26-0)" font-size="12px"><text x="1528.5" y="346.5">time_t timer_milliseconds</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-360-242-26-0)" font-size="12px"><text x="1528.5" y="372.5">void *timer_data</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-386-242-26-0)" font-size="12px"><text x="1528.5" y="398.5">struct run *fds</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-412-242-26-0)" font-size="12px"><text x="1528.5" y="424.5">struct pollinfo *inf</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-438-242-26-0)" font-size="12px"><text x="1528.5" y="450.5">struct pollinfo *first_free</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-464-242-26-0)" font-size="12px"><text x="1528.5" y="476.5">SIMPLE_PATTERN *access_list</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-490-242-26-0)" font-size="12px"><text x="1528.5" y="502.5">void *(*add_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-516-242-26-0)" font-size="12px"><text x="1528.5" y="528.5">void (*dell_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-542-242-26-0)" font-size="12px"><text x="1528.5" y="554.5">int (*rcv_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-568-242-26-0)" font-size="12px"><text x="1528.5" y="580.5">int (*snd_callback)</text></g><g fill="#000000" font-family="Helvetica" clip-path="url(#mx-clip-1527-594-242-26-0)" font-size="12px"><text x="1528.5" y="606.5">void (*tmr_callback)</text></g><path d="M 1403 69 L 1648 69 L 1648 88.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1648 93.88 L 1644.5 86.88 L 1648 88.63 L 1651.5 86.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1773 420 L 1793 420 L 1793 10 L 1279 10 L 1279 22.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1279 27.88 L 1275.5 20.88 L 1279 22.63 L 1282.5 20.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1773 446 L 1793 446 L 1793 10 L 1278 10 L 1278 22.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1278 27.88 L 1274.5 20.88 L 1278 22.63 L 1281.5 20.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/></svg>
\ No newline at end of file
src/collectors/apps.plugin/apps_pid.c
+3 -1
@@ -53,7 +53,9 @@ size_t all_pids_count(void) {
53 }
54
55 void apps_pids_init(void) {
56 - pids.all_pids.aral = aral_create("pid_stat", sizeof(struct pid_stat), 1, 0, NULL, NULL, NULL, false, true);
56 + pids.all_pids.aral = aral_create("pid_stat", sizeof(struct pid_stat),
57 + 1, 0, NULL, NULL, NULL,
58 + false, true, false);
59 simple_hashtable_init_PID(&pids.all_pids.ht, 1024);
60 }
61
src/collectors/ebpf.plugin/ebpf.c
+3 -2
@@ -739,8 +739,9 @@ ARAL *ebpf_allocate_pid_aral(char *name, size_t size)
739 }
740
741 return aral_create(name, size,
742 - 0, 0,
743 - NULL, NULL, NULL, false, false);
742 + 0, 0,
743 + NULL, NULL, NULL,
744 + false, false, false);
745 }
746
747 /*****************************************************************
src/collectors/statsd.plugin/statsd.c
+11 -13
@@ -261,7 +261,7 @@ static struct statsd {
261 size_t udp_packets_received;
262 size_t udp_bytes_read;
263
264 - int32_t update_every;
264 + time_t update_every;
265 bool enabled;
266 bool private_charts_hidden;
267 SIMPLE_PATTERN *charts_for;
@@ -873,12 +873,12 @@ struct statsd_udp {
873 };
874
875 // new TCP client connected
876 -static void *statsd_add_callback(POLLINFO *pi, short int *events, void *data) {
876 +static void *statsd_add_callback(POLLINFO *pi, nd_poll_event_t *events, void *data) {
877 (void)pi;
878 (void)data;
879
880 worker_is_busy(WORKER_JOB_TYPE_TCP_CONNECTED);
881 - *events = POLLIN;
881 + *events = ND_POLL_READ;
882
883 struct statsd_tcp *t = (struct statsd_tcp *)callocz(sizeof(struct statsd_tcp) + STATSD_TCP_BUFFER_SIZE, 1);
884 t->type = STATSD_SOCKET_DATA_TYPE_TCP;
@@ -916,11 +916,11 @@ static void statsd_del_callback(POLLINFO *pi) {
916 }
917
918 // Receive data
919 -static int statsd_rcv_callback(POLLINFO *pi, short int *events) {
919 +static int statsd_rcv_callback(POLLINFO *pi, nd_poll_event_t *events) {
920 int retval = -1;
921 worker_is_busy(WORKER_JOB_TYPE_RCV_DATA);
922
923 - *events = POLLIN;
923 + *events = ND_POLL_READ;
924
925 int fd = pi->fd;
926
@@ -1064,10 +1064,7 @@ cleanup:
1064 return retval;
1065 }
1066
1067 -static int statsd_snd_callback(POLLINFO *pi, short int *events) {
1068 - (void)pi;
1069 - (void)events;
1070 -
1067 +static int statsd_snd_callback(POLLINFO *pi __maybe_unused, nd_poll_event_t *events __maybe_unused) {
1068 worker_is_busy(WORKER_JOB_TYPE_SND_DATA);
1069 netdata_log_error("STATSD: snd_callback() called, but we never requested to send data to statsd clients.");
1070 worker_is_idle();
@@ -2487,11 +2484,12 @@ void *statsd_main(void *ptr) {
2484
2485 statsd.enabled = config_get_boolean(CONFIG_SECTION_PLUGINS, "statsd", statsd.enabled);
2486
2490 - statsd.update_every = default_rrd_update_every;
2487 + statsd.update_every = nd_profile.update_every;
2488 statsd.update_every = (int)config_get_duration_seconds(CONFIG_SECTION_STATSD, "update every (flushInterval)", statsd.update_every);
2492 - if(statsd.update_every < default_rrd_update_every) {
2493 - collector_error("STATSD: minimum flush interval %d given, but the minimum is the update every of netdata. Using %d", statsd.update_every, default_rrd_update_every);
2494 - statsd.update_every = default_rrd_update_every;
2489 + if(statsd.update_every < nd_profile.update_every) {
2490 + collector_error("STATSD: minimum flush interval %d given, but the minimum is the update every of netdata. Using %d",
2491 + (int)statsd.update_every, (int)nd_profile.update_every);
2492 + statsd.update_every = nd_profile.update_every;
2493 config_set_duration_seconds(CONFIG_SECTION_STATSD, "update every (flushInterval)", statsd.update_every);
2494 }
2495
src/collectors/systemd-journal.plugin/systemd-journal-annotations.c
+2
@@ -610,6 +610,8 @@ static void netdata_systemd_journal_message_ids_init(void) {
610 msgid_into_dict("6e2e3839067648968b646045dbf28d66", "Netdata connection to parent");
611 msgid_into_dict("9ce0cb58ab8b44df82c4bf1ad9ee22de", "Netdata alert transition");
612 msgid_into_dict("6db0018e83e34320ae2a659d78019fb7", "Netdata alert notification");
613 + msgid_into_dict("23e93dfccbf64e11aac858b9410d8a82", "Netdata fatal message");
614 +
615 msgid_into_dict("8ddaf5ba33a74078b609250db1e951f3", "Sensor state transition");
616 }
617
src/collectors/windows-events.plugin/windows-events-providers.c
+2 -2
@@ -99,8 +99,8 @@ static inline bool provider_cache_compar(PROVIDER_KEY *a, PROVIDER_KEY *b) {
99
100 void provider_cache_init(void) {
101 simple_hashtable_init_PROVIDER(&pbc.hashtable, 100000);
102 - pbc.aral_providers = aral_create("wevt_providers", sizeof(PROVIDER), 0, 4096, NULL, NULL, NULL, false, true);
103 - pbc.aral_handles = aral_create("wevt_handles", sizeof(PROVIDER_META_HANDLE), 0, 4096, NULL, NULL, NULL, false, true);
102 + pbc.aral_providers = aral_create("wevt_providers", sizeof(PROVIDER), 0, 4096, NULL, NULL, NULL, false, true, false);
103 + pbc.aral_handles = aral_create("wevt_handles", sizeof(PROVIDER_META_HANDLE), 0, 4096, NULL, NULL, NULL, false, true, false);
104 }
105
106 static bool provider_property_get(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content, EVT_PUBLISHER_METADATA_PROPERTY_ID property_id) {
src/daemon/analytics.c
+1 -1
@@ -518,7 +518,7 @@ void analytics_gather_mutable_meta_data(void)
518 analytics_alarms_notifications();
519
520 analytics_set_data(
521 - &analytics_data.netdata_config_is_parent, (rrdhost_hosts_available() > 1 || stream_conf_is_parent(false)) ? "true" : "false");
521 + &analytics_data.netdata_config_is_parent, (rrdhost_hosts_available() > 1 || netdata_conf_is_parent()) ? "true" : "false");
522
523 analytics_set_data(&analytics_data.netdata_host_agent_claimed, is_agent_claimed() ? "true" : "false");
524
src/daemon/config/netdata-conf-cloud.c
+1 -1
@@ -5,7 +5,7 @@
5
6 size_t netdata_conf_cloud_query_threads(void) {
7 size_t cpus = MIN(netdata_conf_cpus(), 256); // max 256 cores
8 - size_t threads = MIN(cpus * (stream_conf_is_parent(false) ? 2 : 1), (size_t)libuv_worker_threads / 2);
8 + size_t threads = MIN(cpus * (netdata_conf_is_parent() ? 2 : 1), (size_t)libuv_worker_threads / 2);
9 threads = MAX(threads, 6);
10
11 threads = config_get_number(CONFIG_SECTION_CLOUD, "query threads", threads);
src/daemon/config/netdata-conf-db.c
+37 -29
@@ -2,11 +2,9 @@
2
3 #include "netdata-conf-db.h"
4
5 -int default_rrd_update_every = UPDATE_EVERY;
5 int default_rrd_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
6
7 bool dbengine_enabled = false; // will become true if and when dbengine is initialized
9 -size_t storage_tiers = 3;
8 bool dbengine_use_direct_io = true;
9 static size_t storage_tiers_grouping_iterations[RRD_STORAGE_TIERS] = {1, 60, 60, 60, 60};
10 static double storage_tiers_retention_days[RRD_STORAGE_TIERS] = {14, 90, 2 * 365, 2 * 365, 2 * 365};
@@ -16,7 +14,7 @@ time_t rrdhost_free_orphan_time_s = 3600;
14 time_t rrdhost_free_ephemeral_time_s = 86400;
15
16 size_t get_tier_grouping(size_t tier) {
19 - if(unlikely(tier >= storage_tiers)) tier = storage_tiers - 1;
17 + if(unlikely(tier >= nd_profile.storage_tiers)) tier = nd_profile.storage_tiers - 1;
18
19 size_t grouping = 1;
20 // first tier is always 1 iteration of whatever update every the chart has
@@ -191,20 +189,20 @@ void netdata_conf_dbengine_init(const char *hostname) {
189 config_set_number(CONFIG_SECTION_DB, "dbengine pages per extent", rrdeng_pages_per_extent);
190 }
191
194 - storage_tiers = config_get_number(CONFIG_SECTION_DB, "storage tiers", storage_tiers);
195 - if(storage_tiers < 1) {
192 + nd_profile.storage_tiers = config_get_number(CONFIG_SECTION_DB, "storage tiers", nd_profile.storage_tiers);
193 + if(nd_profile.storage_tiers < 1) {
194 nd_log(NDLS_DAEMON, NDLP_WARNING, "At least 1 storage tier is required. Assuming 1.");
195
198 - storage_tiers = 1;
199 - config_set_number(CONFIG_SECTION_DB, "storage tiers", storage_tiers);
196 + nd_profile.storage_tiers = 1;
197 + config_set_number(CONFIG_SECTION_DB, "storage tiers", nd_profile.storage_tiers);
198 }
201 - if(storage_tiers > RRD_STORAGE_TIERS) {
199 + if(nd_profile.storage_tiers > RRD_STORAGE_TIERS) {
200 nd_log(NDLS_DAEMON, NDLP_WARNING,
201 "Up to %d storage tier are supported. Assuming %d.",
202 RRD_STORAGE_TIERS, RRD_STORAGE_TIERS);
203
206 - storage_tiers = RRD_STORAGE_TIERS;
207 - config_set_number(CONFIG_SECTION_DB, "storage tiers", storage_tiers);
204 + nd_profile.storage_tiers = RRD_STORAGE_TIERS;
205 + config_set_number(CONFIG_SECTION_DB, "storage tiers", nd_profile.storage_tiers);
206 }
207
208 new_dbengine_defaults =
@@ -221,10 +219,10 @@ void netdata_conf_dbengine_init(const char *hostname) {
219 default_backfill = get_dbengine_backfill(RRD_BACKFILL_NEW);
220 char dbengineconfig[200 + 1];
221
224 - size_t grouping_iterations = default_rrd_update_every;
225 - storage_tiers_grouping_iterations[0] = default_rrd_update_every;
222 + size_t grouping_iterations = nd_profile.update_every;
223 + storage_tiers_grouping_iterations[0] = nd_profile.update_every;
224
227 - for (size_t tier = 1; tier < storage_tiers; tier++) {
225 + for (size_t tier = 1; tier < nd_profile.storage_tiers; tier++) {
226 grouping_iterations = storage_tiers_grouping_iterations[tier];
227 snprintfz(dbengineconfig, sizeof(dbengineconfig) - 1, "dbengine tier %zu update every iterations", tier);
228 grouping_iterations = config_get_number(CONFIG_SECTION_DB, dbengineconfig, grouping_iterations);
@@ -250,7 +248,7 @@ void netdata_conf_dbengine_init(const char *hostname) {
248 // fails on Windows.
249 bool parallel_initialization = false;
250 #else
253 - bool parallel_initialization = (storage_tiers <= netdata_conf_cpus()) ? true : false;
251 + bool parallel_initialization = (nd_profile.storage_tiers <= netdata_conf_cpus()) ? true : false;
252 #endif
253
254 struct dbengine_initialization tiers_init[RRD_STORAGE_TIERS] = {};
@@ -258,7 +256,7 @@ void netdata_conf_dbengine_init(const char *hostname) {
256 size_t created_tiers = 0;
257 char dbenginepath[FILENAME_MAX + 1];
258
261 - for (size_t tier = 0; tier < storage_tiers; tier++) {
259 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
260
261 if (tier == 0)
262 snprintfz(dbenginepath, FILENAME_MAX, "%s/dbengine", netdata_configured_cache_dir);
@@ -294,7 +292,7 @@ void netdata_conf_dbengine_init(const char *hostname) {
292 dbengine_tier_init(&tiers_init[tier]);
293 }
294
297 - for(size_t tier = 0; tier < storage_tiers ;tier++) {
295 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
296 if(parallel_initialization)
297 nd_thread_join(tiers_init[tier].thread);
298
@@ -307,31 +305,32 @@ void netdata_conf_dbengine_init(const char *hostname) {
305 created_tiers++;
306 }
307
310 - if(created_tiers && created_tiers < storage_tiers) {
308 + if(created_tiers && created_tiers < nd_profile.storage_tiers) {
309 nd_log(NDLS_DAEMON, NDLP_WARNING,
310 "DBENGINE on '%s': Managed to create %zu tiers instead of %zu. Continuing with %zu available.",
313 - hostname, created_tiers, storage_tiers, created_tiers);
311 + hostname, created_tiers,
312 + nd_profile.storage_tiers, created_tiers);
313
315 - storage_tiers = created_tiers;
314 + nd_profile.storage_tiers = created_tiers;
315 }
316 else if(!created_tiers)
317 fatal("DBENGINE on '%s', failed to initialize databases at '%s'.", hostname, netdata_configured_cache_dir);
318
320 - for(size_t tier = 0; tier < storage_tiers ;tier++)
319 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++)
320 rrdeng_readiness_wait(multidb_ctx[tier]);
321
322 calculate_tier_disk_space_percentage();
323
324 dbengine_enabled = true;
325 #else
327 - storage_tiers = config_get_number(CONFIG_SECTION_DB, "storage tiers", 1);
328 - if(storage_tiers != 1) {
326 + nd_profile.storage_tiers = config_get_number(CONFIG_SECTION_DB, "storage tiers", 1);
327 + if(nd_profile.storage_tiers != 1) {
328 nd_log(NDLS_DAEMON, NDLP_WARNING,
329 "DBENGINE is not available on '%s', so only 1 database tier can be supported.",
330 hostname);
331
333 - storage_tiers = 1;
334 - config_set_number(CONFIG_SECTION_DB, "storage tiers", storage_tiers);
332 + nd_profile.storage_tiers = 1;
333 + config_set_number(CONFIG_SECTION_DB, "storage tiers", nd_profile.storage_tiers);
334 }
335 dbengine_enabled = false;
336 #endif
@@ -350,11 +349,20 @@ void netdata_conf_section_db(void) {
349 // ------------------------------------------------------------------------
350 // get default database update frequency
351
353 - default_rrd_update_every = (int) config_get_duration_seconds(CONFIG_SECTION_DB, "update every", UPDATE_EVERY);
354 - if(default_rrd_update_every < 1 || default_rrd_update_every > 600) {
355 - netdata_log_error("Invalid data collection frequency (update every) %d given. Defaulting to %d.", default_rrd_update_every, UPDATE_EVERY);
356 - default_rrd_update_every = UPDATE_EVERY;
357 - config_set_duration_seconds(CONFIG_SECTION_DB, "update every", default_rrd_update_every);
352 + nd_profile.update_every = (int) config_get_duration_seconds(CONFIG_SECTION_DB, "update every", nd_profile.update_every);
353 + if(nd_profile.update_every < UPDATE_EVERY_MIN) {
354 + nd_log(NDLS_DAEMON, NDLP_WARNING,
355 + "Data collection frequency in netdata.conf ([" CONFIG_SECTION_DB "].update every), changed from %d to %d",
356 + (int)nd_profile.update_every, UPDATE_EVERY_MIN);
357 + nd_profile.update_every = UPDATE_EVERY_MIN;
358 + config_set_duration_seconds(CONFIG_SECTION_DB, "update every", nd_profile.update_every);
359 + }
360 + if(nd_profile.update_every > UPDATE_EVERY_MAX) {
361 + nd_log(NDLS_DAEMON, NDLP_WARNING,
362 + "Data collection frequency in netdata.conf ([" CONFIG_SECTION_DB "].update every), changed from %d to %d",
363 + (int)nd_profile.update_every, UPDATE_EVERY_MIN);
364 + nd_profile.update_every = UPDATE_EVERY_MAX;
365 + config_set_duration_seconds(CONFIG_SECTION_DB, "update every", nd_profile.update_every);
366 }
367
368 // ------------------------------------------------------------------------
src/daemon/config/netdata-conf-db.h
-2
@@ -6,10 +6,8 @@
6 #include "libnetdata/libnetdata.h"
7
8 extern bool dbengine_enabled;
9 -extern size_t storage_tiers;
9 extern bool dbengine_use_direct_io;
10
12 -extern int default_rrd_update_every;
11 extern int default_rrd_history_entries;
12 extern int gap_when_lost_iterations_above;
13 extern time_t rrdset_free_obsolete_time_s;
src/daemon/config/netdata-conf-global.c
+27 -8
@@ -55,15 +55,33 @@ static int get_hostname(char *buf, size_t buf_size) {
55 return rc;
56 }
57
58 -static void glibc_initialize(void) {
59 - const char *pmax = config_get(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for plugins", "1");
60 - if(pmax && *pmax)
61 - setenv("MALLOC_ARENA_MAX", pmax, 1);
58 +void netdata_conf_glibc_malloc_initialize(size_t wanted_arenas, size_t trim_threshold __maybe_unused) {
59 + wanted_arenas = config_get_number(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for plugins", wanted_arenas);
60 + if(wanted_arenas < 1 || wanted_arenas > os_get_system_cpus_cached(true)) {
61 + if(wanted_arenas < 1) wanted_arenas = 1;
62 + else wanted_arenas = os_get_system_cpus_cached(true);
63 + config_set_number(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for plugins", wanted_arenas);
64 + nd_log(NDLS_DAEMON, NDLP_NOTICE,
65 + "malloc arenas can be from 1 to %zu. Setting it to %zu",
66 + os_get_system_cpus_cached(true), wanted_arenas);
67 + }
68 +
69 + char buf[32];
70 + snprintfz(buf, sizeof(buf), "%zu", wanted_arenas);
71 + setenv("MALLOC_ARENA_MAX", buf, true);
72
73 #if defined(HAVE_C_MALLOPT)
64 - int i = (int)config_get_number(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for netdata", 1);
65 - if(i > 0)
66 - mallopt(M_ARENA_MAX, 1);
74 + wanted_arenas = config_get_number(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for netdata", wanted_arenas);
75 + if(wanted_arenas < 1 || wanted_arenas > os_get_system_cpus_cached(true)) {
76 + if(wanted_arenas < 1) wanted_arenas = 1;
77 + else wanted_arenas = os_get_system_cpus_cached(true);
78 + config_set_number(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for netdata", wanted_arenas);
79 + nd_log(NDLS_DAEMON, NDLP_NOTICE,
80 + "malloc arenas can be from 1 to %zu. Setting it to %zu",
81 + os_get_system_cpus_cached(true), wanted_arenas);
82 + }
83 + mallopt(M_ARENA_MAX, (int)wanted_arenas);
84 + mallopt(M_TRIM_THRESHOLD, (int)trim_threshold);
85
86 #ifdef NETDATA_INTERNAL_CHECKS
87 mallopt(M_PERTURB, 0x5A);
@@ -108,6 +126,8 @@ void netdata_conf_section_global(void) {
126 netdata_log_debug(D_OPTIONS, "hostname set to '%s'", netdata_configured_hostname);
127
128 netdata_conf_section_directories();
129 +
130 + nd_profile_setup(); // required for configuring the database
131 netdata_conf_section_db();
132
133 // --------------------------------------------------------------------
@@ -116,7 +136,6 @@ void netdata_conf_section_global(void) {
136 os_get_system_cpus_uncached();
137 os_get_system_pid_max();
138
119 - glibc_initialize();
139 libuv_initialize();
140 }
141
src/daemon/config/netdata-conf-global.h
+2
@@ -10,6 +10,8 @@ void netdata_conf_section_global_run_as_user(const char **user);
10
11 size_t netdata_conf_cpus(void);
12
13 +void netdata_conf_glibc_malloc_initialize(size_t wanted_arenas, size_t trim_threshold);
14 +
15 #include "netdata-conf.h"
16
17 #endif //NETDATA_NETDATA_CONF_GLOBAL_H
src/daemon/config/netdata-conf-profile.c new
+155
@@ -0,0 +1,155 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "netdata-conf-profile.h"
4 +#include "streaming/stream-conf.h"
5 +#include "netdata-conf.h"
6 +
7 +ENUM_STR_MAP_DEFINE(ND_PROFILE) = {
8 + { .id = ND_PROFILE_STANDALONE, .name = "standalone" },
9 + { .id = ND_PROFILE_PARENT, .name = "parent" },
10 + { .id = ND_PROFILE_CHILD, .name = "child" },
11 + { .id = ND_PROFILE_IOT, .name = "iot" },
12 +
13 + // terminator
14 + { . id = 0, .name = NULL }
15 +};
16 +
17 +BITMAP_STR_DEFINE_FUNCTIONS(ND_PROFILE, ND_PROFILE_NONE, "");
18 +
19 +static inline ND_PROFILE prefer_profile(ND_PROFILE setting, ND_PROFILE preferred, ND_PROFILE out_of) {
20 + if(setting & preferred) {
21 + setting &= ~out_of;
22 + setting |= preferred;
23 + }
24 + return setting;
25 +}
26 +
27 +ND_PROFILE nd_profile_detect_and_configure(bool recheck) {
28 + static ND_PROFILE profile = ND_PROFILE_NONE;
29 + if(!recheck && profile != ND_PROFILE_NONE)
30 + return profile;
31 +
32 + // required for detecting the profile
33 + stream_conf_load();
34 +
35 + ND_PROFILE def_profile = ND_PROFILE_NONE;
36 +
37 + OS_SYSTEM_MEMORY mem = os_system_memory(true);
38 + size_t cpus = os_get_system_cpus_uncached();
39 +
40 + if(cpus <= 1 || (mem.ram_total_bytes && mem.ram_total_bytes < 1ULL * 1024 * 1024 * 1024))
41 + def_profile = ND_PROFILE_IOT;
42 +
43 + else if(stream_conf_is_parent(true))
44 + def_profile = ND_PROFILE_PARENT;
45 +
46 + else if(stream_conf_is_child())
47 + def_profile = ND_PROFILE_CHILD;
48 +
49 + else
50 + def_profile = ND_PROFILE_STANDALONE;
51 +
52 + CLEAN_BUFFER *wb = buffer_create(0, NULL);
53 + ND_PROFILE_2buffer(wb, def_profile, " ");
54 +
55 + CLEAN_CHAR_P *s = strdupz(config_get(CONFIG_SECTION_GLOBAL, "profile", buffer_tostring(wb)));
56 +
57 + char *words[100];
58 + size_t n = quoted_strings_splitter(s, words, _countof(words), isspace_map_whitespace);
59 + ND_PROFILE pt = ND_PROFILE_NONE;
60 + for(size_t i = 0; i < n ;i++) {
61 + ND_PROFILE ptt = ND_PROFILE_2id_one(words[i]);
62 + if(ptt == ND_PROFILE_NONE)
63 + nd_log(NDLS_DAEMON, NDLP_ERR, "Cannot understand netdata.conf [global].profile = %s", words[i]);
64 + pt |= ptt;
65 + }
66 +
67 + // sanity checks
68 +
69 + ND_PROFILE started = pt;
70 +
71 + if(!(pt & ND_CONF_PROFILES_SYSTEM))
72 + // system profile is missing from the settings
73 + pt |= (def_profile & ND_CONF_PROFILES_SYSTEM);
74 +
75 + pt = prefer_profile(pt, ND_PROFILE_PARENT, ND_CONF_PROFILES_SYSTEM);
76 + pt = prefer_profile(pt, ND_PROFILE_STANDALONE, ND_CONF_PROFILES_SYSTEM);
77 + pt = prefer_profile(pt, ND_PROFILE_CHILD, ND_CONF_PROFILES_SYSTEM);
78 + pt = prefer_profile(pt, ND_PROFILE_IOT, ND_CONF_PROFILES_SYSTEM);
79 +
80 + if(pt != started) {
81 + buffer_flush(wb);
82 + ND_PROFILE_2buffer(wb, pt, " ");
83 + config_set(CONFIG_SECTION_GLOBAL, "profile", buffer_tostring(wb));
84 +
85 + nd_log(NDLS_DAEMON, NDLP_WARNING,
86 + "The netdata.conf setting [global].profile has been overwritten to '%s'",
87 + buffer_tostring(wb));
88 + }
89 +
90 + profile = pt;
91 + return profile;
92 +}
93 +
94 +struct nd_profile_t nd_profile = { 0 };
95 +
96 +void nd_profile_setup(void) {
97 + static bool run = false;
98 + if(run) return;
99 + run = true;
100 +
101 + ND_PROFILE profile = nd_profile_detect_and_configure(true); (void)profile;
102 + if(netdata_conf_is_iot()) {
103 + nd_profile.storage_tiers = 3; // MUST BE 1
104 + nd_profile.update_every = 1; // MUST BE 2
105 + nd_profile.malloc_arenas = 1;
106 + nd_profile.malloc_trim = 32 * 1024;
107 + // web server threads = 6
108 + // aclk query threads = 6
109 + // backfill threads = 0
110 + // replication threads = 1 // can we disable them completely?
111 + // ml enabled = false
112 + // health enabled = true if there is no parent, otherwise false
113 + // disable internal plugins: tc, idlejitter, cgroups
114 + // disable external plugins: all except apps, network_viewer, systemd-journal
115 + // disable sqlite
116 + }
117 + else if(netdata_conf_is_parent()) {
118 + nd_profile.storage_tiers = 3;
119 + nd_profile.update_every = 1;
120 + nd_profile.malloc_arenas = os_get_system_cpus_cached(true);
121 + nd_profile.malloc_trim = 256 * 1024;
122 + // web server threads = dynamic
123 + // aclk query threads = dynamic
124 + // backfill threads = dynamic
125 + // replication threads = dynamic
126 + // ml enabled = true
127 + // health enabled = true
128 + }
129 + else if(netdata_conf_is_child()) {
130 + nd_profile.storage_tiers = 3;
131 + nd_profile.update_every = 1;
132 + nd_profile.malloc_arenas = 1;
133 + nd_profile.malloc_trim = 32 * 1024;
134 + // web server threads = 6
135 + // aclk query threads = 6
136 + // backfill threads = 0
137 + // replication threads = 1
138 + // ml enabled = false
139 + // health enabled = false
140 + }
141 + else /* if(netdata_conf_is_standalone()) */ {
142 + nd_profile.storage_tiers = 3;
143 + nd_profile.update_every = 1;
144 + nd_profile.malloc_arenas = 1;
145 + nd_profile.malloc_trim = 64 * 1024;
146 + // web server threads = 6
147 + // aclk query threads = 6
148 + // backfill threads = 0
149 + // replication threads = 1 // can we disable them completely?
150 + // ml enabled = true
151 + // health enabled = true
152 + }
153 +
154 + netdata_conf_glibc_malloc_initialize(nd_profile.malloc_arenas, nd_profile.malloc_trim);
155 +}
src/daemon/config/netdata-conf-profile.h new
+54
@@ -0,0 +1,54 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_NETDATA_CONF_PROFILE_H
4 +#define NETDATA_NETDATA_CONF_PROFILE_H
5 +
6 +#include "libnetdata/libnetdata.h"
7 +
8 +typedef enum {
9 + ND_PROFILE_NONE = (0),
10 +
11 + // system profiles
12 + ND_PROFILE_PARENT = (1 << 30),
13 + ND_PROFILE_STANDALONE = (1 << 29),
14 + ND_PROFILE_CHILD = (1 << 28),
15 + ND_PROFILE_IOT = (1 << 27),
16 +
17 + // optional attributed to profiles
18 +
19 +} ND_PROFILE;
20 +
21 +#define ND_CONF_PROFILES_SYSTEM (ND_PROFILE_STANDALONE | ND_PROFILE_PARENT | ND_PROFILE_CHILD | ND_PROFILE_IOT)
22 +
23 +BITMAP_STR_DEFINE_FUNCTIONS_EXTERN(ND_PROFILE);
24 +
25 +ND_PROFILE nd_profile_detect_and_configure(bool recheck);
26 +
27 +void nd_profile_setup(void);
28 +
29 +static inline bool netdata_conf_is_iot(void) {
30 + return (nd_profile_detect_and_configure(false) & ND_CONF_PROFILES_SYSTEM) == ND_PROFILE_IOT;
31 +}
32 +
33 +static inline bool netdata_conf_is_standalone(void) {
34 + return (nd_profile_detect_and_configure(false) & ND_CONF_PROFILES_SYSTEM) == ND_PROFILE_STANDALONE;
35 +}
36 +
37 +static inline bool netdata_conf_is_child(void) {
38 + return (nd_profile_detect_and_configure(false) & ND_CONF_PROFILES_SYSTEM) == ND_PROFILE_CHILD;
39 +}
40 +
41 +static inline bool netdata_conf_is_parent(void) {
42 + return (nd_profile_detect_and_configure(false) & ND_CONF_PROFILES_SYSTEM) == ND_PROFILE_PARENT;
43 +}
44 +
45 +struct nd_profile_t {
46 + size_t storage_tiers;
47 + time_t update_every;
48 + size_t malloc_arenas;
49 + size_t malloc_trim;
50 +};
51 +
52 +extern struct nd_profile_t nd_profile;
53 +
54 +#endif //NETDATA_NETDATA_CONF_PROFILE_H
src/daemon/config/netdata-conf-web.c
+1 -1
@@ -12,7 +12,7 @@ size_t netdata_conf_web_query_threads(void) {
12 }
13
14 size_t cpus = MIN(netdata_conf_cpus(), 256); // max 256 cores
15 - size_t threads = cpus * (stream_conf_is_parent(false) ? 2 : 1);
15 + size_t threads = cpus * (netdata_conf_is_parent() ? 2 : 1);
16 threads = MAX(threads, 6);
17
18 threads = config_get_number(CONFIG_SECTION_WEB, "web server threads", threads);
src/daemon/config/netdata-conf.h
+1
@@ -12,6 +12,7 @@ bool netdata_conf_load(char *filename, char overwrite_used, const char **user);
12 #include "netdata-conf-db.h"
13 #include "netdata-conf-directories.h"
14 #include "netdata-conf-global.h"
15 +#include "netdata-conf-profile.h"
16 #include "netdata-conf-logs.h"
17 #include "netdata-conf-web.h"
18 #include "netdata-conf-cloud.h"
src/daemon/daemon-shutdown.c
+5 -5
@@ -81,7 +81,7 @@ static void rrdeng_flush_everything_and_wait(bool wait_flush, bool wait_collecto
81 return;
82
83 nd_log(NDLS_DAEMON, NDLP_INFO, "Flushing DBENGINE dirty pages...");
84 - for (size_t tier = 0; tier < storage_tiers; tier++)
84 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++)
85 rrdeng_quiesce(multidb_ctx[tier]);
86
87 struct pgc_statistics pgc_main_stats = pgc_get_statistics(main_cache);
@@ -94,7 +94,7 @@ static void rrdeng_flush_everything_and_wait(bool wait_flush, bool wait_collecto
94 size_t count = 10;
95 while (running && count) {
96 running = 0;
97 - for (size_t tier = 0; tier < storage_tiers; tier++)
97 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++)
98 running += rrdeng_collectors_running(multidb_ctx[tier]);
99
100 if (running) {
@@ -236,14 +236,14 @@ void netdata_cleanup_and_exit(int ret, const char *action, const char *action_re
236 rrdeng_flush_everything_and_wait(true, true);
237 watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_COLLECTORS_TO_FINISH);
238
239 - ND_THREAD *th[storage_tiers];
240 - for (size_t tier = 0; tier < storage_tiers; tier++)
239 + ND_THREAD *th[nd_profile.storage_tiers];
240 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++)
241 th[tier] = nd_thread_create("rrdeng-exit", NETDATA_THREAD_OPTION_JOINABLE, rrdeng_exit_background, multidb_ctx[tier]);
242
243 // flush anything remaining again - just in case
244 rrdeng_flush_everything_and_wait(true, false);
245
246 - for (size_t tier = 0; tier < storage_tiers; tier++)
246 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++)
247 nd_thread_join(th[tier]);
248
249 rrdeng_enq_cmd(NULL, RRDENG_OPCODE_SHUTDOWN_EVLOOP, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
src/daemon/environment.c
+1 -1
@@ -36,7 +36,7 @@ static const char *verify_or_create_required_private_directory(const char *dir)
36 void set_environment_for_plugins_and_scripts(void) {
37 {
38 char b[16];
39 - snprintfz(b, sizeof(b) - 1, "%d", default_rrd_update_every);
39 + snprintfz(b, sizeof(b) - 1, "%d", (int)nd_profile.update_every);
40 nd_setenv("NETDATA_UPDATE_EVERY", b, 1);
41 }
42
src/daemon/main.c
+14 -8
@@ -212,10 +212,10 @@ int windows_perflib_dump(const char *key);
212 int unittest_prepare_rrd(const char **user) {
213 netdata_conf_section_global_run_as_user(user);
214 netdata_conf_section_global();
215 - default_rrd_update_every = 1;
215 + nd_profile.update_every = 1;
216 default_rrd_memory_mode = RRD_MEMORY_MODE_RAM;
217 health_plugin_disable();
218 - storage_tiers = 1;
218 + nd_profile.storage_tiers = 1;
219 registry_init();
220 if(rrd_init("unittest", NULL, true)) {
221 fprintf(stderr, "rrd_init failed for unittest\n");
@@ -383,6 +383,7 @@ int netdata_main(int argc, char **argv) {
383 if (ctx_unittest()) return 1;
384 if (uuid_unittest()) return 1;
385 if (dyncfg_unittest()) return 1;
386 + if (unittest_waiting_queue()) return 1;
387 sqlite_library_shutdown();
388 fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
389 return 0;
@@ -398,6 +399,10 @@ int netdata_main(int argc, char **argv) {
399 unittest_running = true;
400 return aral_unittest(10000);
401 }
402 + else if(strcmp(optarg, "waitqtest") == 0) {
403 + unittest_running = true;
404 + return unittest_waiting_queue();
405 + }
406 else if(strcmp(optarg, "stringtest") == 0) {
407 unittest_running = true;
408 return string_unittest(10000);
@@ -463,7 +468,7 @@ int netdata_main(int argc, char **argv) {
468 unsigned history_seconds = strtoul(optarg, NULL, 0);
469 netdata_conf_section_global_run_as_user(&user);
470 netdata_conf_section_global();
466 - default_rrd_update_every = 1;
471 + nd_profile.update_every = 1;
472 registry_init();
473 if(rrd_init("dbengine-dataset", NULL, true)) {
474 fprintf(stderr, "rrd_init failed for unittest\n");
@@ -768,12 +773,14 @@ int netdata_main(int argc, char **argv) {
773 workers_utilization_enable();
774
775 // ----------------------------------------------------------------------------------------------------------------
771 - // streaming, replication, backfilling
776 + // profiles
777
773 - stream_conf_load();
774 - check_local_streaming_capabilities();
775 - replication_initialize();
778 + nd_profile_setup();
779
780 + // ----------------------------------------------------------------------------------------------------------------
781 + // streaming, replication, functions initialization
782 +
783 + replication_initialize();
784 rrd_functions_inflight_init();
785
786 {
@@ -967,7 +974,6 @@ int netdata_main(int argc, char **argv) {
974 delta_startup_time("start the static threads");
975
976 netdata_conf_section_web();
970 - backfill_threads_detect_from_stream_conf();
977
978 set_late_analytics_variables(system_info);
979 for (i = 0; static_threads[i].name != NULL ; i++) {
src/daemon/pulse/pulse-aral.c
+1 -1
@@ -167,7 +167,7 @@ void pulse_aral_do(bool extended) {
167 ai->rd_utilization = rrddim_add(ai->st_utilization, "utilization", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
168 }
169
170 - rrddim_set_by_pointer(ai->st_utilization, ai->rd_utilization, (collected_number)utilization * 1000LL);
170 + rrddim_set_by_pointer(ai->st_utilization, ai->rd_utilization, (collected_number)(utilization * 1000.0));
171 rrdset_done(ai->st_utilization);
172 }
173 }
src/daemon/pulse/pulse-daemon-memory.c
+8 -8
@@ -2,7 +2,7 @@
2
3 #define PULSE_INTERNALS 1
4 #include "pulse-daemon-memory.h"
5 -#include "streaming/replication.h"
5 +#include "streaming/stream-replication-sender.h"
6
7 #define dictionary_stats_memory_total(stats) \
8 ((stats).memory.dict + (stats).memory.values + (stats).memory.index)
@@ -93,13 +93,13 @@ void pulse_daemon_memory_do(bool extended) {
93 netdata_buffers_statistics.buffers_streaming +
94 netdata_buffers_statistics.cbuffers_streaming +
95 netdata_buffers_statistics.buffers_web +
96 - replication_allocated_buffers() + aral_by_size_free_bytes() + judy_aral_free_bytes();
96 + replication_sender_allocated_buffers() + aral_by_size_free_bytes() + judy_aral_free_bytes();
97
98 int sqlite3_memory_used_current = 0, sqlite3_memory_used_highwater = 0;
99 sqlite3_status(SQLITE_STATUS_MEMORY_USED, &sqlite3_memory_used_current, &sqlite3_memory_used_highwater, 1);
100
101 - size_t strings = 0;
102 - string_statistics(NULL, NULL, NULL, NULL, NULL, &strings, NULL, NULL);
101 + size_t strings_memory = 0, strings_index = 0;
102 + string_statistics(NULL, NULL, NULL, NULL, NULL, &strings_memory, &strings_index, NULL, NULL);
103
104 rrddim_set_by_pointer(st_memory, rd_db_dbengine, (collected_number)pulse_dbengine_total_memory);
105 rrddim_set_by_pointer(st_memory, rd_db_rrd, (collected_number)pulse_rrd_memory_size);
@@ -128,7 +128,7 @@ void pulse_daemon_memory_do(bool extended) {
128 (collected_number)dictionary_stats_memory_total(dictionary_stats_category_functions));
129
130 rrddim_set_by_pointer(st_memory, rd_replication,
131 - (collected_number)dictionary_stats_memory_total(dictionary_stats_category_replication) + (collected_number)replication_allocated_memory());
131 + (collected_number)dictionary_stats_memory_total(dictionary_stats_category_replication) + (collected_number)replication_sender_allocated_memory());
132 #else
133 uint64_t metadata =
134 aral_by_size_structures_bytes() + aral_by_size_used_bytes() +
@@ -140,7 +140,7 @@ void pulse_daemon_memory_do(bool extended) {
140 dictionary_stats_category_functions.memory.dict + dictionary_stats_category_functions.memory.index +
141 dictionary_stats_category_replication.memory.dict + dictionary_stats_category_replication.memory.index +
142 netdata_buffers_statistics.rrdhost_allocations_size +
143 - replication_allocated_memory();
143 + replication_sender_allocated_memory();
144
145 rrddim_set_by_pointer(st_memory, rd_metadata, (collected_number)metadata);
146 #endif
@@ -153,7 +153,7 @@ void pulse_daemon_memory_do(bool extended) {
153 (collected_number)pulse_ml_get_current_memory_usage());
154
155 rrddim_set_by_pointer(st_memory, rd_strings,
156 - (collected_number)strings);
156 + (collected_number)(strings_memory + strings_index));
157
158 rrddim_set_by_pointer(st_memory, rd_streaming,
159 (collected_number)netdata_buffers_statistics.rrdhost_senders + (collected_number)netdata_buffers_statistics.rrdhost_receivers);
@@ -243,7 +243,7 @@ void pulse_daemon_memory_do(bool extended) {
243 rrddim_set_by_pointer(st_memory_buffers, rd_buffers_streaming, (collected_number)netdata_buffers_statistics.buffers_streaming);
244 rrddim_set_by_pointer(st_memory_buffers, rd_cbuffers_streaming, (collected_number)netdata_buffers_statistics.cbuffers_streaming);
245 rrddim_set_by_pointer(st_memory_buffers, rd_buffers_web, (collected_number)netdata_buffers_statistics.buffers_web);
246 - rrddim_set_by_pointer(st_memory_buffers, rd_buffers_replication, (collected_number)replication_allocated_buffers());
246 + rrddim_set_by_pointer(st_memory_buffers, rd_buffers_replication, (collected_number)replication_sender_allocated_buffers());
247 rrddim_set_by_pointer(st_memory_buffers, rd_buffers_aral, (collected_number)aral_by_size_free_bytes());
248 rrddim_set_by_pointer(st_memory_buffers, rd_buffers_judy, (collected_number)judy_aral_free_bytes());
249
src/daemon/pulse/pulse-db-dbengine.c
+140 -38
@@ -7,6 +7,13 @@ size_t pulse_dbengine_total_memory = 0;
7
8 #if defined(ENABLE_DBENGINE)
9
10 +static usec_t time_and_count_delta_average(struct time_and_count *prev, struct time_and_count *latest) {
11 + if(latest->count > prev->count && latest->usec > prev->usec)
12 + return (latest->usec - prev->usec) / (latest->count - prev->count);
13 +
14 + return 0;
15 +}
16 +
17 struct dbengine2_cache_pointers {
18 RRDSET *st_cache_hit_ratio;
19 RRDDIM *rd_hit_ratio_closest;
@@ -573,18 +580,18 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
580 priority++;
581 }
582
576 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_relocated, (collected_number)pgc_stats->waste_evict_relocated);
577 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_flushes_cancelled, (collected_number)pgc_stats->waste_flushes_cancelled);
578 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_acquire_spins, (collected_number)pgc_stats->waste_acquire_spins);
579 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_release_spins, (collected_number)pgc_stats->waste_release_spins);
580 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_insert_spins, (collected_number)pgc_stats->waste_insert_spins);
581 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_delete_spins, (collected_number)pgc_stats->waste_delete_spins);
582 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_spins, (collected_number)pgc_stats->waste_evict_useless_spins);
583 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_thread_signals, (collected_number)pgc_stats->waste_evict_thread_signals);
584 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_inline_on_add, (collected_number)pgc_stats->waste_evictions_inline_on_add);
585 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_inline_on_release, (collected_number)pgc_stats->waste_evictions_inline_on_release);
586 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_flush_inline_on_add, (collected_number)pgc_stats->waste_flush_on_add);
587 - rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_flush_inline_on_release, (collected_number)pgc_stats->waste_flush_on_release);
583 + rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_relocated, (collected_number)pgc_stats->p2_waste_evict_relocated);
584 + rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_flushes_cancelled, (collected_number)pgc_stats->p2_waste_flushes_cancelled);
585 + rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_acquire_spins, (collected_number)pgc_stats->p2_waste_acquire_spins);
586 + rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_release_spins, (collected_number)pgc_stats->p2_waste_release_spins);
587 + rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_insert_spins, (collected_number)pgc_stats->p2_waste_insert_spins);
588 + rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_delete_spins, (collected_number)pgc_stats->p2_waste_delete_spins);
589 + rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_spins, (collected_number)pgc_stats->p2_waste_evict_useless_spins);
590 + rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_thread_signals, (collected_number)pgc_stats->p2_waste_evict_thread_signals);
591 + rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_inline_on_add, (collected_number)pgc_stats->p2_waste_evictions_inline_on_add);
592 + rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_evict_inline_on_release, (collected_number)pgc_stats->p2_waste_evictions_inline_on_release);
593 + rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_flush_inline_on_add, (collected_number)pgc_stats->p2_waste_flush_on_add);
594 + rrddim_set_by_pointer(ptrs->st_pgc_waste, ptrs->rd_pgc_waste_flush_inline_on_release, (collected_number)pgc_stats->p2_waste_flush_on_release);
595
596 rrdset_done(ptrs->st_pgc_waste);
597 }
@@ -627,12 +634,12 @@ static void dbengine2_cache_statistics_charts(struct dbengine2_cache_pointers *p
634 priority++;
635 }
636
630 - rrddim_set_by_pointer(ptrs->st_pgc_workers, ptrs->rd_pgc_workers_searchers, (collected_number)pgc_stats->workers_search);
631 - rrddim_set_by_pointer(ptrs->st_pgc_workers, ptrs->rd_pgc_workers_adders, (collected_number)pgc_stats->workers_add);
632 - rrddim_set_by_pointer(ptrs->st_pgc_workers, ptrs->rd_pgc_workers_evictors, (collected_number)pgc_stats->workers_evict);
633 - rrddim_set_by_pointer(ptrs->st_pgc_workers, ptrs->rd_pgc_workers_flushers, (collected_number)pgc_stats->workers_flush);
634 - rrddim_set_by_pointer(ptrs->st_pgc_workers, ptrs->rd_pgc_workers_hot2dirty, (collected_number)pgc_stats->workers_hot2dirty);
635 - rrddim_set_by_pointer(ptrs->st_pgc_workers, ptrs->rd_pgc_workers_jv2_flushers, (collected_number)pgc_stats->workers_jv2_flush);
637 + rrddim_set_by_pointer(ptrs->st_pgc_workers, ptrs->rd_pgc_workers_searchers, (collected_number)pgc_stats->p2_workers_search);
638 + rrddim_set_by_pointer(ptrs->st_pgc_workers, ptrs->rd_pgc_workers_adders, (collected_number)pgc_stats->p2_workers_add);
639 + rrddim_set_by_pointer(ptrs->st_pgc_workers, ptrs->rd_pgc_workers_evictors, (collected_number)pgc_stats->p0_workers_evict);
640 + rrddim_set_by_pointer(ptrs->st_pgc_workers, ptrs->rd_pgc_workers_flushers, (collected_number)pgc_stats->p2_workers_flush);
641 + rrddim_set_by_pointer(ptrs->st_pgc_workers, ptrs->rd_pgc_workers_hot2dirty, (collected_number)pgc_stats->p2_workers_hot2dirty);
642 + rrddim_set_by_pointer(ptrs->st_pgc_workers, ptrs->rd_pgc_workers_jv2_flushers, (collected_number)pgc_stats->p2_workers_jv2_flush);
643
644 rrdset_done(ptrs->st_pgc_workers);
645 }
@@ -1012,9 +1019,9 @@ void pulse_dbengine_do(bool extended) {
1019 }
1020 priority++;
1021
1015 - rrddim_set_by_pointer(st_queries, rd_total, (collected_number)cache_efficiency_stats.queries);
1016 - rrddim_set_by_pointer(st_queries, rd_open, (collected_number)cache_efficiency_stats.queries_open);
1017 - rrddim_set_by_pointer(st_queries, rd_jv2, (collected_number)cache_efficiency_stats.queries_journal_v2);
1022 + rrddim_set_by_pointer(st_queries, rd_total, (collected_number)cache_efficiency_stats.prep_time_in_main_cache_lookup.count);
1023 + rrddim_set_by_pointer(st_queries, rd_open, (collected_number)cache_efficiency_stats.prep_time_in_open_cache_lookup.count);
1024 + rrddim_set_by_pointer(st_queries, rd_jv2, (collected_number)cache_efficiency_stats.prep_time_in_journal_v2_lookup.count);
1025 rrddim_set_by_pointer(st_queries, rd_planned_with_gaps, (collected_number)cache_efficiency_stats.queries_planned_with_gaps);
1026 rrddim_set_by_pointer(st_queries, rd_executed_with_gaps, (collected_number)cache_efficiency_stats.queries_executed_with_gaps);
1027
@@ -1303,7 +1310,9 @@ void pulse_dbengine_do(bool extended) {
1310
1311 {
1312 static RRDSET *st_prep_timings = NULL;
1306 - static RRDDIM *rd_routing = NULL;
1313 + static RRDDIM *rd_routing_sync = NULL;
1314 + static RRDDIM *rd_routing_syncfirst = NULL;
1315 + static RRDDIM *rd_routing_async = NULL;
1316 static RRDDIM *rd_main_cache = NULL;
1317 static RRDDIM *rd_open_cache = NULL;
1318 static RRDDIM *rd_journal_v2 = NULL;
@@ -1316,7 +1325,7 @@ void pulse_dbengine_do(bool extended) {
1325 NULL,
1326 "dbengine query router",
1327 NULL,
1319 - "Netdata Query Preparation Timings",
1328 + "Netdata Query Planning Timings",
1329 "usec/s",
1330 "netdata",
1331 "pulse",
@@ -1324,7 +1333,9 @@ void pulse_dbengine_do(bool extended) {
1333 localhost->rrd_update_every,
1334 RRDSET_TYPE_STACKED);
1335
1327 - rd_routing = rrddim_add(st_prep_timings, "routing", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1336 + rd_routing_sync = rrddim_add(st_prep_timings, "pdc sync", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1337 + rd_routing_syncfirst = rrddim_add(st_prep_timings, "pdc syncfirst", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1338 + rd_routing_async = rrddim_add(st_prep_timings, "pdc async", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1339 rd_main_cache = rrddim_add(st_prep_timings, "main cache", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1340 rd_open_cache = rrddim_add(st_prep_timings, "open cache", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1341 rd_journal_v2 = rrddim_add(st_prep_timings, "journal v2", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
@@ -1332,11 +1343,59 @@ void pulse_dbengine_do(bool extended) {
1343 }
1344 priority++;
1345
1335 - rrddim_set_by_pointer(st_prep_timings, rd_routing, (collected_number)cache_efficiency_stats.prep_time_to_route);
1336 - rrddim_set_by_pointer(st_prep_timings, rd_main_cache, (collected_number)cache_efficiency_stats.prep_time_in_main_cache_lookup);
1337 - rrddim_set_by_pointer(st_prep_timings, rd_open_cache, (collected_number)cache_efficiency_stats.prep_time_in_open_cache_lookup);
1338 - rrddim_set_by_pointer(st_prep_timings, rd_journal_v2, (collected_number)cache_efficiency_stats.prep_time_in_journal_v2_lookup);
1339 - rrddim_set_by_pointer(st_prep_timings, rd_pass4, (collected_number)cache_efficiency_stats.prep_time_in_pass4_lookup);
1346 + rrddim_set_by_pointer(st_prep_timings, rd_routing_sync, (collected_number)cache_efficiency_stats.prep_time_to_route_sync.usec);
1347 + rrddim_set_by_pointer(st_prep_timings, rd_routing_syncfirst, (collected_number)cache_efficiency_stats.prep_time_to_route_syncfirst.usec);
1348 + rrddim_set_by_pointer(st_prep_timings, rd_routing_async, (collected_number)cache_efficiency_stats.prep_time_to_route_async.usec);
1349 + rrddim_set_by_pointer(st_prep_timings, rd_main_cache, (collected_number)cache_efficiency_stats.prep_time_in_main_cache_lookup.usec);
1350 + rrddim_set_by_pointer(st_prep_timings, rd_open_cache, (collected_number)cache_efficiency_stats.prep_time_in_open_cache_lookup.usec);
1351 + rrddim_set_by_pointer(st_prep_timings, rd_journal_v2, (collected_number)cache_efficiency_stats.prep_time_in_journal_v2_lookup.usec);
1352 + rrddim_set_by_pointer(st_prep_timings, rd_pass4, (collected_number)cache_efficiency_stats.prep_time_in_pass4_lookup.usec);
1353 +
1354 + rrdset_done(st_prep_timings);
1355 + }
1356 +
1357 + {
1358 + static RRDSET *st_prep_timings = NULL;
1359 + static RRDDIM *rd_routing_sync = NULL;
1360 + static RRDDIM *rd_routing_syncfirst = NULL;
1361 + static RRDDIM *rd_routing_async = NULL;
1362 + static RRDDIM *rd_main_cache = NULL;
1363 + static RRDDIM *rd_open_cache = NULL;
1364 + static RRDDIM *rd_journal_v2 = NULL;
1365 + static RRDDIM *rd_pass4 = NULL;
1366 +
1367 + if (unlikely(!st_prep_timings)) {
1368 + st_prep_timings = rrdset_create_localhost(
1369 + "netdata",
1370 + "dbengine_prep_average_timings",
1371 + NULL,
1372 + "dbengine query router",
1373 + NULL,
1374 + "Netdata Query Planning Average Timings",
1375 + "usec/s",
1376 + "netdata",
1377 + "pulse",
1378 + priority + 1,
1379 + localhost->rrd_update_every,
1380 + RRDSET_TYPE_STACKED);
1381 +
1382 + rd_routing_sync = rrddim_add(st_prep_timings, "pdc sync", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1383 + rd_routing_syncfirst = rrddim_add(st_prep_timings, "pdc syncfirst", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1384 + rd_routing_async = rrddim_add(st_prep_timings, "pdc async", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1385 + rd_main_cache = rrddim_add(st_prep_timings, "main cache", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1386 + rd_open_cache = rrddim_add(st_prep_timings, "open cache", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1387 + rd_journal_v2 = rrddim_add(st_prep_timings, "journal v2", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1388 + rd_pass4 = rrddim_add(st_prep_timings, "pass4", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1389 + }
1390 + priority++;
1391 +
1392 + rrddim_set_by_pointer(st_prep_timings,rd_routing_sync, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.prep_time_to_route_sync, &cache_efficiency_stats.prep_time_to_route_sync));
1393 + rrddim_set_by_pointer(st_prep_timings,rd_routing_syncfirst, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.prep_time_to_route_syncfirst, &cache_efficiency_stats.prep_time_to_route_syncfirst));
1394 + rrddim_set_by_pointer(st_prep_timings,rd_routing_async, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.prep_time_to_route_async, &cache_efficiency_stats.prep_time_to_route_async));
1395 + rrddim_set_by_pointer(st_prep_timings, rd_main_cache, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.prep_time_in_main_cache_lookup, &cache_efficiency_stats.prep_time_in_main_cache_lookup));
1396 + rrddim_set_by_pointer(st_prep_timings, rd_open_cache, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.prep_time_in_open_cache_lookup, &cache_efficiency_stats.prep_time_in_open_cache_lookup));
1397 + rrddim_set_by_pointer(st_prep_timings, rd_journal_v2, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.prep_time_in_journal_v2_lookup, &cache_efficiency_stats.prep_time_in_journal_v2_lookup));
1398 + rrddim_set_by_pointer(st_prep_timings, rd_pass4, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.prep_time_in_pass4_lookup, &cache_efficiency_stats.prep_time_in_pass4_lookup));
1399
1400 rrdset_done(st_prep_timings);
1401 }
@@ -1365,8 +1424,8 @@ void pulse_dbengine_do(bool extended) {
1424 localhost->rrd_update_every,
1425 RRDSET_TYPE_STACKED);
1426
1368 - rd_init = rrddim_add(st_query_timings, "init", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1369 - rd_prep_wait = rrddim_add(st_query_timings, "prep wait", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1427 + rd_init = rrddim_add(st_query_timings, "plan", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1428 + rd_prep_wait = rrddim_add(st_query_timings, "async wait", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1429 rd_next_page_disk_fast = rrddim_add(st_query_timings, "next page disk fast", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1430 rd_next_page_disk_slow = rrddim_add(st_query_timings, "next page disk slow", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1431 rd_next_page_preload_fast = rrddim_add(st_query_timings, "next page preload fast", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
@@ -1374,16 +1433,59 @@ void pulse_dbengine_do(bool extended) {
1433 }
1434 priority++;
1435
1377 - rrddim_set_by_pointer(st_query_timings, rd_init, (collected_number)cache_efficiency_stats.query_time_init);
1378 - rrddim_set_by_pointer(st_query_timings, rd_prep_wait, (collected_number)cache_efficiency_stats.query_time_wait_for_prep);
1379 - rrddim_set_by_pointer(st_query_timings, rd_next_page_disk_fast, (collected_number)cache_efficiency_stats.query_time_to_fast_disk_next_page);
1380 - rrddim_set_by_pointer(st_query_timings, rd_next_page_disk_slow, (collected_number)cache_efficiency_stats.query_time_to_slow_disk_next_page);
1381 - rrddim_set_by_pointer(st_query_timings, rd_next_page_preload_fast, (collected_number)cache_efficiency_stats.query_time_to_fast_preload_next_page);
1382 - rrddim_set_by_pointer(st_query_timings, rd_next_page_preload_slow, (collected_number)cache_efficiency_stats.query_time_to_slow_preload_next_page);
1436 + rrddim_set_by_pointer(st_query_timings, rd_init, (collected_number)cache_efficiency_stats.query_time_init.usec);
1437 + rrddim_set_by_pointer(st_query_timings, rd_prep_wait, (collected_number)cache_efficiency_stats.query_time_wait_for_prep.usec);
1438 + rrddim_set_by_pointer(st_query_timings, rd_next_page_disk_fast, (collected_number)cache_efficiency_stats.query_time_to_fast_disk_next_page.usec);
1439 + rrddim_set_by_pointer(st_query_timings, rd_next_page_disk_slow, (collected_number)cache_efficiency_stats.query_time_to_slow_disk_next_page.usec);
1440 + rrddim_set_by_pointer(st_query_timings, rd_next_page_preload_fast, (collected_number)cache_efficiency_stats.query_time_to_fast_preload_next_page.usec);
1441 + rrddim_set_by_pointer(st_query_timings, rd_next_page_preload_slow, (collected_number)cache_efficiency_stats.query_time_to_slow_preload_next_page.usec);
1442
1443 rrdset_done(st_query_timings);
1444 }
1445
1446 + {
1447 + static RRDSET *st_query_timings_average = NULL;
1448 + static RRDDIM *rd_init = NULL;
1449 + static RRDDIM *rd_prep_wait = NULL;
1450 + static RRDDIM *rd_next_page_disk_fast = NULL;
1451 + static RRDDIM *rd_next_page_disk_slow = NULL;
1452 + static RRDDIM *rd_next_page_preload_fast = NULL;
1453 + static RRDDIM *rd_next_page_preload_slow = NULL;
1454 +
1455 + if (unlikely(!st_query_timings_average)) {
1456 + st_query_timings_average = rrdset_create_localhost(
1457 + "netdata",
1458 + "dbengine_query_timings_average",
1459 + NULL,
1460 + "dbengine query router",
1461 + NULL,
1462 + "Netdata Query Average Timings",
1463 + "usec/s",
1464 + "netdata",
1465 + "pulse",
1466 + priority + 1,
1467 + localhost->rrd_update_every,
1468 + RRDSET_TYPE_STACKED);
1469 +
1470 + rd_init = rrddim_add(st_query_timings_average, "plan", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1471 + rd_prep_wait = rrddim_add(st_query_timings_average, "async wait", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1472 + rd_next_page_disk_fast = rrddim_add(st_query_timings_average, "next page disk fast", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1473 + rd_next_page_disk_slow = rrddim_add(st_query_timings_average, "next page disk slow", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1474 + rd_next_page_preload_fast = rrddim_add(st_query_timings_average, "next page preload fast", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1475 + rd_next_page_preload_slow = rrddim_add(st_query_timings_average, "next page preload slow", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1476 + }
1477 + priority++;
1478 +
1479 + rrddim_set_by_pointer(st_query_timings_average, rd_init, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.query_time_init, &cache_efficiency_stats.query_time_init));
1480 + rrddim_set_by_pointer(st_query_timings_average, rd_prep_wait, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.query_time_wait_for_prep, &cache_efficiency_stats.query_time_wait_for_prep));
1481 + rrddim_set_by_pointer(st_query_timings_average, rd_next_page_disk_fast, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.query_time_to_fast_disk_next_page, &cache_efficiency_stats.query_time_to_fast_disk_next_page));
1482 + rrddim_set_by_pointer(st_query_timings_average, rd_next_page_disk_slow, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.query_time_to_slow_disk_next_page, &cache_efficiency_stats.query_time_to_slow_disk_next_page));
1483 + rrddim_set_by_pointer(st_query_timings_average, rd_next_page_preload_fast, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.query_time_to_fast_preload_next_page, &cache_efficiency_stats.query_time_to_fast_preload_next_page));
1484 + rrddim_set_by_pointer(st_query_timings_average, rd_next_page_preload_slow, (collected_number)time_and_count_delta_average(&cache_efficiency_stats_old.query_time_to_slow_preload_next_page, &cache_efficiency_stats.query_time_to_slow_preload_next_page));
1485 +
1486 + rrdset_done(st_query_timings_average);
1487 + }
1488 +
1489 if(netdata_rwlock_tryrdlock(&rrd_rwlock) == 0) {
1490 priority = 135400;
1491
@@ -1396,7 +1498,7 @@ void pulse_dbengine_do(bool extended) {
1498 if (!rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED)) {
1499
1500 /* get localhost's DB engine's statistics for each tier */
1399 - for(size_t tier = 0; tier < storage_tiers ;tier++) {
1501 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
1502 if(host->db[tier].mode != RRD_MEMORY_MODE_DBENGINE) continue;
1503 if(!host->db[tier].si) continue;
1504
src/daemon/pulse/pulse-gorilla.c
+5 -9
@@ -6,15 +6,11 @@
6 static struct gorilla_statistics {
7 bool enabled;
8
9 - CACHE_LINE_PADDING();
10 - uint64_t tier0_hot_gorilla_buffers;
11 -
12 - CACHE_LINE_PADDING();
13 - uint64_t gorilla_tier0_disk_actual_bytes;
14 - CACHE_LINE_PADDING();
15 - uint64_t gorilla_tier0_disk_optimal_bytes;
16 - CACHE_LINE_PADDING();
17 - uint64_t gorilla_tier0_disk_original_bytes;
9 + PAD64(uint64_t) tier0_hot_gorilla_buffers;
10 +
11 + PAD64(uint64_t) gorilla_tier0_disk_actual_bytes;
12 + PAD64(uint64_t) gorilla_tier0_disk_optimal_bytes;
13 + PAD64(uint64_t) gorilla_tier0_disk_original_bytes;
14 } gorilla_statistics = { 0 };
15
16 void pulse_gorilla_hot_buffer_added() {
src/daemon/pulse/pulse-http-api.c
+11 -20
@@ -8,26 +8,17 @@
8 static struct web_statistics {
9 bool extended;
10
11 - CACHE_LINE_PADDING();
12 - uint16_t connected_clients;
13 - CACHE_LINE_PADDING();
14 - uint64_t web_client_count; // oops! this is used for giving unique IDs to web_clients!
15 -
16 - CACHE_LINE_PADDING();
17 - uint64_t web_requests;
18 - CACHE_LINE_PADDING();
19 - uint64_t web_usec;
20 - CACHE_LINE_PADDING();
21 - uint64_t web_usec_max;
22 - CACHE_LINE_PADDING();
23 - uint64_t bytes_received;
24 - CACHE_LINE_PADDING();
25 - uint64_t bytes_sent;
26 -
27 - CACHE_LINE_PADDING();
28 - uint64_t content_size_uncompressed;
29 - CACHE_LINE_PADDING();
30 - uint64_t content_size_compressed;
11 + PAD64(uint16_t) connected_clients;
12 + PAD64(uint64_t) web_client_count; // oops! this is used for giving unique IDs to web_clients!
13 +
14 + PAD64(uint64_t) web_requests;
15 + PAD64(uint64_t) web_usec;
16 + PAD64(uint64_t) web_usec_max;
17 + PAD64(uint64_t) bytes_received;
18 + PAD64(uint64_t) bytes_sent;
19 +
20 + PAD64(uint64_t) content_size_uncompressed;
21 + PAD64(uint64_t) content_size_compressed;
22 } web_statistics;
23
24 uint64_t pulse_web_client_connected(void) {
src/daemon/pulse/pulse-ingestion.c
+4 -4
@@ -8,14 +8,14 @@ static struct ingest_statistics {
8 } ingest_statistics;
9
10 void pulse_queries_rrdset_collection_completed(size_t *points_read_per_tier_array) {
11 - for(size_t tier = 0; tier < storage_tiers ;tier++) {
11 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
12 __atomic_fetch_add(&ingest_statistics.db_points_stored_per_tier[tier], points_read_per_tier_array[tier], __ATOMIC_RELAXED);
13 points_read_per_tier_array[tier] = 0;
14 }
15 }
16
17 static inline void pulse_ingestion_copy(struct ingest_statistics *gs) {
18 - for(size_t tier = 0; tier < storage_tiers ;tier++)
18 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++)
19 gs->db_points_stored_per_tier[tier] = __atomic_load_n(&ingest_statistics.db_points_stored_per_tier[tier], __ATOMIC_RELAXED);
20 }
21
@@ -43,14 +43,14 @@ void pulse_ingestion_do(bool extended __maybe_unused) {
43 , RRDSET_TYPE_STACKED
44 );
45
46 - for(size_t tier = 0; tier < storage_tiers ;tier++) {
46 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
47 char buf[30 + 1];
48 snprintfz(buf, sizeof(buf) - 1, "tier%zu", tier);
49 rds[tier] = rrddim_add(st_points_stored, buf, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
50 }
51 }
52
53 - for(size_t tier = 0; tier < storage_tiers ;tier++)
53 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++)
54 rrddim_set_by_pointer(st_points_stored, rds[tier], (collected_number)gs.db_points_stored_per_tier[tier]);
55
56 rrdset_done(st_points_stored);
src/daemon/pulse/pulse-ml.c
+8 -16
@@ -4,22 +4,14 @@
4 #include "pulse-ml.h"
5
6 static struct ml_statistics {
7 - CACHE_LINE_PADDING();
8 - uint64_t ml_models_consulted;
9 - CACHE_LINE_PADDING();
10 - uint64_t ml_models_received;
11 - CACHE_LINE_PADDING();
12 - uint64_t ml_models_ignored;
13 - CACHE_LINE_PADDING();
14 - uint64_t ml_models_sent;
15 - CACHE_LINE_PADDING();
16 - uint64_t ml_models_deserialization_failures;
17 - CACHE_LINE_PADDING();
18 - uint64_t ml_memory_consumption;
19 - CACHE_LINE_PADDING();
20 - uint64_t ml_memory_new;
21 - CACHE_LINE_PADDING();
22 - uint64_t ml_memory_delete;
7 + PAD64(uint64_t) ml_models_consulted;
8 + PAD64(uint64_t) ml_models_received;
9 + PAD64(uint64_t) ml_models_ignored;
10 + PAD64(uint64_t) ml_models_sent;
11 + PAD64(uint64_t) ml_models_deserialization_failures;
12 + PAD64(uint64_t) ml_memory_consumption;
13 + PAD64(uint64_t) ml_memory_new;
14 + PAD64(uint64_t) ml_memory_delete;
15 } ml_statistics = {0};
16
17 void pulse_ml_models_received()
src/daemon/pulse/pulse-queries.c
+26 -45
@@ -2,53 +2,34 @@
2
3 #define PULSE_INTERNALS 1
4 #include "pulse-queries.h"
5 -#include "streaming/replication.h"
5 +#include "streaming/stream-replication-sender.h"
6
7 static struct query_statistics {
8 - CACHE_LINE_PADDING();
9 - uint64_t api_data_queries_made;
10 - CACHE_LINE_PADDING();
11 - uint64_t api_data_db_points_read;
12 - CACHE_LINE_PADDING();
13 - uint64_t api_data_result_points_generated;
14 -
15 - CACHE_LINE_PADDING();
16 - uint64_t api_weights_queries_made;
17 - CACHE_LINE_PADDING();
18 - uint64_t api_weights_db_points_read;
19 - CACHE_LINE_PADDING();
20 - uint64_t api_weights_result_points_generated;
21 -
22 - CACHE_LINE_PADDING();
23 - uint64_t api_badges_queries_made;
24 - CACHE_LINE_PADDING();
25 - uint64_t api_badges_db_points_read;
26 - CACHE_LINE_PADDING();
27 - uint64_t api_badges_result_points_generated;
28 -
29 - CACHE_LINE_PADDING();
30 - uint64_t health_queries_made;
31 - CACHE_LINE_PADDING();
32 - uint64_t health_db_points_read;
33 - CACHE_LINE_PADDING();
34 - uint64_t health_result_points_generated;
35 -
36 - CACHE_LINE_PADDING();
37 - uint64_t ml_queries_made;
38 - CACHE_LINE_PADDING();
39 - uint64_t ml_db_points_read;
40 - CACHE_LINE_PADDING();
41 - uint64_t ml_result_points_generated;
42 -
43 - CACHE_LINE_PADDING();
44 - uint64_t backfill_queries_made;
45 - CACHE_LINE_PADDING();
46 - uint64_t backfill_db_points_read;
47 -
48 - CACHE_LINE_PADDING();
49 - uint64_t exporters_queries_made;
50 - CACHE_LINE_PADDING();
51 - uint64_t exporters_db_points_read;
8 + PAD64(uint64_t) api_data_queries_made;
9 + PAD64(uint64_t) api_data_db_points_read;
10 + PAD64(uint64_t) api_data_result_points_generated;
11 +
12 + PAD64(uint64_t) api_weights_queries_made;
13 + PAD64(uint64_t) api_weights_db_points_read;
14 + PAD64(uint64_t) api_weights_result_points_generated;
15 +
16 + PAD64(uint64_t) api_badges_queries_made;
17 + PAD64(uint64_t) api_badges_db_points_read;
18 + PAD64(uint64_t) api_badges_result_points_generated;
19 +
20 + PAD64(uint64_t) health_queries_made;
21 + PAD64(uint64_t) health_db_points_read;
22 + PAD64(uint64_t) health_result_points_generated;
23 +
24 + PAD64(uint64_t) ml_queries_made;
25 + PAD64(uint64_t) ml_db_points_read;
26 + PAD64(uint64_t) ml_result_points_generated;
27 +
28 + PAD64(uint64_t) backfill_queries_made;
29 + PAD64(uint64_t) backfill_db_points_read;
30 +
31 + PAD64(uint64_t) exporters_queries_made;
32 + PAD64(uint64_t) exporters_db_points_read;
33 } query_statistics;
34
35 void pulse_queries_ml_query_completed(size_t points_read) {
src/daemon/pulse/pulse-sqlite3.c
+15 -29
@@ -6,34 +6,20 @@
6 static struct sqlite3_statistics {
7 bool enabled;
8
9 - CACHE_LINE_PADDING();
10 - uint64_t sqlite3_queries_made;
11 - CACHE_LINE_PADDING();
12 - uint64_t sqlite3_queries_ok;
13 - CACHE_LINE_PADDING();
14 - uint64_t sqlite3_queries_failed;
15 - CACHE_LINE_PADDING();
16 - uint64_t sqlite3_queries_failed_busy;
17 - CACHE_LINE_PADDING();
18 - uint64_t sqlite3_queries_failed_locked;
19 - CACHE_LINE_PADDING();
20 - uint64_t sqlite3_rows;
21 - CACHE_LINE_PADDING();
22 - uint64_t sqlite3_metadata_cache_hit;
23 - CACHE_LINE_PADDING();
24 - uint64_t sqlite3_context_cache_hit;
25 - CACHE_LINE_PADDING();
26 - uint64_t sqlite3_metadata_cache_miss;
27 - CACHE_LINE_PADDING();
28 - uint64_t sqlite3_context_cache_miss;
29 - CACHE_LINE_PADDING();
30 - uint64_t sqlite3_metadata_cache_spill;
31 - CACHE_LINE_PADDING();
32 - uint64_t sqlite3_context_cache_spill;
33 - CACHE_LINE_PADDING();
34 - uint64_t sqlite3_metadata_cache_write;
35 - CACHE_LINE_PADDING();
36 - uint64_t sqlite3_context_cache_write;
9 + PAD64(uint64_t) sqlite3_queries_made;
10 + PAD64(uint64_t) sqlite3_queries_ok;
11 + PAD64(uint64_t) sqlite3_queries_failed;
12 + PAD64(uint64_t) sqlite3_queries_failed_busy;
13 + PAD64(uint64_t) sqlite3_queries_failed_locked;
14 + PAD64(uint64_t) sqlite3_rows;
15 + PAD64(uint64_t) sqlite3_metadata_cache_hit;
16 + PAD64(uint64_t) sqlite3_context_cache_hit;
17 + PAD64(uint64_t) sqlite3_metadata_cache_miss;
18 + PAD64(uint64_t) sqlite3_context_cache_miss;
19 + PAD64(uint64_t) sqlite3_metadata_cache_spill;
20 + PAD64(uint64_t) sqlite3_context_cache_spill;
21 + PAD64(uint64_t) sqlite3_metadata_cache_write;
22 + PAD64(uint64_t) sqlite3_context_cache_write;
23 } sqlite3_statistics = { };
24
25 void pulse_sqlite3_query_completed(bool success, bool busy, bool locked) {
@@ -71,7 +57,7 @@ static inline void sqlite3_statistics_copy(struct sqlite3_statistics *gs) {
57 gs->sqlite3_queries_failed_locked = __atomic_load_n(&sqlite3_statistics.sqlite3_queries_failed_locked, __ATOMIC_RELAXED);
58 gs->sqlite3_rows = __atomic_load_n(&sqlite3_statistics.sqlite3_rows, __ATOMIC_RELAXED);
59
74 - usec_t timeout = default_rrd_update_every * USEC_PER_SEC + default_rrd_update_every * USEC_PER_SEC / 3;
60 + usec_t timeout = nd_profile.update_every * USEC_PER_SEC + nd_profile.update_every * USEC_PER_SEC / 3;
61 usec_t now = now_monotonic_usec();
62 if(!last_run)
63 last_run = now;
src/daemon/pulse/pulse-string.c
+7 -4
@@ -10,13 +10,14 @@ void pulse_string_do(bool extended) {
10 static RRDDIM *rd_ops_inserts = NULL, *rd_ops_deletes = NULL;
11 static RRDDIM *rd_entries_entries = NULL;
12 static RRDDIM *rd_mem = NULL;
13 + static RRDDIM *rd_mem_idx = NULL;
14 #ifdef NETDATA_INTERNAL_CHECKS
15 static RRDDIM *rd_entries_refs = NULL, *rd_ops_releases = NULL, *rd_ops_duplications = NULL, *rd_ops_searches = NULL;
16 #endif
17
17 - size_t inserts, deletes, searches, entries, references, memory, duplications, releases;
18 + size_t inserts, deletes, searches, entries, references, memory, memory_index, duplications, releases;
19
19 - string_statistics(&inserts, &deletes, &searches, &entries, &references, &memory, &duplications, &releases);
20 + string_statistics(&inserts, &deletes, &searches, &entries, &references, &memory, &memory_index, &duplications, &releases);
21
22 if (unlikely(!st_ops)) {
23 st_ops = rrdset_create_localhost(
@@ -91,11 +92,13 @@ void pulse_string_do(bool extended) {
92 , "pulse"
93 , 910001
94 , localhost->rrd_update_every
94 - , RRDSET_TYPE_AREA);
95 + , RRDSET_TYPE_STACKED);
96
96 - rd_mem = rrddim_add(st_mem, "memory", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
97 + rd_mem = rrddim_add(st_mem, "memory", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
98 + rd_mem_idx = rrddim_add(st_mem, "index", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
99 }
100
101 rrddim_set_by_pointer(st_mem, rd_mem, (collected_number)memory);
102 + rrddim_set_by_pointer(st_mem, rd_mem_idx, (collected_number)memory_index);
103 rrdset_done(st_mem);
104 }
src/daemon/pulse/pulse-workers.c
+74 -19
@@ -20,11 +20,14 @@ struct worker_job_type_gs {
20 STRING *name;
21 STRING *units;
22
23 - size_t jobs_started;
24 - usec_t busy_time;
23 + struct {
24 + size_t jobs_started;
25 + usec_t busy_time;
26 + } data[2];
27
28 RRDDIM *rd_jobs_started;
29 RRDDIM *rd_busy_time;
30 + RRDDIM *rd_avg_time;
31
32 WORKER_METRIC_TYPE type;
33 NETDATA_DOUBLE min_value;
@@ -102,7 +105,8 @@ struct worker_utilization {
105 RRDDIM *rd_workers_threads_busy;
106
107 RRDSET *st_workers_jobs_per_job_type;
105 - RRDSET *st_workers_busy_per_job_type;
108 + RRDSET *st_workers_time_per_job_type;
109 + RRDSET *st_workers_avg_time_per_job_type;
110
111 RRDDIM *rd_total_cpu_utilizaton;
112
@@ -403,7 +407,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
407 }
408 #endif
409
406 - // ----------------------------------------------------------------------
410 + // ----------------------------------------------------------------------------------------------------------------
411
412 if(unlikely(!wu->st_workers_jobs_per_job_type)) {
413 char name[RRD_ID_LENGTH_MAX + 1];
@@ -439,23 +443,23 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
443 if(unlikely(!wu->per_job_type[i].rd_jobs_started))
444 wu->per_job_type[i].rd_jobs_started = rrddim_add(wu->st_workers_jobs_per_job_type, string2str(wu->per_job_type[i].name), NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
445
442 - rrddim_set_by_pointer(wu->st_workers_jobs_per_job_type, wu->per_job_type[i].rd_jobs_started, (collected_number)(wu->per_job_type[i].jobs_started));
446 + rrddim_set_by_pointer(wu->st_workers_jobs_per_job_type, wu->per_job_type[i].rd_jobs_started, (collected_number)(wu->per_job_type[i].data[0].jobs_started));
447 }
448 }
449 }
450
451 rrdset_done(wu->st_workers_jobs_per_job_type);
452
449 - // ----------------------------------------------------------------------
453 + // ----------------------------------------------------------------------------------------------------------------
454
451 - if(unlikely(!wu->st_workers_busy_per_job_type)) {
455 + if(unlikely(!wu->st_workers_time_per_job_type)) {
456 char name[RRD_ID_LENGTH_MAX + 1];
457 snprintfz(name, RRD_ID_LENGTH_MAX, "workers_busy_time_by_type_%s", wu->name_lowercase);
458
459 char context[RRD_ID_LENGTH_MAX + 1];
460 snprintf(context, RRD_ID_LENGTH_MAX, "netdata.workers.%s.time_by_type", wu->name_lowercase);
461
458 - wu->st_workers_busy_per_job_type = rrdset_create_localhost(
462 + wu->st_workers_time_per_job_type = rrdset_create_localhost(
463 "netdata"
464 , name
465 , NULL
@@ -480,16 +484,62 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
484 if (wu->per_job_type[i].name) {
485
486 if(unlikely(!wu->per_job_type[i].rd_busy_time))
483 - wu->per_job_type[i].rd_busy_time = rrddim_add(wu->st_workers_busy_per_job_type, string2str(wu->per_job_type[i].name), NULL, 1, USEC_PER_MS, RRD_ALGORITHM_ABSOLUTE);
487 + wu->per_job_type[i].rd_busy_time = rrddim_add(wu->st_workers_time_per_job_type, string2str(wu->per_job_type[i].name), NULL, 1, USEC_PER_MS, RRD_ALGORITHM_ABSOLUTE);
488
485 - rrddim_set_by_pointer(wu->st_workers_busy_per_job_type, wu->per_job_type[i].rd_busy_time, (collected_number)(wu->per_job_type[i].busy_time));
489 + rrddim_set_by_pointer(wu->st_workers_time_per_job_type, wu->per_job_type[i].rd_busy_time, (collected_number)(wu->per_job_type[i].data[0].busy_time));
490 }
491 }
492 }
493
490 - rrdset_done(wu->st_workers_busy_per_job_type);
494 + rrdset_done(wu->st_workers_time_per_job_type);
495
492 - // ----------------------------------------------------------------------
496 + // ----------------------------------------------------------------------------------------------------------------
497 +
498 + if(unlikely(!wu->st_workers_avg_time_per_job_type)) {
499 + char name[RRD_ID_LENGTH_MAX + 1];
500 + snprintfz(name, RRD_ID_LENGTH_MAX, "workers_avg_time_by_type_%s", wu->name_lowercase);
501 +
502 + char context[RRD_ID_LENGTH_MAX + 1];
503 + snprintf(context, RRD_ID_LENGTH_MAX, "netdata.workers.%s.avg_time_by_type", wu->name_lowercase);
504 +
505 + wu->st_workers_avg_time_per_job_type = rrdset_create_localhost(
506 + "netdata"
507 + , name
508 + , NULL
509 + , wu->family
510 + , context
511 + , "Netdata Workers Average Time by Type"
512 + , "ms"
513 + , "netdata"
514 + , "pulse"
515 + , wu->priority + 4
516 + , localhost->rrd_update_every
517 + , RRDSET_TYPE_STACKED
518 + );
519 + }
520 +
521 + {
522 + size_t i;
523 + for(i = 0; i <= wu->workers_max_job_id ;i++) {
524 + if(unlikely(wu->per_job_type[i].type != WORKER_METRIC_IDLE_BUSY))
525 + continue;
526 +
527 + if (wu->per_job_type[i].name) {
528 +
529 + if(unlikely(!wu->per_job_type[i].rd_avg_time))
530 + wu->per_job_type[i].rd_avg_time = rrddim_add(wu->st_workers_avg_time_per_job_type, string2str(wu->per_job_type[i].name), NULL, 1, USEC_PER_MS, RRD_ALGORITHM_ABSOLUTE);
531 +
532 + ssize_t jobs_delta = (ssize_t)wu->per_job_type[i].data[0].jobs_started;
533 + susec_t time_delta = (susec_t)wu->per_job_type[i].data[0].busy_time;
534 + susec_t average = (jobs_delta != 0) ? time_delta / jobs_delta : 0;
535 + rrddim_set_by_pointer(wu->st_workers_avg_time_per_job_type, wu->per_job_type[i].rd_avg_time, (collected_number)average);
536 + }
537 + }
538 + }
539 +
540 + rrdset_done(wu->st_workers_avg_time_per_job_type);
541 +
542 + // ----------------------------------------------------------------------------------------------------------------
543
544 if(wu->st_workers_threads || wu->workers_registered > 1) {
545 if(unlikely(!wu->st_workers_threads)) {
@@ -509,7 +559,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
559 , "threads"
560 , "netdata"
561 , "pulse"
512 - , wu->priority + 4
562 + , wu->priority + 5
563 , localhost->rrd_update_every
564 , RRDSET_TYPE_STACKED
565 );
@@ -544,7 +594,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
594 , "locks"
595 , "netdata"
596 , "pulse"
547 - , wu->priority + 5
597 + , wu->priority + 6
598 , localhost->rrd_update_every
599 , RRDSET_TYPE_LINE
600 );
@@ -582,7 +632,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
632 , "spins"
633 , "netdata"
634 , "pulse"
585 - , wu->priority + 6
635 + , wu->priority + 7
636 , localhost->rrd_update_every
637 , RRDSET_TYPE_LINE
638 );
@@ -745,8 +795,13 @@ static void workers_utilization_reset_statistics(struct worker_utilization *wu)
795 for( ; *s ; s++) *s = tolower(*s);
796 }
797
748 - wu->per_job_type[i].jobs_started = 0;
749 - wu->per_job_type[i].busy_time = 0;
798 + // copy the usage data
799 + wu->per_job_type[i].data[1].jobs_started = wu->per_job_type[i].data[0].jobs_started;
800 + wu->per_job_type[i].data[1].busy_time = wu->per_job_type[i].data[0].busy_time;
801 +
802 + // reset them for the next collection
803 + wu->per_job_type[i].data[0].jobs_started = 0;
804 + wu->per_job_type[i].data[0].busy_time = 0;
805
806 wu->per_job_type[i].min_value = NAN;
807 wu->per_job_type[i].max_value = NAN;
@@ -910,8 +965,8 @@ static void worker_utilization_charts_callback(void *ptr
965
966 wu->per_job_type[i].type = job_types_metric_types[i];
967
913 - wu->per_job_type[i].jobs_started += job_types_jobs_started[i];
914 - wu->per_job_type[i].busy_time += job_types_busy_time[i];
968 + wu->per_job_type[i].data[0].jobs_started += job_types_jobs_started[i];
969 + wu->per_job_type[i].data[0].busy_time += job_types_busy_time[i];
970
971 NETDATA_DOUBLE value = job_types_custom_metrics[i];
972 if(netdata_double_isnumber(value)) {
src/daemon/pulse/pulse.h
+33
@@ -28,4 +28,37 @@ extern bool pulse_extended_enabled;
28 void *pulse_thread_main(void *ptr);
29 void *pulse_thread_sqlite3_main(void *ptr);
30
31 +#define p1_add_fetch(variable, value) __atomic_add_fetch(variable, value, __ATOMIC_RELAXED)
32 +#define p1_sub_fetch(variable, value) __atomic_sub_fetch(variable, value, __ATOMIC_RELAXED)
33 +
34 +#define p1_fetch_add(variable, value) __atomic_fetch_add(variable, value, __ATOMIC_RELAXED)
35 +#define p1_fetch_sub(variable, value) __atomic_fetch_sub(variable, value, __ATOMIC_RELAXED)
36 +
37 +#define p1_store(variable, value) __atomic_store_n(variable, value, __ATOMIC_RELAXED)
38 +#define p1_load(variable) __atomic_load_n(variable, value, __ATOMIC_RELAXED)
39 +
40 +#if !defined(PULSE_EXTENDED_STATISTICS) && (defined(NETDATA_INTERNAL_CHECKS) || defined(NETDATA_GOD_MODE))
41 +#define PULSE_EXTENDED_STATISTICS
42 +#endif
43 +
44 +#if defined(PULSE_EXTENDED_STATISTICS)
45 +#define p2_add_fetch(variable, value) __atomic_add_fetch(variable, value, __ATOMIC_RELAXED)
46 +#define p2_sub_fetch(variable, value) __atomic_sub_fetch(variable, value, __ATOMIC_RELAXED)
47 +
48 +#define p2_fetch_add(variable, value) __atomic_fetch_add(variable, value, __ATOMIC_RELAXED)
49 +#define p2_fetch_sub(variable, value) __atomic_fetch_sub(variable, value, __ATOMIC_RELAXED)
50 +
51 +#define p2_store(variable, value) __atomic_store_n(variable, value, __ATOMIC_RELAXED)
52 +#define p2_load(variable) __atomic_load_n(variable, value, __ATOMIC_RELAXED)
53 +#else
54 +#define p2_add_fetch(variable, value) debug_dummy()
55 +#define p2_sub_fetch(variable, value) debug_dummy()
56 +
57 +#define p2_fetch_add(variable, value) debug_dummy()
58 +#define p2_fetch_sub(variable, value) debug_dummy()
59 +
60 +#define p2_store(variable, value) debug_dummy()
61 +#define p2_load(variable) debug_dummy()
62 +#endif
63 +
64 #endif /* NETDATA_PULSE_H */
src/daemon/static_threads.c
+1 -1
@@ -174,7 +174,7 @@ const struct netdata_static_thread static_threads_common[] = {
174 .name = "BACKFILL",
175 .config_section = NULL,
176 .config_name = NULL,
177 - .enable_routine = backfill_threads_detect_from_stream_conf,
177 + .enable_routine = netdata_conf_is_parent,
178 .enabled = 0,
179 .thread = NULL,
180 .init_routine = NULL,
src/daemon/unit_test.c
+2 -2
@@ -1269,7 +1269,7 @@ int run_test(struct test *test)
1269 fprintf(stderr, "\nRunning test '%s':\n%s\n", test->name, test->description);
1270
1271 default_rrd_memory_mode = RRD_MEMORY_MODE_ALLOC;
1272 - default_rrd_update_every = test->update_every;
1272 + nd_profile.update_every = test->update_every;
1273
1274 char name[101];
1275 snprintfz(name, sizeof(name) - 1, "unittest-%s", test->name);
@@ -1538,7 +1538,7 @@ int unit_test(long delay, long shift)
1538
1539 //debug_flags = 0xffffffff;
1540 default_rrd_memory_mode = RRD_MEMORY_MODE_ALLOC;
1541 - default_rrd_update_every = 1;
1541 + nd_profile.update_every = 1;
1542
1543 int do_abs = 1;
1544 int do_inc = 1;
src/database/contexts/api_v2_contexts_agents.c
+1 -1
@@ -104,7 +104,7 @@ void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now
104
105 buffer_json_member_add_array(wb, "db_size");
106 size_t group_seconds;
107 - for (size_t tier = 0; tier < storage_tiers; tier++) {
107 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
108 STORAGE_ENGINE *eng = localhost->db[tier].eng;
109 if (!eng) continue;
110
src/database/contexts/query_target.c
+6 -6
@@ -219,7 +219,7 @@ static inline void query_metric_release(QUERY_TARGET *qt, QUERY_METRIC *qm) {
219 qm->plan.used = 0;
220
221 // reset the tiers
222 - for(size_t tier = 0; tier < storage_tiers ;tier++) {
222 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
223 if(qm->tiers[tier].smh) {
224 STORAGE_ENGINE *eng = query_metric_storage_engine(qt, qm, tier);
225 eng->api.metric_release(qm->tiers[tier].smh);
@@ -244,9 +244,9 @@ static bool query_metric_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CON
244 time_t db_first_time_s;
245 time_t db_last_time_s;
246 time_t db_update_every_s;
247 - } tier_retention[storage_tiers];
247 + } tier_retention[nd_profile.storage_tiers];
248
249 - for (size_t tier = 0; tier < storage_tiers; tier++) {
249 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
250 STORAGE_ENGINE *eng = qn->rrdhost->db[tier].eng;
251 tier_retention[tier].eng = eng;
252 tier_retention[tier].db_update_every_s = (time_t) (qn->rrdhost->db[tier].tier_grouping * ri->update_every_s);
@@ -284,7 +284,7 @@ static bool query_metric_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CON
284 }
285 }
286
287 - for (size_t tier = 0; tier < storage_tiers; tier++) {
287 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
288 if(!qt->db.tiers[tier].update_every || (tier_retention[tier].db_update_every_s && tier_retention[tier].db_update_every_s < qt->db.tiers[tier].update_every))
289 qt->db.tiers[tier].update_every = tier_retention[tier].db_update_every_s;
290
@@ -328,7 +328,7 @@ static bool query_metric_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CON
328 if (!qt->db.last_time_s || common_last_time_s > qt->db.last_time_s)
329 qt->db.last_time_s = common_last_time_s;
330
331 - for (size_t tier = 0; tier < storage_tiers; tier++) {
331 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
332 internal_fatal(tier_retention[tier].eng != query_metric_storage_engine(qt, qm, tier), "QUERY TARGET: storage engine mismatch");
333 qm->tiers[tier].smh = tier_retention[tier].smh;
334 qm->tiers[tier].db_first_time_s = tier_retention[tier].db_first_time_s;
@@ -340,7 +340,7 @@ static bool query_metric_add(QUERY_TARGET_LOCALS *qtl, QUERY_NODE *qn, QUERY_CON
340 }
341
342 // cleanup anything we allocated to the retention we will not use
343 - for(size_t tier = 0; tier < storage_tiers ;tier++) {
343 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
344 if (tier_retention[tier].smh) {
345 tier_retention[tier].eng->api.metric_release(tier_retention[tier].smh);
346 tier_retention[tier].smh = NULL;
src/database/contexts/worker.c
+3 -3
@@ -248,12 +248,12 @@ bool rrdmetric_update_retention(RRDMETRIC *rm) {
248 }
249 else {
250 RRDHOST *rrdhost = rm->ri->rc->rrdhost;
251 - for (size_t tier = 0; tier < storage_tiers; tier++) {
251 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
252 STORAGE_ENGINE *eng = rrdhost->db[tier].eng;
253
254 - time_t first_time_t, last_time_t;
254 + time_t first_time_t = 0, last_time_t = 0;
255 if (eng->api.metric_retention_by_uuid(rrdhost->db[tier].si, &rm->uuid, &first_time_t, &last_time_t)) {
256 - if (first_time_t < min_first_time_t)
256 + if (first_time_t > 0 && first_time_t < min_first_time_t)
257 min_first_time_t = first_time_t;
258
259 if (last_time_t > max_last_time_t)
src/database/engine/cache.c
+188 -190
@@ -15,7 +15,7 @@
15 */
16
17 typedef int32_t REFCOUNT;
18 -#define REFCOUNT_DELETING (-100)
18 +#define REFCOUNT_DICONNECTED (-100)
19
20 // to use ARAL uncomment the following line:
21 #if !defined(FSANITIZE_ADDRESS)
@@ -225,49 +225,27 @@ static inline size_t pgc_indexing_partition(PGC *cache, Word_t metric_id) {
225 return last_partition;
226 }
227
228 -static inline void pgc_index_read_lock(PGC *cache, size_t partition) {
229 - rw_spinlock_read_lock(&cache->index[partition].rw_spinlock);
230 -}
231 -static inline void pgc_index_read_unlock(PGC *cache, size_t partition) {
232 - rw_spinlock_read_unlock(&cache->index[partition].rw_spinlock);
233 -}
234 -static inline void pgc_index_write_lock(PGC *cache, size_t partition) {
235 - rw_spinlock_write_lock(&cache->index[partition].rw_spinlock);
236 -}
237 -static inline void pgc_index_write_unlock(PGC *cache, size_t partition) {
238 - rw_spinlock_write_unlock(&cache->index[partition].rw_spinlock);
239 -}
240 -static inline bool pgc_index_trywrite_lock(PGC *cache, size_t partition, bool force) {
241 - if(force) {
242 - rw_spinlock_write_lock(&cache->index[partition].rw_spinlock);
243 - return true;
244 - }
245 - return rw_spinlock_trywrite_lock(&cache->index[partition].rw_spinlock);
246 -}
247 -
248 -static inline bool pgc_queue_trylock(PGC *cache __maybe_unused, struct pgc_queue *ll) {
249 - return spinlock_trylock(&ll->spinlock);
250 -}
251 -
252 -static inline void pgc_queue_lock(PGC *cache __maybe_unused, struct pgc_queue *ll) {
253 - spinlock_lock(&ll->spinlock);
254 -}
255 -
256 -static inline void pgc_queue_unlock(PGC *cache __maybe_unused, struct pgc_queue *ll) {
257 - spinlock_unlock(&ll->spinlock);
258 -}
259 -
260 -static inline bool page_transition_trylock(PGC *cache __maybe_unused, PGC_PAGE *page) {
261 - return spinlock_trylock(&page->transition_spinlock);
262 -}
263 -
264 -static inline void page_transition_lock(PGC *cache __maybe_unused, PGC_PAGE *page) {
265 - spinlock_lock(&page->transition_spinlock);
266 -}
267 -
268 -static inline void page_transition_unlock(PGC *cache __maybe_unused, PGC_PAGE *page) {
269 - spinlock_unlock(&page->transition_spinlock);
270 -}
228 +#define pgc_index_read_lock(cache, partition) rw_spinlock_read_lock(&(cache)->index[partition].rw_spinlock)
229 +#define pgc_index_read_unlock(cache, partition) rw_spinlock_read_unlock(&(cache)->index[partition].rw_spinlock)
230 +#define pgc_index_write_lock(cache, partition) rw_spinlock_write_lock(&(cache)->index[partition].rw_spinlock)
231 +#define pgc_index_write_unlock(cache, partition) rw_spinlock_write_unlock(&(cache)->index[partition].rw_spinlock)
232 +#define pgc_index_trywrite_lock(cache, partition, force) ({ \
233 + bool _result; \
234 + if (force) { \
235 + rw_spinlock_write_lock(&(cache)->index[partition].rw_spinlock); \
236 + _result = true; \
237 + } else \
238 + _result = rw_spinlock_trywrite_lock(&(cache)->index[partition].rw_spinlock); \
239 + _result; \
240 +})
241 +
242 +#define pgc_queue_trylock(cache, ll) spinlock_trylock(&(ll)->spinlock)
243 +#define pgc_queue_lock(cache, ll) spinlock_lock(&(ll)->spinlock)
244 +#define pgc_queue_unlock(cache, ll) spinlock_unlock(&(ll)->spinlock)
245 +
246 +#define page_transition_trylock(cache, page) spinlock_trylock(&(page)->transition_spinlock)
247 +#define page_transition_lock(cache, page) spinlock_lock(&(page)->transition_spinlock)
248 +#define page_transition_unlock(cache, page) spinlock_unlock(&(page)->transition_spinlock)
249
250 // ----------------------------------------------------------------------------
251 // size histogram
@@ -457,7 +435,7 @@ static inline size_t cache_usage_per1000(PGC *cache, size_t *size_to_evict) {
435
436 if(signal) {
437 completion_mark_complete_a_job(&cache->evictor.completion);
460 - __atomic_add_fetch(&cache->stats.waste_evict_thread_signals, 1, __ATOMIC_RELAXED);
438 + p2_add_fetch(&cache->stats.p2_waste_evict_thread_signals, 1);
439 }
440 }
441
@@ -481,13 +459,13 @@ static bool evict_pages_with_filter(PGC *cache, size_t max_skip, size_t max_evic
459 static inline bool flushing_critical(PGC *cache);
460 static bool flush_pages(PGC *cache, size_t max_flushes, Word_t section, bool wait, bool all_of_them);
461
484 -static void signal_evict_thread_or_evict_inline(PGC *cache, bool on_release) {
462 +static void evict_pages_inline(PGC *cache, bool on_release) {
463 const size_t per1000 = cache_usage_per1000(cache, NULL);
464
465 if(!(cache->config.options & PGC_OPTIONS_EVICT_PAGES_NO_INLINE)) {
466 if (per1000 > cache->config.aggressive_evict_per1000 && !on_release) {
467 // the threads that add pages, turn into evictors when the cache needs evictions aggressively
490 - __atomic_add_fetch(&cache->stats.waste_evictions_inline_on_add, 1, __ATOMIC_RELAXED);
468 + p2_add_fetch(&cache->stats.p2_waste_evictions_inline_on_add, 1);
469 evict_pages(cache,
470 cache->config.max_skip_pages_per_inline_eviction,
471 cache->config.max_pages_per_inline_eviction,
@@ -495,7 +473,7 @@ static void signal_evict_thread_or_evict_inline(PGC *cache, bool on_release) {
473 }
474 else if (per1000 > cache->config.severe_pressure_per1000 && on_release) {
475 // the threads that are releasing pages, turn into evictors when the cache is critical
498 - __atomic_add_fetch(&cache->stats.waste_evictions_inline_on_release, 1, __ATOMIC_RELAXED);
476 + p2_add_fetch(&cache->stats.p2_waste_evictions_inline_on_release, 1);
477
478 evict_pages(cache,
479 cache->config.max_skip_pages_per_inline_eviction,
@@ -506,19 +484,19 @@ static void signal_evict_thread_or_evict_inline(PGC *cache, bool on_release) {
484 }
485
486 static inline void evict_on_clean_page_added(PGC *cache) {
509 - signal_evict_thread_or_evict_inline(cache, false);
487 + evict_pages_inline(cache, false);
488 }
489
490 static inline void evict_on_page_release_when_permitted(PGC *cache) {
513 - signal_evict_thread_or_evict_inline(cache, true);
491 + evict_pages_inline(cache, true);
492 }
493
494 static inline void flush_inline(PGC *cache, bool on_release) {
495 if(!(cache->config.options & PGC_OPTIONS_FLUSH_PAGES_NO_INLINE) && flushing_critical(cache)) {
496 if (on_release)
519 - __atomic_add_fetch(&cache->stats.waste_flush_on_release, 1, __ATOMIC_RELAXED);
497 + p2_add_fetch(&cache->stats.p2_waste_flush_on_release, 1);
498 else
521 - __atomic_add_fetch(&cache->stats.waste_flush_on_add, 1, __ATOMIC_RELAXED);
499 + p2_add_fetch(&cache->stats.p2_waste_flush_on_add, 1);
500
501 flush_pages(cache, cache->config.max_flushes_inline, PGC_SECTION_ALL, false, false);
502 }
@@ -582,7 +560,8 @@ static void pgc_section_pages_static_aral_init(void) {
560
561 if(!pgc_sections_aral) {
562 pgc_sections_aral = aral_create(
585 - "pgc-sections", sizeof(struct section_pages), 0, 0, &pgc_aral_statistics, NULL, NULL, false, false);
563 + "pgc-sections", sizeof(struct section_pages), 0, 0, &pgc_aral_statistics,
564 + NULL, NULL, false, false, false);
565
566 pulse_aral_register_statistics(&pgc_aral_statistics, "pgc");
567 }
@@ -885,7 +864,7 @@ static inline bool page_acquire(PGC *cache, PGC_PAGE *page) {
864 } while(!__atomic_compare_exchange_n(&page->refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
865
866 if(unlikely(spins > 1))
888 - __atomic_add_fetch(&cache->stats.waste_acquire_spins, spins - 1, __ATOMIC_RELAXED);
867 + p2_add_fetch(&cache->stats.p2_waste_acquire_spins, spins - 1);
868
869 if(desired == 1)
870 PGC_REFERENCED_PAGES_PLUS1(cache, page);
@@ -913,7 +892,7 @@ static inline void page_release(PGC *cache, PGC_PAGE *page, bool evict_if_necess
892 } while(!__atomic_compare_exchange_n(&page->refcount, &expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
893
894 if(unlikely(spins > 1))
916 - __atomic_add_fetch(&cache->stats.waste_release_spins, spins - 1, __ATOMIC_RELAXED);
895 + p2_add_fetch(&cache->stats.p2_waste_release_spins, spins - 1);
896
897 if(desired == 0) {
898 PGC_REFERENCED_PAGES_MINUS1(cache, assumed_size);
@@ -939,7 +918,7 @@ static inline bool non_acquired_page_get_for_deletion___while_having_clean_locke
918 spins++;
919
920 if (expected == 0) {
942 - desired = REFCOUNT_DELETING;
921 + desired = REFCOUNT_DICONNECTED;
922 delete_it = true;
923 }
924 else {
@@ -958,7 +937,7 @@ static inline bool non_acquired_page_get_for_deletion___while_having_clean_locke
937 }
938
939 if(unlikely(spins > 1))
961 - __atomic_add_fetch(&cache->stats.waste_delete_spins, spins - 1, __ATOMIC_RELAXED);
940 + p2_add_fetch(&cache->stats.p2_waste_delete_spins, spins - 1);
941
942 return delete_it;
943 }
@@ -982,7 +961,7 @@ static inline bool acquired_page_get_for_deletion_or_release_it(PGC *cache __may
961
962 if (expected == 1) {
963 // we are the only one having this page referenced
985 - desired = REFCOUNT_DELETING;
964 + desired = REFCOUNT_DICONNECTED;
965 delete_it = true;
966 }
967 else {
@@ -1004,7 +983,7 @@ static inline bool acquired_page_get_for_deletion_or_release_it(PGC *cache __may
983 }
984
985 if(unlikely(spins > 1))
1007 - __atomic_add_fetch(&cache->stats.waste_delete_spins, spins - 1, __ATOMIC_RELAXED);
986 + p2_add_fetch(&cache->stats.p2_waste_delete_spins, spins - 1);
987
988 return delete_it;
989 }
@@ -1149,9 +1128,9 @@ static bool evict_pages_with_filter(PGC *cache, size_t max_skip, size_t max_evic
1128 return false;
1129
1130 bool under_sever_pressure = per1000 >= cache->config.severe_pressure_per1000;
1152 - size_t workers_running = __atomic_add_fetch(&cache->stats.workers_evict, 1, __ATOMIC_RELAXED);
1131 + size_t workers_running = __atomic_add_fetch(&cache->stats.p0_workers_evict, 1, __ATOMIC_RELAXED);
1132 if(!wait && !all_of_them && workers_running > cache->config.max_workers_evict_inline && !under_sever_pressure) {
1154 - __atomic_sub_fetch(&cache->stats.workers_evict, 1, __ATOMIC_RELAXED);
1133 + __atomic_sub_fetch(&cache->stats.p0_workers_evict, 1, __ATOMIC_RELAXED);
1134 return false;
1135 }
1136
@@ -1186,18 +1165,17 @@ static bool evict_pages_with_filter(PGC *cache, size_t max_skip, size_t max_evic
1165 else if(unlikely(wait)) {
1166 // evict as many as necessary for the cache to go at the predefined threshold
1167 per1000 = cache_usage_per1000(cache, &max_size_to_evict);
1189 - max_size_to_evict /= 3; // do it in 3 steps
1168 if(per1000 >= cache->config.severe_pressure_per1000) {
1169 under_sever_pressure = true;
1192 - max_pages_to_evict = max_pages_to_evict ? max_pages_to_evict * 2 : 512;
1193 - if(max_pages_to_evict > 4096)
1194 - max_pages_to_evict = 4096;
1170 + max_pages_to_evict = max_pages_to_evict ? max_pages_to_evict * 2 : 16;
1171 + if(max_pages_to_evict > 64)
1172 + max_pages_to_evict = 64;
1173 }
1174 else if(per1000 >= cache->config.aggressive_evict_per1000) {
1175 under_sever_pressure = false;
1198 - max_pages_to_evict = max_pages_to_evict ? max_pages_to_evict * 2 : 32;
1199 - if(max_pages_to_evict > 1024)
1200 - max_pages_to_evict = 1024;
1176 + max_pages_to_evict = max_pages_to_evict ? max_pages_to_evict * 2 : 4;
1177 + if(max_pages_to_evict > 16)
1178 + max_pages_to_evict = 16;
1179 }
1180 else {
1181 under_sever_pressure = false;
@@ -1220,7 +1198,7 @@ static bool evict_pages_with_filter(PGC *cache, size_t max_skip, size_t max_evic
1198 }
1199
1200 if(++spins > 1 && !this_loop_evicted)
1223 - __atomic_add_fetch(&cache->stats.waste_evict_useless_spins, 1, __ATOMIC_RELAXED);
1201 + p2_add_fetch(&cache->stats.p2_waste_evict_useless_spins, 1);
1202
1203 this_loop_evicted = 0;
1204
@@ -1423,9 +1401,9 @@ static bool evict_pages_with_filter(PGC *cache, size_t max_skip, size_t max_evic
1401
1402 premature_exit:
1403 if(unlikely(total_pages_relocated))
1426 - __atomic_add_fetch(&cache->stats.waste_evict_relocated, total_pages_relocated, __ATOMIC_RELAXED);
1404 + p2_add_fetch(&cache->stats.p2_waste_evict_relocated, total_pages_relocated);
1405
1428 - __atomic_sub_fetch(&cache->stats.workers_evict, 1, __ATOMIC_RELAXED);
1406 + __atomic_sub_fetch(&cache->stats.p0_workers_evict, 1, __ATOMIC_RELAXED);
1407
1408 return stopped_before_finishing;
1409 }
@@ -1434,7 +1412,7 @@ static PGC_PAGE *page_add(PGC *cache, PGC_ENTRY *entry, bool *added) {
1412 internal_fatal(entry->start_time_s < 0 || entry->end_time_s < 0,
1413 "DBENGINE CACHE: timestamps are negative");
1414
1437 - __atomic_add_fetch(&cache->stats.workers_add, 1, __ATOMIC_RELAXED);
1415 + p2_add_fetch(&cache->stats.p2_workers_add, 1);
1416
1417 size_t partition = pgc_indexing_partition(cache, entry->metric_id);
1418
@@ -1451,8 +1429,7 @@ static PGC_PAGE *page_add(PGC *cache, PGC_ENTRY *entry, bool *added) {
1429 entry->end_time_s = 0;
1430
1431 do {
1454 - if(++spins > 1)
1455 - __atomic_add_fetch(&cache->stats.waste_insert_spins, 1, __ATOMIC_RELAXED);
1432 + spins++;
1433
1434 pgc_index_write_lock(cache, partition);
1435
@@ -1553,7 +1530,10 @@ static PGC_PAGE *page_add(PGC *cache, PGC_ENTRY *entry, bool *added) {
1530 aral_freez(cache->index[partition].aral, allocation);
1531 #endif
1532
1556 - __atomic_sub_fetch(&cache->stats.workers_add, 1, __ATOMIC_RELAXED);
1533 + if(spins > 1)
1534 + p2_add_fetch(&cache->stats.p2_waste_insert_spins, spins - 1);
1535 +
1536 + p2_sub_fetch(&cache->stats.p2_workers_add, 1);
1537
1538 if(!entry->hot)
1539 evict_on_clean_page_added(cache);
@@ -1563,9 +1543,95 @@ static PGC_PAGE *page_add(PGC *cache, PGC_ENTRY *entry, bool *added) {
1543 return page;
1544 }
1545
1566 -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) {
1567 - *retry = false;
1546 +static inline PGC_PAGE *page_find_and_acquire_exact_unsafe(PGC *cache, Pvoid_t *pages_judy_pptr, time_t start_time_s) {
1547 + Pvoid_t *page_ptr = JudyLGet(*pages_judy_pptr, start_time_s, PJE0);
1548 + if(!page_ptr)
1549 + return NULL;
1550 +
1551 + if (unlikely(page_ptr == PJERR))
1552 + fatal("DBENGINE CACHE: corrupted page in pages judy array");
1553 +
1554 + PGC_PAGE *page = *page_ptr;
1555 + if(page && page_acquire(cache, page))
1556 + // we have our page acquired
1557 + return page;
1558 +
1559 + return NULL;
1560 +}
1561 +
1562 +static inline PGC_PAGE *page_find_and_acquire_first_unsafe(PGC *cache, Pvoid_t *pages_judy_pptr, time_t start_time_s) {
1563 + Word_t time = start_time_s;
1564 + for(Pvoid_t *page_ptr = JudyLFirst(*pages_judy_pptr, &time, PJE0);
1565 + page_ptr ;
1566 + page_ptr = JudyLNext(*pages_judy_pptr, &time, PJE0)) {
1567 +
1568 + if (unlikely(page_ptr == PJERR))
1569 + fatal("DBENGINE CACHE: corrupted page in pages judy array");
1570 +
1571 + PGC_PAGE *page = *page_ptr;
1572 + if(page && page_acquire(cache, page))
1573 + // we have our page acquired
1574 + return page;
1575 + }
1576 +
1577 + return NULL;
1578 +}
1579 +
1580 +static inline PGC_PAGE *page_find_and_acquire_next_unsafe(PGC *cache, Pvoid_t *pages_judy_pptr, time_t start_time_s) {
1581 + Word_t time = start_time_s;
1582 + for(Pvoid_t *page_ptr = JudyLNext(*pages_judy_pptr, &time, PJE0);
1583 + page_ptr ;
1584 + page_ptr = JudyLNext(*pages_judy_pptr, &time, PJE0)) {
1585 +
1586 + if (unlikely(page_ptr == PJERR))
1587 + fatal("DBENGINE CACHE: corrupted page in pages judy array");
1588 +
1589 + PGC_PAGE *page = *page_ptr;
1590 + if(page && page_acquire(cache, page))
1591 + // we have our page acquired
1592 + return page;
1593 + }
1594 +
1595 + return NULL;
1596 +}
1597 +
1598 +static inline PGC_PAGE *page_find_and_acquire_last_unsafe(PGC *cache, Pvoid_t *pages_judy_pptr, time_t start_time_s) {
1599 + Word_t time = start_time_s;
1600 + for(Pvoid_t *page_ptr = JudyLLast(*pages_judy_pptr, &time, PJE0);
1601 + page_ptr ;
1602 + page_ptr = JudyLPrev(*pages_judy_pptr, &time, PJE0)) {
1603 +
1604 + if (unlikely(page_ptr == PJERR))
1605 + fatal("DBENGINE CACHE: corrupted page in pages judy array");
1606 +
1607 + PGC_PAGE *page = *page_ptr;
1608 + if(page && page_acquire(cache, page))
1609 + // we have our page acquired
1610 + return page;
1611 + }
1612 +
1613 + return NULL;
1614 +}
1615 +
1616 +static inline PGC_PAGE *page_find_and_acquire_prev_unsafe(PGC *cache, Pvoid_t *pages_judy_pptr, time_t start_time_s) {
1617 + Word_t time = start_time_s;
1618 + for(Pvoid_t *page_ptr = JudyLPrev(*pages_judy_pptr, &time, PJE0);
1619 + page_ptr ;
1620 + page_ptr = JudyLPrev(*pages_judy_pptr, &time, PJE0)) {
1621
1622 + if (unlikely(page_ptr == PJERR))
1623 + fatal("DBENGINE CACHE: corrupted page in pages judy array");
1624 +
1625 + PGC_PAGE *page = *page_ptr;
1626 + if(page && page_acquire(cache, page))
1627 + // we have our page acquired
1628 + return page;
1629 + }
1630 +
1631 + return NULL;
1632 +}
1633 +
1634 +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) {
1635 PGC_PAGE *page = NULL;
1636 size_t partition = pgc_indexing_partition(cache, metric_id);
1637
@@ -1592,105 +1658,47 @@ static PGC_PAGE *page_find_and_acquire_once(PGC *cache, Word_t section, Word_t m
1658 switch(method) {
1659 default:
1660 case PGC_SEARCH_CLOSEST: {
1595 - Pvoid_t *page_ptr = JudyLGet(*pages_judy_pptr, start_time_s, PJE0);
1596 - if (unlikely(page_ptr == PJERR))
1597 - fatal("DBENGINE CACHE: corrupted page in pages judy array");
1598 -
1599 - if (page_ptr)
1600 - page = *page_ptr;
1601 -
1602 - else {
1603 - Word_t time = start_time_s;
1604 -
1605 - // find the previous page
1606 - page_ptr = JudyLPrev(*pages_judy_pptr, &time, PJE0);
1607 - if(unlikely(page_ptr == PJERR))
1608 - fatal("DBENGINE CACHE: corrupted page in pages judy array #2");
1609 -
1610 - if(page_ptr) {
1661 + page = page_find_and_acquire_exact_unsafe(cache, pages_judy_pptr, start_time_s);
1662 + if(!page) {
1663 + page = page_find_and_acquire_prev_unsafe(cache, pages_judy_pptr, start_time_s);
1664 + if(page && start_time_s > page->end_time_s) {
1665 // found a page starting before our timestamp
1612 - // check if our timestamp is included
1613 - page = *page_ptr;
1614 - if(start_time_s > page->end_time_s)
1615 - // it is not good for us
1616 - page = NULL;
1666 + // but our timestamp is not included in it
1667 + page_release(cache, page, false);
1668 + page = NULL;
1669 }
1670
1619 - if(!page) {
1620 - // find the next page then...
1621 - time = start_time_s;
1622 - page_ptr = JudyLNext(*pages_judy_pptr, &time, PJE0);
1623 - if(page_ptr)
1624 - page = *page_ptr;
1625 - }
1671 + if(!page)
1672 + page = page_find_and_acquire_next_unsafe(cache, pages_judy_pptr, start_time_s);
1673 }
1674 }
1675 break;
1676
1630 - case PGC_SEARCH_EXACT: {
1631 - Pvoid_t *page_ptr = JudyLGet(*pages_judy_pptr, start_time_s, PJE0);
1632 - if (unlikely(page_ptr == PJERR))
1633 - fatal("DBENGINE CACHE: corrupted page in pages judy array");
1634 -
1635 - if (page_ptr)
1636 - page = *page_ptr;
1637 - }
1638 - break;
1639 -
1640 - case PGC_SEARCH_FIRST: {
1641 - Word_t time = start_time_s;
1642 - Pvoid_t *page_ptr = JudyLFirst(*pages_judy_pptr, &time, PJE0);
1643 - if (unlikely(page_ptr == PJERR))
1644 - fatal("DBENGINE CACHE: corrupted page in pages judy array");
1645 -
1646 - if (page_ptr)
1647 - page = *page_ptr;
1648 - }
1649 - break;
1650 -
1651 - case PGC_SEARCH_NEXT: {
1652 - Word_t time = start_time_s;
1653 - Pvoid_t *page_ptr = JudyLNext(*pages_judy_pptr, &time, PJE0);
1654 - if (unlikely(page_ptr == PJERR))
1655 - fatal("DBENGINE CACHE: corrupted page in pages judy array");
1656 -
1657 - if (page_ptr)
1658 - page = *page_ptr;
1659 - }
1660 - break;
1677 + case PGC_SEARCH_EXACT:
1678 + page = page_find_and_acquire_exact_unsafe(cache, pages_judy_pptr, start_time_s);
1679 + break;
1680
1662 - case PGC_SEARCH_LAST: {
1663 - Word_t time = start_time_s;
1664 - Pvoid_t *page_ptr = JudyLLast(*pages_judy_pptr, &time, PJE0);
1665 - if (unlikely(page_ptr == PJERR))
1666 - fatal("DBENGINE CACHE: corrupted page in pages judy array");
1681 + case PGC_SEARCH_FIRST:
1682 + page = page_find_and_acquire_first_unsafe(cache, pages_judy_pptr, start_time_s);
1683 + break;
1684
1668 - if (page_ptr)
1669 - page = *page_ptr;
1670 - }
1671 - break;
1685 + case PGC_SEARCH_NEXT:
1686 + page = page_find_and_acquire_next_unsafe(cache, pages_judy_pptr, start_time_s);
1687 + break;
1688
1673 - case PGC_SEARCH_PREV: {
1674 - Word_t time = start_time_s;
1675 - Pvoid_t *page_ptr = JudyLPrev(*pages_judy_pptr, &time, PJE0);
1676 - if (unlikely(page_ptr == PJERR))
1677 - fatal("DBENGINE CACHE: corrupted page in pages judy array");
1689 + case PGC_SEARCH_LAST:
1690 + page = page_find_and_acquire_last_unsafe(cache, pages_judy_pptr, start_time_s);
1691 + break;
1692
1679 - if (page_ptr)
1680 - page = *page_ptr;
1681 - }
1682 - break;
1693 + case PGC_SEARCH_PREV:
1694 + page = page_find_and_acquire_prev_unsafe(cache, pages_judy_pptr, start_time_s);
1695 + break;
1696 }
1697
1685 - if(page) {
1698 +#ifdef NETDATA_PGC_POINTER_CHECK
1699 + if(page)
1700 pointer_check(cache, page);
1687 -
1688 - if(!page_acquire(cache, page)) {
1689 - // this page is not good to use
1690 - *retry = true;
1691 - page = NULL;
1692 - }
1693 - }
1701 +#endif
1702
1703 cleanup:
1704 pgc_index_read_unlock(cache, partition);
@@ -1752,7 +1760,7 @@ static bool flush_pages(PGC *cache, size_t max_flushes, Word_t section, bool wai
1760 return false;
1761 }
1762
1755 - __atomic_add_fetch(&cache->stats.workers_flush, 1, __ATOMIC_RELAXED);
1763 + p2_add_fetch(&cache->stats.p2_workers_flush, 1);
1764
1765 bool have_dirty_lock = true;
1766
@@ -1865,8 +1873,8 @@ static bool flush_pages(PGC *cache, size_t max_flushes, Word_t section, bool wai
1873 // page ptr may be invalid now
1874 }
1875
1868 - __atomic_add_fetch(&cache->stats.waste_flushes_cancelled, pages_cancelled, __ATOMIC_RELAXED);
1869 - __atomic_add_fetch(&cache->stats.flushes_cancelled_size, pages_cancelled_size, __ATOMIC_RELAXED);
1876 + p2_add_fetch(&cache->stats.p2_waste_flushes_cancelled, pages_cancelled);
1877 + p2_add_fetch(&cache->stats.flushes_cancelled_size, pages_cancelled_size);
1878
1879 internal_fatal(pages_added != pages_cancelled || pages_added_size != pages_cancelled_size,
1880 "DBENGINE CACHE: flushing cancel pages mismatch");
@@ -1938,7 +1946,7 @@ static bool flush_pages(PGC *cache, size_t max_flushes, Word_t section, bool wai
1946 pgc_queue_unlock(cache, &cache->dirty);
1947 }
1948
1941 - __atomic_sub_fetch(&cache->stats.workers_flush, 1, __ATOMIC_RELAXED);
1949 + p2_sub_fetch(&cache->stats.p2_workers_flush, 1);
1950
1951 return stopped_before_finishing;
1952 }
@@ -2066,10 +2074,8 @@ PGC *pgc_create(const char *name,
2074 0,
2075 0,
2076 &pgc_aral_statistics,
2069 - NULL,
2070 - NULL,
2071 - false,
2072 - false);
2077 + NULL, NULL,
2078 + false, false, false);
2079 }
2080 }
2081 #endif
@@ -2165,7 +2171,7 @@ void pgc_page_release(PGC *cache, PGC_PAGE *page) {
2171 }
2172
2173 void pgc_page_hot_to_dirty_and_release(PGC *cache, PGC_PAGE *page, bool never_flush) {
2168 - __atomic_add_fetch(&cache->stats.workers_hot2dirty, 1, __ATOMIC_RELAXED);
2174 + p2_add_fetch(&cache->stats.p2_workers_hot2dirty, 1);
2175
2176 //#ifdef NETDATA_INTERNAL_CHECKS
2177 // page_transition_lock(cache, page);
@@ -2180,7 +2186,7 @@ void pgc_page_hot_to_dirty_and_release(PGC *cache, PGC_PAGE *page, bool never_fl
2186 page_release(cache, page, true);
2187 // page ptr may be invalid now
2188
2183 - __atomic_sub_fetch(&cache->stats.workers_hot2dirty, 1, __ATOMIC_RELAXED);
2189 + p2_sub_fetch(&cache->stats.p2_workers_hot2dirty, 1);
2190
2191 // flush, if we have to
2192 if(!never_flush)
@@ -2190,7 +2196,7 @@ void pgc_page_hot_to_dirty_and_release(PGC *cache, PGC_PAGE *page, bool never_fl
2196 bool pgc_page_to_clean_evict_or_release(PGC *cache, PGC_PAGE *page) {
2197 bool ret;
2198
2193 - __atomic_add_fetch(&cache->stats.workers_hot2dirty, 1, __ATOMIC_RELAXED);
2199 + p2_add_fetch(&cache->stats.p2_workers_hot2dirty, 1);
2200
2201 // prevent accesses from increasing the accesses counter
2202 page_flag_set(page, PGC_PAGE_HAS_NO_DATA_IGNORE_ACCESSES);
@@ -2208,7 +2214,7 @@ bool pgc_page_to_clean_evict_or_release(PGC *cache, PGC_PAGE *page) {
2214 ret = false;
2215 }
2216
2211 - __atomic_sub_fetch(&cache->stats.workers_hot2dirty, 1, __ATOMIC_RELAXED);
2217 + p2_sub_fetch(&cache->stats.p2_workers_hot2dirty, 1);
2218
2219 return ret;
2220 }
@@ -2371,7 +2377,7 @@ void pgc_page_hot_set_end_time_s(PGC *cache __maybe_unused, PGC_PAGE *page, time
2377 PGC_PAGE *pgc_page_get_and_acquire(PGC *cache, Word_t section, Word_t metric_id, time_t start_time_s, PGC_SEARCH method) {
2378 PGC_PAGE *page = NULL;
2379
2374 - __atomic_add_fetch(&cache->stats.workers_search, 1, __ATOMIC_RELAXED);
2380 + p2_add_fetch(&cache->stats.p2_workers_search, 1);
2381
2382 size_t *stats_hit_ptr, *stats_miss_ptr;
2383
@@ -2386,17 +2392,7 @@ PGC_PAGE *pgc_page_get_and_acquire(PGC *cache, Word_t section, Word_t metric_id,
2392 stats_miss_ptr = &cache->stats.searches_exact_misses;
2393 }
2394
2389 - while(1) {
2390 - bool retry = false;
2391 -
2392 - page = page_find_and_acquire_once(cache, section, metric_id, start_time_s, method, &retry);
2393 -
2394 - if(page || !retry)
2395 - break;
2396 -
2397 - tinysleep();
2398 - }
2399 -
2395 + page = page_find_and_acquire_once(cache, section, metric_id, start_time_s, method);
2396 if(page) {
2397 __atomic_add_fetch(stats_hit_ptr, 1, __ATOMIC_RELAXED);
2398 page_has_been_accessed(cache, page);
@@ -2404,7 +2400,7 @@ PGC_PAGE *pgc_page_get_and_acquire(PGC *cache, Word_t section, Word_t metric_id,
2400 else
2401 __atomic_add_fetch(stats_miss_ptr, 1, __ATOMIC_RELAXED);
2402
2407 - __atomic_sub_fetch(&cache->stats.workers_search, 1, __ATOMIC_RELAXED);
2403 + p2_sub_fetch(&cache->stats.p2_workers_search, 1);
2404
2405 return page;
2406 }
@@ -2427,7 +2423,7 @@ size_t pgc_hot_and_dirty_entries(PGC *cache) {
2423
2424 void pgc_open_cache_to_journal_v2(PGC *cache, Word_t section, unsigned datafile_fileno, uint8_t type, migrate_to_v2_callback cb, void *data) {
2425 __atomic_add_fetch(&rrdeng_cache_efficiency_stats.journal_v2_indexing_started, 1, __ATOMIC_RELAXED);
2430 - __atomic_add_fetch(&cache->stats.workers_jv2_flush, 1, __ATOMIC_RELAXED);
2426 + p2_add_fetch(&cache->stats.p2_workers_jv2_flush, 1);
2427
2428 pgc_queue_lock(cache, &cache->hot);
2429
@@ -2559,7 +2555,8 @@ void pgc_open_cache_to_journal_v2(PGC *cache, Word_t section, unsigned datafile_
2555 page_release(cache, page, false);
2556 }
2557
2562 - pgc_queue_lock(cache, &cache->hot);
2558 + yield_the_processor(); // do not lock too aggressively
2559 + pgc_queue_lock(cache, &cache->hot);
2560 }
2561
2562 spinlock_unlock(&sp->migration_to_v2_spinlock);
@@ -2582,6 +2579,7 @@ void pgc_open_cache_to_journal_v2(PGC *cache, Word_t section, unsigned datafile_
2579 struct jv2_page_info *pi = *PValue2;
2580
2581 // balance-parents: transition from hot to clean directly
2582 + yield_the_processor(); // do not lock too aggressively
2583 page_set_clean(cache, pi->page, true, false);
2584 page_transition_unlock(cache, pi->page);
2585 page_release(cache, pi->page, true);
@@ -2616,7 +2614,7 @@ void pgc_open_cache_to_journal_v2(PGC *cache, Word_t section, unsigned datafile_
2614 aral_by_size_release(ar_pi);
2615 aral_by_size_release(ar_mi);
2616
2619 - __atomic_sub_fetch(&cache->stats.workers_jv2_flush, 1, __ATOMIC_RELAXED);
2617 + p2_sub_fetch(&cache->stats.p2_workers_jv2_flush, 1);
2618
2619 // balance-parents: do not flush, there is nothing dirty
2620 // flush_pages(cache, cache->config.max_flushes_inline, PGC_SECTION_ALL, false, false);
src/database/engine/cache.h
+64 -128
@@ -48,180 +48,116 @@ struct pgc_size_histogram {
48 };
49
50 struct pgc_queue_statistics {
51 - CACHE_LINE_PADDING();
51 struct pgc_size_histogram size_histogram;
52
54 - CACHE_LINE_PADDING();
55 - size_t entries;
56 - CACHE_LINE_PADDING();
57 - size_t size;
53 + PAD64(size_t) entries;
54 + PAD64(size_t) size;
55
59 - CACHE_LINE_PADDING();
60 - size_t max_entries;
61 - CACHE_LINE_PADDING();
62 - size_t max_size;
56 + PAD64(size_t) max_entries;
57 + PAD64(size_t) max_size;
58
64 - CACHE_LINE_PADDING();
65 - size_t added_entries;
66 - CACHE_LINE_PADDING();
67 - size_t added_size;
59 + PAD64(size_t) added_entries;
60 + PAD64(size_t) added_size;
61
69 - CACHE_LINE_PADDING();
70 - size_t removed_entries;
71 - CACHE_LINE_PADDING();
72 - size_t removed_size;
73 -
74 - CACHE_LINE_PADDING();
62 + PAD64(size_t) removed_entries;
63 + PAD64(size_t) removed_size;
64 };
65
66 struct pgc_statistics {
78 - CACHE_LINE_PADDING();
79 - size_t wanted_cache_size;
80 - CACHE_LINE_PADDING();
81 - size_t current_cache_size;
82 - CACHE_LINE_PADDING();
67 + PAD64(size_t) wanted_cache_size;
68 + PAD64(size_t) current_cache_size;
69
70 // ----------------------------------------------------------------------------------------------------------------
71 // volume
72
87 - CACHE_LINE_PADDING();
88 - size_t entries; // all the entries (includes clean, dirty, hot)
89 - CACHE_LINE_PADDING();
90 - size_t size; // all the entries (includes clean, dirty, hot)
73 + PAD64(size_t) entries; // all the entries (includes clean, dirty, hot)
74 + PAD64(size_t) size; // all the entries (includes clean, dirty, hot)
75
92 - CACHE_LINE_PADDING();
93 - size_t referenced_entries; // all the entries currently referenced
94 - CACHE_LINE_PADDING();
95 - size_t referenced_size; // all the entries currently referenced
76 + PAD64(size_t) referenced_entries; // all the entries currently referenced
77 + PAD64(size_t) referenced_size; // all the entries currently referenced
78
97 - CACHE_LINE_PADDING();
98 - size_t added_entries;
99 - CACHE_LINE_PADDING();
100 - size_t added_size;
79 + PAD64(size_t) added_entries;
80 + PAD64(size_t) added_size;
81
102 - CACHE_LINE_PADDING();
103 - size_t removed_entries;
104 - CACHE_LINE_PADDING();
105 - size_t removed_size;
82 + PAD64(size_t) removed_entries;
83 + PAD64(size_t) removed_size;
84
85 #ifdef PGC_COUNT_POINTS_COLLECTED
108 - CACHE_LINE_PADDING();
109 - size_t points_collected;
86 + PAD64(size_t) points_collected;
87 #endif
88
89 // ----------------------------------------------------------------------------------------------------------------
90 // migrations
91
115 - CACHE_LINE_PADDING();
116 - size_t evicting_entries;
117 - CACHE_LINE_PADDING();
118 - size_t evicting_size;
92 + PAD64(size_t) evicting_entries;
93 + PAD64(size_t) evicting_size;
94
120 - CACHE_LINE_PADDING();
121 - size_t flushing_entries;
122 - CACHE_LINE_PADDING();
123 - size_t flushing_size;
95 + PAD64(size_t) flushing_entries;
96 + PAD64(size_t) flushing_size;
97
125 - CACHE_LINE_PADDING();
126 - size_t hot2dirty_entries;
127 - CACHE_LINE_PADDING();
128 - size_t hot2dirty_size;
98 + PAD64(size_t) hot2dirty_entries;
99 + PAD64(size_t) hot2dirty_size;
100
130 - CACHE_LINE_PADDING();
131 - size_t hot_empty_pages_evicted_immediately;
132 - CACHE_LINE_PADDING();
133 - size_t hot_empty_pages_evicted_later;
101 + PAD64(size_t) hot_empty_pages_evicted_immediately;
102 + PAD64(size_t) hot_empty_pages_evicted_later;
103
104 // ----------------------------------------------------------------------------------------------------------------
105 // workload
106
138 - CACHE_LINE_PADDING();
139 - size_t acquires;
140 - CACHE_LINE_PADDING();
141 - size_t releases;
142 -
143 - CACHE_LINE_PADDING();
144 - size_t acquires_for_deletion;
145 -
146 - CACHE_LINE_PADDING();
147 - size_t searches_exact;
148 - CACHE_LINE_PADDING();
149 - size_t searches_exact_hits;
150 - CACHE_LINE_PADDING();
151 - size_t searches_exact_misses;
152 -
153 - CACHE_LINE_PADDING();
154 - size_t searches_closest;
155 - CACHE_LINE_PADDING();
156 - size_t searches_closest_hits;
157 - CACHE_LINE_PADDING();
158 - size_t searches_closest_misses;
159 -
160 - CACHE_LINE_PADDING();
161 - size_t flushes_completed;
162 - CACHE_LINE_PADDING();
163 - size_t flushes_completed_size;
164 - CACHE_LINE_PADDING();
165 - size_t flushes_cancelled_size;
107 + PAD64(size_t) acquires;
108 + PAD64(size_t) releases;
109 +
110 + PAD64(size_t) acquires_for_deletion;
111 +
112 + PAD64(size_t) searches_exact;
113 + PAD64(size_t) searches_exact_hits;
114 + PAD64(size_t) searches_exact_misses;
115 +
116 + PAD64(size_t) searches_closest;
117 + PAD64(size_t) searches_closest_hits;
118 + PAD64(size_t) searches_closest_misses;
119 +
120 + PAD64(size_t) flushes_completed;
121 + PAD64(size_t) flushes_completed_size;
122 + PAD64(size_t) flushes_cancelled_size;
123
124 // ----------------------------------------------------------------------------------------------------------------
125 // critical events
126
170 - CACHE_LINE_PADDING();
171 - size_t events_cache_under_severe_pressure;
172 - CACHE_LINE_PADDING();
173 - size_t events_cache_needs_space_aggressively;
174 - CACHE_LINE_PADDING();
175 - size_t events_flush_critical;
127 + PAD64(size_t) events_cache_under_severe_pressure;
128 + PAD64(size_t) events_cache_needs_space_aggressively;
129 + PAD64(size_t) events_flush_critical;
130
131 // ----------------------------------------------------------------------------------------------------------------
132 // worker threads
133
180 - CACHE_LINE_PADDING();
181 - size_t workers_search;
182 - CACHE_LINE_PADDING();
183 - size_t workers_add;
184 - CACHE_LINE_PADDING();
185 - size_t workers_evict;
186 - CACHE_LINE_PADDING();
187 - size_t workers_flush;
188 - CACHE_LINE_PADDING();
189 - size_t workers_jv2_flush;
190 - CACHE_LINE_PADDING();
191 - size_t workers_hot2dirty;
134 + PAD64(size_t) p2_workers_search;
135 + PAD64(size_t) p2_workers_add;
136 + PAD64(size_t) p0_workers_evict; // priority 0, we always need this when inline evictions are enabled
137 + PAD64(size_t) p2_workers_flush;
138 + PAD64(size_t) p2_workers_jv2_flush;
139 + PAD64(size_t) p2_workers_hot2dirty;
140
141 // ----------------------------------------------------------------------------------------------------------------
142 // waste events
143
144 // waste events - spins
197 - CACHE_LINE_PADDING();
198 - size_t waste_insert_spins;
199 - CACHE_LINE_PADDING();
200 - size_t waste_evict_useless_spins;
201 - CACHE_LINE_PADDING();
202 - size_t waste_release_spins;
203 - CACHE_LINE_PADDING();
204 - size_t waste_acquire_spins;
205 - CACHE_LINE_PADDING();
206 - size_t waste_delete_spins;
145 + PAD64(size_t) p2_waste_insert_spins;
146 + PAD64(size_t) p2_waste_evict_useless_spins;
147 + PAD64(size_t) p2_waste_release_spins;
148 + PAD64(size_t) p2_waste_acquire_spins;
149 + PAD64(size_t) p2_waste_delete_spins;
150
151 // waste events - eviction
209 - CACHE_LINE_PADDING();
210 - size_t waste_evict_relocated;
211 - CACHE_LINE_PADDING();
212 - size_t waste_evict_thread_signals;
213 - CACHE_LINE_PADDING();
214 - size_t waste_evictions_inline_on_add;
215 - CACHE_LINE_PADDING();
216 - size_t waste_evictions_inline_on_release;
152 + PAD64(size_t) p2_waste_evict_relocated;
153 + PAD64(size_t) p2_waste_evict_thread_signals;
154 + PAD64(size_t) p2_waste_evictions_inline_on_add;
155 + PAD64(size_t) p2_waste_evictions_inline_on_release;
156
157 // waste events - flushing
219 - CACHE_LINE_PADDING();
220 - size_t waste_flush_on_add;
221 - CACHE_LINE_PADDING();
222 - size_t waste_flush_on_release;
223 - CACHE_LINE_PADDING();
224 - size_t waste_flushes_cancelled;
158 + PAD64(size_t) p2_waste_flush_on_add;
159 + PAD64(size_t) p2_waste_flush_on_release;
160 + PAD64(size_t) p2_waste_flushes_cancelled;
161
162 // ----------------------------------------------------------------------------------------------------------------
163 // per queue statistics
src/database/engine/datafile.c
+1 -1
@@ -33,7 +33,7 @@ static struct rrdengine_datafile *datafile_alloc_and_init(struct rrdengine_insta
33
34 spinlock_init(&datafile->users.spinlock);
35 spinlock_init(&datafile->writers.spinlock);
36 - spinlock_init(&datafile->extent_queries.spinlock);
36 + rw_spinlock_init(&datafile->extent_epdl.spinlock);
37
38 return datafile;
39 }
src/database/engine/datafile.h
+9 -3
@@ -38,6 +38,12 @@ typedef enum __attribute__ ((__packed__)) {
38 DATAFILE_ACQUIRE_MAX,
39 } DATAFILE_ACQUIRE_REASONS;
40
41 +struct extent_page_details_list;
42 +typedef struct {
43 + SPINLOCK spinlock;
44 + struct extent_page_details_list *base;
45 +} EPDL_EXTENT;
46 +
47 /* only one event loop is supported for now */
48 struct rrdengine_datafile {
49 unsigned tier;
@@ -70,9 +76,9 @@ struct rrdengine_datafile {
76 } users;
77
78 struct {
73 - SPINLOCK spinlock;
74 - Pvoid_t pending_epdl_by_extent_offset_judyL;
75 - } extent_queries;
79 + RW_SPINLOCK spinlock;
80 + Pvoid_t epdl_per_extent;
81 + } extent_epdl;
82 };
83
84 bool datafile_acquire(struct rrdengine_datafile *df, DATAFILE_ACQUIRE_REASONS reason);
src/database/engine/dbengine-stresstest.c
+1 -1
@@ -18,7 +18,7 @@ static RRDHOST *dbengine_rrdhost_find_or_create(char *name) {
18 netdata_configured_utc_offset,
19 program_name,
20 NETDATA_VERSION,
21 - default_rrd_update_every,
21 + nd_profile.update_every,
22 default_rrd_history_entries,
23 RRD_MEMORY_MODE_DBENGINE,
24 health_plugin_enabled(),
src/database/engine/dbengine-unittest.c
+1 -1
@@ -104,7 +104,7 @@ static RRDHOST *dbengine_rrdhost_find_or_create(char *name) {
104 netdata_configured_utc_offset,
105 program_name,
106 NETDATA_VERSION,
107 - default_rrd_update_every,
107 + nd_profile.update_every,
108 default_rrd_history_entries,
109 RRD_MEMORY_MODE_DBENGINE,
110 health_plugin_enabled(),
src/database/engine/journalfile.c
+2 -2
@@ -296,7 +296,7 @@ static bool journalfile_v2_mounted_data_unmount(struct rrdengine_journalfile *jo
296 void journalfile_v2_data_unmount_cleanup(time_t now_s) {
297 // DO NOT WAIT ON ANY LOCK!!!
298
299 - for(size_t tier = 0; tier < (size_t)storage_tiers ;tier++) {
299 + for(size_t tier = 0; tier < (size_t)nd_profile.storage_tiers;tier++) {
300 struct rrdengine_instance *ctx = multidb_ctx[tier];
301 if(!ctx) continue;
302
@@ -1338,7 +1338,7 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1338 total_file_size += sizeof(struct journal_v2_block_trailer);
1339
1340 int fd_v2;
1341 - uint8_t *data_start = netdata_mmap(path, total_file_size, MAP_SHARED, 0, false, &fd_v2);
1341 + uint8_t *data_start = netdata_mmap(path, total_file_size, MAP_SHARED, 0, false, true, &fd_v2);
1342 uint8_t *data = data_start;
1343
1344 memset(data_start, 0, extent_offset);
src/database/engine/metric.c
+8 -7
@@ -5,7 +5,7 @@
5 #include "rrddiskprotocol.h"
6
7 typedef int32_t REFCOUNT;
8 -#define REFCOUNT_DELETING (-100)
8 +#define REFCOUNT_DICONNECTED (-100)
9
10 struct metric {
11 nd_uuid_t uuid; // never changes
@@ -192,7 +192,7 @@ static inline void acquired_for_deletion_metric_delete(MRG *mrg, METRIC *metric)
192
193 rc = JudyHSDel(&mrg->index[partition].uuid_judy, &metric->uuid, sizeof(nd_uuid_t), PJE0);
194
195 - int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
195 + judy_mem = JudyAllocThreadPulseGetAndReset();
196
197 if(unlikely(!rc))
198 fatal("DBENGINE METRIC: cannot delete UUID from JudyHS");
@@ -242,22 +242,22 @@ static inline bool metric_release(MRG *mrg, METRIC *metric) {
242 }
243
244 if(expected == 1 && !acquired_metric_has_retention(mrg, metric))
245 - desired = REFCOUNT_DELETING;
245 + desired = REFCOUNT_DICONNECTED;
246 else
247 desired = expected - 1;
248
249 } while(!__atomic_compare_exchange_n(&metric->refcount, &expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
250
251 - if(desired == 0 || desired == REFCOUNT_DELETING) {
251 + if(desired == 0 || desired == REFCOUNT_DICONNECTED) {
252 __atomic_sub_fetch(&mrg->index[partition].stats.entries_acquired, 1, __ATOMIC_RELAXED);
253
254 - if(desired == REFCOUNT_DELETING)
254 + if(desired == REFCOUNT_DICONNECTED)
255 acquired_for_deletion_metric_delete(mrg, metric);
256 }
257
258 __atomic_sub_fetch(&mrg->index[partition].stats.current_references, 1, __ATOMIC_RELAXED);
259
260 - return desired == REFCOUNT_DELETING;
260 + return desired == REFCOUNT_DICONNECTED;
261 }
262
263 static inline METRIC *metric_add_and_acquire(MRG *mrg, MRG_ENTRY *entry, bool *ret) {
@@ -386,7 +386,8 @@ inline MRG *mrg_create(ssize_t partitions) {
386 char buf[ARAL_MAX_NAME + 1];
387 snprintfz(buf, ARAL_MAX_NAME, "mrg[%zu]", i);
388
389 - mrg->index[i].aral = aral_create(buf, sizeof(METRIC), 0, 16384, &mrg_aral_statistics, NULL, NULL, false, false);
389 + mrg->index[i].aral = aral_create(buf, sizeof(METRIC), 0, 16384, &mrg_aral_statistics, NULL, NULL,
390 + false, false, true);
391 }
392 pulse_aral_register_statistics(&mrg_aral_statistics, "mrg");
393
src/database/engine/metric.h
+6 -13
@@ -30,21 +30,14 @@ struct mrg_statistics {
30
31 // --- atomic --- multiple readers / writers
32
33 - CACHE_LINE_PADDING();
34 - ssize_t entries_acquired;
33 + PAD64(ssize_t) entries_acquired;
34 + PAD64(ssize_t) current_references;
35
36 - CACHE_LINE_PADDING();
37 - ssize_t current_references;
36 + PAD64(size_t) search_hits;
37 + PAD64(size_t) search_misses;
38
39 - CACHE_LINE_PADDING();
40 - size_t search_hits;
41 - CACHE_LINE_PADDING();
42 - size_t search_misses;
43 -
44 - CACHE_LINE_PADDING();
45 - size_t writers;
46 - CACHE_LINE_PADDING();
47 - size_t writers_conflicts;
39 + PAD64(size_t) writers;
40 + PAD64(size_t) writers_conflicts;
41 };
42
43 MRG *mrg_create(ssize_t partitions);
src/database/engine/page.c
+17 -1
@@ -99,6 +99,13 @@ static size_t aral_sizes[] = {
99 // our structures
100 sizeof(gorilla_writer_t),
101 sizeof(PGD),
102 +
103 + 512, 1024, 1536, 2048, 5 * 512, 6 * 512, 7 * 512,
104 + 1 * 4096, 2 * 4096, 3 * 4096, 4 * 4096, 5 * 4096,
105 + 6 * 4096, 7 * 4096, 8 * 4096, 9 * 4096, 10 * 4096,
106 + 11 * 4096, 12 * 4096, 13 * 4096, 14 * 4096, 15 * 4096,
107 + 16 * 4096, 17 * 4096, 18 * 4096, 19 * 4096, 20 * 4096,
108 + 21 * 4096, 22 * 4096, 23 * 4096, 24 * 4096, 25 * 4096,
109 };
110 static ARAL **arals = NULL;
111
@@ -131,6 +138,15 @@ void pgd_init_arals(void) {
138 for(size_t i = 0; i < RRD_STORAGE_TIERS ;i++)
139 aral_sizes[i] = tier_page_size[i];
140
141 + if(!netdata_conf_is_parent()) {
142 + // this agent is not a parent
143 + // do not use ARAL for sizes above 4KiB
144 + for(size_t i = RRD_STORAGE_TIERS ; i < _countof(aral_sizes) ;i++) {
145 + if(aral_sizes[i] > 4096)
146 + aral_sizes[i] = 0;
147 + }
148 + }
149 +
150 size_t max_delta = 0;
151 for(size_t i = 0; i < aral_sizes_count ;i++) {
152 size_t wanted = aral_sizes[i];
@@ -181,7 +197,7 @@ void pgd_init_arals(void) {
197 0,
198 0,
199 &pgd_aral_statistics,
184 - NULL, NULL, false, false);
200 + NULL, NULL, false, false, true);
201 }
202 }
203
src/database/engine/pagecache.c
+40 -27
@@ -227,7 +227,7 @@ static size_t get_page_list_from_pgc(PGC *cache, METRIC *metric, struct rrdengin
227 uint32_t dt_s = mrg_metric_get_update_every_s(main_mrg, metric);
228
229 if(!dt_s)
230 - dt_s = default_rrd_update_every;
230 + dt_s = nd_profile.update_every;
231
232 time_t previous_page_end_time_s = now_s - dt_s;
233 bool first = true;
@@ -402,7 +402,7 @@ static size_t list_has_time_gaps(
402 time_t now_s = wanted_start_time_s;
403 time_t dt_s = mrg_metric_get_update_every_s(main_mrg, metric);
404 if(!dt_s)
405 - dt_s = default_rrd_update_every;
405 + dt_s = nd_profile.update_every;
406
407 size_t pages_pass2 = 0, pages_pass3 = 0;
408 while((pd = pdc_find_page_for_time(
@@ -656,7 +656,7 @@ static Pvoid_t get_page_list(
656 pages_total = 0;
657
658 size_t cache_gaps = 0, query_gaps = 0;
659 - bool done_v2 = false, done_open = false;
659 + bool done_v2 = false, done_open = false, done_pass4 = false;
660
661 usec_t pass1_ut = 0, pass2_ut = 0, pass3_ut = 0, pass4_ut = 0, finish_ut = 0;
662
@@ -721,28 +721,36 @@ static Pvoid_t get_page_list(
721 query_gaps = list_has_time_gaps(ctx, metric, JudyL_page_array, wanted_start_time_s, wanted_end_time_s,
722 &pages_total, &pages_found_pass4, pages_to_load_from_disk, &pages_overlapping,
723 optimal_end_time_s, true, common_status);
724 + done_pass4 = true;
725
726 we_are_done:
727 finish_ut = now_monotonic_usec();
727 - time_delta(finish_ut, pass4_ut);
728 - time_delta(finish_ut, pass3_ut);
729 - time_delta(finish_ut, pass2_ut);
730 - time_delta(finish_ut, pass1_ut);
731 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.prep_time_in_main_cache_lookup, pass1_ut, __ATOMIC_RELAXED);
732 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.prep_time_in_open_cache_lookup, pass2_ut, __ATOMIC_RELAXED);
733 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.prep_time_in_journal_v2_lookup, pass3_ut, __ATOMIC_RELAXED);
734 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.prep_time_in_pass4_lookup, pass4_ut, __ATOMIC_RELAXED);
735 -
736 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.queries, 1, __ATOMIC_RELAXED);
728 + time_delta(finish_ut, pass4_ut); // do not change the order
729 + time_delta(finish_ut, pass3_ut); // do not change the order
730 + time_delta(finish_ut, pass2_ut); // do not change the order
731 + time_delta(finish_ut, pass1_ut); // do not change the order
732 +
733 + time_and_count_add(&rrdeng_cache_efficiency_stats.prep_time_in_main_cache_lookup, pass1_ut);
734 +
735 + if(done_open) {
736 + time_and_count_add(&rrdeng_cache_efficiency_stats.prep_time_in_open_cache_lookup, pass2_ut);
737 + __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_meta_source_open_cache, pages_found_in_open_cache, __ATOMIC_RELAXED);
738 + }
739 +
740 + if(done_v2) {
741 + time_and_count_add(&rrdeng_cache_efficiency_stats.prep_time_in_journal_v2_lookup, pass3_ut);
742 + __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_meta_source_journal_v2, pages_found_in_journals_v2, __ATOMIC_RELAXED);
743 + }
744 +
745 + if(done_pass4) {
746 + time_and_count_add(&rrdeng_cache_efficiency_stats.prep_time_in_pass4_lookup, pass4_ut);
747 + __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_data_source_main_cache_at_pass4, pages_found_pass4, __ATOMIC_RELAXED);
748 + }
749 +
750 __atomic_add_fetch(&rrdeng_cache_efficiency_stats.queries_planned_with_gaps, (query_gaps) ? 1 : 0, __ATOMIC_RELAXED);
738 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.queries_open, done_open ? 1 : 0, __ATOMIC_RELAXED);
739 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.queries_journal_v2, done_v2 ? 1 : 0, __ATOMIC_RELAXED);
751 __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_total, pages_total, __ATOMIC_RELAXED);
752 __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_meta_source_main_cache, pages_found_in_main_cache, __ATOMIC_RELAXED);
742 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_meta_source_open_cache, pages_found_in_open_cache, __ATOMIC_RELAXED);
743 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_meta_source_journal_v2, pages_found_in_journals_v2, __ATOMIC_RELAXED);
753 __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_data_source_main_cache, pages_found_in_main_cache, __ATOMIC_RELAXED);
745 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_data_source_main_cache_at_pass4, pages_found_pass4, __ATOMIC_RELAXED);
754 __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_to_load_from_disk, *pages_to_load_from_disk, __ATOMIC_RELAXED);
755 __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_overlapping_skipped, pages_overlapping, __ATOMIC_RELAXED);
756
@@ -754,7 +762,7 @@ inline void rrdeng_prep_wait(PDC *pdc) {
762 usec_t started_ut = now_monotonic_usec();
763 completion_wait_for(&pdc->prep_completion);
764 pdc->prep_done = true;
757 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.query_time_wait_for_prep, now_monotonic_usec() - started_ut, __ATOMIC_RELAXED);
765 + time_and_count_add(&rrdeng_cache_efficiency_stats.query_time_wait_for_prep, now_monotonic_usec() - started_ut);
766 }
767 }
768
@@ -781,13 +789,18 @@ void rrdeng_prep_query(struct page_details_control *pdc, bool worker) {
789 if (pdc->pages_to_load_from_disk && pdc->page_list_JudyL) {
790 pdc_acquire(pdc); // we get 1 for the 1st worker in the chain: do_read_page_list_work()
791 usec_t start_ut = now_monotonic_usec();
784 - if(likely(pdc->priority == STORAGE_PRIORITY_SYNCHRONOUS))
792 + if(likely(pdc->priority == STORAGE_PRIORITY_SYNCHRONOUS)) {
793 pdc_route_synchronously(pdc->ctx, pdc);
786 - else if(likely(pdc->priority == STORAGE_PRIORITY_SYNCHRONOUS_FIRST))
794 + time_and_count_add(&rrdeng_cache_efficiency_stats.prep_time_to_route_sync, now_monotonic_usec() - start_ut);
795 + }
796 + else if(likely(pdc->priority == STORAGE_PRIORITY_SYNCHRONOUS_FIRST)) {
797 pdc_route_synchronously_first(pdc->ctx, pdc);
788 - else
798 + time_and_count_add(&rrdeng_cache_efficiency_stats.prep_time_to_route_syncfirst, now_monotonic_usec() - start_ut);
799 + }
800 + else {
801 pdc_route_asynchronously(pdc->ctx, pdc);
790 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.prep_time_to_route, now_monotonic_usec() - start_ut, __ATOMIC_RELAXED);
802 + time_and_count_add(&rrdeng_cache_efficiency_stats.prep_time_to_route_async, now_monotonic_usec() - start_ut);
803 + }
804 }
805 else
806 completion_mark_complete(&pdc->page_completion);
@@ -973,15 +986,15 @@ struct pgc_page *pg_cache_lookup_next(
986
987 if(waited) {
988 if(preloaded)
976 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.query_time_to_slow_preload_next_page, now_monotonic_usec() - start_ut, __ATOMIC_RELAXED);
989 + time_and_count_add(&rrdeng_cache_efficiency_stats.query_time_to_slow_preload_next_page, now_monotonic_usec() - start_ut);
990 else
978 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.query_time_to_slow_disk_next_page, now_monotonic_usec() - start_ut, __ATOMIC_RELAXED);
991 + time_and_count_add(&rrdeng_cache_efficiency_stats.query_time_to_slow_disk_next_page, now_monotonic_usec() - start_ut);
992 }
993 else {
994 if(preloaded)
982 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.query_time_to_fast_preload_next_page, now_monotonic_usec() - start_ut, __ATOMIC_RELAXED);
995 + time_and_count_add(&rrdeng_cache_efficiency_stats.query_time_to_fast_preload_next_page, now_monotonic_usec() - start_ut);
996 else
984 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.query_time_to_fast_disk_next_page, now_monotonic_usec() - start_ut, __ATOMIC_RELAXED);
997 + time_and_count_add(&rrdeng_cache_efficiency_stats.query_time_to_fast_disk_next_page, now_monotonic_usec() - start_ut);
998 }
999
1000 return page;
src/database/engine/pdc.c
+55 -30
@@ -55,7 +55,7 @@ void pdc_init(void) {
55 0,
56 0,
57 NULL,
58 - NULL, NULL, false, false
58 + NULL, NULL, false, false, true
59 );
60
61 pulse_aral_register(pdc_globals.pdc.ar, "pdc");
@@ -85,7 +85,7 @@ void page_details_init(void) {
85 0,
86 0,
87 NULL,
88 - NULL, NULL, false, false
88 + NULL, NULL, false, false, true
89 );
90 pulse_aral_register(pdc_globals.pd.ar, "pd");
91 }
@@ -114,7 +114,7 @@ void epdl_init(void) {
114 0,
115 0,
116 NULL,
117 - NULL, NULL, false, false
117 + NULL, NULL, false, false, true
118 );
119 pulse_aral_register(pdc_globals.epdl.ar, "epdl");
120 }
@@ -143,7 +143,7 @@ void deol_init(void) {
143 0,
144 0,
145 NULL,
146 - NULL, NULL, false, false
146 + NULL, NULL, false, false, true
147 );
148
149 pulse_aral_register(pdc_globals.deol.ar, "deol");
@@ -455,50 +455,75 @@ void epdl_cmd_dequeued(void *epdl_ptr) {
455 epdl->cmd = NULL;
456 }
457
458 -static struct rrdeng_cmd *epdl_get_cmd(void *epdl_ptr) {
458 +static inline struct rrdeng_cmd *epdl_get_cmd(void *epdl_ptr) {
459 EPDL *epdl = epdl_ptr;
460 return epdl->cmd;
461 }
462
463 -static bool epdl_pending_add(EPDL *epdl) {
464 - bool added_new;
463 +static EPDL_EXTENT *epdl_find_extent_base(EPDL *epdl) {
464 + EPDL_EXTENT *e = NULL;
465 + rw_spinlock_read_lock(&epdl->datafile->extent_epdl.spinlock);
466 + Pvoid_t *PValue = JudyLGet(epdl->datafile->extent_epdl.epdl_per_extent, epdl->extent_offset, PJE0);
467 + internal_fatal(PValue == PJERR, "DBENGINE: corrupted pending extent judy");
468 + if(PValue)
469 + e = *PValue;
470 + rw_spinlock_read_unlock(&epdl->datafile->extent_epdl.spinlock);
471 +
472 + if(!e) {
473 + EPDL_EXTENT *e_to_free = NULL;
474 + e = callocz(1, sizeof(*e));
475 +
476 + rw_spinlock_write_lock(&epdl->datafile->extent_epdl.spinlock);
477 + Pvoid_t *PValue = JudyLIns(&epdl->datafile->extent_epdl.epdl_per_extent, epdl->extent_offset, PJE0);
478 + internal_fatal(!PValue || PValue == PJERR, "DBENGINE: corrupted pending extent judy");
479 + if(!*PValue) {
480 + *PValue = e;
481 + spinlock_init(&e->spinlock);
482 + }
483 + else {
484 + e_to_free = e;
485 + e = *PValue;
486 + }
487 + rw_spinlock_write_unlock(&epdl->datafile->extent_epdl.spinlock);
488
466 - spinlock_lock(&epdl->datafile->extent_queries.spinlock);
467 - Pvoid_t *PValue = JudyLIns(&epdl->datafile->extent_queries.pending_epdl_by_extent_offset_judyL, epdl->extent_offset, PJE0);
468 - internal_fatal(!PValue || PValue == PJERR, "DBENGINE: corrupted pending extent judy");
489 + freez(e_to_free);
490 + }
491
470 - EPDL *base = *PValue;
492 + return e;
493 +}
494
472 - if(!base) {
473 - added_new = true;
474 - epdl->head_to_datafile_extent_queries_pending_for_extent = true;
475 - }
476 - else {
495 +static bool epdl_pending_add(EPDL *epdl) {
496 + EPDL_EXTENT *e = epdl_find_extent_base(epdl);
497 + spinlock_lock(&e->spinlock);
498 +
499 + bool added_new;
500 + if(unlikely(e->base)) {
501 added_new = false;
502 epdl->head_to_datafile_extent_queries_pending_for_extent = false;
503 +
504 __atomic_add_fetch(&rrdeng_cache_efficiency_stats.pages_load_extent_merged, 1, __ATOMIC_RELAXED);
505
481 - if(base->pdc->priority > epdl->pdc->priority)
482 - rrdeng_req_cmd(epdl_get_cmd, base, epdl->pdc->priority);
506 +// if(e->base->pdc->priority > epdl->pdc->priority) {
507 +// e->base->pdc->priority = epdl->pdc->priority;
508 +// rrdeng_req_cmd(epdl_get_cmd, e->base, epdl->pdc->priority);
509 +// }
510 + }
511 + else {
512 + added_new = true;
513 + epdl->head_to_datafile_extent_queries_pending_for_extent = true;
514 }
515
485 - DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(base, epdl, query.prev, query.next);
486 - *PValue = base;
487 -
488 - spinlock_unlock(&epdl->datafile->extent_queries.spinlock);
516 + DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(e->base, epdl, query.prev, query.next);
517 + spinlock_unlock(&e->spinlock);
518
519 return added_new;
520 }
521
522 static void epdl_pending_del(EPDL *epdl) {
494 - spinlock_lock(&epdl->datafile->extent_queries.spinlock);
495 - if(epdl->head_to_datafile_extent_queries_pending_for_extent) {
496 - epdl->head_to_datafile_extent_queries_pending_for_extent = false;
497 - int rc = JudyLDel(&epdl->datafile->extent_queries.pending_epdl_by_extent_offset_judyL, epdl->extent_offset, PJE0);
498 - (void) rc;
499 - internal_fatal(!rc, "DBENGINE: epdl not found in pending list");
500 - }
501 - spinlock_unlock(&epdl->datafile->extent_queries.spinlock);
523 + EPDL_EXTENT *e = epdl_find_extent_base(epdl);
524 + spinlock_lock(&e->spinlock);
525 + e->base = NULL;
526 + spinlock_unlock(&e->spinlock);
527 }
528
529 void pdc_to_epdl_router(struct rrdengine_instance *ctx, PDC *pdc, execute_extent_page_details_list_t exec_first_extent_list, execute_extent_page_details_list_t exec_rest_extent_list)
src/database/engine/rrdengine.c
+26 -10
@@ -142,7 +142,7 @@ static void work_request_init(void) {
142 0,
143 0,
144 NULL,
145 - NULL, NULL, false, false
145 + NULL, NULL, false, false, true
146 );
147
148 pulse_aral_register(rrdeng_main.work_cmd.ar, "workers");
@@ -263,7 +263,7 @@ void page_descriptors_init(void) {
263 0,
264 0,
265 NULL,
266 - NULL, NULL, false, false);
266 + NULL, NULL, false, false, true);
267
268 pulse_aral_register(rrdeng_main.descriptors.ar, "descriptors");
269 }
@@ -288,7 +288,7 @@ static void extent_io_descriptor_init(void) {
288 0,
289 0,
290 NULL,
291 - NULL, NULL, false, false
291 + NULL, NULL, false, false, true
292 );
293
294 pulse_aral_register(rrdeng_main.xt_io_descr.ar, "extent io");
@@ -314,7 +314,7 @@ void rrdeng_query_handle_init(void) {
314 0,
315 0,
316 NULL,
317 - NULL, NULL, false, false);
317 + NULL, NULL, false, false, true);
318
319 pulse_aral_register(rrdeng_main.handles.ar, "query handles");
320 }
@@ -359,7 +359,7 @@ static void wal_cleanup1(void) {
359 if(!spinlock_trylock(&wal_globals.protected.spinlock))
360 return;
361
362 - if(wal_globals.protected.available_items && wal_globals.protected.available > storage_tiers) {
362 + if(wal_globals.protected.available_items && wal_globals.protected.available > nd_profile.storage_tiers) {
363 wal = wal_globals.protected.available_items;
364 DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(wal_globals.protected.available_items, wal, cache.prev, cache.next);
365 wal_globals.protected.available--;
@@ -436,7 +436,7 @@ static void rrdeng_cmd_queue_init(void) {
436 0,
437 0,
438 NULL,
439 - NULL, NULL, false, false);
439 + NULL, NULL, false, false, true);
440
441 pulse_aral_register(rrdeng_main.cmd_queue.ar, "opcodes");
442 }
@@ -1289,6 +1289,22 @@ void datafile_delete(struct rrdengine_instance *ctx, struct rrdengine_datafile *
1289 netdata_log_info("DBENGINE: deleted data file \"%s\".", path);
1290 deleted_bytes += datafile_bytes;
1291 }
1292 +
1293 + {
1294 + rw_spinlock_write_lock(&datafile->extent_epdl.spinlock);
1295 + bool first = true;
1296 + Word_t idx = 0;
1297 + Pvoid_t *PValue;
1298 + while ((PValue = JudyLFirstThenNext(datafile->extent_epdl.epdl_per_extent, &idx, &first))) {
1299 + EPDL_EXTENT *e = *PValue;
1300 + internal_fatal(e->base, "The should not be any EPDLs ");
1301 + freez(e);
1302 + *PValue = NULL;
1303 + }
1304 + JudyLFreeArray(&datafile->extent_epdl.epdl_per_extent, PJE0);
1305 + rw_spinlock_write_unlock(&datafile->extent_epdl.spinlock);
1306 + }
1307 +
1308 freez(journal_file);
1309 freez(datafile);
1310
@@ -1694,7 +1710,7 @@ static void retention_timer_cb(uv_timer_t *handle) {
1710 uv_stop(handle->loop);
1711 uv_update_time(handle->loop);
1712
1697 - for (size_t tier = 0; tier < storage_tiers; tier++) {
1713 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
1714 STORAGE_ENGINE *eng = localhost->db[tier].eng;
1715 if (!eng || eng->seb != STORAGE_ENGINE_BACKEND_DBENGINE)
1716 continue;
@@ -1831,7 +1847,7 @@ void calculate_tier_disk_space_percentage(void)
1847 return;
1848
1849 uint64_t total_diskspace = 0;
1834 - for(size_t tier = 0; tier < storage_tiers ;tier++) {
1850 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
1851 STORAGE_ENGINE *eng = localhost->db[tier].eng;
1852 if (!eng || eng->seb != STORAGE_ENGINE_BACKEND_DBENGINE) {
1853 tier_space[tier] = 0;
@@ -1845,7 +1861,7 @@ void calculate_tier_disk_space_percentage(void)
1861 }
1862
1863 if (total_diskspace) {
1848 - for (size_t tier = 0; tier < storage_tiers; tier++) {
1864 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
1865 multidb_ctx[tier]->config.disk_percentage = (100 * tier_space[tier] / total_diskspace);
1866 }
1867 }
@@ -1861,7 +1877,7 @@ void dbengine_retention_statistics(void)
1877
1878 calculate_tier_disk_space_percentage();
1879
1864 - for (size_t tier = 0; tier < storage_tiers; tier++) {
1880 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
1881 STORAGE_ENGINE *eng = localhost->db[tier].eng;
1882 if (!eng || eng->seb != STORAGE_ENGINE_BACKEND_DBENGINE)
1883 continue;
src/database/engine/rrdengine.h
+39 -71
@@ -327,57 +327,38 @@ void wal_release(WAL *wal);
327 * They only describe operations since DB engine instance load time.
328 */
329 struct rrdengine_statistics {
330 - CACHE_LINE_PADDING();
331 - rrdeng_stats_t before_decompress_bytes;
332 - CACHE_LINE_PADDING();
333 - rrdeng_stats_t after_decompress_bytes;
334 - CACHE_LINE_PADDING();
335 - rrdeng_stats_t before_compress_bytes;
336 - CACHE_LINE_PADDING();
337 - rrdeng_stats_t after_compress_bytes;
338 -
339 - CACHE_LINE_PADDING();
340 - rrdeng_stats_t io_write_bytes;
341 - CACHE_LINE_PADDING();
342 - rrdeng_stats_t io_write_requests;
343 - CACHE_LINE_PADDING();
344 - rrdeng_stats_t io_read_bytes;
345 - CACHE_LINE_PADDING();
346 - rrdeng_stats_t io_read_requests;
347 -
348 - CACHE_LINE_PADDING();
349 - rrdeng_stats_t datafile_creations;
350 - CACHE_LINE_PADDING();
351 - rrdeng_stats_t datafile_deletions;
352 - CACHE_LINE_PADDING();
353 - rrdeng_stats_t journalfile_creations;
354 - CACHE_LINE_PADDING();
355 - rrdeng_stats_t journalfile_deletions;
356 -
357 - CACHE_LINE_PADDING();
358 - rrdeng_stats_t io_errors;
359 - CACHE_LINE_PADDING();
360 - rrdeng_stats_t fs_errors;
330 + PAD64(rrdeng_stats_t) before_decompress_bytes;
331 + PAD64(rrdeng_stats_t) after_decompress_bytes;
332 + PAD64(rrdeng_stats_t) before_compress_bytes;
333 + PAD64(rrdeng_stats_t) after_compress_bytes;
334 +
335 + PAD64(rrdeng_stats_t) io_write_bytes;
336 + PAD64(rrdeng_stats_t) io_write_requests;
337 + PAD64(rrdeng_stats_t) io_read_bytes;
338 + PAD64(rrdeng_stats_t) io_read_requests;
339 +
340 + PAD64(rrdeng_stats_t) datafile_creations;
341 + PAD64(rrdeng_stats_t) datafile_deletions;
342 + PAD64(rrdeng_stats_t) journalfile_creations;
343 + PAD64(rrdeng_stats_t) journalfile_deletions;
344 +
345 + PAD64(rrdeng_stats_t) io_errors;
346 + PAD64(rrdeng_stats_t) fs_errors;
347 };
348
349 struct rrdeng_global_stats {
364 - CACHE_LINE_PADDING();
350 /* I/O errors global counter */
366 - rrdeng_stats_t global_io_errors;
351 + PAD64(rrdeng_stats_t) global_io_errors;
352
368 - CACHE_LINE_PADDING();
353 /* File-System errors global counter */
370 - rrdeng_stats_t global_fs_errors;
354 + PAD64(rrdeng_stats_t) global_fs_errors;
355
372 - CACHE_LINE_PADDING();
356 /* number of File-Descriptors that have been reserved by dbengine */
374 - rrdeng_stats_t rrdeng_reserved_file_descriptors;
357 + PAD64(rrdeng_stats_t) rrdeng_reserved_file_descriptors;
358
376 - CACHE_LINE_PADDING();
359 /* inability to flush global counters */
378 - rrdeng_stats_t global_pg_cache_over_half_dirty_events;
379 - CACHE_LINE_PADDING();
380 - rrdeng_stats_t global_flushing_pressure_page_deletions; /* number of deleted pages */
360 + PAD64(rrdeng_stats_t) global_pg_cache_over_half_dirty_events;
361 + PAD64(rrdeng_stats_t) global_flushing_pressure_page_deletions; /* number of deleted pages */
362 };
363
364 extern struct rrdeng_global_stats global_stats;
@@ -413,36 +394,23 @@ struct rrdengine_instance {
394 } njfv2idx;
395
396 struct {
416 - CACHE_LINE_PADDING();
417 - unsigned last_fileno; // newest index of datafile and journalfile
418 - CACHE_LINE_PADDING();
419 - unsigned last_flush_fileno; // newest index of datafile received data
420 -
421 - CACHE_LINE_PADDING();
422 - size_t collectors_running;
423 - CACHE_LINE_PADDING();
424 - size_t collectors_running_duplicate;
425 - CACHE_LINE_PADDING();
426 - size_t inflight_queries; // the number of queries currently running
427 - CACHE_LINE_PADDING();
428 - uint64_t current_disk_space; // the current disk space size used
429 -
430 - CACHE_LINE_PADDING();
431 - uint64_t transaction_id; // the transaction id of the next extent flushing
432 -
433 - CACHE_LINE_PADDING();
434 - bool migration_to_v2_running;
435 - CACHE_LINE_PADDING();
436 - bool now_deleting_files;
437 - CACHE_LINE_PADDING();
438 - unsigned extents_currently_being_flushed; // non-zero until we commit data to disk (both datafile and journal file)
439 -
440 - CACHE_LINE_PADDING();
441 - time_t first_time_s;
442 - CACHE_LINE_PADDING();
443 - uint64_t metrics;
444 - CACHE_LINE_PADDING();
445 - uint64_t samples;
397 + PAD64(unsigned) last_fileno; // newest index of datafile and journalfile
398 + PAD64(unsigned) last_flush_fileno; // newest index of datafile received data
399 +
400 + PAD64(size_t) collectors_running;
401 + PAD64(size_t) collectors_running_duplicate;
402 + PAD64(size_t) inflight_queries; // the number of queries currently running
403 + PAD64(uint64_t) current_disk_space; // the current disk space size used
404 +
405 + PAD64(uint64_t) transaction_id; // the transaction id of the next extent flushing
406 +
407 + PAD64(bool) migration_to_v2_running;
408 + PAD64(bool) now_deleting_files;
409 + PAD64(unsigned) extents_currently_being_flushed; // non-zero until we commit data to disk (both datafile and journal file)
410 +
411 + PAD64(time_t) first_time_s;
412 + PAD64(uint64_t) metrics;
413 + PAD64(uint64_t) samples;
414 } atomic;
415
416 struct {
src/database/engine/rrdengineapi.c
+6 -6
@@ -761,8 +761,8 @@ void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *smh,
761
762 handle->dt_s = db_update_every_s;
763 if (!handle->dt_s) {
764 - handle->dt_s = default_rrd_update_every;
765 - mrg_metric_set_update_every_s_if_zero(main_mrg, metric, default_rrd_update_every);
764 + handle->dt_s = nd_profile.update_every;
765 + mrg_metric_set_update_every_s_if_zero(main_mrg, metric, nd_profile.update_every);
766 }
767
768 seqh->handle = (STORAGE_QUERY_HANDLE *) handle;
@@ -773,7 +773,7 @@ void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *smh,
773
774 pg_cache_preload(handle);
775
776 - __atomic_add_fetch(&rrdeng_cache_efficiency_stats.query_time_init, now_monotonic_usec() - started_ut, __ATOMIC_RELAXED);
776 + time_and_count_add(&rrdeng_cache_efficiency_stats.query_time_init, now_monotonic_usec() - started_ut);
777 }
778 else {
779 handle->start_time_s = start_time_s;
@@ -1065,7 +1065,7 @@ static void rrdeng_populate_mrg(struct rrdengine_instance *ctx) {
1065 datafiles++;
1066 uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
1067
1068 - ssize_t cpus = (ssize_t)netdata_conf_cpus() / (ssize_t)storage_tiers;
1068 + ssize_t cpus = (ssize_t)netdata_conf_cpus() / (ssize_t)nd_profile.storage_tiers;
1069 if(cpus > (ssize_t)datafiles)
1070 cpus = (ssize_t)datafiles;
1071
@@ -1293,7 +1293,7 @@ static void populate_v2_statistics(struct rrdengine_datafile *datafile, RRDENG_S
1293 if(likely(points > 1))
1294 update_every_s = (time_t) ((end_time_s - start_time_s) / (points - 1));
1295 else {
1296 - update_every_s = (time_t) (default_rrd_update_every * get_tier_grouping(datafile->ctx->config.tier));
1296 + update_every_s = (time_t) (nd_profile.update_every * get_tier_grouping(datafile->ctx->config.tier));
1297 stats->single_point_pages++;
1298 }
1299
@@ -1375,7 +1375,7 @@ RRDENG_SIZE_STATS rrdeng_size_statistics(struct rrdengine_instance *ctx) {
1375 // stats.sizeof_metric_in_index = 40;
1376 // stats.sizeof_page_in_index = 24;
1377
1378 - stats.default_granularity_secs = (size_t)default_rrd_update_every * get_tier_grouping(ctx->config.tier);
1378 + stats.default_granularity_secs = (size_t)nd_profile.update_every * get_tier_grouping(ctx->config.tier);
1379
1380 return stats;
1381 }
src/database/engine/rrdengineapi.h
+66 -57
@@ -128,83 +128,92 @@ typedef struct rrdengine_size_statistics {
128 double average_page_size_bytes;
129 } RRDENG_SIZE_STATS;
130
131 +struct time_and_count {
132 + size_t count;
133 + usec_t usec;
134 +};
135 +
136 +static inline void time_and_count_add(struct time_and_count *tc, usec_t dt) {
137 + __atomic_add_fetch(&tc->count, 1, __ATOMIC_RELAXED);
138 + __atomic_add_fetch(&tc->usec, dt, __ATOMIC_RELAXED);
139 +}
140 +
141 struct rrdeng_cache_efficiency_stats {
132 - size_t queries;
133 - size_t queries_planned_with_gaps;
134 - size_t queries_executed_with_gaps;
135 - size_t queries_open;
136 - size_t queries_journal_v2;
142 + PAD64(size_t) queries_planned_with_gaps;
143 + PAD64(size_t) queries_executed_with_gaps;
144
138 - size_t currently_running_queries;
145 + PAD64(size_t) currently_running_queries;
146
147 // query planner output of the queries
141 - size_t pages_total;
142 - size_t pages_to_load_from_disk;
143 - size_t extents_loaded_from_disk;
148 + PAD64(size_t) pages_total;
149 + PAD64(size_t) pages_to_load_from_disk;
150 + PAD64(size_t) extents_loaded_from_disk;
151
152 // pages metadata sources
146 - size_t pages_meta_source_main_cache;
147 - size_t pages_meta_source_open_cache;
148 - size_t pages_meta_source_journal_v2;
153 + PAD64(size_t) pages_meta_source_main_cache;
154 + PAD64(size_t) pages_meta_source_open_cache;
155 + PAD64(size_t) pages_meta_source_journal_v2;
156
157 // preloading
151 - size_t page_next_wait_failed;
152 - size_t page_next_wait_loaded;
153 - size_t page_next_nowait_failed;
154 - size_t page_next_nowait_loaded;
158 + PAD64(size_t) page_next_wait_failed;
159 + PAD64(size_t) page_next_wait_loaded;
160 + PAD64(size_t) page_next_nowait_failed;
161 + PAD64(size_t) page_next_nowait_loaded;
162
163 // pages data sources
157 - size_t pages_data_source_main_cache;
158 - size_t pages_data_source_main_cache_at_pass4;
159 - size_t pages_data_source_disk;
160 - size_t pages_data_source_extent_cache; // loaded by a cached extent
164 + PAD64(size_t) pages_data_source_main_cache;
165 + PAD64(size_t) pages_data_source_main_cache_at_pass4;
166 + PAD64(size_t) pages_data_source_disk;
167 + PAD64(size_t) pages_data_source_extent_cache; // loaded by a cached extent
168
169 // cache hits at different points
163 - size_t pages_load_ok_loaded_but_cache_hit_while_inserting; // found in cache while inserting it (conflict)
170 + PAD64(size_t) pages_load_ok_loaded_but_cache_hit_while_inserting; // found in cache while inserting it (conflict)
171
172 // loading
166 - size_t pages_load_extent_merged;
167 - size_t pages_load_ok_uncompressed;
168 - size_t pages_load_ok_compressed;
169 - size_t pages_load_fail_invalid_page_in_extent;
170 - size_t pages_load_fail_cant_mmap_extent;
171 - size_t pages_load_fail_datafile_not_available;
172 - size_t pages_load_fail_unroutable;
173 - size_t pages_load_fail_not_found;
174 - size_t pages_load_fail_invalid_extent;
175 - size_t pages_load_fail_cancelled;
176 -
177 - // timings for query preparation
178 - size_t prep_time_to_route;
179 - size_t prep_time_in_main_cache_lookup;
180 - size_t prep_time_in_open_cache_lookup;
181 - size_t prep_time_in_journal_v2_lookup;
182 - size_t prep_time_in_pass4_lookup;
173 + PAD64(size_t) pages_load_extent_merged;
174 + PAD64(size_t) pages_load_ok_uncompressed;
175 + PAD64(size_t) pages_load_ok_compressed;
176 + PAD64(size_t) pages_load_fail_invalid_page_in_extent;
177 + PAD64(size_t) pages_load_fail_cant_mmap_extent;
178 + PAD64(size_t) pages_load_fail_datafile_not_available;
179 + PAD64(size_t) pages_load_fail_unroutable;
180 + PAD64(size_t) pages_load_fail_not_found;
181 + PAD64(size_t) pages_load_fail_invalid_extent;
182 + PAD64(size_t) pages_load_fail_cancelled;
183 +
184 + // count of queries and times spent in them
185 + PAD64(struct time_and_count) prep_time_to_route_sync;
186 + PAD64(struct time_and_count) prep_time_to_route_syncfirst;
187 + PAD64(struct time_and_count) prep_time_to_route_async;
188 + PAD64(struct time_and_count) prep_time_in_main_cache_lookup;
189 + PAD64(struct time_and_count) prep_time_in_open_cache_lookup;
190 + PAD64(struct time_and_count) prep_time_in_journal_v2_lookup;
191 + PAD64(struct time_and_count) prep_time_in_pass4_lookup;
192
193 // timings the query thread experiences
185 - size_t query_time_init;
186 - size_t query_time_wait_for_prep;
187 - size_t query_time_to_slow_disk_next_page;
188 - size_t query_time_to_fast_disk_next_page;
189 - size_t query_time_to_slow_preload_next_page;
190 - size_t query_time_to_fast_preload_next_page;
194 + PAD64(struct time_and_count) query_time_init;
195 + PAD64(struct time_and_count) query_time_wait_for_prep;
196 + PAD64(struct time_and_count) query_time_to_slow_disk_next_page;
197 + PAD64(struct time_and_count) query_time_to_fast_disk_next_page;
198 + PAD64(struct time_and_count) query_time_to_slow_preload_next_page;
199 + PAD64(struct time_and_count) query_time_to_fast_preload_next_page;
200
201 // query issues
193 - size_t pages_zero_time_skipped;
194 - size_t pages_past_time_skipped;
195 - size_t pages_overlapping_skipped;
196 - size_t pages_invalid_size_skipped;
197 - size_t pages_invalid_update_every_fixed;
198 - size_t pages_invalid_entries_fixed;
202 + PAD64(size_t) pages_zero_time_skipped;
203 + PAD64(size_t) pages_past_time_skipped;
204 + PAD64(size_t) pages_overlapping_skipped;
205 + PAD64(size_t) pages_invalid_size_skipped;
206 + PAD64(size_t) pages_invalid_update_every_fixed;
207 + PAD64(size_t) pages_invalid_entries_fixed;
208
209 // database events
201 - size_t journal_v2_mapped;
202 - size_t journal_v2_unmapped;
203 - size_t datafile_creation_started;
204 - size_t datafile_deletion_started;
205 - size_t datafile_deletion_spin;
206 - size_t journal_v2_indexing_started;
207 - size_t metrics_retention_started;
210 + PAD64(size_t) journal_v2_mapped;
211 + PAD64(size_t) journal_v2_unmapped;
212 + PAD64(size_t) datafile_creation_started;
213 + PAD64(size_t) datafile_deletion_started;
214 + PAD64(size_t) datafile_deletion_spin;
215 + PAD64(size_t) journal_v2_indexing_started;
216 + PAD64(size_t) metrics_retention_started;
217 };
218
219 typedef enum rrdeng_mem {
src/database/ram/rrddim_mem.c
+4 -4
@@ -54,7 +54,7 @@ rrddim_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *si __maybe_unused) {
54 netdata_rwlock_wrlock(&rrddim_JudyHS_rwlock);
55 JudyAllocThreadPulseReset();
56 Pvoid_t *PValue = JudyHSIns(&rrddim_JudyHS_array, &rd->metric_uuid, sizeof(nd_uuid_t), PJE0);
57 - int64_t mem = JudyAllocThreadPulseGetAndReset();
57 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
58 mh = *PValue;
59 if(!mh) {
60 mh = callocz(1, sizeof(struct mem_metric_handle));
@@ -62,7 +62,7 @@ rrddim_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *si __maybe_unused) {
62 mh->refcount = 1;
63 update_metric_handle_from_rrddim(mh, rd);
64 *PValue = mh;
65 - pulse_db_rrd_memory_change(mem + (int64_t)sizeof(struct mem_metric_handle));
65 + pulse_db_rrd_memory_change(judy_mem + (int64_t)sizeof(struct mem_metric_handle));
66 }
67 else {
68 if(__atomic_add_fetch(&mh->refcount, 1, __ATOMIC_RELAXED) <= 0)
@@ -111,11 +111,11 @@ void rrddim_metric_release(STORAGE_METRIC_HANDLE *smh __maybe_unused) {
111 netdata_rwlock_wrlock(&rrddim_JudyHS_rwlock);
112 JudyAllocThreadPulseReset();
113 JudyHSDel(&rrddim_JudyHS_array, &rd->metric_uuid, sizeof(nd_uuid_t), PJE0);
114 - int64_t mem = JudyAllocThreadPulseGetAndReset();
114 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
115 netdata_rwlock_wrunlock(&rrddim_JudyHS_rwlock);
116
117 freez(mh);
118 - pulse_db_rrd_memory_change(mem - (int64_t)sizeof(struct mem_metric_handle));
118 + pulse_db_rrd_memory_change(judy_mem - (int64_t)sizeof(struct mem_metric_handle));
119 }
120 }
121 }
src/database/rrd.h
+50 -33
@@ -11,7 +11,7 @@ extern "C" {
11 #include "rrd-database-mode.h"
12 #include "streaming/stream-traffic-types.h"
13 #include "streaming/stream-sender-commit.h"
14 -#include "rrdhost-state-id.h"
14 +#include "streaming/stream-replication-tracking.h"
15 #include "health/health-alert-log.h"
16 #include "rrdhost-system-info.h"
17
@@ -115,11 +115,13 @@ typedef enum __attribute__ ((__packed__)) rrdset_flags {
115 RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS = (1 << 21), // the receiving side has replication in progress
116 RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED = (1 << 22), // the receiving side has completed replication
117
118 - RRDSET_FLAG_UPSTREAM_SEND_VARIABLES = (1 << 23), // a custom variable has been updated and needs to be exposed to parent
118 + RRDSET_FLAG_BACKFILLED_HIGH_TIERS = (1 << 23), // we have backfilled this chart
119
120 - RRDSET_FLAG_COLLECTION_FINISHED = (1 << 24), // when set, data collection is not available for this chart
120 + RRDSET_FLAG_UPSTREAM_SEND_VARIABLES = (1 << 24), // a custom variable has been updated and needs to be exposed to parent
121
122 - RRDSET_FLAG_HAS_RRDCALC_LINKED = (1 << 25), // this chart has at least one rrdcal linked
122 + RRDSET_FLAG_COLLECTION_FINISHED = (1 << 25), // when set, data collection is not available for this chart
123 +
124 + RRDSET_FLAG_HAS_RRDCALC_LINKED = (1 << 26), // this chart has at least one rrdcal linked
125 } RRDSET_FLAGS;
126
127 #include "daemon/common.h"
@@ -178,7 +180,7 @@ typedef enum __attribute__ ((__packed__)) {
180 RRD_BACKFILL_NEW
181 } RRD_BACKFILL;
182
181 -#define UPDATE_EVERY 1
183 +#define UPDATE_EVERY_MIN 1
184 #define UPDATE_EVERY_MAX 3600
185
186 #define RRD_DEFAULT_HISTORY_ENTRIES 3600
@@ -256,10 +258,11 @@ typedef enum __attribute__ ((__packed__)) rrddim_flags {
258 // this is 8 bit
259 } RRDDIM_FLAGS;
260
259 -#define rrddim_flag_get(rd) __atomic_load_n(&((rd)->flags), __ATOMIC_ACQUIRE)
260 -#define rrddim_flag_check(rd, flag) (__atomic_load_n(&((rd)->flags), __ATOMIC_ACQUIRE) & (flag))
261 -#define rrddim_flag_set(rd, flag) __atomic_or_fetch(&((rd)->flags), (flag), __ATOMIC_RELEASE)
262 -#define rrddim_flag_clear(rd, flag) __atomic_and_fetch(&((rd)->flags), ~(flag), __ATOMIC_RELEASE)
261 +#define rrddim_flag_get(rd) atomic_flags_get(&((rd)->flags))
262 +#define rrddim_flag_check(rd, flag) atomic_flags_check(&((rd)->flags), flag)
263 +#define rrddim_flag_set(rd, flag) atomic_flags_set(&((rd)->flags), flag)
264 +#define rrddim_flag_clear(rd, flag) atomic_flags_clear(&((rd)->flags), flag)
265 +#define rrddim_flag_set_and_clear(rd, set, clear) atomic_flags_set_and_clear(&((rd)->flags), set, clear)
266
267 // ----------------------------------------------------------------------------
268 // engine-specific iterator state for dimension data collection
@@ -490,7 +493,7 @@ static inline time_t storage_engine_global_first_time_s(STORAGE_ENGINE_BACKEND s
493 return rrdeng_global_first_time_s(si);
494 #endif
495
493 - return now_realtime_sec() - (time_t)(default_rrd_history_entries * default_rrd_update_every);
496 + return now_realtime_sec() - (time_t)(default_rrd_history_entries * nd_profile.update_every);
497 }
498
499 size_t rrdeng_currently_collected_metrics(STORAGE_INSTANCE *si);
@@ -679,10 +682,11 @@ STORAGE_ENGINE* storage_engine_find(const char* name);
682 // ----------------------------------------------------------------------------
683 // RRDSET - this is a chart
684
682 -#define rrdset_flag_get(st) __atomic_load_n(&((st)->flags), __ATOMIC_ACQUIRE)
683 -#define rrdset_flag_check(st, flag) (__atomic_load_n(&((st)->flags), __ATOMIC_ACQUIRE) & (flag))
684 -#define rrdset_flag_set(st, flag) __atomic_or_fetch(&((st)->flags), flag, __ATOMIC_RELEASE)
685 -#define rrdset_flag_clear(st, flag) __atomic_and_fetch(&((st)->flags), ~(flag), __ATOMIC_RELEASE)
685 +#define rrdset_flag_get(st) atomic_flags_get(&((st)->flags))
686 +#define rrdset_flag_check(st, flag) atomic_flags_check(&((st)->flags), flag)
687 +#define rrdset_flag_set(st, flag) atomic_flags_set(&((st)->flags), flag)
688 +#define rrdset_flag_clear(st, flag) atomic_flags_clear(&((st)->flags), flag)
689 +#define rrdset_flag_set_and_clear(st, set, clear) atomic_flags_set_and_clear(&((st)->flags), set, clear)
690
691 #define rrdset_is_replicating(st) (rrdset_flag_check(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS|RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS) \
692 && !rrdset_flag_check(st, RRDSET_FLAG_SENDER_REPLICATION_FINISHED|RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED))
@@ -772,9 +776,17 @@ struct rrdset {
776 uint32_t sent_version;
777 uint32_t chart_slot;
778 uint32_t dim_last_slot_used;
775 -
779 +#ifdef REPLICATION_TRACKING
780 + REPLAY_WHO who;
781 +#endif
782 time_t resync_time_s; // the timestamp up to which we should resync clock upstream
783 } snd;
784 +
785 + struct {
786 +#ifdef REPLICATION_TRACKING
787 + REPLAY_WHO who;
788 +#endif
789 + } rcv;
790 } stream;
791
792 // ------------------------------------------------------------------------
@@ -953,30 +965,31 @@ typedef enum __attribute__ ((__packed__)) rrdhost_flags {
965 // Health
966 RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION = (1 << 17), // contains charts and dims with uninitialized variables
967 RRDHOST_FLAG_INITIALIZED_HEALTH = (1 << 18), // the host has initialized health structures
956 - RRDHOST_FLAG_HEALTH_RUNNING_NOW = (1 << 19), // health is currently executing health check on this host
968
969 // Exporting
959 - RRDHOST_FLAG_EXPORTING_SEND = (1 << 20), // send it to external databases
960 - RRDHOST_FLAG_EXPORTING_DONT_SEND = (1 << 21), // don't send it to external databases
970 + RRDHOST_FLAG_EXPORTING_SEND = (1 << 19), // send it to external databases
971 + RRDHOST_FLAG_EXPORTING_DONT_SEND = (1 << 20), // don't send it to external databases
972
973 // ACLK
963 - RRDHOST_FLAG_ACLK_STREAM_CONTEXTS = (1 << 22), // when set, we should send ACLK stream context updates
964 - RRDHOST_FLAG_ACLK_STREAM_ALERTS = (1 << 23), // Host should stream alerts
974 + RRDHOST_FLAG_ACLK_STREAM_CONTEXTS = (1 << 21), // when set, we should send ACLK stream context updates
975 + RRDHOST_FLAG_ACLK_STREAM_ALERTS = (1 << 22), // Host should stream alerts
976
977 // Metadata
967 - RRDHOST_FLAG_METADATA_UPDATE = (1 << 24), // metadata needs to be stored in the database
968 - RRDHOST_FLAG_METADATA_LABELS = (1 << 25), // metadata needs to be stored in the database
969 - RRDHOST_FLAG_METADATA_INFO = (1 << 26), // metadata needs to be stored in the database
970 - RRDHOST_FLAG_PENDING_CONTEXT_LOAD = (1 << 27), // Context needs to be loaded
978 + RRDHOST_FLAG_METADATA_UPDATE = (1 << 23), // metadata needs to be stored in the database
979 + RRDHOST_FLAG_METADATA_LABELS = (1 << 24), // metadata needs to be stored in the database
980 + RRDHOST_FLAG_METADATA_INFO = (1 << 25), // metadata needs to be stored in the database
981 + RRDHOST_FLAG_PENDING_CONTEXT_LOAD = (1 << 26), // Context needs to be loaded
982
972 - RRDHOST_FLAG_METADATA_CLAIMID = (1 << 28), // metadata needs to be stored in the database
983 + RRDHOST_FLAG_METADATA_CLAIMID = (1 << 27), // metadata needs to be stored in the database
984
974 - RRDHOST_FLAG_GLOBAL_FUNCTIONS_UPDATED = (1 << 29), // set when the host has updated global functions
985 + RRDHOST_FLAG_GLOBAL_FUNCTIONS_UPDATED = (1 << 28), // set when the host has updated global functions
986 } RRDHOST_FLAGS;
987
977 -#define rrdhost_flag_check(host, flag) (__atomic_load_n(&((host)->flags), __ATOMIC_SEQ_CST) & (flag))
978 -#define rrdhost_flag_set(host, flag) __atomic_or_fetch(&((host)->flags), flag, __ATOMIC_SEQ_CST)
979 -#define rrdhost_flag_clear(host, flag) __atomic_and_fetch(&((host)->flags), ~(flag), __ATOMIC_SEQ_CST)
988 +#define rrdhost_flag_get(host) atomic_flags_get(&((host)->flags))
989 +#define rrdhost_flag_check(host, flag) atomic_flags_check(&((host)->flags), flag)
990 +#define rrdhost_flag_set(host, flag) atomic_flags_set(&((host)->flags), flag)
991 +#define rrdhost_flag_clear(host, flag) atomic_flags_clear(&((host)->flags), flag)
992 +#define rrdhost_flag_set_and_clear(host, set, clear) atomic_flags_set_and_clear(&((host)->flags), set, clear)
993
994 #ifdef NETDATA_INTERNAL_CHECKS
995 #define rrdset_debug(st, fmt, args...) do { if(unlikely(debug_flags & D_RRD_STATS && rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) \
@@ -1030,8 +1043,7 @@ struct rrdhost {
1043 STRING *program_name; // the program name that collects metrics for this host
1044 STRING *program_version; // the program version that collects metrics for this host
1045
1033 - REFCOUNT state_refcount;
1034 - RRDHOST_STATE state_id; // every time data collection (stream receiver) (dis)connects,
1046 + OBJECT_STATE state_id; // every time data collection (stream receiver) (dis)connects,
1047 // this gets incremented - it is used to detect stale functions,
1048 // stale backfilling requests, etc.
1049
@@ -1088,7 +1100,9 @@ struct rrdhost {
1100 uint32_t connections; // the number of times this sender has connected
1101
1102 struct {
1091 - size_t charts; // the number of charts currently being replicated to a parent
1103 + uint32_t counter_in; // counts the number of replication statements we have received
1104 + uint32_t counter_out; // counts the number of replication statements we have sent
1105 + uint32_t charts; // the number of charts currently being replicated to a parent
1106 } replication;
1107 } status;
1108
@@ -1123,7 +1137,10 @@ struct rrdhost {
1137 STREAM_HANDSHAKE exit_reason; // the last receiver exit reason
1138
1139 struct {
1126 - size_t charts; // the number of charts currently being replicated from a child
1140 + uint32_t counter_in; // counts the number of replication statements we have received
1141 + uint32_t counter_out; // counts the number of replication statements we have sent
1142 + uint32_t backfill_pending; // the number of replication requests pending on us
1143 + uint32_t charts; // the number of charts currently being replicated from a child
1144 NETDATA_DOUBLE percent; // the % of replication completion
1145 } replication;
1146 } status;
src/database/rrddim.c
+11 -11
@@ -62,7 +62,7 @@ static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v
62 size_t entries = st->db.entries;
63 if(!entries) entries = 5;
64
65 - rd->db.data = netdata_mmap(NULL, entries * sizeof(storage_number), MAP_PRIVATE, 1, false, NULL);
65 + rd->db.data = netdata_mmap(NULL, entries * sizeof(storage_number), MAP_PRIVATE, 1, false, true, NULL);
66 if(rd->db.data) {
67 rd->db.memsize = entries * sizeof(storage_number);
68 pulse_db_rrd_memory_add(rd->db.memsize);
@@ -90,7 +90,7 @@ static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v
90 // initialize the db tiers
91 {
92 size_t initialized = 0;
93 - for(size_t tier = 0; tier < storage_tiers ; tier++) {
93 + for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
94 STORAGE_ENGINE *eng = host->db[tier].eng;
95 rd->tiers[tier].seb = eng->seb;
96 rd->tiers[tier].tier_grouping = host->db[tier].tier_grouping;
@@ -112,7 +112,7 @@ static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v
112 // initialize data collection for all tiers
113 {
114 size_t initialized = 0;
115 - for (size_t tier = 0; tier < storage_tiers; tier++) {
115 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
116 if (rd->tiers[tier].smh) {
117 rd->tiers[tier].sch =
118 storage_metric_store_init(rd->tiers[tier].seb, rd->tiers[tier].smh, st->rrdhost->db[tier].tier_grouping * st->update_every, rd->rrdset->smg[tier]);
@@ -174,7 +174,7 @@ bool rrddim_finalize_collection_and_check_retention(RRDDIM *rd) {
174
175 size_t tiers_available = 0, tiers_said_no_retention = 0;
176
177 - for(size_t tier = 0; tier < storage_tiers ;tier++) {
177 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
178 spinlock_lock(&rd->tiers[tier].spinlock);
179
180 if(rd->tiers[tier].sch) {
@@ -212,7 +212,7 @@ static void rrddim_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, v
212 metaqueue_delete_dimension_uuid(&rd->metric_uuid);
213 }
214
215 - for(size_t tier = 0; tier < storage_tiers ;tier++) {
215 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
216 spinlock_lock(&rd->tiers[tier].spinlock);
217 if(rd->tiers[tier].smh) {
218 STORAGE_ENGINE *eng = host->db[tier].eng;
@@ -249,7 +249,7 @@ static bool rrddim_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused,
249 rc += rrddim_set_multiplier(st, rd, ctr->multiplier);
250 rc += rrddim_set_divisor(st, rd, ctr->divisor);
251
252 - for(size_t tier = 0; tier < storage_tiers ;tier++) {
252 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
253 if (!rd->tiers[tier].sch)
254 rd->tiers[tier].sch =
255 storage_metric_store_init(rd->tiers[tier].seb, rd->tiers[tier].smh, st->rrdhost->db[tier].tier_grouping * st->update_every, rd->rrdset->smg[tier]);
@@ -283,7 +283,7 @@ static void rrddim_react_callback(const DICTIONARY_ITEM *item __maybe_unused, vo
283 }
284
285 size_t rrddim_size(void) {
286 - return sizeof(RRDDIM) + storage_tiers * sizeof(struct rrddim_tier);
286 + return sizeof(RRDDIM) + nd_profile.storage_tiers * sizeof(struct rrddim_tier);
287 }
288
289 void rrddim_index_init(RRDSET *st) {
@@ -406,7 +406,7 @@ inline int rrddim_set_divisor(RRDSET *st, RRDDIM *rd, int32_t divisor) {
406 // ----------------------------------------------------------------------------
407
408 time_t rrddim_last_entry_s_of_tier(RRDDIM *rd, size_t tier) {
409 - if(unlikely(tier > storage_tiers || !rd->tiers[tier].smh))
409 + if(unlikely(tier > nd_profile.storage_tiers || !rd->tiers[tier].smh))
410 return 0;
411
412 return storage_engine_latest_time_s(rd->tiers[tier].seb, rd->tiers[tier].smh);
@@ -416,7 +416,7 @@ time_t rrddim_last_entry_s_of_tier(RRDDIM *rd, size_t tier) {
416 time_t rrddim_last_entry_s(RRDDIM *rd) {
417 time_t latest_time_s = rrddim_last_entry_s_of_tier(rd, 0);
418
419 - for(size_t tier = 1; tier < storage_tiers ;tier++) {
419 + for(size_t tier = 1; tier < nd_profile.storage_tiers;tier++) {
420 if(unlikely(!rd->tiers[tier].smh)) continue;
421
422 time_t t = rrddim_last_entry_s_of_tier(rd, tier);
@@ -428,7 +428,7 @@ time_t rrddim_last_entry_s(RRDDIM *rd) {
428 }
429
430 time_t rrddim_first_entry_s_of_tier(RRDDIM *rd, size_t tier) {
431 - if(unlikely(tier > storage_tiers || !rd->tiers[tier].smh))
431 + if(unlikely(tier > nd_profile.storage_tiers || !rd->tiers[tier].smh))
432 return 0;
433
434 return storage_engine_oldest_time_s(rd->tiers[tier].seb, rd->tiers[tier].smh);
@@ -437,7 +437,7 @@ time_t rrddim_first_entry_s_of_tier(RRDDIM *rd, size_t tier) {
437 time_t rrddim_first_entry_s(RRDDIM *rd) {
438 time_t oldest_time_s = 0;
439
440 - for(size_t tier = 0; tier < storage_tiers ;tier++) {
440 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
441 time_t t = rrddim_first_entry_s_of_tier(rd, tier);
442 if(t != 0 && (oldest_time_s == 0 || t < oldest_time_s))
443 oldest_time_s = t;
src/database/rrdfunctions-internals.h
+1 -1
@@ -29,7 +29,7 @@ struct rrd_host_function {
29 rrd_function_execute_cb_t execute_cb;
30 void *execute_cb_data;
31
32 - RRDHOST_STATE rrdhost_state_id;
32 + OBJECT_STATE_ID rrdhost_state_id;
33 struct rrd_collector *collector;
34 };
35
src/database/rrdfunctions.c
+6 -6
@@ -20,7 +20,7 @@ static void rrd_functions_insert_callback(const DICTIONARY_ITEM *item __maybe_un
20
21 rrd_collector_started();
22 rdcf->collector = rrd_collector_acquire_current_thread();
23 - rdcf->rrdhost_state_id = rrdhost_state_id(host);
23 + rdcf->rrdhost_state_id = object_state_id(&host->state_id);
24
25 if(!rdcf->priority)
26 rdcf->priority = RRDFUNCTIONS_PRIORITY_DEFAULT;
@@ -58,14 +58,14 @@ static bool rrd_functions_conflict_callback(const DICTIONARY_ITEM *item __maybe_
58 changed = true;
59 }
60
61 - if(rdcf->rrdhost_state_id != rrdhost_state_id(host)) {
61 + if(rdcf->rrdhost_state_id != object_state_id(&host->state_id)) {
62 nd_log(NDLS_DAEMON, NDLP_DEBUG,
63 "FUNCTIONS: function '%s' of host '%s' changed state id from %u to %u",
64 dictionary_acquired_item_name(item), rrdhost_hostname(host),
65 rdcf->rrdhost_state_id,
66 - rrdhost_state_id(host));
66 + object_state_id(&host->state_id));
67
68 - rdcf->rrdhost_state_id = rrdhost_state_id(host);
68 + rdcf->rrdhost_state_id = object_state_id(&host->state_id);
69 changed = true;
70 }
71
@@ -272,7 +272,7 @@ int rrd_functions_find_by_name(RRDHOST *host, BUFFER *wb, const char *name, size
272 strncpyz(buffer, name, sizeof(buffer) - 1);
273 char *s = NULL;
274
275 - RRDHOST_STATE state_id = rrdhost_state_id(host);
275 + OBJECT_STATE_ID state_id = object_state_id(&host->state_id);
276
277 bool found = false;
278 *item = NULL;
@@ -341,7 +341,7 @@ bool rrd_function_available(RRDHOST *host, const char *function) {
341 const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(host->functions, function);
342 if(item) {
343 struct rrd_host_function *rdcf = dictionary_acquired_item_value(item);
344 - if(rrd_collector_running(rdcf->collector) && rdcf->rrdhost_state_id == rrdhost_state_id(host))
344 + if(rrd_collector_running(rdcf->collector) && rdcf->rrdhost_state_id == object_state_id(&host->state_id))
345 ret = true;
346
347 dictionary_acquired_item_release(host->functions, item);
src/database/rrdhost-state-id.c deleted
-73
@@ -1,73 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "rrdhost-state-id.h"
4 -#include "rrd.h"
5 -
6 -RRDHOST_STATE rrdhost_state_id(struct rrdhost *host) {
7 - return __atomic_load_n(&host->state_id, __ATOMIC_RELAXED);
8 -}
9 -
10 -bool rrdhost_state_connected(RRDHOST *host) {
11 - __atomic_add_fetch(&host->state_id, 1, __ATOMIC_RELAXED);
12 -
13 - int32_t expected = __atomic_load_n(&host->state_refcount, __ATOMIC_RELAXED);
14 - int32_t desired;
15 -
16 - do {
17 - if(expected >= 0) {
18 - internal_fatal(true, "Cannot get the node connected");
19 - return false;
20 - }
21 -
22 - desired = 0;
23 -
24 - } while(!__atomic_compare_exchange_n(
25 - &host->state_refcount, &expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
26 -
27 - return true;
28 -}
29 -
30 -bool rrdhost_state_disconnected(RRDHOST *host) {
31 - __atomic_add_fetch(&host->state_id, 1, __ATOMIC_RELAXED);
32 -
33 - int32_t expected = __atomic_load_n(&host->state_refcount, __ATOMIC_RELAXED);
34 - int32_t desired;
35 -
36 - do {
37 - if(expected < 0) {
38 - internal_fatal(true, "Cannot get the node disconnected");
39 - return false;
40 - }
41 -
42 - desired = -1;
43 -
44 - } while(!__atomic_compare_exchange_n(
45 - &host->state_refcount, &expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
46 -
47 - return true;
48 -}
49 -
50 -bool rrdhost_state_acquire(RRDHOST *host, RRDHOST_STATE wanted_state_id) {
51 - int32_t expected = __atomic_load_n(&host->state_refcount, __ATOMIC_RELAXED);
52 - int32_t desired;
53 -
54 - do {
55 - if(expected < 0)
56 - return false;
57 -
58 - desired = expected + 1;
59 -
60 - } while(!__atomic_compare_exchange_n(
61 - &host->state_refcount, &expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
62 -
63 - if(rrdhost_state_id(host) != wanted_state_id) {
64 - rrdhost_state_release(host);
65 - return false;
66 - }
67 -
68 - return true;
69 -}
70 -
71 -void rrdhost_state_release(RRDHOST *host) {
72 - __atomic_sub_fetch(&host->state_refcount, 1, __ATOMIC_RELAXED);
73 -}
src/database/rrdhost-state-id.h deleted
-19
@@ -1,19 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_RRDHOST_STATE_ID_H
4 -#define NETDATA_RRDHOST_STATE_ID_H
5 -
6 -#include "libnetdata/libnetdata.h"
7 -
8 -typedef uint32_t RRDHOST_STATE;
9 -
10 -struct rrdhost;
11 -RRDHOST_STATE rrdhost_state_id(struct rrdhost *host);
12 -
13 -bool rrdhost_state_connected(struct rrdhost *host);
14 -bool rrdhost_state_disconnected(struct rrdhost *host);
15 -
16 -bool rrdhost_state_acquire(struct rrdhost *host, RRDHOST_STATE wanted_state_id);
17 -void rrdhost_state_release(struct rrdhost *host);
18 -
19 -#endif //NETDATA_RRDHOST_STATE_ID_H
src/database/rrdhost-system-info.c
+1
@@ -4,6 +4,7 @@
4 #include "rrdhost-system-info.h"
5 #include "daemon/common.h"
6 #include "aclk/schema-wrappers/node_info.h"
7 +#include "daemon/win_system-info.h"
8
9 // coverity[ +tainted_string_sanitize_content : arg-0 ]
10 static inline void coverity_remove_taint(char *s __maybe_unused) { }
src/database/rrdhost.c
+18 -17
@@ -327,7 +327,7 @@ static RRDHOST *rrdhost_create(
327 }
328
329 RRDHOST *host = callocz(1, sizeof(RRDHOST));
330 - host->state_refcount = -1;
330 + host->state_id = OBJECT_STATE_INIT_DEACTIVATED;
331
332 __atomic_add_fetch(&netdata_buffers_statistics.rrdhost_allocations_size, sizeof(RRDHOST), __ATOMIC_RELAXED);
333
@@ -394,7 +394,7 @@ static RRDHOST *rrdhost_create(
394 return NULL;
395 }
396 else {
397 - for(size_t tier = 0; tier < storage_tiers ; tier++) {
397 + for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
398 host->db[tier].mode = RRD_MEMORY_MODE_DBENGINE;
399 host->db[tier].eng = storage_engine_get(host->db[tier].mode);
400 host->db[tier].si = (STORAGE_INSTANCE *)multidb_ctx[tier];
@@ -413,7 +413,7 @@ static RRDHOST *rrdhost_create(
413
414 #ifdef ENABLE_DBENGINE
415 // the first tier is reserved for the non-dbengine modes
416 - for(size_t tier = 1; tier < storage_tiers ; tier++) {
416 + for(size_t tier = 1; tier < nd_profile.storage_tiers; tier++) {
417 host->db[tier].mode = RRD_MEMORY_MODE_DBENGINE;
418 host->db[tier].eng = storage_engine_get(host->db[tier].mode);
419 host->db[tier].si = (STORAGE_INSTANCE *) multidb_ctx[tier];
@@ -750,13 +750,14 @@ RRDHOST *rrdhost_find_or_create(
750
751 bool rrdhost_should_be_removed(RRDHOST *host, RRDHOST *protected_host, time_t now_s) {
752 if(host != protected_host
753 - && host != localhost
754 - && rrdhost_receiver_replicating_charts(host) == 0
755 - && rrdhost_sender_replicating_charts(host) == 0
756 - && rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN)
757 - && !rrdhost_flag_check(host, RRDHOST_FLAG_PENDING_CONTEXT_LOAD | RRDHOST_FLAG_HEALTH_RUNNING_NOW | RRDHOST_FLAG_COLLECTOR_ONLINE)
758 - && host->stream.rcv.status.last_disconnected
759 - && host->stream.rcv.status.last_disconnected + rrdhost_free_orphan_time_s < now_s)
753 + && host != localhost
754 + && rrdhost_receiver_replicating_charts(host) == 0
755 + && rrdhost_sender_replicating_charts(host) == 0
756 + && rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN)
757 + && !rrdhost_flag_check(host, RRDHOST_FLAG_PENDING_CONTEXT_LOAD | RRDHOST_FLAG_COLLECTOR_ONLINE)
758 + && health_evloop_current_iteration() - rrdhost_health_evloop_last_iteration(host) > 10
759 + && host->stream.rcv.status.last_disconnected
760 + && host->stream.rcv.status.last_disconnected + rrdhost_free_orphan_time_s < now_s)
761 return true;
762
763 return false;
@@ -801,15 +802,15 @@ int rrd_init(const char *hostname, struct rrdhost_system_info *system_info, bool
802 netdata_conf_dbengine_init(hostname);
803 }
804 else
804 - storage_tiers = 1;
805 + nd_profile.storage_tiers = 1;
806
807 if (!dbengine_enabled) {
807 - if (storage_tiers > 1) {
808 + if (nd_profile.storage_tiers > 1) {
809 nd_log(NDLS_DAEMON, NDLP_WARNING,
810 "dbengine is not enabled, but %zu tiers have been requested. Resetting tiers to 1",
810 - storage_tiers);
811 + nd_profile.storage_tiers);
812
812 - storage_tiers = 1;
813 + nd_profile.storage_tiers = 1;
814 }
815
816 if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) {
@@ -835,8 +836,8 @@ int rrd_init(const char *hostname, struct rrdhost_system_info *system_info, bool
836 , netdata_configured_utc_offset
837 , program_name
838 , NETDATA_VERSION
838 - , default_rrd_update_every
839 - , default_rrd_history_entries
839 + ,
840 + nd_profile.update_every, default_rrd_history_entries
841 , default_rrd_memory_mode
842 , health_plugin_enabled()
843 , stream_send.enabled
@@ -855,7 +856,7 @@ int rrd_init(const char *hostname, struct rrdhost_system_info *system_info, bool
856 return 1;
857
858 rrdhost_flag_set(localhost, RRDHOST_FLAG_COLLECTOR_ONLINE);
858 - rrdhost_state_connected(localhost);
859 + object_state_activate(&localhost->state_id);
860
861 ml_host_start(localhost);
862 dyncfg_host_init(localhost);
src/database/rrdlabels.c
+2 -2
@@ -70,7 +70,7 @@ static inline void STATS_PLUS_MEMORY(struct dictionary_stats *stats, int64_t jud
70 __atomic_fetch_add(&stats->memory.index, judy_mem, __ATOMIC_RELAXED);
71
72 if(item_size)
73 - __atomic_fetch_add(&stats->memory.dict, (long)item_size, __ATOMIC_RELAXED);
73 + __atomic_fetch_add(&stats->memory.dict, (ssize_t)item_size, __ATOMIC_RELAXED);
74
75 if(value_size)
76 __atomic_fetch_add(&stats->memory.values, (long)value_size, __ATOMIC_RELAXED);
@@ -81,7 +81,7 @@ static inline void STATS_MINUS_MEMORY(struct dictionary_stats *stats, int64_t ju
81 __atomic_fetch_add(&stats->memory.index, judy_mem, __ATOMIC_RELAXED);
82
83 if(item_size)
84 - __atomic_fetch_sub(&stats->memory.dict, (long)item_size, __ATOMIC_RELAXED);
84 + __atomic_fetch_sub(&stats->memory.dict, (ssize_t)item_size, __ATOMIC_RELAXED);
85
86 if(value_size)
87 __atomic_fetch_sub(&stats->memory.values, (long)value_size, __ATOMIC_RELAXED);
src/database/rrdset.c
+6 -6
@@ -271,7 +271,7 @@ static void rrdset_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v
271
272 // initialize the db tiers
273 {
274 - for(size_t tier = 0; tier < storage_tiers ; tier++) {
274 + for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
275 STORAGE_ENGINE *eng = st->rrdhost->db[tier].eng;
276 if(!eng) continue;
277
@@ -318,7 +318,7 @@ void rrdset_finalize_collection(RRDSET *st, bool dimensions_too) {
318 rrddim_foreach_done(rd);
319 }
320
321 - for(size_t tier = 0; tier < storage_tiers ; tier++) {
321 + for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
322 STORAGE_ENGINE *eng = st->rrdhost->db[tier].eng;
323 if(!eng) continue;
324
@@ -672,7 +672,7 @@ time_t rrdset_first_entry_s(RRDSET *st) {
672 }
673
674 time_t rrdset_first_entry_s_of_tier(RRDSET *st, size_t tier) {
675 - if(unlikely(tier > storage_tiers))
675 + if(unlikely(tier > nd_profile.storage_tiers))
676 return 0;
677
678 RRDDIM *rd;
@@ -843,7 +843,7 @@ void rrdset_reset(RRDSET *st) {
843 rd->collector.counter = 0;
844
845 if(!rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) {
846 - for(size_t tier = 0; tier < storage_tiers ;tier++)
846 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++)
847 storage_engine_store_flush(rd->tiers[tier].sch);
848 }
849 }
@@ -1273,7 +1273,7 @@ void rrddim_store_metric(RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n,
1273 .flags = flags
1274 };
1275
1276 - for(size_t tier = 1; tier < storage_tiers ;tier++) {
1276 + for(size_t tier = 1; tier < nd_profile.storage_tiers;tier++) {
1277 if(unlikely(!rd->tiers[tier].smh)) continue;
1278
1279 struct rrddim_tier *t = &rd->tiers[tier];
@@ -2020,7 +2020,7 @@ time_t rrdset_set_update_every_s(RRDSET *st, time_t update_every_s) {
2020 // switch update every to the storage engine
2021 RRDDIM *rd;
2022 rrddim_foreach_read(rd, st) {
2023 - for (size_t tier = 0; tier < storage_tiers; tier++) {
2023 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
2024 if (rd->tiers[tier].sch)
2025 storage_engine_store_change_collection_frequency(
2026 rd->tiers[tier].sch,
src/database/sqlite/sqlite_aclk_node.c
+62 -16
@@ -105,13 +105,19 @@ void aclk_check_node_info_and_collectors(void)
105 return;
106
107 size_t context_loading = 0;
108 - size_t replicating = 0;
108 + size_t replicating_rcv = 0;
109 + size_t replicating_snd = 0;
110 size_t context_pp = 0;
111
112 STRING *context_loading_host = NULL;
112 - STRING *replicating_host = NULL;
113 + STRING *replicating_rcv_host = NULL;
114 + STRING *replicating_snd_host = NULL;
115 STRING *context_pp_host = NULL;
114 -
116 +
117 +#ifdef REPLICATION_TRACKING
118 + struct replay_who_counters replay_counters = { 0 };
119 +#endif
120 +
121 time_t now = now_realtime_sec();
122 dfe_start_reentrant(rrdhost_root_index, host)
123 {
@@ -129,13 +135,29 @@ void aclk_check_node_info_and_collectors(void)
135 if (!wc->node_info_send_time && !wc->node_collectors_send)
136 continue;
137
138 + bool replicating = false;
139 +
140 if (unlikely(rrdhost_receiver_replicating_charts(host))) {
133 - internal_error(true, "ACLK SYNC: Host %s is still replicating", rrdhost_hostname(host));
134 - replicating++;
135 - replicating_host = host->hostname;
136 - continue;
141 + internal_error(true, "ACLK SYNC: Host %s is still replicating in", rrdhost_hostname(host));
142 + replicating_rcv++;
143 + replicating_rcv_host = host->hostname;
144 + replicating = true;
145 + }
146 +
147 + if (unlikely(rrdhost_sender_replicating_charts(host))) {
148 + internal_error(true, "ACLK SYNC: Host %s is still replicating out", rrdhost_hostname(host));
149 + replicating_snd++;
150 + replicating_snd_host = host->hostname;
151 + replicating = true;
152 }
153
154 +#ifdef REPLICATION_TRACKING
155 + replication_tracking_counters(host, &replay_counters);
156 +#endif
157 +
158 + if(replicating)
159 + continue;
160 +
161 bool pp_queue_empty = !(host->rrdctx.pp_queue && dictionary_entries(host->rrdctx.pp_queue));
162
163 if (!pp_queue_empty && (wc->node_info_send_time || wc->node_collectors_send)) {
@@ -158,31 +180,55 @@ void aclk_check_node_info_and_collectors(void)
180 }
181 dfe_done(host);
182
161 - if (context_loading || replicating || context_pp) {
183 + if (context_loading || replicating_rcv || replicating_snd || context_pp) {
184 +#ifdef REPLICATION_TRACKING
185 + char replay_counters_txt[1024];
186 + snprintfz(replay_counters_txt, sizeof(replay_counters_txt),
187 + " - REPLAY WHO RCV { %zu unknown, %zu me, %zu them, %zu finished } - "
188 + "REPLAY WHO SND { %zu unknown, %zu me, %zu them, %zu finished }",
189 + replay_counters.rcv[REPLAY_WHO_UNKNOWN], replay_counters.rcv[REPLAY_WHO_ME], replay_counters.rcv[REPLAY_WHO_THEM], replay_counters.rcv[REPLAY_WHO_FINISHED],
190 + replay_counters.snd[REPLAY_WHO_UNKNOWN], replay_counters.snd[REPLAY_WHO_ME], replay_counters.snd[REPLAY_WHO_THEM], replay_counters.snd[REPLAY_WHO_FINISHED]
191 + );
192 +#else
193 + char *replay_counters_txt = "";
194 +#endif
195 +
196 const char *context_loading_pre = "", *context_loading_body = "", *context_loading_post = "";
197 if(context_loading == 1) {
198 context_loading_pre = " (host '";
199 context_loading_body = string2str(context_loading_host);
200 context_loading_post = "')";
201 }
168 - const char *replicating_pre = "", *replicating_body = "", *replicating_post = "";
169 - if(replicating == 1) {
170 - replicating_pre = " (host '";
171 - replicating_body = string2str(replicating_host);
172 - replicating_post = "')";
202 +
203 + const char *replicating_rcv_pre = "", *replicating_rcv_body = "", *replicating_rcv_post = "";
204 + if(replicating_rcv == 1) {
205 + replicating_rcv_pre = " (host '";
206 + replicating_rcv_body = string2str(replicating_rcv_host);
207 + replicating_rcv_post = "')";
208 + }
209 +
210 + const char *replicating_snd_pre = "", *replicating_snd_body = "", *replicating_snd_post = "";
211 + if(replicating_snd == 1) {
212 + replicating_snd_pre = " (host '";
213 + replicating_snd_body = string2str(replicating_snd_host);
214 + replicating_snd_post = "')";
215 }
216 +
217 const char *context_pp_pre = "", *context_pp_body = "", *context_pp_post = "";
218 if(context_pp == 1) {
219 context_pp_pre = " (host '";
220 context_pp_body = string2str(context_pp_host);
221 context_pp_post = "')";
222 }
223 +
224 nd_log_limit_static_thread_var(erl, 10, 100 * USEC_PER_MS);
225 nd_log_limit(&erl, NDLS_DAEMON, NDLP_INFO,
182 - "%zu nodes loading contexts%s%s%s, %zu replicating data%s%s%s, %zu pending context post processing%s%s%s",
226 + "NODES INFO: %zu nodes loading contexts%s%s%s, %zu receiving replication%s%s%s, %zu sending replication%s%s%s, %zu pending context post processing%s%s%s%s",
227 context_loading, context_loading_pre, context_loading_body, context_loading_post,
184 - replicating, replicating_pre, replicating_body, replicating_post,
185 - context_pp, context_pp_pre, context_pp_body, context_pp_post
228 + replicating_rcv, replicating_rcv_pre, replicating_rcv_body, replicating_rcv_post,
229 + replicating_snd, replicating_snd_pre, replicating_snd_body, replicating_snd_post,
230 + context_pp, context_pp_pre, context_pp_body, context_pp_post,
231 + replay_counters_txt
232 );
233 }
234 }
src/database/sqlite/sqlite_metadata.c
+1 -1
@@ -1093,7 +1093,7 @@ static bool dimension_can_be_deleted(nd_uuid_t *dim_uuid __maybe_unused, sqlite3
1093 #ifdef ENABLE_DBENGINE
1094 if(dbengine_enabled) {
1095 bool no_retention = true;
1096 - for (size_t tier = 0; tier < storage_tiers; tier++) {
1096 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
1097 if (!multidb_ctx[tier])
1098 continue;
1099 time_t first_time_t = 0, last_time_t = 0;
src/exporting/send_data.c
+1 -1
@@ -289,7 +289,7 @@ void simple_connector_worker(void *instance_p)
289
290 if (exporting_tls_is_enabled(instance->config.type, options) && sock != -1) {
291 if (netdata_ssl_exporting_ctx) {
292 - if (sock_delnonblock(sock) < 0)
292 + if (sock_setnonblock(sock, false) != 0)
293 netdata_log_error("Exporting cannot remove the non-blocking flag from socket %d", sock);
294
295 if(netdata_ssl_open(&connector_specific_data->ssl, netdata_ssl_exporting_ctx, sock)) {
src/health/health-alert-log.h
+1
@@ -22,6 +22,7 @@ typedef struct health {
22 bool enabled; // 1 when this host has health enabled
23 bool use_summary_for_notifications; // whether to use the summary field as a subject for notifications
24 int32_t pending_transitions; // pending alert transitions to store
25 + uint64_t evloop_iteration; // the last health iteration that evaluated this host
26 } HEALTH;
27
28 #endif //NETDATA_HEALTH_ALERT_LOG_H
src/health/health.h
+4
@@ -99,4 +99,8 @@ int alert_variable_lookup_trace(RRDHOST *host, RRDSET *st, const char *variable,
99 typedef void (*prototype_metadata_cb_t)(void *data, STRING *type, STRING *component, STRING *classification, STRING *recipient);
100 void health_prototype_metadata_foreach(void *data, prototype_metadata_cb_t cb);
101
102 +uint64_t health_evloop_current_iteration(void);
103 +void rrdhost_set_health_evloop_iteration(RRDHOST *host);
104 +uint64_t rrdhost_health_evloop_last_iteration(RRDHOST *host);
105 +
106 #endif //NETDATA_HEALTH_H
src/health/health_event_loop.c
+24 -9
@@ -19,6 +19,21 @@
19 #error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 10
20 #endif
21
22 +static uint64_t health_evloop_iteration = 0;
23 +
24 +uint64_t health_evloop_current_iteration(void) {
25 + return __atomic_load_n(&health_evloop_iteration, __ATOMIC_RELAXED);
26 +}
27 +
28 +uint64_t rrdhost_health_evloop_last_iteration(RRDHOST *host) {
29 + return __atomic_load_n(&host->health.evloop_iteration, __ATOMIC_RELAXED);
30 +}
31 +
32 +void rrdhost_set_health_evloop_iteration(RRDHOST *host) {
33 + __atomic_store_n(&host->health.evloop_iteration,
34 + health_evloop_current_iteration(), __ATOMIC_RELAXED);
35 +}
36 +
37 // ----------------------------------------------------------------------------
38 // health main thread and friends
39
@@ -90,18 +105,20 @@ static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run)
105 return 1;
106 }
107
93 -static void health_sleep(time_t next_run, unsigned int loop __maybe_unused) {
108 +static void health_sleep(time_t next_run, uint64_t loop) {
109 time_t now = now_realtime_sec();
110 if(now < next_run) {
111 worker_is_idle();
97 - netdata_log_debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs", loop, (int) (next_run - now));
112 + netdata_log_debug(D_HEALTH, "Health monitoring iteration no %llu done. Next iteration in %d secs",
113 + (unsigned long long)loop, (int) (next_run - now));
114 while (now < next_run && service_running(SERVICE_HEALTH)) {
115 sleep_usec(USEC_PER_SEC);
116 now = now_realtime_sec();
117 }
118 }
119 else {
104 - netdata_log_debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration now", loop);
120 + netdata_log_debug(D_HEALTH, "Health monitoring iteration no %llu done. Next iteration now",
121 + (unsigned long long)loop);
122 }
123 }
124
@@ -215,6 +232,8 @@ static void health_event_loop_for_host(RRDHOST *host, bool apply_hibernation_del
232 if(unlikely(!rrdhost_should_run_health(host)))
233 return;
234
235 + rrdhost_set_health_evloop_iteration(host);
236 +
237 //#define rrdhost_pending_alert_transitions(host) (__atomic_load_n(&((host)->aclk_config.alert_transition.pending), __ATOMIC_RELAXED))
238
239 if (unlikely(__atomic_load_n(&host->health.pending_transitions, __ATOMIC_RELAXED))) {
@@ -625,7 +644,6 @@ static void health_event_loop_for_host(RRDHOST *host, bool apply_hibernation_del
644 }
645
646 static void health_event_loop(void) {
628 - unsigned int loop = 0;
647
648 while(service_running(SERVICE_HEALTH)) {
649 if(!stream_control_health_should_be_running()) {
@@ -634,9 +652,6 @@ static void health_event_loop(void) {
652 continue;
653 }
654
637 - loop++;
638 - netdata_log_debug(D_HEALTH, "Health monitoring iteration no %u started", loop);
639 -
655 time_t now = now_realtime_sec();
656 bool apply_hibernation_delay = false;
657 time_t next_run = now + health_globals.config.run_at_least_every_seconds;
@@ -661,14 +676,14 @@ static void health_event_loop(void) {
676 }
677
678 worker_is_busy(WORKER_HEALTH_JOB_RRD_LOCK);
679 + uint64_t loop = __atomic_add_fetch(&health_evloop_iteration, 1, __ATOMIC_RELAXED);
680 +
681 RRDHOST *host;
682 dfe_start_reentrant(rrdhost_root_index, host) {
683 if(unlikely(!service_running(SERVICE_HEALTH)))
684 break;
685
669 - rrdhost_flag_set(host, RRDHOST_FLAG_HEALTH_RUNNING_NOW);
686 health_event_loop_for_host(host, apply_hibernation_delay, now, &next_run);
671 - rrdhost_flag_clear(host, RRDHOST_FLAG_HEALTH_RUNNING_NOW);
687 }
688 dfe_done(host);
689
src/health/health_log.c
+11 -5
@@ -66,7 +66,10 @@ void health_log_alert_transition_with_trace(RRDHOST *host, ALARM_ENTRY *ae, int
66 break;
67
68 case RRDCALC_STATUS_CLEAR:
69 - priority = NDLP_INFO;
69 + if(ae->old_status == RRDCALC_STATUS_UNINITIALIZED)
70 + priority = NDLP_DEBUG;
71 + else
72 + priority = NDLP_INFO;
73 break;
74
75 case RRDCALC_STATUS_WARNING:
@@ -81,10 +84,13 @@ void health_log_alert_transition_with_trace(RRDHOST *host, ALARM_ENTRY *ae, int
84 }
85
86 netdata_logger(NDLS_HEALTH, priority, file, function, line,
84 - "ALERT '%s' of instance '%s' on node '%s', transitioned from %s to %s",
85 - string2str(ae->name), string2str(ae->chart), string2str(host->hostname),
86 - rrdcalc_status2string(ae->old_status), rrdcalc_status2string(ae->new_status)
87 - );
87 + "ALERT '%s' of instance '%s' on node '%s', transitioned from %s to %s.\n"
88 + "%s value got from %f %s, to %f %s.",
89 + string2str(ae->name), string2str(ae->chart), string2str(host->hostname),
90 + rrdcalc_status2string(ae->old_status), rrdcalc_status2string(ae->new_status),
91 + string2str(ae->name),
92 + ae->old_value, string2str(ae->units),
93 + ae->new_value, string2str(ae->units));
94 }
95
96 // ----------------------------------------------------------------------------
src/libnetdata/aral/aral.c
+92 -56
@@ -56,27 +56,29 @@ typedef struct aral_page {
56 struct {
57 SPINLOCK spinlock;
58 ARAL_FREE *list;
59 - } free;
59 + } available;
60 +
61 + struct {
62 + SPINLOCK spinlock;
63 + ARAL_FREE *list;
64 + } incoming;
65
66 } ARAL_PAGE;
67
68 typedef enum {
69 ARAL_LOCKLESS = (1 << 0),
70 ARAL_ALLOCATED_STATS = (1 << 1),
71 + ARAL_DONT_DUMP = (1 << 2),
72 } ARAL_OPTIONS;
73
74 struct aral_ops {
75 struct {
70 - CACHE_LINE_PADDING();
71 - size_t allocators; // the number of threads currently trying to allocate memory
72 - CACHE_LINE_PADDING();
73 - size_t deallocators; // the number of threads currently trying to deallocate memory
74 - CACHE_LINE_PADDING();
75 - bool last_allocated_or_deallocated; // stability detector, true when was last allocated
76 + PAD64(size_t) allocators; // the number of threads currently trying to allocate memory
77 + PAD64(size_t) deallocators; // the number of threads currently trying to deallocate memory
78 + PAD64(bool) last_allocated_or_deallocated; // stability detector, true when was last allocated
79 } atomic;
80
81 struct {
79 - CACHE_LINE_PADDING();
82 SPINLOCK spinlock;
83 size_t allocating_elements; // currently allocating elements
84 size_t allocation_size; // current / next allocation size
@@ -201,14 +203,24 @@ static inline void aral_unlock_with_trace(ARAL *ar, const char *func) {
203 #define aral_lock(ar) aral_lock_with_trace(ar, __FUNCTION__)
204 #define aral_unlock(ar) aral_unlock_with_trace(ar, __FUNCTION__)
205
204 -static inline void aral_page_free_lock(ARAL *ar, ARAL_PAGE *page) {
206 +static inline void aral_page_available_lock(ARAL *ar, ARAL_PAGE *page) {
207 + if(likely(!(ar->config.options & ARAL_LOCKLESS)))
208 + spinlock_lock(&page->available.spinlock);
209 +}
210 +
211 +static inline void aral_page_available_unlock(ARAL *ar, ARAL_PAGE *page) {
212 if(likely(!(ar->config.options & ARAL_LOCKLESS)))
206 - spinlock_lock(&page->free.spinlock);
213 + spinlock_unlock(&page->available.spinlock);
214 }
215
209 -static inline void aral_page_free_unlock(ARAL *ar, ARAL_PAGE *page) {
216 +static inline void aral_page_incoming_lock(ARAL *ar, ARAL_PAGE *page) {
217 if(likely(!(ar->config.options & ARAL_LOCKLESS)))
211 - spinlock_unlock(&page->free.spinlock);
218 + spinlock_lock(&page->incoming.spinlock);
219 +}
220 +
221 +static inline void aral_page_incoming_unlock(ARAL *ar, ARAL_PAGE *page) {
222 + if(likely(!(ar->config.options & ARAL_LOCKLESS)))
223 + spinlock_unlock(&page->incoming.spinlock);
224 }
225
226 static inline bool aral_adders_trylock(ARAL *ar, bool marked) {
@@ -490,7 +502,7 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
502 page->filename = strdupz(filename);
503 page->mapped = true;
504
493 - page->data = netdata_mmap(page->filename, size, MAP_SHARED, 0, false, NULL);
505 + page->data = netdata_mmap(page->filename, size, MAP_SHARED, 0, false, ar->config.options & ARAL_DONT_DUMP, NULL);
506 if (unlikely(!page->data))
507 fatal("ARAL: '%s' cannot allocate aral buffer of size %zu on filename '%s'",
508 ar->config.name, size, page->filename);
@@ -512,7 +524,7 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
524
525 if (size >= ARAL_MMAP_PAGES_ABOVE) {
526 bool mapped;
515 - uint8_t *ptr = netdata_mmap(NULL, size, MAP_PRIVATE, 1, false, NULL);
527 + uint8_t *ptr = netdata_mmap(NULL, size, MAP_PRIVATE, 1, false, ar->config.options & ARAL_DONT_DUMP, NULL);
528 if (ptr) {
529 mapped = true;
530 stats = &ar->stats->mmap;
@@ -539,7 +551,8 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
551 }
552 #endif
553
542 - spinlock_init(&page->free.spinlock);
554 + spinlock_init(&page->available.spinlock);
555 + spinlock_init(&page->incoming.spinlock);
556 page->size = size;
557 page->max_elements = aral_elements_in_page_size(ar, page->size);
558 page->aral_lock.free_elements = page->max_elements;
@@ -560,7 +573,7 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
573
574 fr->size = page->max_elements * ar->config.element_size;
575 fr->next = NULL;
563 - page->free.list = fr;
576 + page->available.list = fr;
577
578 aral_free_validate_internal_check(ar, fr);
579
@@ -741,37 +754,31 @@ static inline ARAL_PAGE *aral_get_first_page_with_a_free_slot(ARAL *ar, bool mar
754 return page;
755 }
756
744 -void *aral_callocz_internal(ARAL *ar, bool marked TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
745 - void *r = aral_mallocz_internal(ar, marked TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS);
746 - memset(r, 0, ar->config.requested_element_size);
747 - return r;
748 -}
749 -
750 -void *aral_mallocz_internal(ARAL *ar, bool marked TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
751 -#if defined(FSANITIZE_ADDRESS)
752 - return mallocz(ar->config.requested_element_size);
753 -#endif
754 -
755 - // reserve a slot on a free page
756 - ARAL_PAGE *page = aral_get_first_page_with_a_free_slot(ar, marked TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS);
757 - // the page returned has reserved a slot for us
757 +static void *aral_get_free_slot___no_lock_required(ARAL *ar, ARAL_PAGE *page, bool marked) {
758 + aral_page_available_lock(ar, page);
759
759 - aral_page_free_lock(ar, page);
760 + if(!page->available.list) {
761 + aral_page_incoming_lock(ar, page);
762 + page->available.list = page->incoming.list;
763 + page->incoming.list = NULL;
764 + aral_page_incoming_unlock(ar, page);
765 + }
766
761 - internal_fatal(!page->free.list,
762 - "ARAL: '%s' free item to use, cannot be NULL.", ar->config.name);
767 + ARAL_FREE *found_fr;
768 + found_fr = page->available.list;
769
764 - internal_fatal(page->free.list->size < ar->config.element_size,
765 - "ARAL: '%s' free item size %zu, cannot be smaller than %zu",
766 - ar->config.name, page->free.list->size, ar->config.element_size);
770 + internal_fatal(!found_fr,
771 + "ARAL: '%s' incoming free list, cannot be NULL.", ar->config.name);
772
768 - ARAL_FREE *found_fr = page->free.list;
773 + internal_fatal(found_fr->size < ar->config.element_size,
774 + "ARAL: '%s' free element size %zu, cannot be smaller than %zu",
775 + ar->config.name, page->available.list->size, ar->config.element_size);
776
777 // check if the remaining size (after we use this slot) is not enough for another element
778 if(unlikely(found_fr->size - ar->config.element_size < ar->config.element_size)) {
779 // we can use the entire free space entry
780
774 - page->free.list = found_fr->next;
781 + page->available.list = found_fr->next;
782 }
783 else {
784 // we can split the free space entry
@@ -783,12 +790,12 @@ void *aral_mallocz_internal(ARAL *ar, bool marked TRACE_ALLOCATIONS_FUNCTION_DEF
790
791 // link the free slot first in the page
792 fr->next = found_fr->next;
786 - page->free.list = fr;
793 + page->available.list = fr;
794
795 aral_free_validate_internal_check(ar, fr);
796 }
797
791 - aral_page_free_unlock(ar, page);
798 + aral_page_available_unlock(ar, page);
799
800 // put the page pointer after the element
801 aral_set_page_pointer_after_element___do_NOT_have_aral_lock(ar, page, found_fr, marked);
@@ -798,9 +805,44 @@ void *aral_mallocz_internal(ARAL *ar, bool marked TRACE_ALLOCATIONS_FUNCTION_DEF
805 else
806 __atomic_add_fetch(&ar->stats->malloc.used_bytes, ar->config.requested_element_size, __ATOMIC_RELAXED);
807
801 - internal_fatal((uintptr_t)found_fr % SYSTEM_REQUIRED_ALIGNMENT != 0, "Pointer is not aligned properly");
808 + return found_fr;
809 +}
810 +
811 +static inline void aral_add_free_slot___no_lock_required(ARAL *ar, ARAL_PAGE *page, void *ptr) {
812 + ARAL_FREE *fr = (ARAL_FREE *)ptr;
813 + fr->size = ar->config.element_size;
814 +
815 + aral_page_incoming_lock(ar, page);
816 + fr->next = page->incoming.list;
817 + page->incoming.list = fr;
818 + aral_page_incoming_unlock(ar, page);
819 +
820 + if(unlikely(ar->config.mmap.enabled || page->mapped))
821 + __atomic_sub_fetch(&ar->stats->mmap.used_bytes, ar->config.requested_element_size, __ATOMIC_RELAXED);
822 + else
823 + __atomic_sub_fetch(&ar->stats->malloc.used_bytes, ar->config.requested_element_size, __ATOMIC_RELAXED);
824 +}
825 +
826 +void *aral_callocz_internal(ARAL *ar, bool marked TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
827 + void *r = aral_mallocz_internal(ar, marked TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS);
828 + memset(r, 0, ar->config.requested_element_size);
829 + return r;
830 +}
831 +
832 +void *aral_mallocz_internal(ARAL *ar, bool marked TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
833 +#if defined(FSANITIZE_ADDRESS)
834 + return mallocz(ar->config.requested_element_size);
835 +#endif
836 +
837 + // reserve a slot on a free page
838 + ARAL_PAGE *page = aral_get_first_page_with_a_free_slot(ar, marked TRACE_ALLOCATIONS_FUNCTION_CALL_PARAMS);
839 + // the page returned has reserved a slot for us
840 +
841 + void *data = aral_get_free_slot___no_lock_required(ar, page, marked);
842 +
843 + internal_fatal((uintptr_t)data % SYSTEM_REQUIRED_ALIGNMENT != 0, "Pointer is not aligned properly");
844
803 - return (void *)found_fr;
845 + return data;
846 }
847
848 // returns true if it moved the page to the unmarked list
@@ -869,13 +911,7 @@ void aral_freez_internal(ARAL *ar, void *ptr TRACE_ALLOCATIONS_FUNCTION_DEFINITI
911 __atomic_sub_fetch(&ar->stats->malloc.used_bytes, ar->config.requested_element_size, __ATOMIC_RELAXED);
912
913 // make this element available
872 - ARAL_FREE *fr = (ARAL_FREE *)ptr;
873 - fr->size = ar->config.element_size;
874 -
875 - aral_page_free_lock(ar, page);
876 - fr->next = page->free.list;
877 - page->free.list = fr;
878 - aral_page_free_unlock(ar, page);
914 + aral_add_free_slot___no_lock_required(ar, page, ptr);
915
916 aral_lock(ar);
917
@@ -989,9 +1025,10 @@ size_t aral_actual_element_size(ARAL *ar) {
1025 }
1026
1027 ARAL *aral_create(const char *name, size_t element_size, size_t initial_page_elements, size_t max_page_size,
992 - struct aral_statistics *stats, const char *filename, const char **cache_dir, bool mmap, bool lockless) {
1028 + struct aral_statistics *stats, const char *filename, const char **cache_dir,
1029 + bool mmap, bool lockless, bool dont_dump) {
1030 ARAL *ar = callocz(1, sizeof(ARAL));
994 - ar->config.options = ((lockless) ? ARAL_LOCKLESS : 0);
1031 + ar->config.options = ((lockless) ? ARAL_LOCKLESS : 0) | ((dont_dump) ? ARAL_DONT_DUMP : 0);
1032 ar->config.requested_element_size = element_size;
1033 ar->config.initial_page_elements = initial_page_elements;
1034 ar->config.requested_max_page_size = max_page_size;
@@ -1148,7 +1185,7 @@ ARAL *aral_by_size_acquire(size_t size) {
1185 aral_by_size_globals.array[size].refcount++;
1186
1187 internal_fatal(
1151 - aral_requested_element_size(ar) != size, "DICTIONARY: aral has size %zu but we want %zu",
1188 + aral_requested_element_size(ar) != size, "ARAL BY SIZE: aral has size %zu but we want %zu",
1189 aral_requested_element_size(ar), size);
1190 }
1191
@@ -1160,7 +1197,7 @@ ARAL *aral_by_size_acquire(size_t size) {
1197 0,
1198 0,
1199 &aral_by_size_globals.shared_statistics,
1163 - NULL, NULL, false, false);
1200 + NULL, NULL, false, false, false);
1201
1202 if(size <= ARAL_BY_SIZE_MAX_SIZE) {
1203 aral_by_size_globals.array[size].ar = ar;
@@ -1347,7 +1384,7 @@ int aral_stress_test(size_t threads, size_t elements, size_t seconds) {
1384 16384,
1385 NULL,
1386 "aral-stress-test",
1350 - NULL, false, false),
1387 + NULL, false, false, false),
1388 .elements = elements,
1389 .errors = 0,
1390 };
@@ -1424,8 +1461,7 @@ int aral_unittest(size_t elements) {
1461 NULL,
1462 "aral-test",
1463 &cache_dir,
1427 - false,
1428 - false),
1464 + false, false, false),
1465 .elements = elements,
1466 .errors = 0,
1467 };
src/libnetdata/aral/aral.h
+8 -13
@@ -9,22 +9,16 @@
9 typedef struct aral ARAL;
10
11 struct aral_page_type_stats {
12 - CACHE_LINE_PADDING();
13 - size_t allocations;
14 - CACHE_LINE_PADDING();
15 - size_t allocated_bytes;
16 - CACHE_LINE_PADDING();
17 - size_t used_bytes;
18 - CACHE_LINE_PADDING();
19 - size_t padding_bytes;
12 + PAD64(size_t) allocations;
13 + PAD64(size_t) allocated_bytes;
14 + PAD64(size_t) used_bytes;
15 + PAD64(size_t) padding_bytes;
16 };
17
18 struct aral_statistics {
19 struct {
24 - CACHE_LINE_PADDING();
25 - size_t allocations;
26 - CACHE_LINE_PADDING();
27 - size_t allocated_bytes;
20 + PAD64(size_t) allocations;
21 + PAD64(size_t) allocated_bytes;
22 } structures;
23
24 struct aral_page_type_stats malloc;
@@ -36,7 +30,8 @@ struct aral_statistics {
30 const char *aral_name(ARAL *ar);
31
32 ARAL *aral_create(const char *name, size_t element_size, size_t initial_page_elements, size_t max_page_size,
39 - struct aral_statistics *stats, const char *filename, const char **cache_dir, bool mmap, bool lockless);
33 + struct aral_statistics *stats, const char *filename, const char **cache_dir,
34 + bool mmap, bool lockless, bool dont_dump);
35
36 // --------------------------------------------------------------------------------------------------------------------
37
src/libnetdata/atomics/atomic_flags.h new
+28
@@ -0,0 +1,28 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_ATOMIC_FLAGS_H
4 +#define NETDATA_ATOMIC_FLAGS_H
5 +
6 +#define atomic_flags_get(pvalue) \
7 + __atomic_load_n((pvalue), __ATOMIC_ACQUIRE)
8 +
9 +#define atomic_flags_check(pvalue, flag) \
10 + (__atomic_load_n((pvalue), __ATOMIC_ACQUIRE) & (flag))
11 +
12 +#define atomic_flags_set(pvalue, flag) \
13 + __atomic_or_fetch((pvalue), (flag), __ATOMIC_RELEASE)
14 +
15 +#define atomic_flags_clear(pvalue, flag) \
16 + __atomic_and_fetch((pvalue), ~(flag), __ATOMIC_RELEASE)
17 +
18 +// returns the old flags (before applying the changes)
19 +#define atomic_flags_set_and_clear(pvalue, set, clear) ({ \
20 + __typeof__(*pvalue) __old = *(pvalue), __new; \
21 + do { \
22 + __new = (__old | (set)) & ~(clear); \
23 + } while(!__atomic_compare_exchange_n((pvalue), &__old, __new, \
24 + false, __ATOMIC_RELEASE, __ATOMIC_RELAXED)); \
25 + __old; \
26 +})
27 +
28 +#endif //NETDATA_ATOMIC_FLAGS_H
src/libnetdata/atomics/atomics.h new
+8
@@ -0,0 +1,8 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_ATOMICS_H
4 +#define NETDATA_ATOMICS_H
5 +
6 +#include "atomic_flags.h"
7 +
8 +#endif //NETDATA_ATOMICS_H
src/libnetdata/common.h
+1 -1
@@ -396,7 +396,7 @@ typedef uint32_t uid_t;
396
397 #define CONCAT_INDIRECT(a, b) a##b
398 #define CONCAT(a, b) CONCAT_INDIRECT(a, b)
399 -#define CACHE_LINE_PADDING() uint8_t CONCAT(padding, __COUNTER__)[64 - sizeof(size_t)];
399 +#define PAD64(type) uint8_t CONCAT(padding, __COUNTER__)[64 - sizeof(type)]; type
400
401 // --------------------------------------------------------------------------------------------------------------------
402
src/libnetdata/dictionary/dictionary-internals.h
+1 -1
@@ -47,7 +47,7 @@ typedef enum __attribute__ ((__packed__)) item_flags {
47 #define item_shared_flag_set(item, flag) __atomic_or_fetch(&((item)->shared->flags), flag, __ATOMIC_RELAXED)
48 #define item_shared_flag_clear(item, flag) __atomic_and_fetch(&((item)->shared->flags), ~(flag), __ATOMIC_RELAXED)
49
50 -#define REFCOUNT_DELETING (-100)
50 +#define REFCOUNT_DICONNECTED (-100)
51
52 #define ITEM_FLAGS_TYPE uint8_t
53 #define KEY_LEN_TYPE uint32_t
src/libnetdata/dictionary/dictionary-refcount.h
+2 -2
@@ -189,7 +189,7 @@ static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY
189 // if we can set refcount to REFCOUNT_DELETING, we can delete this item
190
191 size_t spins = 0;
192 - REFCOUNT refcount, desired = REFCOUNT_DELETING;
192 + REFCOUNT refcount, desired = REFCOUNT_DICONNECTED;
193
194 int ret = RC_ITEM_OK;
195
@@ -234,7 +234,7 @@ static inline bool item_shared_release_and_check_if_it_can_be_freed(DICTIONARY *
234 // if we can set refcount to REFCOUNT_DELETING, we can delete this item
235
236 REFCOUNT links = __atomic_sub_fetch(&item->shared->links, 1, __ATOMIC_RELEASE);
237 - if(links == 0 && __atomic_compare_exchange_n(&item->shared->links, &links, REFCOUNT_DELETING, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
237 + if(links == 0 && __atomic_compare_exchange_n(&item->shared->links, &links, REFCOUNT_DICONNECTED, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
238
239 // we can delete it
240 return true;
src/libnetdata/dictionary/dictionary.h
+26 -52
@@ -66,75 +66,49 @@ struct dictionary_stats {
66 const char *name; // the name of the category
67
68 struct {
69 - CACHE_LINE_PADDING();
70 - size_t active; // the number of active dictionaries
71 - CACHE_LINE_PADDING();
72 - size_t deleted; // the number of dictionaries queued for destruction
69 + PAD64(size_t) active; // the number of active dictionaries
70 + PAD64(size_t) deleted; // the number of dictionaries queued for destruction
71 } dictionaries;
72
73 struct {
76 - CACHE_LINE_PADDING();
77 - long entries; // active items in the dictionary
78 - CACHE_LINE_PADDING();
79 - long pending_deletion; // pending deletion items in the dictionary
80 - CACHE_LINE_PADDING();
81 - long referenced; // referenced items in the dictionary
74 + PAD64(long) entries; // active items in the dictionary
75 + PAD64(long) pending_deletion; // pending deletion items in the dictionary
76 + PAD64(long) referenced; // referenced items in the dictionary
77 } items;
78
79 struct {
85 - CACHE_LINE_PADDING();
86 - size_t creations; // dictionary creations
87 - CACHE_LINE_PADDING();
88 - size_t destructions; // dictionary destructions
89 - CACHE_LINE_PADDING();
90 - size_t flushes; // dictionary flushes
91 - CACHE_LINE_PADDING();
92 - size_t traversals; // dictionary foreach
93 - CACHE_LINE_PADDING();
94 - size_t walkthroughs; // dictionary walkthrough
95 - CACHE_LINE_PADDING();
96 - size_t garbage_collections; // dictionary garbage collections
97 - CACHE_LINE_PADDING();
98 - size_t searches; // item searches
99 - CACHE_LINE_PADDING();
100 - size_t inserts; // item inserts
101 - CACHE_LINE_PADDING();
102 - size_t resets; // item resets
103 - CACHE_LINE_PADDING();
104 - size_t deletes; // item deletes
80 + PAD64(size_t) creations; // dictionary creations
81 + PAD64(size_t) destructions; // dictionary destructions
82 + PAD64(size_t) flushes; // dictionary flushes
83 + PAD64(size_t) traversals; // dictionary foreach
84 + PAD64(size_t) walkthroughs; // dictionary walkthrough
85 + PAD64(size_t) garbage_collections; // dictionary garbage collections
86 + PAD64(size_t) searches; // item searches
87 + PAD64(size_t) inserts; // item inserts
88 + PAD64(size_t) resets; // item resets
89 + PAD64(size_t) deletes; // item deletes
90 } ops;
91
92 struct {
108 - CACHE_LINE_PADDING();
109 - size_t inserts; // number of times the insert callback is called
110 - CACHE_LINE_PADDING();
111 - size_t conflicts; // number of times the conflict callback is called
112 - CACHE_LINE_PADDING();
113 - size_t reacts; // number of times the react callback is called
114 - CACHE_LINE_PADDING();
115 - size_t deletes; // number of times the delete callback is called
93 + PAD64(size_t) inserts; // number of times the insert callback is called
94 + PAD64(size_t) conflicts; // number of times the conflict callback is called
95 + PAD64(size_t) reacts; // number of times the react callback is called
96 + PAD64(size_t) deletes; // number of times the delete callback is called
97 } callbacks;
98
99 // memory
100 struct {
120 - CACHE_LINE_PADDING();
121 - ssize_t index; // bytes of keys indexed (indication of the index size)
122 - CACHE_LINE_PADDING();
123 - ssize_t values; // bytes of caller structures
124 - CACHE_LINE_PADDING();
125 - ssize_t dict; // bytes of the structures dictionary needs
101 + PAD64(ssize_t) index; // bytes of keys indexed (indication of the index size)
102 + PAD64(ssize_t) values; // bytes of caller structures
103 + PAD64(ssize_t) dict; // bytes of the structures dictionary needs
104 } memory;
105
106 // spin locks
107 struct {
130 - CACHE_LINE_PADDING();
131 - size_t use_spins; // number of times a reference to item had to spin to acquire it or ignore it
132 - CACHE_LINE_PADDING();
133 - size_t search_spins; // number of times a successful search result had to be thrown away
134 - CACHE_LINE_PADDING();
135 - size_t insert_spins; // number of times an insertion to the hash table had to be repeated
136 - CACHE_LINE_PADDING();
137 - size_t delete_spins; // number of times a deletion had to spin to get a decision
108 + PAD64(size_t) use_spins; // number of times a reference to item had to spin to acquire it or ignore it
109 + PAD64(size_t) search_spins; // number of times a successful search result had to be thrown away
110 + PAD64(size_t) insert_spins; // number of times an insertion to the hash table had to be repeated
111 + PAD64(size_t) delete_spins; // number of times a deletion had to spin to get a decision
112 } spin_locks;
113 };
114
src/libnetdata/http/http_defs.c
-1
@@ -6,7 +6,6 @@ ENUM_STR_MAP_DEFINE(HTTP_REQUEST_MODE) =
6 {
7 { .name = "OPTIONS", .id = HTTP_REQUEST_MODE_OPTIONS },
8 { .name = "GET", .id = HTTP_REQUEST_MODE_GET },
9 - { .name = "FILECOPY", .id = HTTP_REQUEST_MODE_FILECOPY },
9 { .name = "POST", .id = HTTP_REQUEST_MODE_POST },
10 { .name = "PUT", .id = HTTP_REQUEST_MODE_PUT },
11 { .name = "DELETE", .id = HTTP_REQUEST_MODE_DELETE },
src/libnetdata/libjudy/judy-malloc.c
+1 -1
@@ -30,7 +30,7 @@ __attribute__((constructor)) void aral_judy_init(void) {
30 0,
31 0,
32 &judy_sizes_aral_statistics,
33 - NULL, NULL, false, false);
33 + NULL, NULL, false, false, false);
34 }
35 }
36
src/libnetdata/libjudy/judyl-typed.h
+14 -17
@@ -5,7 +5,8 @@
5
6 #include <Judy.h>
7
8 -#define DEFINE_JUDYL_TYPED(NAME, TYPE) \
8 +// Advanced macro for types requiring conversion
9 +#define DEFINE_JUDYL_TYPED_ADVANCED(NAME, TYPE, PACK_MACRO, UNPACK_MACRO) \
10 _Static_assert(sizeof(TYPE) <= sizeof(Word_t), \
11 #NAME "_type_must_have_same_size_as_Word_t"); \
12 typedef struct { \
@@ -19,45 +20,37 @@
20 static inline bool NAME##_SET(NAME##_JudyLSet *set, Word_t index, TYPE value) { \
21 Pvoid_t *pValue = JudyLIns(&set->judyl, index, PJE0); \
22 if (pValue == PJERR) return false; \
22 - *pValue = (void *)(uintptr_t)value; \
23 + *pValue = (void *)PACK_MACRO(value); \
24 return true; \
25 } \
26 \
27 static inline TYPE NAME##_GET(NAME##_JudyLSet *set, Word_t index) { \
28 Pvoid_t *pValue = JudyLGet(set->judyl, index, PJE0); \
28 - return (pValue != NULL) ? (TYPE)(uintptr_t)(*pValue) : (TYPE)0; \
29 - } \
30 - \
31 - static inline TYPE *NAME##_GETPTR(NAME##_JudyLSet *set, Word_t index) { \
32 - Pvoid_t *pValue = JudyLGet(set->judyl, index, PJE0); \
33 - return (TYPE *)pValue; \
29 + return (pValue != NULL) ? (TYPE)UNPACK_MACRO(*pValue) : (TYPE){0}; \
30 } \
31 \
32 static inline bool NAME##_DEL(NAME##_JudyLSet *set, Word_t index) { \
37 - int Rc; \
38 - PPvoid_t ppJudy = &set->judyl; \
39 - Rc = JudyLDel(ppJudy, index, PJE0); \
40 - return Rc == 1; \
33 + return JudyLDel(&set->judyl, index, PJE0) == 1; \
34 } \
35 \
36 static inline TYPE NAME##_FIRST(NAME##_JudyLSet *set, Word_t *index) { \
37 Pvoid_t *pValue = JudyLFirst(set->judyl, index, PJE0); \
45 - return (pValue != NULL) ? (TYPE)(uintptr_t)(*pValue) : (TYPE)0; \
38 + return (pValue != NULL) ? (TYPE)UNPACK_MACRO(*pValue) : (TYPE){0}; \
39 } \
40 \
41 static inline TYPE NAME##_NEXT(NAME##_JudyLSet *set, Word_t *index) { \
42 Pvoid_t *pValue = JudyLNext(set->judyl, index, PJE0); \
50 - return (pValue != NULL) ? (TYPE)(uintptr_t)(*pValue) : (TYPE)0; \
43 + return (pValue != NULL) ? (TYPE)UNPACK_MACRO(*pValue) : (TYPE){0}; \
44 } \
45 \
46 static inline TYPE NAME##_LAST(NAME##_JudyLSet *set, Word_t *index) { \
47 Pvoid_t *pValue = JudyLLast(set->judyl, index, PJE0); \
55 - return (pValue != NULL) ? (TYPE)(uintptr_t)(*pValue) : (TYPE)0; \
48 + return (pValue != NULL) ? (TYPE)UNPACK_MACRO(*pValue) : (TYPE){0}; \
49 } \
50 \
51 static inline TYPE NAME##_PREV(NAME##_JudyLSet *set, Word_t *index) { \
52 Pvoid_t *pValue = JudyLPrev(set->judyl, index, PJE0); \
60 - return (pValue != NULL) ? (TYPE)(uintptr_t)(*pValue) : (TYPE)0; \
53 + return (pValue != NULL) ? (TYPE)UNPACK_MACRO(*pValue) : (TYPE){0}; \
54 } \
55 \
56 static inline void NAME##_FREE(NAME##_JudyLSet *set, void (*callback)(Word_t, TYPE)) { \
@@ -67,12 +60,16 @@
60 for (pValue = JudyLFirst(set->judyl, &index, PJE0); \
61 pValue != NULL; \
62 pValue = JudyLNext(set->judyl, &index, PJE0)) { \
70 - callback(index, (TYPE)(uintptr_t)(*pValue)); \
63 + callback(index, (TYPE)UNPACK_MACRO(*pValue)); \
64 } \
65 } \
66 JudyLFreeArray(&set->judyl, PJE0); \
67 }
68
69 +// Basic macro for types with no conversion
70 +#define JUDYL_TYPED_NO_CONVERSION(value) (uintptr_t)(value)
71
72 +#define DEFINE_JUDYL_TYPED(NAME, TYPE) \
73 + DEFINE_JUDYL_TYPED_ADVANCED(NAME, TYPE, JUDYL_TYPED_NO_CONVERSION, JUDYL_TYPED_NO_CONVERSION)
74
75 #endif //NETDATA_JUDYL_TYPED_H
src/libnetdata/libnetdata.c
+11 -19
@@ -449,19 +449,11 @@ void posix_memfree(void *ptr) {
449 #endif
450
451 void mallocz_release_as_much_memory_to_the_system(void) {
452 -#if defined(HAVE_C_MALLOPT) || defined(HAVE_C_MALLOC_TRIM)
452 +#if defined(HAVE_C_MALLOC_TRIM)
453 static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
454 spinlock_lock(&spinlock);
455
456 -#ifdef HAVE_C_MALLOPT
457 - // the default is 128KiB
458 - size_t trim_threshold = 65ULL * 1024;
459 - mallopt(M_TRIM_THRESHOLD, (int)trim_threshold);
460 -#endif
461 -
462 -#ifdef HAVE_C_MALLOC_TRIM
456 malloc_trim(0);
464 -#endif
457
458 spinlock_unlock(&spinlock);
459 #endif
@@ -527,7 +519,7 @@ inline int madvise_sequential(void *mem, size_t len) {
519 int ret = madvise(mem, len, MADV_SEQUENTIAL);
520
521 if (ret != 0 && logger-- > 0)
530 - netdata_log_error("madvise(MADV_SEQUENTIAL) failed.");
522 + netdata_log_error("madvise(MADV_SEQUENTIAL) of size %zu, failed.", len);
523 return ret;
524 }
525
@@ -536,7 +528,7 @@ inline int madvise_random(void *mem, size_t len) {
528 int ret = madvise(mem, len, MADV_RANDOM);
529
530 if (ret != 0 && logger-- > 0)
539 - netdata_log_error("madvise(MADV_RANDOM) failed.");
531 + netdata_log_error("madvise(MADV_RANDOM) of size %zu, failed.", len);
532 return ret;
533 }
534
@@ -545,7 +537,7 @@ inline int madvise_dontfork(void *mem, size_t len) {
537 int ret = madvise(mem, len, MADV_DONTFORK);
538
539 if (ret != 0 && logger-- > 0)
548 - netdata_log_error("madvise(MADV_DONTFORK) failed.");
540 + netdata_log_error("madvise(MADV_DONTFORK) of size %zu, failed.", len);
541 return ret;
542 }
543
@@ -554,7 +546,7 @@ inline int madvise_willneed(void *mem, size_t len) {
546 int ret = madvise(mem, len, MADV_WILLNEED);
547
548 if (ret != 0 && logger-- > 0)
557 - netdata_log_error("madvise(MADV_WILLNEED) failed.");
549 + netdata_log_error("madvise(MADV_WILLNEED) of size %zu, failed.", len);
550 return ret;
551 }
552
@@ -563,7 +555,7 @@ inline int madvise_dontneed(void *mem, size_t len) {
555 int ret = madvise(mem, len, MADV_DONTNEED);
556
557 if (ret != 0 && logger-- > 0)
566 - netdata_log_error("madvise(MADV_DONTNEED) failed.");
558 + netdata_log_error("madvise(MADV_DONTNEED) of size %zu, failed.", len);
559 return ret;
560 }
561
@@ -573,7 +565,7 @@ inline int madvise_dontdump(void *mem __maybe_unused, size_t len __maybe_unused)
565 int ret = madvise(mem, len, MADV_DONTDUMP);
566
567 if (ret != 0 && logger-- > 0)
576 - netdata_log_error("madvise(MADV_DONTDUMP) failed.");
568 + netdata_log_error("madvise(MADV_DONTDUMP) of size %zu, failed.", len);
569 return ret;
570 #else
571 return 0;
@@ -586,14 +578,14 @@ inline int madvise_mergeable(void *mem __maybe_unused, size_t len __maybe_unused
578 int ret = madvise(mem, len, MADV_MERGEABLE);
579
580 if (ret != 0 && logger-- > 0)
589 - netdata_log_error("madvise(MADV_MERGEABLE) failed.");
581 + netdata_log_error("madvise(MADV_MERGEABLE) of size %zu, failed.", len);
582 return ret;
583 #else
584 return 0;
585 #endif
586 }
587
596 -void *netdata_mmap(const char *filename, size_t size, int flags, int ksm, bool read_only, int *open_fd)
588 +void *netdata_mmap(const char *filename, size_t size, int flags, int ksm, bool read_only, bool dont_dump, int *open_fd)
589 {
590 // netdata_log_info("netdata_mmap('%s', %zu", filename, size);
591
@@ -651,8 +643,8 @@ void *netdata_mmap(const char *filename, size_t size, int flags, int ksm, bool r
643 }
644
645 // madvise_sequential(mem, size);
654 - madvise_dontfork(mem, size);
655 - madvise_dontdump(mem, size);
646 + // madvise_dontfork(mem, size); // aral is initialized before we daemonize
647 + if(dont_dump) madvise_dontdump(mem, size);
648 // if(flags & MAP_SHARED) madvise_willneed(mem, size);
649 if(ksm) madvise_mergeable(mem, size);
650 }
src/libnetdata/libnetdata.h
+4 -3
@@ -9,15 +9,15 @@ extern "C" {
9
10 #include "common.h"
11
12 -#define JUDYHS_INDEX_SIZE_ESTIMATE(key_bytes) (((key_bytes) + sizeof(Word_t) - 1) / sizeof(Word_t) * 4)
13 -
12 // NETDATA_TRACE_ALLOCATIONS does not work under musl libc, so don't enable it
13 //#if defined(NETDATA_INTERNAL_CHECKS) && !defined(NETDATA_TRACE_ALLOCATIONS)
14 //#define NETDATA_TRACE_ALLOCATIONS 1
15 //#endif
16
17 +#include "atomics/atomics.h"
18 #include "libjudy/judy-malloc.h"
19
20 +#include "object-state/object-state.h"
21 #include "storage-point.h"
22 #include "paths/paths.h"
23
@@ -67,7 +67,7 @@ void posix_memfree(void *ptr);
67 void json_escape_string(char *dst, const char *src, size_t size);
68 void json_fix_string(char *s);
69
70 -void *netdata_mmap(const char *filename, size_t size, int flags, int ksm, bool read_only, int *open_fd);
70 +void *netdata_mmap(const char *filename, size_t size, int flags, int ksm, bool read_only, bool dont_dump, int *open_fd);
71 int netdata_munmap(void *ptr, size_t size);
72 int memory_file_save(const char *filename, void *mem, size_t size);
73
@@ -125,6 +125,7 @@ extern const char *netdata_configured_host_prefix;
125 #include "locks/spinlock.h"
126 #include "locks/rw-spinlock.h"
127 #include "completion/completion.h"
128 +#include "waiting-queue/waiting-queue.h"
129 #include "clocks/clocks.h"
130 #include "simple_pattern/simple_pattern.h"
131 #include "libnetdata/log/nd_log.h"
src/libnetdata/local-sockets/local-sockets.h
+2 -2
@@ -1192,14 +1192,14 @@ static inline void local_sockets_init(LS_STATE *ls) {
1192 sizeof(LOCAL_SOCKET),
1193 65536 / sizeof(LOCAL_SOCKET),
1194 65536,
1195 - NULL, NULL, NULL, false, true);
1195 + NULL, NULL, NULL, false, true, true);
1196
1197 ls->pid_socket_aral = aral_create(
1198 "pid-sockets",
1199 sizeof(struct pid_socket),
1200 65536 / sizeof(struct pid_socket),
1201 65536,
1202 - NULL, NULL, NULL, false, true);
1202 + NULL, NULL, NULL, false, true, true);
1203
1204 memset(&ls->stats, 0, sizeof(ls->stats));
1205
src/libnetdata/locks/rw-spinlock.c
+108 -45
@@ -5,78 +5,141 @@
5 // ----------------------------------------------------------------------------
6 // rw_spinlock implementation
7
8 -void rw_spinlock_init_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
9 - rw_spinlock->readers = 0;
10 - spinlock_init_with_trace(&rw_spinlock->spinlock, func);
8 +void rw_spinlock_init_with_trace(RW_SPINLOCK *rw_spinlock, const char *func __maybe_unused) {
9 + rw_spinlock->counter = 0;
10 +}
11 +
12 +bool rw_spinlock_tryread_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
13 + size_t spins = 0;
14 +
15 + REFCOUNT expected = rw_spinlock->counter;
16 + while (true) {
17 + if(expected < 0)
18 + // writer is active
19 + return false;
20 +
21 + // increment reader count
22 + if (__atomic_compare_exchange_n(
23 + &rw_spinlock->counter,
24 + &expected,
25 + expected + 1,
26 + false, // Strong CAS
27 + __ATOMIC_ACQUIRE, // Success memory order
28 + __ATOMIC_RELAXED // Failure memory order
29 + ))
30 + break;
31 +
32 + spins++;
33 + }
34 +
35 + worker_spinlock_contention(func, spins);
36 + nd_thread_rwspinlock_read_locked();
37 + return true;
38 }
39
40 void rw_spinlock_read_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
14 - spinlock_lock_with_trace(&rw_spinlock->spinlock, func);
15 - __atomic_add_fetch(&rw_spinlock->readers, 1, __ATOMIC_RELAXED);
16 - spinlock_unlock_with_trace(&rw_spinlock->spinlock, func);
41 + size_t spins = 0;
42 +
43 + REFCOUNT expected = rw_spinlock->counter;
44 +
45 + // we should not increase it if it is negative (a writer holds the lock)
46 + if(expected < 0) expected = 0;
47 +
48 + while (true) {
49 + // Attempt to increment reader count
50 + if (__atomic_compare_exchange_n(
51 + &rw_spinlock->counter,
52 + &expected,
53 + expected + 1,
54 + false, // Strong CAS
55 + __ATOMIC_ACQUIRE, // Success memory order
56 + __ATOMIC_RELAXED // Failure memory order
57 + ))
58 + break;
59 +
60 + spins++;
61 +
62 + if (expected < 0) {
63 + // writer is active
64
65 + // we should not increase it if it is negative (a writer holds the lock)
66 + expected = 0;
67 +
68 + // wait a bit before retrying
69 + tinysleep();
70 + yield_the_processor();
71 + }
72 + }
73 +
74 + worker_spinlock_contention(func, spins);
75 nd_thread_rwspinlock_read_locked();
76 }
77
78 void rw_spinlock_read_unlock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func __maybe_unused) {
79 #ifndef NETDATA_INTERNAL_CHECKS
23 - __atomic_sub_fetch(&rw_spinlock->readers, 1, __ATOMIC_RELAXED);
80 + __atomic_sub_fetch(&rw_spinlock->counter, 1, __ATOMIC_RELEASE);
81 #else
25 - int32_t x = __atomic_sub_fetch(&rw_spinlock->readers, 1, __ATOMIC_RELAXED);
26 - if(x < 0)
82 + REFCOUNT x = __atomic_sub_fetch(&rw_spinlock->counter, 1, __ATOMIC_RELEASE);
83 + if (x < 0)
84 fatal("RW_SPINLOCK: readers is negative %d", x);
85 #endif
86
87 nd_thread_rwspinlock_read_unlocked();
88 }
89
90 +bool rw_spinlock_trywrite_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
91 + REFCOUNT expected = 0;
92 +
93 + // Attempt to acquire writer lock when no readers or writers are active
94 + if (!__atomic_compare_exchange_n(
95 + &rw_spinlock->counter,
96 + &expected,
97 + -1,
98 + false, // Strong CAS
99 + __ATOMIC_ACQUIRE, // Success memory order
100 + __ATOMIC_RELAXED // Failure memory order
101 + )) {
102 + return false;
103 + }
104 +
105 + worker_spinlock_contention(func, 0);
106 + nd_thread_rwspinlock_write_locked();
107 + return true;
108 +}
109 +
110 void rw_spinlock_write_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
111 size_t spins = 0;
35 - while(true) {
36 - spinlock_lock_with_trace(&rw_spinlock->spinlock, func);
112
38 - if(__atomic_load_n(&rw_spinlock->readers, __ATOMIC_RELAXED) == 0)
113 + while (true) {
114 + REFCOUNT expected = 0;
115 +
116 + // Attempt to acquire writer lock when no readers or writers are active
117 + if (__atomic_compare_exchange_n(
118 + &rw_spinlock->counter,
119 + &expected,
120 + -1,
121 + false, // Strong CAS
122 + __ATOMIC_ACQUIRE, // Success memory order
123 + __ATOMIC_RELAXED // Failure memory order
124 + )) {
125 break;
126 + }
127
41 - // Busy wait until all readers have released their locks.
42 - spinlock_unlock_with_trace(&rw_spinlock->spinlock, func);
43 - tinysleep();
128 spins++;
129 + tinysleep();
130 }
131
132 worker_spinlock_contention(func, spins);
133 nd_thread_rwspinlock_write_locked();
134 }
135
51 -void rw_spinlock_write_unlock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
52 - spinlock_unlock_with_trace(&rw_spinlock->spinlock, func);
53 - nd_thread_rwspinlock_write_unlocked();
54 -}
55 -
56 -bool rw_spinlock_tryread_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
57 - if(spinlock_trylock_with_trace(&rw_spinlock->spinlock, func)) {
58 - __atomic_add_fetch(&rw_spinlock->readers, 1, __ATOMIC_RELAXED);
59 - spinlock_unlock_with_trace(&rw_spinlock->spinlock, func);
60 - nd_thread_rwspinlock_read_locked();
61 - return true;
62 - }
63 -
64 - return false;
65 -}
66 -
67 -bool rw_spinlock_trywrite_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
68 - if(spinlock_trylock_with_trace(&rw_spinlock->spinlock, func)) {
69 - if (__atomic_load_n(&rw_spinlock->readers, __ATOMIC_RELAXED) == 0) {
70 - // No readers, we've successfully acquired the write lock
71 - nd_thread_rwspinlock_write_locked();
72 - return true;
73 - }
74 - else {
75 - // There are readers, unlock the spinlock and return false
76 - spinlock_unlock_with_trace(&rw_spinlock->spinlock, func);
77 - }
78 - }
136 +void rw_spinlock_write_unlock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func __maybe_unused) {
137 +#ifdef NETDATA_INTERNAL_CHECKS
138 + int32_t x = __atomic_load_n(&rw_spinlock->counter, __ATOMIC_RELAXED);
139 + if (x != -1)
140 + fatal("RW_SPINLOCK: writer unlock encountered unexpected state: %d", x);
141 +#endif
142
80 - return false;
143 + __atomic_store_n(&rw_spinlock->counter, 0, __ATOMIC_RELEASE); // Release writer lock
144 + nd_thread_rwspinlock_write_unlocked();
145 }
82 -
src/libnetdata/locks/rw-spinlock.h
+2 -3
@@ -7,11 +7,10 @@
7 #include "spinlock.h"
8
9 typedef struct netdata_rw_spinlock {
10 - int32_t readers;
11 - SPINLOCK spinlock;
10 + REFCOUNT counter; // positive is readers, negative is a writer
11 } RW_SPINLOCK;
12
14 -#define RW_SPINLOCK_INITIALIZER { .readers = 0, .spinlock = SPINLOCK_INITIALIZER}
13 +#define RW_SPINLOCK_INITIALIZER { .counter = 0, }
14
15 void rw_spinlock_init_with_trace(RW_SPINLOCK *rw_spinlock, const char *func);
16 void rw_spinlock_read_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func);
src/libnetdata/locks/spinlock.c
+14 -13
@@ -2,6 +2,8 @@
2
3 #include "libnetdata/libnetdata.h"
4
5 +#define MAX_USEC 512 // Maximum backoff limit in microseconds
6 +
7 // ----------------------------------------------------------------------------
8 // spinlock implementation
9 // https://www.youtube.com/watch?v=rmGJc9PXpuE&t=41s
@@ -14,22 +16,21 @@ void spinlock_init_with_trace(SPINLOCK *spinlock, const char *func __maybe_unuse
16
17 void spinlock_lock_with_trace(SPINLOCK *spinlock, const char *func) {
18 size_t spins = 0;
19 + usec_t usec = 1;
20
18 - for(int i = 1;
19 - __atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) ||
20 - __atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)
21 - ; i++
22 - ) {
21 + while (true) {
22 + if (!__atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) &&
23 + !__atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)) {
24 + // Acquired the lock
25 + break;
26 + }
27
28 + // Backoff strategy with exponential growth
29 spins++;
25 - if(unlikely(i % 8 == 0)) {
26 - i = 0;
27 - tinysleep();
28 - }
30 + microsleep(usec);
31 + usec = usec > MAX_USEC ? MAX_USEC : usec * 2;
32 }
33
31 - // we have the lock
32 -
34 #ifdef NETDATA_INTERNAL_CHECKS
35 spinlock->spins += spins;
36 spinlock->locker_pid = gettid_cached();
@@ -50,9 +51,9 @@ void spinlock_unlock_with_trace(SPINLOCK *spinlock, const char *func __maybe_unu
51 }
52
53 bool spinlock_trylock_with_trace(SPINLOCK *spinlock, const char *func __maybe_unused) {
53 - if(!__atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) &&
54 + if (!__atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) &&
55 !__atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)) {
55 - // we got the lock
56 + // Acquired the lock
57 nd_thread_spinlock_locked();
58 return true;
59 }
src/libnetdata/log/nd_log.c
+27 -9
@@ -409,21 +409,40 @@ void netdata_logger_with_limit(ERROR_LIMIT *erl, ND_LOG_SOURCES source, ND_LOG_F
409 erl->count = 0;
410 }
411
412 -void netdata_logger_fatal( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) {
413 - int saved_errno = errno;
412 +void netdata_logger_fatal(const char *file, const char *function, const unsigned long line, const char *fmt, ... ) {
413 + static size_t already_in_fatal = 0;
414 +
415 + size_t recursion = __atomic_add_fetch(&already_in_fatal, 1, __ATOMIC_SEQ_CST);
416 + if(recursion > 1) {
417 + // exit immediately, nothing more to be done
418 + fprintf(stderr, "RECURSIVE FATAL STATEMENTS, latest from %lu@%s() of %s, EXITING NOW!\n",
419 + line, function, file);
420 + fflush(stderr);
421 + _exit(1);
422 + }
423
424 + int saved_errno = errno;
425 size_t saved_winerror = 0;
426 #if defined(OS_WINDOWS)
427 saved_winerror = GetLastError();
428 #endif
429
420 - ND_LOG_SOURCES source = NDLS_DAEMON;
421 - source = nd_log_validate_source(source);
430 + // make sure the msg id does not leak
431 + {
432 + ND_LOG_STACK lgs[] = {
433 + ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &netdata_fatal_msgid),
434 + ND_LOG_FIELD_END(),
435 + };
436 + ND_LOG_STACK_PUSH(lgs);
437
423 - va_list args;
424 - va_start(args, fmt);
425 - nd_logger(file, function, line, source, NDLP_ALERT, true, saved_errno, saved_winerror, fmt, args);
426 - va_end(args);
438 + ND_LOG_SOURCES source = NDLS_DAEMON;
439 + source = nd_log_validate_source(source);
440 +
441 + va_list args;
442 + va_start(args, fmt);
443 + nd_logger(file, function, line, source, NDLP_ALERT, true, saved_errno, saved_winerror, fmt, args);
444 + va_end(args);
445 + }
446
447 char date[LOG_DATE_LENGTH];
448 log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
@@ -462,4 +481,3 @@ void netdata_logger_fatal( const char *file, const char *function, const unsigne
481
482 netdata_cleanup_and_exit(1, "FATAL", action_result, action_data);
483 }
465 -
src/libnetdata/log/systemd-journal-helpers.c
+1 -1
@@ -51,7 +51,7 @@ int journal_direct_fd(const char *path) {
51 int fd = socket(AF_UNIX, SOCK_DGRAM| DEFAULT_SOCKET_FLAGS, 0);
52 if (fd < 0) return -1;
53
54 - sock_setcloexec(fd);
54 + sock_setcloexec(fd, true);
55
56 struct sockaddr_un addr;
57 memset(&addr, 0, sizeof(struct sockaddr_un));
src/libnetdata/object-state/object-state.c new
+76
@@ -0,0 +1,76 @@
1 +#include "object-state.h"
2 +
3 +OBJECT_STATE_ID object_state_id(OBJECT_STATE *os) {
4 + return __atomic_load_n(&os->state_id, __ATOMIC_ACQUIRE);
5 +}
6 +
7 +void object_state_activate(OBJECT_STATE *os) {
8 + __atomic_add_fetch(&os->state_id, 1, __ATOMIC_RELAXED);
9 +
10 + REFCOUNT expected = __atomic_load_n(&os->state_refcount, __ATOMIC_RELAXED);
11 + REFCOUNT desired;
12 +
13 + do {
14 + if(expected != OBJECT_STATE_DEACTIVATED) {
15 + fatal("OBJECT_STATE: attempt to activate already activated object (state refcount is %d)", expected);
16 + return;
17 + }
18 +
19 + desired = 0;
20 +
21 + } while(!__atomic_compare_exchange_n(
22 + &os->state_refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
23 +}
24 +
25 +void object_state_deactivate(OBJECT_STATE *os) {
26 + __atomic_add_fetch(&os->state_id, 1, __ATOMIC_RELAXED);
27 +
28 + REFCOUNT expected = __atomic_load_n(&os->state_refcount, __ATOMIC_RELAXED);
29 + REFCOUNT desired;
30 +
31 + do {
32 + if(expected == OBJECT_STATE_DEACTIVATED) {
33 + fatal("OBJECT_STATE: attempt to deactivate object that is already deactivated (state refcount %d)", expected);
34 + return;
35 + }
36 + else if(expected < 0) {
37 + fatal("OBJECT_STATE: attempt to deactivate object that is already deactivating (state refcount %d)", expected);
38 + return;
39 + }
40 +
41 + // Current (-INT32_MAX) + holders
42 + desired = OBJECT_STATE_DEACTIVATED + expected;
43 +
44 + } while(!__atomic_compare_exchange_n(
45 + &os->state_refcount, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED));
46 +
47 + // Now wait for all holders to release
48 + while(__atomic_load_n(&os->state_refcount, __ATOMIC_ACQUIRE) != OBJECT_STATE_DEACTIVATED)
49 + tinysleep(); // Busy wait until all holders are gone
50 +}
51 +
52 +bool object_state_acquire(OBJECT_STATE *os, OBJECT_STATE_ID wanted_state_id) {
53 + REFCOUNT expected = __atomic_load_n(&os->state_refcount, __ATOMIC_RELAXED);
54 + REFCOUNT desired;
55 +
56 + do {
57 + // If refcount is negative, it means deactivation is in progress or complete
58 + if(expected < 0)
59 + return false;
60 +
61 + desired = expected + 1;
62 +
63 + } while(!__atomic_compare_exchange_n(
64 + &os->state_refcount, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
65 +
66 + if(object_state_id(os) != wanted_state_id) {
67 + object_state_release(os);
68 + return false;
69 + }
70 +
71 + return true;
72 +}
73 +
74 +void object_state_release(OBJECT_STATE *os) {
75 + __atomic_sub_fetch(&os->state_refcount, 1, __ATOMIC_RELEASE);
76 +}
src/libnetdata/object-state/object-state.h new
+34
@@ -0,0 +1,34 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_OBJECT_STATE_ID_H
4 +#define NETDATA_OBJECT_STATE_ID_H
5 +
6 +#include "libnetdata/libnetdata.h"
7 +
8 +typedef uint32_t OBJECT_STATE_ID;
9 +
10 +typedef struct {
11 + OBJECT_STATE_ID state_id;
12 + REFCOUNT state_refcount;
13 +} OBJECT_STATE;
14 +
15 +#define OBJECT_STATE_DEACTIVATED (-INT32_MAX)
16 +
17 +#define OBJECT_STATE_INIT_ACTIVATED (OBJECT_STATE){ .state_id = 0, .state_refcount = 0, };
18 +#define OBJECT_STATE_INIT_DEACTIVATED (OBJECT_STATE){ .state_id = 0, .state_refcount = OBJECT_STATE_DEACTIVATED, };
19 +
20 +// get the current state id of the object
21 +OBJECT_STATE_ID object_state_id(OBJECT_STATE *os);
22 +
23 +// increments the object's state id
24 +// enables using the object - users may acquire and release the object
25 +void object_state_activate(OBJECT_STATE *os);
26 +
27 +// increments the object's state id
28 +// prevents users from acquiring it, and waits until all of its holders have released it
29 +void object_state_deactivate(OBJECT_STATE *os);
30 +
31 +bool object_state_acquire(OBJECT_STATE *os, OBJECT_STATE_ID wanted_state_id);
32 +void object_state_release(OBJECT_STATE *os);
33 +
34 +#endif //NETDATA_OBJECT_STATE_ID_H
src/libnetdata/required_dummies.h
+1 -1
@@ -14,7 +14,7 @@ void netdata_cleanup_and_exit(int ret, const char *action, const char *action_re
14 }
15
16 void rrdset_thread_rda_free(void){}
17 -void sender_commit_thread_buffer_free(void){}
17 +void sender_thread_buffer_free(void){}
18 void query_target_free(void){}
19 void service_exits(void){}
20 void rrd_collector_finished(void){}
src/libnetdata/socket/connect-to.c
+2 -2
@@ -26,7 +26,7 @@ static inline int connect_to_unix(const char *path, struct timeval *timeout) {
26 path);
27 }
28
29 - sock_setcloexec(fd);
29 + sock_setcloexec(fd, true);
30
31 struct sockaddr_un addr;
32 memset(&addr, 0, sizeof(addr));
@@ -145,7 +145,7 @@ int connect_to_this_ip46(
145 "Failed to set timeout on the socket to ip '%s' port '%s'",
146 hostBfr, servBfr);
147 }
148 - sock_setcloexec(fd);
148 + sock_setcloexec(fd, true);
149
150 errno_clear();
151 if(connect(fd, ai->ai_addr, ai->ai_addrlen) < 0) {
src/libnetdata/socket/listen-sockets.c
+51 -13
@@ -70,9 +70,13 @@ static int create_listen_socket_unix(const char *path, int listen_backlog) {
70 return -1;
71 }
72
73 - sock_setnonblock(sock);
74 - sock_setcloexec(sock);
75 - sock_enlarge_in(sock);
73 + if(sock_setnonblock(sock, true) != 1)
74 + nd_log(NDLS_DAEMON, NDLP_ERR,
75 + "LISTENER: UNIX socket on path '%s' failed to set non-blocking mode.",
76 + path);
77 +
78 + sock_setcloexec(sock, true);
79 + sock_enlarge_rcv_buf(sock);
80
81 struct sockaddr_un name;
82 memset(&name, 0, sizeof(struct sockaddr_un));
@@ -124,11 +128,24 @@ static int create_listen_socket4(int socktype, const char *ip, uint16_t port, in
128
129 return -1;
130 }
127 - sock_setreuse(sock, 1);
128 - sock_setreuse_port(sock, 0);
129 - sock_setnonblock(sock);
130 - sock_setcloexec(sock);
131 - sock_enlarge_in(sock);
131 +
132 + if(sock_setreuse_addr(sock, true) != 1)
133 + nd_log(NDLS_DAEMON, NDLP_ERR,
134 + "LISTENER: IPv4 socket on ip '%s' port %d, socktype %d failed to enable reuse address.",
135 + ip, port, socktype);
136 +
137 + if(sock_setreuse_port(sock, false) == 1) // -1 means not supported
138 + nd_log(NDLS_DAEMON, NDLP_ERR,
139 + "LISTENER: IPv4 socket on ip '%s' port %d, socktype %d failed to disable reuse port.",
140 + ip, port, socktype);
141 +
142 + if(sock_setnonblock(sock, true) != 1)
143 + nd_log(NDLS_DAEMON, NDLP_ERR,
144 + "LISTENER: IPv4 socket on ip '%s' port %d, socktype %d failed to set non-blocking mode.",
145 + ip, port, socktype);
146 +
147 + sock_setcloexec(sock, true);
148 + sock_enlarge_rcv_buf(sock);
149
150 struct sockaddr_in name;
151 memset(&name, 0, sizeof(struct sockaddr_in));
@@ -163,6 +180,10 @@ static int create_listen_socket4(int socktype, const char *ip, uint16_t port, in
180 return -1;
181 }
182
183 + // Add TCP_DEFER_ACCEPT for TCP sockets
184 + if(socktype == SOCK_STREAM)
185 + sock_set_tcp_defer_accept(sock, true);
186 +
187 nd_log(NDLS_DAEMON, NDLP_DEBUG,
188 "LISTENER: Listening on IPv4 ip '%s' port %d, socktype %d",
189 ip, port, socktype);
@@ -182,11 +203,24 @@ static int create_listen_socket6(int socktype, uint32_t scope_id, const char *ip
203
204 return -1;
205 }
185 - sock_setreuse(sock, 1);
186 - sock_setreuse_port(sock, 0);
187 - sock_setnonblock(sock);
188 - sock_setcloexec(sock);
189 - sock_enlarge_in(sock);
206 +
207 + if(sock_setreuse_addr(sock, true) != 1)
208 + nd_log(NDLS_DAEMON, NDLP_ERR,
209 + "LISTENER: IPv6 socket on ip '%s' port %d, socktype %d failed to set reuse address.",
210 + ip, port, socktype);
211 +
212 + if(sock_setreuse_port(sock, false) == 1) // -1 means not supported
213 + nd_log(NDLS_DAEMON, NDLP_ERR,
214 + "LISTENER: IPv6 socket on ip '%s' port %d, socktype %d failed to disable reuse port.",
215 + ip, port, socktype);
216 +
217 + if(sock_setnonblock(sock, true) != 1)
218 + nd_log(NDLS_DAEMON, NDLP_ERR,
219 + "LISTENER: IPv6 socket on ip '%s' port %d, socktype %d, failed to set non-blocking mode.",
220 + ip, port, socktype);
221 +
222 + sock_setcloexec(sock, true);
223 + sock_enlarge_rcv_buf(sock);
224
225 /* IPv6 only */
226 if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&ipv6only, sizeof(ipv6only)) != 0)
@@ -230,6 +264,10 @@ static int create_listen_socket6(int socktype, uint32_t scope_id, const char *ip
264 return -1;
265 }
266
267 + // Add TCP_DEFER_ACCEPT for TCP sockets
268 + if(socktype == SOCK_STREAM)
269 + sock_set_tcp_defer_accept(sock, true);
270 +
271 nd_log(NDLS_DAEMON, NDLP_DEBUG,
272 "LISTENER: Listening on IPv6 ip '%s' port %d, socktype %d",
273 ip, port, socktype);
src/libnetdata/socket/nd-poll.c
+112 -50
@@ -2,6 +2,10 @@
2
3 #include "nd-poll.h"
4
5 +#ifndef POLLRDHUP
6 +#define POLLRDHUP 0
7 +#endif
8 +
9 #if defined(OS_LINUX)
10 #include <sys/epoll.h>
11
@@ -13,6 +17,7 @@ struct nd_poll_t {
17 struct epoll_event ev[MAX_EVENTS_PER_CALL];
18 size_t last_pos;
19 size_t used;
20 + size_t nfds;
21 };
22
23 // Initialize the event poll context
@@ -28,53 +33,92 @@ nd_poll_t *nd_poll_create() {
33 return ndpl;
34 }
35
36 +static inline void nd_poll_replace_data_on_loaded_events(nd_poll_t *ndpl, int fd __maybe_unused, void *old_data, void *new_data) {
37 + for(size_t i = ndpl->last_pos; i < ndpl->used; i++) {
38 + if(ndpl->ev[i].data.ptr == old_data)
39 + ndpl->ev[i].data.ptr = new_data;
40 + }
41 +}
42 +
43 // Add a file descriptor to the event poll
44 bool nd_poll_add(nd_poll_t *ndpl, int fd, nd_poll_event_t events, void *data) {
45 + internal_fatal(!data, "nd_poll() does not support NULL data pointers");
46 +
47 struct epoll_event ev = {
48 .events = (events & ND_POLL_READ ? EPOLLIN : 0) | (events & ND_POLL_WRITE ? EPOLLOUT : 0),
49 .data.ptr = data,
50 };
37 - return epoll_ctl(ndpl->epoll_fd, EPOLL_CTL_ADD, fd, &ev) == 0;
51 + bool rc = epoll_ctl(ndpl->epoll_fd, EPOLL_CTL_ADD, fd, &ev) == 0;
52 + if(rc) ndpl->nfds++;
53 + internal_fatal(!rc, "epoll_ctl() failed");
54 + return rc;
55 }
56
57 // Remove a file descriptor from the event poll
41 -bool nd_poll_del(nd_poll_t *ndpl, int fd) {
42 - return epoll_ctl(ndpl->epoll_fd, EPOLL_CTL_DEL, fd, NULL) == 0;
58 +bool nd_poll_del(nd_poll_t *ndpl, int fd, void *data) {
59 + internal_fatal(!data, "nd_poll() does not support NULL data pointers");
60 +
61 + ndpl->nfds--; // we can't check for success/failure here, because epoll() removes fds when they are closed
62 + bool rc = epoll_ctl(ndpl->epoll_fd, EPOLL_CTL_DEL, fd, NULL) == 0;
63 + internal_error(!rc, "epoll_ctl() failed (is the socket already closed)"); // this is ok if the socket is already closed
64 +
65 + // we may have an event pending for this fd.
66 + // but epoll() does not give us fd in the events,
67 + // so we use the data pointer to invalidate it
68 + nd_poll_replace_data_on_loaded_events(ndpl, fd, data, NULL);
69 + return rc;
70 }
71
72 // Update an existing file descriptor in the event poll
73 bool nd_poll_upd(nd_poll_t *ndpl, int fd, nd_poll_event_t events, void *data) {
74 + internal_fatal(!data, "nd_poll() does not support NULL data pointers - you should also NEVER change the pointer with an update");
75 +
76 struct epoll_event ev = {
77 .events = (events & ND_POLL_READ ? EPOLLIN : 0) | (events & ND_POLL_WRITE ? EPOLLOUT : 0),
78 .data.ptr = data,
79 };
51 - return epoll_ctl(ndpl->epoll_fd, EPOLL_CTL_MOD, fd, &ev) == 0;
80 + bool rc = epoll_ctl(ndpl->epoll_fd, EPOLL_CTL_MOD, fd, &ev) == 0;
81 + internal_fatal(!rc, "epoll_ctl() failed");
82 + return rc;
83 }
84
54 -static inline bool nd_poll_get_next_event(nd_poll_t *ndpl, nd_poll_result_t *result) {
55 - for(size_t i = ndpl->last_pos; i < ndpl->used ;i++) {
56 - *result = (nd_poll_result_t){
57 - .events = 0,
58 - .data = ndpl->ev[i].data.ptr,
59 - };
85 +static inline nd_poll_event_t nd_poll_events_from_epoll_events(uint32_t events) {
86 + nd_poll_event_t nd_poll_events = ND_POLL_NONE;
87 +
88 + if (events & (EPOLLIN|EPOLLPRI|EPOLLRDNORM|EPOLLRDBAND))
89 + nd_poll_events |= ND_POLL_READ;
90
61 - if (ndpl->ev[i].events & EPOLLIN)
62 - result->events |= ND_POLL_READ;
91 + if (events & (EPOLLOUT|EPOLLWRNORM|EPOLLWRBAND))
92 + nd_poll_events |= ND_POLL_WRITE;
93
64 - if (ndpl->ev[i].events & EPOLLOUT)
65 - result->events |= ND_POLL_WRITE;
94 + if (events & EPOLLERR)
95 + nd_poll_events |= ND_POLL_ERROR;
96
67 - if (ndpl->ev[i].events & EPOLLERR)
68 - result->events |= ND_POLL_ERROR;
97 + if (events & (EPOLLHUP|EPOLLRDHUP))
98 + nd_poll_events |= ND_POLL_HUP;
99
70 - if (ndpl->ev[i].events & EPOLLHUP)
71 - result->events |= ND_POLL_HUP;
100 + return nd_poll_events;
101 +}
102
73 - ndpl->last_pos = i + 1;
103 +static inline bool nd_poll_get_next_event(nd_poll_t *ndpl, nd_poll_result_t *result) {
104 + while(ndpl->last_pos < ndpl->used) {
105 + void *data = ndpl->ev[ndpl->last_pos].data.ptr;
106 +
107 + // Skip events that have been invalidated by nd_poll_del()
108 + if(!data) {
109 + ndpl->last_pos++;
110 + continue;
111 + }
112 +
113 + *result = (nd_poll_result_t){
114 + .events = nd_poll_events_from_epoll_events(ndpl->ev[ndpl->last_pos].events),
115 + .data = data,
116 + };
117 +
118 + ndpl->last_pos++;
119 return true;
120 }
121
77 - ndpl->last_pos = _countof(ndpl->ev);
122 return false;
123 }
124
@@ -87,10 +131,15 @@ int nd_poll_wait(nd_poll_t *ndpl, int timeout_ms, nd_poll_result_t *result) {
131 errno_clear();
132 ndpl->last_pos = 0;
133 ndpl->used = 0;
90 - int n = epoll_wait(ndpl->epoll_fd, &ndpl->ev[0], _countof(ndpl->ev), timeout_ms);
134 +
135 + int maxevents = ndpl->nfds / 2;
136 + if(maxevents > (int)_countof(ndpl->ev)) maxevents = (int)_countof(ndpl->ev);
137 + if(maxevents < 2) maxevents = 2;
138 +
139 + int n = epoll_wait(ndpl->epoll_fd, &ndpl->ev[0], maxevents, timeout_ms);
140
141 if(unlikely(n <= 0)) {
93 - if (n == 0) {
142 + if(n == 0) {
143 result->events = ND_POLL_TIMEOUT;
144 result->data = NULL;
145 return 0;
@@ -99,15 +148,17 @@ int nd_poll_wait(nd_poll_t *ndpl, int timeout_ms, nd_poll_result_t *result) {
148 if(errno == EINTR || errno == EAGAIN)
149 continue;
150
102 - result->events = ND_POLL_OTHER_ERROR;
151 + result->events = ND_POLL_POLL_FAILED;
152 result->data = NULL;
153 return -1;
154 }
155
156 ndpl->used = n;
157 + ndpl->last_pos = 0;
158 if (nd_poll_get_next_event(ndpl, result))
159 return 1;
160
161 + internal_fatal(true, "nd_poll_get_next_event() should have 1 event!");
162 } while(true);
163 }
164
@@ -172,13 +223,18 @@ bool nd_poll_add(nd_poll_t *ndpl, int fd, nd_poll_event_t events, void *data) {
223 }
224
225 // Remove a file descriptor from the event poll
175 -bool nd_poll_del(nd_poll_t *ndpl, int fd) {
226 +bool nd_poll_del(nd_poll_t *ndpl, int fd, void *data __maybe_unused) {
227 for (nfds_t i = 0; i < ndpl->nfds; i++) {
228 if (ndpl->fds[i].fd == fd) {
229 +
230 // Remove the file descriptor by shifting the array
231 memmove(&ndpl->fds[i], &ndpl->fds[i + 1], (ndpl->nfds - i - 1) * sizeof(struct pollfd));
232 ndpl->nfds--;
233 POINTERS_DEL(&ndpl->pointers, fd);
234 +
235 + if(i < ndpl->last_pos)
236 + ndpl->last_pos--;
237 +
238 return true;
239 }
240 }
@@ -205,31 +261,37 @@ bool nd_poll_upd(nd_poll_t *ndpl, int fd, nd_poll_event_t events, void *data) {
261 return false;
262 }
263
208 -static inline bool nd_poll_get_next_event(nd_poll_t *ndpl, nd_poll_result_t *result) {
209 - for (nfds_t i = ndpl->last_pos; i < ndpl->nfds; i++) {
210 - if (ndpl->fds[i].revents != 0) {
264 +static inline nd_poll_event_t nd_poll_events_from_poll_revents(short int events) {
265 + nd_poll_event_t nd_poll_events = ND_POLL_NONE;
266
212 - result->data = POINTERS_GET(&ndpl->pointers, ndpl->fds[i].fd);
267 + if (events & (POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND))
268 + nd_poll_events |= ND_POLL_READ;
269
214 - result->events = 0;
215 - if (ndpl->fds[i].revents & (POLLIN|POLLPRI))
216 - result->events |= ND_POLL_READ;
270 + if (events & (POLLOUT|POLLWRNORM|POLLWRBAND))
271 + nd_poll_events |= ND_POLL_WRITE;
272
218 - if (ndpl->fds[i].revents & POLLOUT)
219 - result->events |= ND_POLL_WRITE;
273 + if (events & POLLERR)
274 + nd_poll_events |= ND_POLL_ERROR;
275
221 - if (ndpl->fds[i].revents & POLLERR)
222 - result->events |= ND_POLL_ERROR;
276 + if (events & (POLLHUP|POLLRDHUP))
277 + nd_poll_events |= ND_POLL_HUP;
278
224 - if (ndpl->fds[i].revents & POLLHUP)
225 - result->events |= ND_POLL_HUP;
279 + if (events & (POLLNVAL))
280 + nd_poll_events |= ND_POLL_INVALID;
281 +
282 + return nd_poll_events;
283 +}
284 +
285 +static inline bool nd_poll_get_next_event(nd_poll_t *ndpl, nd_poll_result_t *result) {
286 + for (nfds_t i = ndpl->last_pos; i < ndpl->nfds; i++) {
287 + if (ndpl->fds[i].revents != 0) {
288
227 - if (ndpl->fds[i].revents & POLLNVAL)
228 - result->events |= ND_POLL_INVALID;
289 + result->data = POINTERS_GET(&ndpl->pointers, ndpl->fds[i].fd);
290 + result->events = nd_poll_events_from_poll_revents(ndpl->fds[i].revents);
291 + ndpl->fds[i].revents = 0;
292
230 - ndpl->fds[i].revents = 0; // Clear the event after handling
293 ndpl->last_pos = i + 1;
232 - return true; // Return only the first triggered event
294 + return true;
295 }
296 }
297
@@ -259,25 +321,25 @@ int nd_poll_wait(nd_poll_t *ndpl, int timeout_ms, nd_poll_result_t *result) {
321 int ret = poll(ndpl->fds, ndpl->nfds, timeout_ms);
322
323 if(unlikely(ret <= 0)) {
262 - if (ret < 0) {
263 - if(errno == EAGAIN || errno == EINTR)
264 - continue;
265 -
266 - result->events = ND_POLL_OTHER_ERROR;
267 - result->data = NULL;
268 - return -1;
269 - }
270 - else {
324 + if(ret == 0) {
325 result->events = ND_POLL_TIMEOUT;
326 result->data = NULL;
327 return 0;
328 }
329 +
330 + if(errno == EAGAIN || errno == EINTR)
331 + continue;
332 +
333 + result->events = ND_POLL_POLL_FAILED;
334 + result->data = NULL;
335 + return -1;
336 }
337
338 // Process the next event
339 if (nd_poll_get_next_event(ndpl, result))
340 return 1;
341
342 + internal_fatal(true, "nd_poll_get_next_event() should have 1 event!");
343 } while (true);
344 }
345
src/libnetdata/socket/nd-poll.h
+25 -11
@@ -3,16 +3,19 @@
3 #ifndef NETDATA_ND_POLL_H
4 #define NETDATA_ND_POLL_H
5
6 -#include "libnetdata/libnetdata.h"
6 +#include "libnetdata/common.h"
7 +
8 +typedef enum __attribute__((packed)) {
9 + ND_POLL_NONE = 0,
10 +
11 + ND_POLL_READ = 1 << 0, // same as EPOLLIN, POLLIN
12 + ND_POLL_WRITE = 1 << 2, // same as EPOLLOUT, POLLOUT
13 + ND_POLL_ERROR = 1 << 3, // same as EPOLLERR, POLLERR
14 + ND_POLL_HUP = 1 << 4, // same as EPOLLHUP, POLLHUP
15 + ND_POLL_INVALID = 1 << 5, // same as POLLNVAL
16
8 -typedef enum {
9 - ND_POLL_READ = 1 << 0,
10 - ND_POLL_WRITE = 1 << 1,
11 - ND_POLL_ERROR = 1 << 2,
12 - ND_POLL_HUP = 1 << 3,
13 - ND_POLL_INVALID = 1 << 4,
14 - ND_POLL_TIMEOUT = 1 << 5,
15 - ND_POLL_OTHER_ERROR = 1 << 6,
17 + ND_POLL_TIMEOUT = 1 << 6,
18 + ND_POLL_POLL_FAILED = 1 << 7,
19 } nd_poll_event_t;
20
21 typedef struct {
@@ -23,13 +26,24 @@ typedef struct {
26 typedef struct nd_poll_t nd_poll_t;
27
28 nd_poll_t *nd_poll_create();
29 +void nd_poll_destroy(nd_poll_t *ndpl);
30 +
31 +// the events can be updated with nd_poll_upd
32 +// the data pointer SHOULD NEVER be changed and cannot be NULL
33 bool nd_poll_add(nd_poll_t *ndpl, int fd, nd_poll_event_t events, void *data);
27 -bool nd_poll_del(nd_poll_t *ndpl, int fd);
34 +
35 +// give the same data pointer used in nd_poll_add()
36 +// otherwise, you may receive back invalid events
37 +bool nd_poll_del(nd_poll_t *ndpl, int fd, void *data);
38 +
39 +// this is for updating events
40 +// the data pointer must be the same used in nd_poll_add()
41 +// to change the data pointer, delete and add the same fd again
42 bool nd_poll_upd(nd_poll_t *ndpl, int fd, nd_poll_event_t events, void *data);
43
44 // returns -1 = error, 0 = timeout, 1 = event in result
45 int nd_poll_wait(nd_poll_t *ndpl, int timeout_ms, nd_poll_result_t *result);
46
33 -void nd_poll_destroy(nd_poll_t *ndpl);
47 +#include "libnetdata/libnetdata.h"
48
49 #endif //NETDATA_ND_POLL_H
src/libnetdata/socket/poll-events.c
+151 -294
@@ -2,25 +2,31 @@
2
3 #include "libnetdata/libnetdata.h"
4
5 +static inline void poll_process_updated_events(POLLINFO *pi) {
6 + if(pi->events != pi->events_we_wait_for) {
7 + if(!nd_poll_upd(pi->p->ndpl, pi->fd, pi->events, pi))
8 + nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to update socket %d to nd_poll", pi->fd);
9 + pi->events_we_wait_for = pi->events;
10 + }
11 +}
12 +
13 // poll() based listener
14 // this should be the fastest possible listener for up to 100 sockets
15 // above 100, an epoll() interface is needed on Linux
16
9 -#define POLL_FDS_INCREASE_STEP 10
10 -
11 -inline POLLINFO *poll_add_fd(POLLJOB *p
12 - , int fd
13 - , int socktype
14 - , HTTP_ACL port_acl
15 - , uint32_t flags
16 - , const char *client_ip
17 - , const char *client_port
18 - , const char *client_host
19 - , void *(*add_callback)(POLLINFO * /*pi*/, short int * /*events*/, void * /*data*/)
20 - , void (*del_callback)(POLLINFO * /*pi*/)
21 - , int (*rcv_callback)(POLLINFO * /*pi*/, short int * /*events*/)
22 - , int (*snd_callback)(POLLINFO * /*pi*/, short int * /*events*/)
23 - , void *data
17 +POLLINFO *poll_add_fd(POLLJOB *p
18 + , int fd
19 + , int socktype
20 + , HTTP_ACL port_acl
21 + , uint32_t flags
22 + , const char *client_ip
23 + , const char *client_port
24 + , const char *client_host
25 + , poll_events_add_callback_t add_callback
26 + , poll_events_del_callback_t del_callback
27 + , poll_events_rcv_callback_t rcv_callback
28 + , poll_events_snd_callback_t snd_callback
29 + , void *data
30 ) {
31 if(unlikely(fd < 0)) return NULL;
32
@@ -30,56 +36,14 @@ inline POLLINFO *poll_add_fd(POLLJOB *p
36 // return NULL;
37 //}
38
33 - if(unlikely(!p->first_free)) {
34 - size_t new_slots = p->slots + POLL_FDS_INCREASE_STEP;
35 -
36 - p->fds = reallocz(p->fds, sizeof(struct pollfd) * new_slots);
37 - p->inf = reallocz(p->inf, sizeof(POLLINFO) * new_slots);
38 -
39 - // reset all the newly added slots
40 - ssize_t i;
41 - for(i = new_slots - 1; i >= (ssize_t)p->slots ; i--) {
42 - p->fds[i].fd = -1;
43 - p->fds[i].events = 0;
44 - p->fds[i].revents = 0;
45 -
46 - p->inf[i].p = p;
47 - p->inf[i].slot = (size_t)i;
48 - p->inf[i].flags = 0;
49 - p->inf[i].socktype = -1;
50 - p->inf[i].port_acl = -1;
51 -
52 - p->inf[i].client_ip = NULL;
53 - p->inf[i].client_port = NULL;
54 - p->inf[i].client_host = NULL;
55 - p->inf[i].del_callback = p->del_callback;
56 - p->inf[i].rcv_callback = p->rcv_callback;
57 - p->inf[i].snd_callback = p->snd_callback;
58 - p->inf[i].data = NULL;
59 -
60 - // link them so that the first free will be earlier in the array
61 - // (we loop decrementing i)
62 - p->inf[i].next = p->first_free;
63 - p->first_free = &p->inf[i];
64 - }
65 -
66 - p->slots = new_slots;
67 - }
68 -
69 - POLLINFO *pi = p->first_free;
70 - p->first_free = p->first_free->next;
71 -
72 - struct pollfd *pf = &p->fds[pi->slot];
73 - pf->fd = fd;
74 - pf->events = POLLIN;
75 - pf->revents = 0;
39 + POLLINFO *pi = callocz(1, sizeof(*pi));
40
41 pi->fd = fd;
42 + pi->events = ND_POLL_READ;
43 pi->p = p;
44 pi->socktype = socktype;
45 pi->port_acl = port_acl;
46 pi->flags = flags;
82 - pi->next = NULL;
47 pi->client_ip = strdupz(client_ip);
48 pi->client_port = strdupz(client_port);
49 pi->client_host = strdupz(client_host);
@@ -96,81 +60,48 @@ inline POLLINFO *poll_add_fd(POLLJOB *p
60 pi->send_count = 0;
61
62 p->used++;
99 - if(unlikely(pi->slot > p->max))
100 - p->max = pi->slot;
63
102 - if(pi->flags & POLLINFO_FLAG_CLIENT_SOCKET) {
103 - pi->data = add_callback(pi, &pf->events, data);
104 - }
64 + if(pi->flags & POLLINFO_FLAG_CLIENT_SOCKET)
65 + pi->data = add_callback(pi, &pi->events, data);
66
106 - if(pi->flags & POLLINFO_FLAG_SERVER_SOCKET) {
107 - p->min = pi->slot;
108 - }
67 + DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(p->ll, pi, prev, next);
68 + pi->events_we_wait_for = pi->events;
69 + if(!nd_poll_add(pi->p->ndpl, pi->fd, pi->events, pi))
70 + nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to add socket %d to nd_poll", pi->fd);
71
72 return pi;
73 }
74
113 -inline void poll_close_fd(POLLINFO *pi) {
75 +static inline void poll_close_fd(POLLINFO *pi, const char *func) {
76 POLLJOB *p = pi->p;
77
116 - struct pollfd *pf = &p->fds[pi->slot];
117 -
118 - if(unlikely(pf->fd == -1)) return;
78 + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(p->ll, pi, prev, next);
79 + if(!nd_poll_del(p->ndpl, pi->fd, pi))
80 + // this is ok, if the socket is already closed
81 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
82 + "Failed to delete socket %d from nd_poll() - called from %s() - is the socket already closed?",
83 + pi->fd, func);
84
85 if(pi->flags & POLLINFO_FLAG_CLIENT_SOCKET) {
86 pi->del_callback(pi);
87
88 if(likely(!(pi->flags & POLLINFO_FLAG_DONT_CLOSE))) {
124 - if(close(pf->fd) == -1)
89 + if(close(pi->fd) == -1)
90 nd_log(NDLS_DAEMON, NDLP_ERR,
91 "Failed to close() poll_events() socket %d",
127 - pf->fd);
92 + pi->fd);
93 }
94 }
95
131 - pf->fd = -1;
132 - pf->events = 0;
133 - pf->revents = 0;
134 -
135 - pi->fd = -1;
136 - pi->socktype = -1;
137 - pi->flags = 0;
138 - pi->data = NULL;
139 -
140 - pi->del_callback = NULL;
141 - pi->rcv_callback = NULL;
142 - pi->snd_callback = NULL;
143 -
96 freez(pi->client_ip);
145 - pi->client_ip = NULL;
146 -
97 freez(pi->client_port);
148 - pi->client_port = NULL;
149 -
98 freez(pi->client_host);
151 - pi->client_host = NULL;
152 -
153 - pi->next = p->first_free;
154 - p->first_free = pi;
99 + freez(pi);
100
101 p->used--;
157 - if(unlikely(p->max == pi->slot)) {
158 - p->max = p->min;
159 - ssize_t i;
160 - for(i = (ssize_t)pi->slot; i > (ssize_t)p->min ;i--) {
161 - if (unlikely(p->fds[i].fd != -1)) {
162 - p->max = (size_t)i;
163 - break;
164 - }
165 - }
166 - }
102 }
103
169 -void *poll_default_add_callback(POLLINFO *pi, short int *events, void *data) {
170 - (void)pi;
171 - (void)events;
172 - (void)data;
173 -
104 +void *poll_default_add_callback(POLLINFO *pi __maybe_unused, nd_poll_event_t *events __maybe_unused, void *data __maybe_unused) {
105 return NULL;
106 }
107
@@ -180,8 +111,8 @@ void poll_default_del_callback(POLLINFO *pi) {
111 "POLLFD: internal error: del_callback_default() called with data pointer - possible memory leak");
112 }
113
183 -int poll_default_rcv_callback(POLLINFO *pi, short int *events) {
184 - *events |= POLLIN;
114 +int poll_default_rcv_callback(POLLINFO *pi, nd_poll_event_t *events) {
115 + *events |= ND_POLL_READ;
116
117 char buffer[1024 + 1];
118
@@ -208,8 +139,8 @@ int poll_default_rcv_callback(POLLINFO *pi, short int *events) {
139 return 0;
140 }
141
211 -int poll_default_snd_callback(POLLINFO *pi, short int *events) {
212 - *events &= ~POLLOUT;
142 +int poll_default_snd_callback(POLLINFO *pi, nd_poll_event_t *events) {
143 + *events &= ~ND_POLL_WRITE;
144
145 nd_log(NDLS_DAEMON, NDLP_WARNING,
146 "POLLFD: internal error: poll_default_snd_callback(): nothing to send on socket %d",
@@ -226,16 +157,17 @@ static void poll_events_cleanup(void *pptr) {
157 POLLJOB *p = CLEANUP_FUNCTION_GET_PTR(pptr);
158 if(!p) return;
159
229 - for(size_t i = 0 ; i <= p->max ; i++) {
230 - POLLINFO *pi = &p->inf[i];
231 - poll_close_fd(pi);
160 + while(p->ll) {
161 + POLLINFO *pi = p->ll;
162 + pi->flags &= ~(POLLINFO_FLAG_DONT_CLOSE);
163 + poll_close_fd(pi, __FUNCTION__ );
164 }
165
234 - freez(p->fds);
235 - freez(p->inf);
166 + nd_poll_destroy(p->ndpl);
167 + p->ndpl = NULL;
168 }
169
238 -static int poll_process_error(POLLINFO *pi, struct pollfd *pf, short int revents) {
170 +static int poll_process_error(POLLINFO *pi, nd_poll_event_t revents) {
171 ND_LOG_STACK lgs[] = {
172 ND_LOG_FIELD_TXT(NDF_SRC_IP, pi->client_ip),
173 ND_LOG_FIELD_TXT(NDF_SRC_PORT, pi->client_port),
@@ -244,62 +176,50 @@ static int poll_process_error(POLLINFO *pi, struct pollfd *pf, short int revents
176 ND_LOG_STACK_PUSH(lgs);
177
178 nd_log(NDLS_DAEMON, NDLP_DEBUG,
247 - "POLLFD: LISTENER: received %s %s %s on socket at slot %zu (fd %d) client '%s' port '%s' expecting %s %s %s, having %s %s %s"
248 - , revents & POLLERR ? "POLLERR" : ""
249 - , revents & POLLHUP ? "POLLHUP" : ""
250 - , revents & POLLNVAL ? "POLLNVAL" : ""
251 - , pi->slot
179 + "POLLFD: LISTENER: received %s %s %s on socket %d client '%s' port '%s' expecting %s %s, having %s %s"
180 + , revents & ND_POLL_ERROR ? "ERROR" : ""
181 + , revents & ND_POLL_HUP ? "HUP" : ""
182 + , revents & ND_POLL_INVALID ? "INVALID" : ""
183 , pi->fd
184 , pi->client_ip ? pi->client_ip : "<undefined-ip>"
185 , pi->client_port ? pi->client_port : "<undefined-port>"
255 - , pf->events & POLLIN ? "POLLIN" : "", pf->events & POLLOUT ? "POLLOUT" : "", pf->events & POLLPRI ? "POLLPRI" : ""
256 - , revents & POLLIN ? "POLLIN" : "", revents & POLLOUT ? "POLLOUT" : "", revents & POLLPRI ? "POLLPRI" : ""
186 + , pi->events & ND_POLL_READ ? "READ" : "", pi->events & ND_POLL_WRITE ? "WRITE" : ""
187 + , revents & ND_POLL_READ ? "READ" : "", revents & ND_POLL_WRITE ? "WRITE" : ""
188 );
189
259 - pf->events = 0;
260 - poll_close_fd(pi);
190 + poll_close_fd(pi, __FUNCTION__ );
191 return 1;
192 }
193
264 -static inline int poll_process_send(POLLJOB *p, POLLINFO *pi, struct pollfd *pf, time_t now) {
194 +static inline int poll_process_send(POLLINFO *pi, time_t now) {
195 pi->last_sent_t = now;
196 pi->send_count++;
197
268 - pf->events = 0;
269 -
270 - // remember the slot, in case we need to close it later
271 - // the callback may manipulate the socket list and our pf and pi pointers may be invalid after that call
272 - size_t slot = pi->slot;
198 + pi->events = 0;
199
274 - if (unlikely(pi->snd_callback(pi, &pf->events) == -1))
275 - poll_close_fd(&p->inf[slot]);
276 -
277 - // IMPORTANT:
278 - // pf and pi may be invalid below this point, they may have been reallocated.
200 + if (unlikely(pi->snd_callback(pi, &pi->events) == -1))
201 + poll_close_fd(pi, __FUNCTION__ );
202 + else
203 + poll_process_updated_events(pi);
204
205 return 1;
206 }
207
283 -static inline int poll_process_tcp_read(POLLJOB *p, POLLINFO *pi, struct pollfd *pf, time_t now) {
208 +static inline int poll_process_tcp_read(POLLINFO *pi, time_t now) {
209 pi->last_received_t = now;
210 pi->recv_count++;
211
287 - pf->events = 0;
288 -
289 - // remember the slot, in case we need to close it later
290 - // the callback may manipulate the socket list and our pf and pi pointers may be invalid after that call
291 - size_t slot = pi->slot;
212 + pi->events = 0;
213
293 - if (pi->rcv_callback(pi, &pf->events) == -1)
294 - poll_close_fd(&p->inf[slot]);
295 -
296 - // IMPORTANT:
297 - // pf and pi may be invalid below this point, they may have been reallocated.
214 + if (pi->rcv_callback(pi, &pi->events) == -1)
215 + poll_close_fd(pi, __FUNCTION__ );
216 + else
217 + poll_process_updated_events(pi);
218
219 return 1;
220 }
221
302 -static inline int poll_process_udp_read(POLLINFO *pi, struct pollfd *pf, time_t now __maybe_unused) {
222 +static inline int poll_process_udp_read(POLLINFO *pi, time_t now __maybe_unused) {
223 pi->last_received_t = now;
224 pi->recv_count++;
225
@@ -307,17 +227,19 @@ static inline int poll_process_udp_read(POLLINFO *pi, struct pollfd *pf, time_t
227 // but checking the access list on every UDP packet will destroy
228 // performance, especially for statsd.
229
310 - pf->events = 0;
311 - if(pi->rcv_callback(pi, &pf->events) == -1)
312 - return 0;
230 + pi->events = 0;
231
314 - // IMPORTANT:
315 - // pf and pi may be invalid below this point, they may have been reallocated.
316 -
317 - return 1;
232 + if(pi->rcv_callback(pi, &pi->events) == -1)
233 + return 0;
234 + else {
235 + poll_process_updated_events(pi);
236 + return 1;
237 + }
238 }
239
320 -static int poll_process_new_tcp_connection(POLLJOB *p, POLLINFO *pi, struct pollfd *pf, time_t now) {
240 +static int poll_process_new_tcp_connection(POLLINFO *pi, time_t now) {
241 + POLLJOB *p = pi->p;
242 +
243 pi->last_received_t = now;
244 pi->recv_count++;
245
@@ -332,7 +254,7 @@ static int poll_process_new_tcp_connection(POLLJOB *p, POLLINFO *pi, struct poll
254 #endif
255
256 int nfd = accept_socket(
335 - pf->fd, flags,
257 + pi->fd, flags,
258 client_ip, INET6_ADDRSTRLEN, client_port,NI_MAXSERV, client_host, NI_MAXHOST,
259 p->access_list, p->allow_dns
260 );
@@ -358,6 +280,12 @@ static int poll_process_new_tcp_connection(POLLJOB *p, POLLINFO *pi, struct poll
280 "POLLFD: LISTENER: accept() failed.");
281
282 }
283 + else if(is_socket_closed(nfd)) {
284 + nd_log_limit_static_global_var(erl, 10, 1000);
285 + nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
286 + "POLLFD: LISTENER: received client socket %d is closed on accept(), dropping connection", nfd);
287 + close(nfd);
288 + }
289 else {
290 // accept ok
291
@@ -376,9 +304,6 @@ static int poll_process_new_tcp_connection(POLLJOB *p, POLLINFO *pi, struct poll
304 , NULL
305 );
306
379 - // IMPORTANT:
380 - // pf and pi may be invalid below this point, they may have been reallocated.
381 -
307 return 1;
308 }
309
@@ -386,13 +311,13 @@ static int poll_process_new_tcp_connection(POLLJOB *p, POLLINFO *pi, struct poll
311 }
312
313 void poll_events(LISTEN_SOCKETS *sockets
389 - , void *(*add_callback)(POLLINFO * /*pi*/, short int * /*events*/, void * /*data*/)
390 - , void (*del_callback)(POLLINFO * /*pi*/)
391 - , int (*rcv_callback)(POLLINFO * /*pi*/, short int * /*events*/)
392 - , int (*snd_callback)(POLLINFO * /*pi*/, short int * /*events*/)
393 - , void (*tmr_callback)(void * /*timer_data*/)
394 - , bool (*check_to_stop_callback)(void)
395 - , SIMPLE_PATTERN *access_list
314 + , poll_events_add_callback_t add_callback
315 + , poll_events_del_callback_t del_callback
316 + , poll_events_rcv_callback_t rcv_callback
317 + , poll_events_snd_callback_t snd_callback
318 + , poll_events_tmr_callback_t tmr_callback
319 + , bool (*check_to_stop_callback)(void)
320 + , SIMPLE_PATTERN *access_list
321 , int allow_dns
322 , void *data
323 , time_t tcp_request_timeout_seconds
@@ -412,13 +337,9 @@ void poll_events(LISTEN_SOCKETS *sockets
337 int retval;
338
339 POLLJOB p = {
415 - .slots = 0,
340 + .ndpl = nd_poll_create(),
341 .used = 0,
417 - .max = 0,
342 .limit = max_tcp_sockets,
419 - .fds = NULL,
420 - .inf = NULL,
421 - .first_free = NULL,
343
344 .complete_request_timeout = tcp_request_timeout_seconds,
345 .idle_timeout = tcp_idle_timeout_seconds,
@@ -496,21 +417,20 @@ void poll_events(LISTEN_SOCKETS *sockets
417 "%s listening sockets (used TCP sockets %zu, max allowed for this worker %zu)",
418 (listen_sockets_active)?"ENABLING":"DISABLING", p.used, p.limit);
419
499 - for (i = 0; i <= p.max; i++) {
500 - if(p.inf[i].flags & POLLINFO_FLAG_SERVER_SOCKET && p.inf[i].socktype == SOCK_STREAM) {
501 - p.fds[i].events = (short int) ((listen_sockets_active) ? POLLIN : 0);
420 + for(POLLINFO *pi = p.ll; pi ; pi = pi->next) {
421 + if((pi->flags & POLLINFO_FLAG_SERVER_SOCKET) && pi->socktype == SOCK_STREAM) {
422 + pi->events = (short int) ((listen_sockets_active) ? ND_POLL_READ : 0);
423 + poll_process_updated_events(pi);
424 }
425 }
426 }
427
506 - retval = poll(p.fds, p.max + 1, ND_CHECK_CANCELLABILITY_WHILE_WAITING_EVERY_MS);
428 + nd_poll_result_t result;
429 + retval = nd_poll_wait(p.ndpl, ND_CHECK_CANCELLABILITY_WHILE_WAITING_EVERY_MS, &result);
430 time_t now = now_boottime_sec();
431
432 if(unlikely(retval == -1)) {
510 - nd_log(NDLS_DAEMON, NDLP_ERR,
511 - "POLLFD: LISTENER: poll() failed while waiting on %zu sockets.",
512 - p.max + 1);
513 -
433 + nd_log(NDLS_DAEMON, NDLP_ERR, "POLLFD: LISTENER: nd_poll_wait() failed.");
434 break;
435 }
436 else if(unlikely(!retval)) {
@@ -518,134 +438,71 @@ void poll_events(LISTEN_SOCKETS *sockets
438 ;
439 }
440 else {
521 - POLLINFO *pi;
522 - struct pollfd *pf;
523 - size_t idx, processed = 0;
524 - short int revents;
525 -
526 - // keep fast lookup arrays per function
527 - // to avoid looping through the entire list every time
528 - size_t sends[p.max + 1], sends_max = 0;
529 - size_t reads[p.max + 1], reads_max = 0;
530 - size_t conns[p.max + 1], conns_max = 0;
531 - size_t udprd[p.max + 1], udprd_max = 0;
532 -
533 - for (i = 0; i <= p.max; i++) {
534 - pi = &p.inf[i];
535 - pf = &p.fds[i];
536 - revents = pf->revents;
537 -
538 - if(unlikely(revents == 0 || pf->fd == -1))
539 - continue;
540 -
541 - if (unlikely(revents & (POLLERR|POLLHUP|POLLNVAL))) {
542 - // something is wrong to one of our sockets
543 -
544 - pf->revents = 0;
545 - processed += poll_process_error(pi, pf, revents);
546 - }
547 - else if (likely(revents & POLLOUT)) {
548 - // a client is ready to receive data
441 + POLLINFO *pi = result.data;
442
550 - sends[sends_max++] = i;
551 - }
552 - else if (likely(revents & (POLLIN|POLLPRI))) {
553 - if (pi->flags & POLLINFO_FLAG_CLIENT_SOCKET) {
554 - // a client sent data to us
443 + if(result.events & (ND_POLL_HUP | ND_POLL_INVALID | ND_POLL_ERROR))
444 + poll_process_error(pi, result.events);
445
556 - reads[reads_max++] = i;
557 - }
558 - else if (pi->flags & POLLINFO_FLAG_SERVER_SOCKET) {
559 - // something is coming to our server sockets
560 -
561 - if(pi->socktype == SOCK_DGRAM) {
562 - // UDP receive, directly on our listening socket
563 -
564 - udprd[udprd_max++] = i;
565 - }
566 - else if(pi->socktype == SOCK_STREAM) {
567 - // new TCP connection
568 -
569 - conns[conns_max++] = i;
570 - }
571 - else
572 - nd_log(NDLS_DAEMON, NDLP_ERR,
573 - "POLLFD: LISTENER: server slot %zu (fd %d) connection from %s port %s using unhandled socket type %d."
574 - , i
575 - , pi->fd
576 - , pi->client_ip ? pi->client_ip : "<undefined-ip>"
577 - , pi->client_port ? pi->client_port : "<undefined-port>"
578 - , pi->socktype
579 - );
580 - }
581 - else
446 + else if(result.events & ND_POLL_WRITE) {
447 + poll_process_send(pi, now);
448 + }
449 +
450 + else if(result.events & ND_POLL_READ) {
451 + if (pi->flags & POLLINFO_FLAG_CLIENT_SOCKET) {
452 + if (pi->socktype == SOCK_DGRAM)
453 + poll_process_udp_read(pi, now);
454 + else if (pi->socktype == SOCK_STREAM)
455 + poll_process_tcp_read(pi, now);
456 + else {
457 nd_log(NDLS_DAEMON, NDLP_ERR,
583 - "POLLFD: LISTENER: client slot %zu (fd %d) data from %s port %s using flags %08X is neither client nor server."
584 - , i
585 - , pi->fd
586 - , pi->client_ip ? pi->client_ip : "<undefined-ip>"
587 - , pi->client_port ? pi->client_port : "<undefined-port>"
588 - , pi->flags
589 - );
458 + "POLLFD: LISTENER: server slot %zu (fd %d) connection from %s port %s using unhandled socket type %d.",
459 + i,
460 + pi->fd,
461 + pi->client_ip ? pi->client_ip : "<undefined-ip>",
462 + pi->client_port ? pi->client_port : "<undefined-port>",
463 + pi->socktype);
464 +
465 + poll_close_fd(pi, "poll_events1");
466 + }
467 + }
468 + else if (pi->flags & POLLINFO_FLAG_SERVER_SOCKET) {
469 + if(!p.limit || p.used < p.limit)
470 + poll_process_new_tcp_connection(pi, now);
471 }
591 - else
472 + else {
473 nd_log(NDLS_DAEMON, NDLP_ERR,
593 - "POLLFD: LISTENER: socket slot %zu (fd %d) client %s port %s unhandled event id %d."
474 + "POLLFD: LISTENER: client slot %zu (fd %d) data from %s port %s using flags %08X is neither client nor server."
475 , i
476 , pi->fd
477 , pi->client_ip ? pi->client_ip : "<undefined-ip>"
478 , pi->client_port ? pi->client_port : "<undefined-port>"
598 - , revents
479 + , pi->flags
480 );
600 - }
481
602 - // process sends
603 - for (idx = 0; idx < sends_max; idx++) {
604 - i = sends[idx];
605 - pi = &p.inf[i];
606 - pf = &p.fds[i];
607 - pf->revents = 0;
608 - processed += poll_process_send(&p, pi, pf, now);
609 - }
610 -
611 - // process UDP reads
612 - for (idx = 0; idx < udprd_max; idx++) {
613 - i = udprd[idx];
614 - pi = &p.inf[i];
615 - pf = &p.fds[i];
616 - pf->revents = 0;
617 - processed += poll_process_udp_read(pi, pf, now);
618 - }
619 -
620 - // process TCP reads
621 - for (idx = 0; idx < reads_max; idx++) {
622 - i = reads[idx];
623 - pi = &p.inf[i];
624 - pf = &p.fds[i];
625 - pf->revents = 0;
626 - processed += poll_process_tcp_read(&p, pi, pf, now);
627 - }
628 -
629 - if(!processed && (!p.limit || p.used < p.limit)) {
630 - // nothing processed above (rcv, snd) and we have room for another TCP connection
631 - // so, accept one TCP connection
632 - for (idx = 0; idx < conns_max; idx++) {
633 - i = conns[idx];
634 - pi = &p.inf[i];
635 - pf = &p.fds[i];
636 - pf->revents = 0;
637 - if (poll_process_new_tcp_connection(&p, pi, pf, now))
638 - break;
482 + poll_close_fd(pi, "poll_events2");
483 }
484 }
485 + else {
486 + nd_log(NDLS_DAEMON, NDLP_ERR,
487 + "POLLFD: LISTENER: socket slot %zu (fd %d) client %s port %s unhandled event id %d."
488 + , i
489 + , pi->fd
490 + , pi->client_ip ? pi->client_ip : "<undefined-ip>"
491 + , pi->client_port ? pi->client_port : "<undefined-port>"
492 + , (int)result.events
493 + );
494 +
495 + poll_close_fd(pi, "poll_events3");
496 + }
497 }
498
499 if(unlikely(p.checks_every > 0 && now - last_check > p.checks_every)) {
500 last_check = now;
501
502 // cleanup old sockets
647 - for(i = 0; i <= p.max; i++) {
648 - POLLINFO *pi = &p.inf[i];
503 + POLLINFO *pi, *next = NULL;
504 + for(pi = p.ll; pi ; pi = next) {
505 + next = pi->next;
506
507 if(likely(pi->flags & POLLINFO_FLAG_CLIENT_SOCKET)) {
508 if (unlikely(pi->send_count == 0 && p.complete_request_timeout > 0 && (now - pi->connected_t) >= p.complete_request_timeout)) {
@@ -657,7 +514,7 @@ void poll_events(LISTEN_SOCKETS *sockets
514 , pi->client_port ? pi->client_port : "<undefined-port>"
515 , (size_t) p.complete_request_timeout
516 );
660 - poll_close_fd(pi);
517 + poll_close_fd(pi, "poll_events4");
518 }
519 else if(unlikely(pi->recv_count && p.idle_timeout > 0 && now - ((pi->last_received_t > pi->last_sent_t) ? pi->last_received_t : pi->last_sent_t) >= p.idle_timeout )) {
520 nd_log(NDLS_DAEMON, NDLP_DEBUG,
@@ -668,7 +525,7 @@ void poll_events(LISTEN_SOCKETS *sockets
525 , pi->client_port ? pi->client_port : "<undefined-port>"
526 , (size_t) p.idle_timeout
527 );
671 - poll_close_fd(pi);
528 + poll_close_fd(pi, "poll_events5");
529 }
530 }
531 }
src/libnetdata/socket/poll-events.h
+41 -42
@@ -3,15 +3,23 @@
3 #ifndef NETDATA_POLL_EVENTS_H
4 #define NETDATA_POLL_EVENTS_H
5
6 +#include "nd-poll.h"
7 +
8 #define POLLINFO_FLAG_SERVER_SOCKET 0x00000001
9 #define POLLINFO_FLAG_CLIENT_SOCKET 0x00000002
10 #define POLLINFO_FLAG_DONT_CLOSE 0x00000004
11
12 typedef struct poll POLLJOB;
13 +typedef struct pollinfo POLLINFO;
14 +
15 +typedef void *(*poll_events_add_callback_t)(POLLINFO *pi, nd_poll_event_t *events, void *data);
16 +typedef void (*poll_events_del_callback_t)(POLLINFO *pi);
17 +typedef int (*poll_events_rcv_callback_t)(POLLINFO *pi, nd_poll_event_t *events);
18 +typedef int (*poll_events_snd_callback_t)(POLLINFO *pi, nd_poll_event_t *events);
19 +typedef void (*poll_events_tmr_callback_t)(void *timer_data);
20
12 -typedef struct pollinfo {
21 +struct pollinfo {
22 POLLJOB *p; // the parent
14 - size_t slot; // the slot id
23
24 int fd; // the file descriptor
25 int socktype; // the client socket type
@@ -20,6 +28,9 @@ typedef struct pollinfo {
28 char *client_port; // Max NI_MAXSERV bytes
29 char *client_host; // Max NI_MAXHOST bytes
30
31 + nd_poll_event_t events;
32 + nd_poll_event_t events_we_wait_for;
33 +
34 time_t connected_t; // the time the socket connected
35 time_t last_received_t; // the time the socket last received data
36 time_t last_sent_t; // the time the socket last sent data
@@ -30,26 +41,21 @@ typedef struct pollinfo {
41 uint32_t flags; // internal flags
42
43 // callbacks for this socket
33 - void (*del_callback)(struct pollinfo *pi);
34 - int (*rcv_callback)(struct pollinfo *pi, short int *events);
35 - int (*snd_callback)(struct pollinfo *pi, short int *events);
44 + poll_events_del_callback_t del_callback;
45 + poll_events_rcv_callback_t rcv_callback;
46 + poll_events_snd_callback_t snd_callback;
47
48 // the user data
49 void *data;
50
40 - // linking of free pollinfo structures
41 - // for quickly finding the next available
42 - // this is like a stack, it grows and shrinks
43 - // (with gaps - lower empty slots are preferred)
44 - struct pollinfo *next;
45 -} POLLINFO;
51 + struct pollinfo *prev, *next;
52 +};
53
54 struct poll {
48 - size_t slots;
49 - size_t used;
50 - size_t min;
51 - size_t max;
55 + nd_poll_t *ndpl;
56 + POLLINFO *ll;
57
58 + size_t used;
59 size_t limit;
60
61 time_t complete_request_timeout;
@@ -59,26 +65,20 @@ struct poll {
65 time_t timer_milliseconds;
66 void *timer_data;
67
62 - struct pollfd *fds;
63 - struct pollinfo *inf;
64 - struct pollinfo *first_free;
65 -
68 SIMPLE_PATTERN *access_list;
69 int allow_dns;
70
69 - void *(*add_callback)(POLLINFO *pi, short int *events, void *data);
70 - void (*del_callback)(POLLINFO *pi);
71 - int (*rcv_callback)(POLLINFO *pi, short int *events);
72 - int (*snd_callback)(POLLINFO *pi, short int *events);
73 - void (*tmr_callback)(void *timer_data);
71 + poll_events_add_callback_t add_callback;
72 + poll_events_del_callback_t del_callback;
73 + poll_events_rcv_callback_t rcv_callback;
74 + poll_events_snd_callback_t snd_callback;
75 + poll_events_tmr_callback_t tmr_callback;
76 };
77
76 -#define pollinfo_from_slot(p, slot) (&((p)->inf[(slot)]))
77 -
78 -int poll_default_snd_callback(POLLINFO *pi, short int *events);
79 -int poll_default_rcv_callback(POLLINFO *pi, short int *events);
78 +int poll_default_snd_callback(POLLINFO *pi, nd_poll_event_t *events);
79 +int poll_default_rcv_callback(POLLINFO *pi, nd_poll_event_t *events);
80 void poll_default_del_callback(POLLINFO *pi);
81 -void *poll_default_add_callback(POLLINFO *pi, short int *events, void *data);
81 +void *poll_default_add_callback(POLLINFO *pi, nd_poll_event_t *events, void *data);
82
83 POLLINFO *poll_add_fd(POLLJOB *p
84 , int fd
@@ -88,22 +88,21 @@ POLLINFO *poll_add_fd(POLLJOB *p
88 , const char *client_ip
89 , const char *client_port
90 , const char *client_host
91 - , void *(*add_callback)(POLLINFO *pi, short int *events, void *data)
92 - , void (*del_callback)(POLLINFO *pi)
93 - , int (*rcv_callback)(POLLINFO *pi, short int *events)
94 - , int (*snd_callback)(POLLINFO *pi, short int *events)
95 - , void *data
91 + , poll_events_add_callback_t add_callback
92 + , poll_events_del_callback_t del_callback
93 + , poll_events_rcv_callback_t rcv_callback
94 + , poll_events_snd_callback_t snd_callback
95 + , void *data
96 );
97 -void poll_close_fd(POLLINFO *pi);
97
98 void poll_events(LISTEN_SOCKETS *sockets
100 - , void *(*add_callback)(POLLINFO *pi, short int *events, void *data)
101 - , void (*del_callback)(POLLINFO *pi)
102 - , int (*rcv_callback)(POLLINFO *pi, short int *events)
103 - , int (*snd_callback)(POLLINFO *pi, short int *events)
104 - , void (*tmr_callback)(void *timer_data)
105 - , bool (*check_to_stop_callback)(void)
106 - , SIMPLE_PATTERN *access_list
99 + , poll_events_add_callback_t add_callback
100 + , poll_events_del_callback_t del_callback
101 + , poll_events_rcv_callback_t rcv_callback
102 + , poll_events_snd_callback_t snd_callback
103 + , poll_events_tmr_callback_t tmr_callback
104 + , bool (*check_to_stop_callback)(void)
105 + , SIMPLE_PATTERN *access_list
106 , int allow_dns
107 , void *data
108 , time_t tcp_request_timeout_seconds
src/libnetdata/socket/socket.c
+212 -67
@@ -48,12 +48,6 @@ bool ip_to_hostname(const char *ip, char *dst, size_t dst_len) {
48 // --------------------------------------------------------------------------------------------------------------------
49 // various library calls
50
51 -#ifdef __gnu_linux__
52 -#define LARGE_SOCK_SIZE 33554431 // don't ask why - I found it at brubeck source - I guess it is just a large number
53 -#else
54 -#define LARGE_SOCK_SIZE 4096
55 -#endif
56 -
51 bool fd_is_socket(int fd) {
52 int type;
53 socklen_t len = sizeof(type);
@@ -63,7 +57,7 @@ bool fd_is_socket(int fd) {
57 return true;
58 }
59
66 -#ifdef POLLRDHUP
60 +#if defined(POLLRDHUP) && 0 // ktsaou: disabled because the recv() method is faster (1 syscall vs multiple by poll())
61 bool is_socket_closed(int fd) {
62 if(fd < 0)
63 return true;
@@ -114,94 +108,245 @@ bool is_socket_closed(int fd) {
108 }
109 #endif
110
117 -int sock_setnonblock(int fd) {
118 - int flags;
111 +#if defined(OS_LINUX)
112 +// Valid from: 4 KB to 64 MB (typical range)
113 +// Default is usually: 128 KB to 256 KB
114 +// Maximum is controlled by: /proc/sys/net/core/rmem_max and /proc/sys/net/core/wmem_max
115 +// Interactive applications should use: 256 KB
116 +// High-performance applications should use: 8 MB to 64 MB
117 +#define LARGE_SOCK_SIZE (32 * 1024 * 1024)
118 +
119 +#elif defined(OS_FREEBSD)
120 +// Valid from: 4 KB to 16 MB (typical range)
121 +// Default is usually: 64 KB to 256 KB
122 +// Maximum is controlled by: kern.ipc.maxsockbuf
123 +// Interactive applications should use: 128 KB to 256 KB
124 +// High-performance applications should use: 2 MB to 16 MB
125 +#define LARGE_SOCK_SIZE (8 * 1024 * 1024)
126 +
127 +#elif defined(OS_MACOS)
128 +// Valid from: 4 KB to 8 MB (typical range)
129 +// Default is usually: 128 KB
130 +// Maximum is controlled by: net.inet.tcp.sendspace and net.inet.tcp.recvspace
131 +// Interactive applications should use: 128 KB
132 +// High-performance applications should use: 1 MB to 8 MB
133 +#define LARGE_SOCK_SIZE (4 * 1024 * 1024)
134 +
135 +#elif defined(OS_WINDOWS)
136 +// Valid from: 8 KB to 16 MB (typical range)
137 +// Default is usually: 8 KB to 64 KB
138 +// Maximum is controlled by: registry keys such as TcpWindowSize
139 +// Interactive applications should use: 64 KB to 128 KB
140 +// High-performance applications should use: 1 MB to 16 MB
141 +#define LARGE_SOCK_SIZE (8 * 1024 * 1024)
142
120 - flags = fcntl(fd, F_GETFL);
121 - flags |= O_NONBLOCK;
143 +#else
144 +// Valid from: 4 KB to platform-dependent maximum
145 +// Default is usually: 64 KB to 256 KB
146 +// Interactive applications should use: 128 KB to 256 KB
147 +// High-performance applications should use: 1 MB to platform-dependent maximum
148 +#define LARGE_SOCK_SIZE (1 * 1024 * 1024)
149 +#endif
150
123 - int ret = fcntl(fd, F_SETFL, flags);
124 - if(ret < 0)
125 - nd_log(NDLS_DAEMON, NDLP_ERR,
126 - "Failed to set O_NONBLOCK on socket %d",
127 - fd);
151 +// Returns -1 for errors, current buffer size if successful
152 +int sock_enlarge_rcv_buf(int fd) {
153 + int ret = -1;
154 + int bs = LARGE_SOCK_SIZE;
155 + int current_bs = 0;
156 + socklen_t optlen = sizeof(current_bs);
157 +
158 + // Get the current receive buffer size
159 + if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &current_bs, &optlen) == 0) {
160 + // Set the buffer size only if it's smaller than the desired size
161 + if (current_bs < bs) {
162 + setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bs, sizeof(bs));
163 +
164 + // Re-check the buffer size after attempting to set it
165 + if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &current_bs, &optlen) == 0)
166 + ret = current_bs;
167 + } else {
168 + // Current buffer size is already large enough
169 + ret = current_bs;
170 + }
171 + }
172
173 return ret;
174 }
175
132 -int sock_delnonblock(int fd) {
133 - int flags;
176 +// Returns -1 for errors, current buffer size if successful
177 +int sock_enlarge_snd_buf(int fd) {
178 + int ret = -1;
179 + int bs = LARGE_SOCK_SIZE;
180 + int current_bs = 0;
181 + socklen_t optlen = sizeof(current_bs);
182 +
183 + // Get the current send buffer size
184 + if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &current_bs, &optlen) == 0) {
185 + // Set the buffer size only if it's smaller than the desired size
186 + if (current_bs < bs) {
187 + setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bs, sizeof(bs));
188 +
189 + // Re-check the buffer size after attempting to set it
190 + if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &current_bs, &optlen) == 0)
191 + ret = current_bs;
192 + } else {
193 + // Current buffer size is already large enough
194 + ret = current_bs;
195 + }
196 + }
197
135 - flags = fcntl(fd, F_GETFL);
136 - flags &= ~O_NONBLOCK;
198 + return ret;
199 +}
200
138 - int ret = fcntl(fd, F_SETFL, flags);
139 - if(ret < 0)
140 - nd_log(NDLS_DAEMON, NDLP_ERR,
141 - "Failed to remove O_NONBLOCK on socket %d",
142 - fd);
201 +// returns -1 for errors, 0 if cork is unset, 1 if cork is set
202 +int sock_setcork(int fd __maybe_unused, bool cork __maybe_unused) {
203 + int rc = -1;
204
144 - return ret;
205 +#ifdef TCP_CORK
206 + int tcp_cork = (cork) ? 1 : 0;
207 + socklen_t optlen = sizeof(tcp_cork);
208 +
209 + if(setsockopt(fd, IPPROTO_TCP, TCP_CORK, &tcp_cork, optlen) == 0) {
210 + // setting was successful, return the intended state
211 + rc = cork ? 1 : 0;
212 + }
213 + else if(getsockopt(fd, IPPROTO_TCP, TCP_CORK, &tcp_cork, &optlen) == 0) {
214 + // return the current state since retrieval is successful
215 + rc = tcp_cork ? 1 : 0;
216 + }
217 +#endif
218 +
219 + return rc;
220 }
221
147 -int sock_setreuse(int fd, int reuse) {
148 - int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
222 +// Returns -1 for errors, 0 if O_NONBLOCK is unset, 1 if O_NONBLOCK is set
223 +int sock_setnonblock(int fd, bool nonblock) {
224 + int rc = -1;
225 + int flags = fcntl(fd, F_GETFL);
226
150 - if(ret == -1)
151 - nd_log(NDLS_DAEMON, NDLP_ERR,
152 - "Failed to set SO_REUSEADDR on socket %d",
153 - fd);
227 + if (flags < 0) {
228 + // Failed to get current flags
229 + return -1;
230 + }
231
155 - return ret;
232 + int new_flags = nonblock ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK);
233 +
234 + if (fcntl(fd, F_SETFL, new_flags) == 0) {
235 + // Setting was successful, return the intended state
236 + rc = nonblock ? 1 : 0;
237 + } else {
238 + // If setting failed, return the current state
239 + flags = fcntl(fd, F_GETFL);
240 + if (flags >= 0)
241 + rc = (flags & O_NONBLOCK) ? 1 : 0;
242 + }
243 +
244 + return rc;
245 }
246
158 -void sock_setcloexec(int fd)
159 -{
160 - UNUSED(fd);
161 - int flags = fcntl(fd, F_GETFD);
162 - if (flags != -1)
163 - (void) fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
247 +// Returns -1 for errors, 0 if SO_REUSEADDR is unset, 1 if SO_REUSEADDR is set
248 +int sock_setreuse_addr(int fd, bool reuse) {
249 + int rc = -1;
250 + int reuse_val = reuse ? 1 : 0;
251 + socklen_t optlen = sizeof(reuse_val);
252 +
253 + // Attempt to set SO_REUSEADDR
254 + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_val, optlen) == 0) {
255 + // Setting was successful, return the intended state
256 + rc = reuse ? 1 : 0;
257 + } else {
258 + // If setting failed, attempt to retrieve the current state
259 + if (getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_val, &optlen) == 0) {
260 + // Return the current state
261 + rc = reuse_val ? 1 : 0;
262 + }
263 + }
264 +
265 + return rc;
266 }
267
166 -int sock_setreuse_port(int fd __maybe_unused, int reuse __maybe_unused) {
167 - int ret;
268 +// Returns -1 for errors, 0 if SO_REUSEPORT is unset, 1 if SO_REUSEPORT is set
269 +int sock_setreuse_port(int fd __maybe_unused, bool reuse __maybe_unused) {
270 + int rc = -1;
271
272 #ifdef SO_REUSEPORT
170 - ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
171 - if(ret == -1 && errno != ENOPROTOOPT)
172 - nd_log(NDLS_DAEMON, NDLP_ERR,
173 - "failed to set SO_REUSEPORT on socket %d",
174 - fd);
273 + int reuse_val = reuse ? 1 : 0;
274 + socklen_t optlen = sizeof(reuse_val);
275 +
276 + // Attempt to set SO_REUSEPORT
277 + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse_val, optlen) == 0) {
278 + // Setting was successful, return the intended state
279 + rc = reuse ? 1 : 0;
280 + } else if (errno != ENOPROTOOPT) {
281 + // If setting failed for a reason other than unsupported option, check the current state
282 + if (getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse_val, &optlen) == 0) {
283 + // Return the current state
284 + rc = reuse_val ? 1 : 0;
285 + }
286 + }
287 #else
176 - ret = -1;
288 + // SO_REUSEPORT is not supported
289 + errno = ENOPROTOOPT;
290 #endif
291
179 - return ret;
292 + return rc;
293 }
294
182 -int sock_enlarge_in(int fd) {
183 - int ret, bs = LARGE_SOCK_SIZE;
184 -
185 - ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bs, sizeof(bs));
295 +// Returns -1 for errors, 0 if FD_CLOEXEC is unset, 1 if FD_CLOEXEC is set
296 +int sock_setcloexec(int fd, bool cloexec) {
297 + int rc = -1;
298
187 - if(ret == -1)
188 - nd_log(NDLS_DAEMON, NDLP_ERR,
189 - "Failed to set SO_RCVBUF on socket %d",
190 - fd);
299 + // Get current file descriptor flags
300 + int flags = fcntl(fd, F_GETFD);
301 + if (flags == -1)
302 + return -1; // Error retrieving flags
303 +
304 + int new_flags = cloexec ? (flags | FD_CLOEXEC) : (flags & ~FD_CLOEXEC);
305 +
306 + // Set the FD_CLOEXEC flag as requested
307 + if (fcntl(fd, F_SETFD, new_flags) == 0) {
308 + // Setting was successful, return the intended state
309 + rc = cloexec ? 1 : 0;
310 + } else {
311 + // If setting failed, return the current state
312 + flags = fcntl(fd, F_GETFD);
313 + if (flags != -1) {
314 + rc = (flags & FD_CLOEXEC) ? 1 : 0;
315 + }
316 + }
317
192 - return ret;
318 + return rc;
319 }
320
195 -int sock_enlarge_out(int fd) {
196 - int ret, bs = LARGE_SOCK_SIZE;
197 - ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bs, sizeof(bs));
198 -
199 - if(ret == -1)
200 - nd_log(NDLS_DAEMON, NDLP_ERR,
201 - "Failed to set SO_SNDBUF on socket %d",
202 - fd);
321 +// Returns -1 for errors, 0 if TCP_DEFER_ACCEPT is unset, 1 if TCP_DEFER_ACCEPT is set
322 +int sock_set_tcp_defer_accept(int fd __maybe_unused, bool defer __maybe_unused) {
323 +#ifdef TCP_DEFER_ACCEPT
324 + // Check if the file descriptor is a socket
325 + if (!fd_is_socket(fd))
326 + return 0; // Not a socket
327 +
328 + int rc = -1;
329 + int timeout = defer ? 5 : 0; // Set timeout to 5 seconds for enabling, 0 to disable
330 + socklen_t optlen = sizeof(timeout);
331 +
332 + // Attempt to set TCP_DEFER_ACCEPT
333 + if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, optlen) == 0) {
334 + // Setting was successful, return the intended state
335 + rc = defer ? 1 : 0;
336 + } else if (errno != EINVAL && errno != ENOPROTOOPT) {
337 + // If setting failed and it's not because of invalid option or unsupported protocol
338 + // Check the current state
339 + if (getsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, &optlen) == 0) {
340 + rc = timeout > 0 ? 1 : 0;
341 + }
342 + }
343
204 - return ret;
344 + return rc;
345 +#else
346 + // TCP_DEFER_ACCEPT not supported
347 + errno = ENOPROTOOPT;
348 + return -1;
349 +#endif
350 }
351
352 // --------------------------------------------------------------------------------------------------------------------
@@ -437,7 +582,7 @@ int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *clien
582 if (!strcmp(client_ip, "127.0.0.1") || !strcmp(client_ip, "::1")) {
583 strncpyz(client_ip, "localhost", ipsize);
584 }
440 - sock_setcloexec(nfd);
585 + sock_setcloexec(nfd, true);
586
587 #ifdef __FreeBSD__
588 if(((struct sockaddr *)&sadr)->sa_family == AF_LOCAL)
src/libnetdata/socket/socket.h
+23 -7
@@ -13,13 +13,29 @@ int wait_on_socket_or_cancel_with_timeout(NETDATA_SSL *ssl, int fd, int timeout_
13 bool fd_is_socket(int fd);
14 bool is_socket_closed(int fd);
15
16 -int sock_setnonblock(int fd);
17 -int sock_delnonblock(int fd);
18 -int sock_setreuse(int fd, int reuse);
19 -void sock_setcloexec(int fd);
20 -int sock_setreuse_port(int fd, int reuse);
21 -int sock_enlarge_in(int fd);
22 -int sock_enlarge_out(int fd);
16 +// Returns -1 for errors, 0 if TCP_DEFER_ACCEPT is unset, 1 if TCP_DEFER_ACCEPT is set
17 +int sock_set_tcp_defer_accept(int fd, bool defer);
18 +
19 +// Returns -1 for errors, 0 if FD_CLOEXEC is unset, 1 if FD_CLOEXEC is set
20 +int sock_setcloexec(int fd, bool cloexec);
21 +
22 +// Returns -1 for errors, 0 if O_NONBLOCK is unset, 1 if O_NONBLOCK is set
23 +int sock_setnonblock(int fd, bool nonblock);
24 +
25 +// Returns -1 for errors, 0 if SO_REUSEADDR is unset, 1 if SO_REUSEADDR is set
26 +int sock_setreuse_addr(int fd, bool reuse);
27 +
28 +// Returns -1 for errors, 0 if SO_REUSEPORT is unset, 1 if SO_REUSEPORT is set
29 +int sock_setreuse_port(int fd, bool reuse);
30 +
31 +// Returns -1 for errors, current buffer size if successful
32 +int sock_enlarge_rcv_buf(int fd);
33 +
34 +// Returns -1 for errors, current buffer size if successful
35 +int sock_enlarge_snd_buf(int fd);
36 +
37 +// returns -1 for errors, 0 if cork is unset, 1 if cork is set
38 +int sock_setcork(int fd, bool cork);
39
40 int connection_allowed(int fd, char *client_ip, char *client_host, size_t hostsize,
41 SIMPLE_PATTERN *access_list, const char *patname, int allow_dns);
src/libnetdata/spawn_server/log-forwarder.c
+3 -2
@@ -49,7 +49,7 @@ static inline void log_forwarder_wake_up_worker(LOG_FORWARDER *lf) {
49 char ch = 0;
50 ssize_t bytes_written = write(lf->pipe_fds[PIPE_WRITE], &ch, 1);
51 if (bytes_written != 1)
52 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "Failed to write to notification pipe");
52 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "Log forwarder: Failed to write to notification pipe");
53 }
54
55 // --------------------------------------------------------------------------------------------------------------------
@@ -65,7 +65,8 @@ LOG_FORWARDER *log_forwarder_start(void) {
65 }
66
67 // make sure read() will not block on this pipe
68 - sock_setnonblock(lf->pipe_fds[PIPE_READ]);
68 + if(sock_setnonblock(lf->pipe_fds[PIPE_READ], true) != 1)
69 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "Log forwarder: Failed to set non-blocking mode");
70
71 lf->running = true;
72 lf->thread = nd_thread_create("log-fw", NETDATA_THREAD_OPTION_JOINABLE, log_forwarder_thread_func, lf);
src/libnetdata/spawn_server/spawn_server_nofork.c
+4 -4
@@ -662,8 +662,8 @@ static void spawn_server_receive_request(int sock, SPAWN_SERVER *server) {
662 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
663 if (cmsg == NULL || cmsg->cmsg_len != CMSG_LEN(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS)) {
664 nd_log(NDLS_COLLECTORS, NDLP_ERR,
665 - "SPAWN SERVER: Received invalid control message (expected %zu bytes, received %zu bytes)",
666 - CMSG_LEN(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS), cmsg?cmsg->cmsg_len:0);
665 + "SPAWN SERVER: Received invalid control message (expected %zu bytes, received %zu bytes)",
666 + (size_t)(CMSG_LEN(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS)), (size_t)(cmsg?cmsg->cmsg_len:0));
667 close(sock);
668 return;
669 }
@@ -908,7 +908,7 @@ static int spawn_server_event_loop(SPAWN_SERVER *server) {
908 }
909
910 // do not fork this socket
911 - sock_setcloexec(sock);
911 + sock_setcloexec(sock, true);
912
913 // receive the request and process it
914 spawn_server_receive_request(sock, server);
@@ -1091,7 +1091,7 @@ SPAWN_SERVER* spawn_server_create(SPAWN_SERVER_OPTIONS options, const char *name
1091 };
1092 os_close_all_non_std_open_fds_except(fds_to_keep, _countof(fds_to_keep), 0);
1093 nd_log_reopen_log_files_for_spawn_server(buf);
1094 - exit(spawn_server_event_loop(server));
1094 + _exit(spawn_server_event_loop(server));
1095 }
1096 else if (pid > 0) {
1097 // the parent
src/libnetdata/storage_number/storage_number.c
+10 -10
@@ -3,7 +3,7 @@
3 #include "../libnetdata.h"
4
5 bool is_system_ieee754_double(void) {
6 - static bool logged = false;
6 +// static bool logged = false;
7
8 struct {
9 NETDATA_DOUBLE original;
@@ -51,25 +51,25 @@ bool is_system_ieee754_double(void) {
51 uint64_t *ptr = (uint64_t *)&tests[i].original;
52
53 if(*ptr != tests[i].i && (tests[i].original == tests[i].d || (isnan(tests[i].original) && isnan(tests[i].d)))) {
54 - if(!logged)
55 - netdata_log_info("IEEE754: test #%zu, value " NETDATA_DOUBLE_FORMAT_G " is represented in this system as %016llX, but it was expected as %016llX",
56 - i+1, tests[i].original, (long long unsigned int)*ptr, (long long unsigned int)tests[i].i);
54 +// if(!logged)
55 +// netdata_log_info("IEEE754: test #%zu, value " NETDATA_DOUBLE_FORMAT_G " is represented in this system as %016llX, but it was expected as %016llX",
56 +// i+1, tests[i].original, (long long unsigned int)*ptr, (long long unsigned int)tests[i].i);
57 errors++;
58 }
59 }
60
61 if(!errors && sizeof(NETDATA_DOUBLE) == sizeof(uint64_t)) {
62 - if(!logged)
63 - netdata_log_info("IEEE754: system is using IEEE754 DOUBLE PRECISION values");
62 +// if(!logged)
63 +// netdata_log_info("IEEE754: system is using IEEE754 DOUBLE PRECISION values");
64
65 - logged = true;
65 +// logged = true;
66 return true;
67 }
68 else {
69 - if(!logged)
70 - netdata_log_info("IEEE754: system is NOT compatible with IEEE754 DOUBLE PRECISION values");
69 +// if(!logged)
70 +// netdata_log_info("IEEE754: system is NOT compatible with IEEE754 DOUBLE PRECISION values");
71
72 - logged = true;
72 +// logged = true;
73 return false;
74 }
75 }
src/libnetdata/string/string.c
+19 -14
@@ -32,7 +32,8 @@ static struct string_partition {
32 size_t deletes; // the number of successful deleted from the index
33
34 long int entries; // the number of entries in the index
35 - long int memory; // the memory used, with JudyHS (accurate)
35 + long int memory; // the memory used
36 + long int memory_index; // JudyHS (accurate)
37
38 #ifdef NETDATA_INTERNAL_CHECKS
39 // internal statistics
@@ -63,13 +64,14 @@ static struct string_partition {
64 #define string_internal_stats_add(partition, var, val) do {;} while(0)
65 #endif
66
66 -void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_t *entries, size_t *references, size_t *memory, size_t *duplications, size_t *releases) {
67 +void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_t *entries, size_t *references, size_t *memory, size_t *memory_index, size_t *duplications, size_t *releases) {
68 if (inserts) *inserts = 0;
69 if (deletes) *deletes = 0;
70 if (searches) *searches = 0;
71 if (entries) *entries = 0;
72 if (references) *references = 0;
73 if (memory) *memory = 0;
74 + if (memory_index) *memory_index = 0;
75 if (duplications) *duplications = 0;
76 if (releases) *releases = 0;
77
@@ -78,6 +80,7 @@ void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_
80 if (deletes) *deletes += string_base[i].deletes;
81 if (entries) *entries += (size_t) string_base[i].entries;
82 if (memory) *memory += (size_t) string_base[i].memory;
83 + if (memory_index) *memory_index += (string_base[i].memory_index > 0) ? string_base[i].memory_index : 0;
84
85 #ifdef NETDATA_INTERNAL_CHECKS
86 if (searches) *searches += string_base[i].atomic.searches;
@@ -196,7 +199,7 @@ static inline STRING *string_index_insert(const char *str, size_t length) {
199
200 rw_spinlock_write_lock(&string_base[partition].spinlock);
201
199 - int64_t mem = 0;
202 + int64_t judy_mem = 0;
203
204 STRING **ptr;
205 {
@@ -206,7 +209,7 @@ static inline STRING *string_index_insert(const char *str, size_t length) {
209
210 Pvoid_t *Rc = JudyHSIns(&string_base[partition].JudyHSArray, (void *)str, length - 1, &J_Error);
211
209 - mem = JudyAllocThreadPulseGetAndReset();
212 + judy_mem = JudyAllocThreadPulseGetAndReset();
213
214 if (unlikely(Rc == PJERR)) {
215 fatal(
@@ -220,7 +223,7 @@ static inline STRING *string_index_insert(const char *str, size_t length) {
223
224 if (likely(*ptr == 0)) {
225 // a new item added to the index
223 - size_t mem_size = sizeof(STRING) + length;
226 + long mem_size = (long)sizeof(STRING) + (long)length;
227 string = mallocz(mem_size);
228 strcpy((char *)string->str, str);
229 string->length = length;
@@ -228,7 +231,8 @@ static inline STRING *string_index_insert(const char *str, size_t length) {
231 *ptr = string;
232 string_base[partition].inserts++;
233 string_base[partition].entries++;
231 - string_base[partition].memory += (long)(mem_size + mem);
234 + string_base[partition].memory += mem_size;
235 + string_base[partition].memory_index += judy_mem;
236 }
237 else {
238 // the item is already in the index
@@ -264,7 +268,7 @@ static inline void string_index_delete(STRING *string) {
268 #endif
269
270 bool deleted = false;
267 - int64_t mem = 0;
271 + int64_t judy_mem = 0;
272
273 if (likely(string_base[partition].JudyHSArray)) {
274 JError_t J_Error;
@@ -273,7 +277,7 @@ static inline void string_index_delete(STRING *string) {
277
278 int ret = JudyHSDel(&string_base[partition].JudyHSArray, (void *)string->str, string->length - 1, &J_Error);
279
276 - mem = JudyAllocThreadPulseGetAndReset();
280 + judy_mem = JudyAllocThreadPulseGetAndReset();
281
282 if (unlikely(ret == JERR)) {
283 netdata_log_error(
@@ -288,10 +292,11 @@ static inline void string_index_delete(STRING *string) {
292 if (unlikely(!deleted))
293 netdata_log_error("STRING: tried to delete '%s' that is not in the index. Ignoring it.", string->str);
294 else {
291 - size_t mem_size = sizeof(STRING) + string->length;
295 + long mem_size = (long)sizeof(STRING) + (long)string->length;
296 string_base[partition].deletes++;
297 string_base[partition].entries--;
294 - string_base[partition].memory -= (long)(mem_size + mem);
298 + string_base[partition].memory -= mem_size;
299 + string_base[partition].memory_index += judy_mem;
300 freez(string);
301 }
302
@@ -683,8 +688,8 @@ int string_unittest(size_t entries) {
688 ospins = unittest_string_spins();
689 #endif
690
686 - size_t oinserts, odeletes, osearches, oentries, oreferences, omemory, oduplications, oreleases;
687 - string_statistics(&oinserts, &odeletes, &osearches, &oentries, &oreferences, &omemory, &oduplications, &oreleases);
691 + size_t oinserts, odeletes, osearches, oentries, oreferences, omemory, omemory_index, oduplications, oreleases;
692 + string_statistics(&oinserts, &odeletes, &osearches, &oentries, &oreferences, &omemory, &omemory_index, &oduplications, &oreleases);
693
694 time_t seconds_to_run = 5;
695 int threads_to_create = 2;
@@ -707,8 +712,8 @@ int string_unittest(size_t entries) {
712 for (int i = 0; i < threads_to_create; i++)
713 nd_thread_join(threads[i]);
714
710 - size_t inserts, deletes, searches, sentries, references, memory, duplications, releases;
711 - string_statistics(&inserts, &deletes, &searches, &sentries, &references, &memory, &duplications, &releases);
715 + size_t inserts, deletes, searches, sentries, references, memory, memory_index, duplications, releases;
716 + string_statistics(&inserts, &deletes, &searches, &sentries, &references, &memory, &memory_index, &duplications, &releases);
717
718 fprintf(stderr, "inserts %zu, deletes %zu, searches %zu, entries %zu, references %zu, memory %zu, duplications %zu, releases %zu\n",
719 inserts - oinserts, deletes - odeletes, searches - osearches, sentries - oentries, references - oreferences, memory - omemory, duplications - oduplications, releases - oreleases);
src/libnetdata/string/string.h
+1 -1
@@ -36,7 +36,7 @@ static inline int string_strncmp(STRING *string, const char *s, size_t n) {
36 return strncmp(string2str(string), s, n);
37 }
38
39 -void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_t *entries, size_t *references, size_t *memory, size_t *duplications, size_t *releases);
39 +void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_t *entries, size_t *references, size_t *memory, size_t *memory_index, size_t *duplications, size_t *releases);
40
41 int string_unittest(size_t entries);
42
src/libnetdata/template-enum.h
+16 -6
@@ -39,10 +39,11 @@
39
40 // --------------------------------------------------------------------------------------------------------------------
41
42 -#define BITMAP_STR_DEFINE_FUNCTIONS_EXTERN(type) \
43 - type type ## _2id_one(const char *str); \
44 - const char *type##_2str_one(type id); \
45 - const char *type##_2json(BUFFER *wb, const char *key, type id);
42 +#define BITMAP_STR_DEFINE_FUNCTIONS_EXTERN(type) \
43 + type type ## _2id_one(const char *str); \
44 + const char *type##_2str_one(type id); \
45 + void type##_2json(BUFFER *wb, const char *key, type id); \
46 + void type##_2buffer(BUFFER *wb, type id, const char *separator);
47
48 #define BITMAP_STR_DEFINE_FUNCTIONS(type, def, def_str) \
49 type type##_2id_one(const char *str) \
@@ -68,7 +69,7 @@
69 return def_str; \
70 } \
71 \
71 - const char *type##_2json(BUFFER *wb, const char *key, type id) \
72 + void type##_2json(BUFFER *wb, const char *key, type id) \
73 { \
74 buffer_json_member_add_array(wb, key); \
75 for (size_t i = 0; type ## _names[i].name; i++) { \
@@ -76,8 +77,17 @@
77 buffer_json_add_array_item_string(wb, type ## _names[i].name); \
78 } \
79 buffer_json_array_close(wb); \
80 + } \
81 \
80 - return def_str; \
82 + void type##_2buffer(BUFFER *wb, type id, const char *separator) \
83 + { \
84 + size_t added = 0; \
85 + for (size_t i = 0; type ## _names[i].name; i++) { \
86 + if ((id & type ## _names[i].id) == type ## _names[i].id) { \
87 + if(added++) buffer_strcat(wb, separator); \
88 + buffer_strcat(wb, type##_names[i].name); \
89 + } \
90 + } \
91 }
92
93 #endif //NETDATA_TEMPLATE_ENUM_H
src/libnetdata/threads/threads.c
+2 -2
@@ -230,7 +230,7 @@ void netdata_threads_init_for_external_plugins(size_t stacksize) {
230 // ----------------------------------------------------------------------------
231
232 void rrdset_thread_rda_free(void);
233 -void sender_commit_thread_buffer_free(void);
233 +void sender_thread_buffer_free(void);
234 void query_target_free(void);
235 void service_exits(void);
236 void rrd_collector_finished(void);
@@ -299,7 +299,7 @@ static void nd_thread_exit(void *pptr) {
299 nd_log(NDLS_DAEMON, NDLP_DEBUG, "thread with task id %d finished", nti->tid);
300
301 rrd_collector_finished();
302 - sender_commit_thread_buffer_free();
302 + sender_thread_buffer_free();
303 rrdset_thread_rda_free();
304 query_target_free();
305 thread_cache_destroy();
src/libnetdata/uuid/uuid.h
+1
@@ -26,6 +26,7 @@ typedef struct _uuid {
26 #endif
27
28 static const ND_UUID UUID_ZERO = (ND_UUID){ { .parts = { .hig64 = 0, .low64 = 0 } }};
29 +ND_UUID_DEFINE(netdata_fatal_msgid, 0x23, 0xe9, 0x3d, 0xfc, 0xcb, 0xf6, 0x4e, 0x11, 0xaa, 0xc8, 0x58, 0xb9, 0x41, 0x0d, 0x8a, 0x82);
30 ND_UUID_DEFINE(streaming_from_child_msgid, 0xed,0x4c,0xdb, 0x8f, 0x1b, 0xeb, 0x4a, 0xd3, 0xb5, 0x7c, 0xb3, 0xca, 0xe2, 0xd1, 0x62, 0xfa);
31 ND_UUID_DEFINE(streaming_to_parent_msgid, 0x6e, 0x2e, 0x38, 0x39, 0x06, 0x76, 0x48, 0x96, 0x8b, 0x64, 0x60, 0x45, 0xdb, 0xf2, 0x8d, 0x66);
32 ND_UUID_DEFINE(health_alert_transition_msgid, 0x9c, 0xe0, 0xcb, 0x58, 0xab, 0x8b, 0x44, 0xdf, 0x82, 0xc4, 0xbf, 0x1a, 0xd9, 0xee, 0x22, 0xde);
src/libnetdata/waiting-queue/waiting-queue.c new
+416
@@ -0,0 +1,416 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "waiting-queue.h"
4 +
5 +typedef struct waiting_thread {
6 + uv_cond_t cond; // condition variable for this thread
7 + usec_t waiting_since_ut; // when we started waiting
8 + Word_t order;
9 + struct waiting_thread *prev, *next;
10 +} WAITING_THREAD;
11 +
12 +struct waiting_queue {
13 + uv_mutex_t mutex; // protect the queue structure
14 + Word_t last_seqno; // incrementing sequence counter
15 + WAITING_THREAD *list;
16 + size_t running; // number of threads currently running/waiting
17 + SPINLOCK spinlock;
18 +};
19 +
20 +// Determine available bits based on system word size
21 +#if SIZEOF_VOID_P == 8
22 +#define PRIORITY_SHIFT 62ULL
23 +#define SEQNO_MASK ((1ULL << PRIORITY_SHIFT) - 1)
24 +#else
25 +#define PRIORITY_SHIFT 30U
26 +#define SEQNO_MASK ((1U << PRIORITY_SHIFT) - 1)
27 +#endif
28 +
29 +static inline Word_t make_key(WAITING_QUEUE_PRIORITY priority, Word_t seqno) {
30 + return ((Word_t)priority << PRIORITY_SHIFT) | (seqno & SEQNO_MASK);
31 +}
32 +
33 +static inline WAITING_QUEUE_PRIORITY key_get_priority(Word_t key) {
34 + return (WAITING_QUEUE_PRIORITY)(key >> PRIORITY_SHIFT);
35 +}
36 +
37 +static inline Word_t key_get_seqno(Word_t key) {
38 + return key & SEQNO_MASK;
39 +}
40 +
41 +WAITING_QUEUE *waiting_queue_create(void) {
42 + WAITING_QUEUE *wq = callocz(1, sizeof(WAITING_QUEUE));
43 +
44 + int ret = uv_mutex_init(&wq->mutex);
45 + if(ret != 0) {
46 + freez(wq);
47 + return NULL;
48 + }
49 +
50 + spinlock_init(&wq->spinlock);
51 +
52 + wq->running = 0;
53 + return wq;
54 +}
55 +
56 +void waiting_queue_destroy(WAITING_QUEUE *wq) {
57 + if(!wq) return;
58 +
59 + if(wq->running)
60 + fatal("WAITING_QUEUE: destroying waiting queue that still has %zu threads running/waiting", wq->running);
61 +
62 + uv_mutex_destroy(&wq->mutex);
63 + freez(wq);
64 +}
65 +
66 +static inline void WAITERS_SET(WAITING_QUEUE *wq, WAITING_THREAD *wt) {
67 + for(WAITING_THREAD *t = wq->list ; t ;t = t->next) {
68 + if(wt->order < t->order) {
69 + DOUBLE_LINKED_LIST_INSERT_ITEM_BEFORE_UNSAFE(wq->list, t, wt, prev, next);
70 + return;
71 + }
72 + }
73 + DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(wq->list, wt, prev, next);
74 +}
75 +
76 +static inline void WAITERS_DEL(WAITING_QUEUE *wq, WAITING_THREAD *wt) {
77 + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(wq->list, wt, prev, next);
78 +}
79 +
80 +static inline WAITING_THREAD *WAITERS_FIRST(WAITING_QUEUE *wq) {
81 + return wq->list;
82 +}
83 +
84 +static inline void WAITING_THREAD_init(WAITING_QUEUE *wq, WAITING_THREAD *wt, WAITING_QUEUE_PRIORITY priority) {
85 + Word_t seqno = __atomic_add_fetch(&wq->last_seqno, 1, __ATOMIC_RELAXED);
86 + wt->order = make_key(priority, seqno);
87 + wt->waiting_since_ut = now_monotonic_usec();
88 + wt->prev = wt->next = NULL;
89 +
90 + int ret = uv_cond_init(&wt->cond);
91 + if(ret != 0)
92 + fatal("WAITING_QUEUE: cannot initialize condition variable");
93 +}
94 +
95 +static inline void WAITING_THREAD_cleanup(WAITING_QUEUE *wq __maybe_unused, WAITING_THREAD *wt) {
96 + uv_cond_destroy(&wt->cond);
97 +}
98 +
99 +usec_t waiting_queue_wait(WAITING_QUEUE *wq, WAITING_QUEUE_PRIORITY priority) {
100 + // Try fast path first - if we're the only one, just go
101 + if(__atomic_add_fetch(&wq->running, 1, __ATOMIC_RELAXED) == 1) {
102 + if(spinlock_trylock(&wq->spinlock))
103 + return 0;
104 + }
105 +
106 + // Slow path - need to wait
107 +
108 + WAITING_THREAD wt;
109 + WAITING_THREAD_init(wq, &wt, priority);
110 +
111 + uv_mutex_lock(&wq->mutex);
112 + WAITERS_SET(wq, &wt);
113 +
114 + // Wait for our turn
115 + do {
116 + if (WAITERS_FIRST(wq) == &wt && spinlock_trylock(&wq->spinlock))
117 + break;
118 + else
119 + uv_cond_wait(&wt.cond, &wq->mutex);
120 + } while(true);
121 +
122 + WAITERS_DEL(wq, &wt);
123 + uv_mutex_unlock(&wq->mutex);
124 + WAITING_THREAD_cleanup(wq, &wt);
125 +
126 + return now_monotonic_usec() - wt.waiting_since_ut;
127 +}
128 +
129 +void waiting_queue_done(WAITING_QUEUE *wq) {
130 + spinlock_unlock(&wq->spinlock);
131 +
132 + // Fast path if we're alone
133 + if(__atomic_sub_fetch(&wq->running, 1, __ATOMIC_RELAXED) == 0)
134 + return;
135 +
136 + // Slow path - need to signal next in line
137 + uv_mutex_lock(&wq->mutex);
138 +
139 + // Wake up next in line if any
140 + if(wq->list)
141 + uv_cond_signal(&wq->list->cond);
142 +
143 + uv_mutex_unlock(&wq->mutex);
144 +}
145 +
146 +size_t waiting_queue_waiting(WAITING_QUEUE *wq) {
147 + return __atomic_load_n(&wq->running, __ATOMIC_RELAXED);
148 +}
149 +
150 +
151 +// --------------------------------------------------------------------------------------------------------------------
152 +
153 +// For stress test statistics
154 +typedef struct thread_stats {
155 + WAITING_QUEUE_PRIORITY priority;
156 + size_t executions; // how many times we got through
157 + usec_t total_wait_time; // total time spent waiting
158 + usec_t max_wait_time; // maximum time spent waiting
159 +} THREAD_STATS;
160 +
161 +struct thread_args {
162 + THREAD_STATS *stats;
163 + WAITING_QUEUE *wq;
164 + bool with_sleep;
165 + bool *stop_flag;
166 +};
167 +
168 +static const char *priority_to_string(WAITING_QUEUE_PRIORITY p) {
169 + switch(p) {
170 + case WAITING_QUEUE_PRIO_URGENT: return "URGENT";
171 + case WAITING_QUEUE_PRIO_HIGH: return "HIGH";
172 + case WAITING_QUEUE_PRIO_NORMAL: return "NORMAL";
173 + case WAITING_QUEUE_PRIO_LOW: return "LOW";
174 + default: return "UNKNOWN";
175 + }
176 +}
177 +
178 +static int unittest_functional(void) {
179 + int errors = 0;
180 + fprintf(stderr, "\nTesting waiting queue...\n");
181 +
182 + WAITING_QUEUE *wq = waiting_queue_create();
183 +
184 + // Test 1: Fast path should work with no contention
185 + fprintf(stderr, " Test 1: Fast path - no contention: ");
186 + usec_t wait_time = waiting_queue_wait(wq, WAITING_QUEUE_PRIO_NORMAL);
187 + waiting_queue_done(wq);
188 + if(wait_time != 0) {
189 + fprintf(stderr, "FAILED (waited %"PRIu64" usec)\n", wait_time);
190 + errors++;
191 + }
192 + else
193 + fprintf(stderr, "OK\n");
194 +
195 + // Test 2: Priorities should be respected
196 + fprintf(stderr, " Test 2: Priority ordering: ");
197 + WAITING_THREAD threads[100];
198 + for(size_t t = 0; t < _countof(threads); t++) {
199 + __atomic_add_fetch(&wq->running, 1, __ATOMIC_RELAXED);
200 + WAITING_THREAD_init(wq, &threads[t], os_random(WAITING_QUEUE_PRIO_MAX));
201 + WAITERS_SET(wq, &threads[t]);
202 + }
203 +
204 + bool failed = false;
205 + size_t prio_counts[WAITING_QUEUE_PRIO_MAX] = { 0 };
206 + WAITING_QUEUE_PRIORITY last_prio = WAITING_QUEUE_PRIO_URGENT;
207 + Word_t last_seqno = 0;
208 + for(size_t t = 0; t < _countof(threads); t++) {
209 + WAITING_THREAD *wt = WAITERS_FIRST(wq);
210 + WAITERS_DEL(wq, wt);
211 + __atomic_sub_fetch(&wq->running, 1, __ATOMIC_RELAXED);
212 +
213 + WAITING_QUEUE_PRIORITY prio = key_get_priority(wt->order);
214 + Word_t seqno = key_get_seqno(wt->order);
215 +
216 + prio_counts[prio]++;
217 + if(prio < last_prio) {
218 + if(!failed)
219 + fprintf(stderr, "FAILED\n");
220 +
221 + fprintf(stderr, " > ERROR: prio %u is before prio %u\n", prio, last_prio);
222 + errors++;
223 + failed = true;
224 + }
225 + else if(prio == last_prio && seqno < last_seqno) {
226 + if(!failed)
227 + fprintf(stderr, "FAILED\n");
228 +
229 + fprintf(stderr, " > ERROR: seqno %lu is before seqno %lu\n", seqno, last_seqno);
230 + errors++;
231 + failed = true;
232 + }
233 +
234 + last_seqno = seqno;
235 + last_prio = prio;
236 + WAITING_THREAD_cleanup(wq, wt);
237 + }
238 +
239 + if(!failed)
240 + fprintf(stderr, "OK\n");
241 +
242 + for(size_t p = 0; p < WAITING_QUEUE_PRIO_MAX ;p++)
243 + fprintf(stderr, " > prio %zu got %zu waiters\n", p, prio_counts[p]);
244 +
245 + // Test 3: Queue stats should be accurate
246 + fprintf(stderr, " Test 3: Queue statistics: ");
247 + size_t waiting = waiting_queue_waiting(wq);
248 + if(waiting != 0) {
249 + fprintf(stderr, "FAILED (queue shows %zu waiting)\n", waiting);
250 + errors++;
251 + }
252 + else
253 + fprintf(stderr, "OK\n");
254 +
255 + waiting_queue_destroy(wq);
256 + return errors;
257 +}
258 +
259 +static void *stress_thread(void *arg) {
260 + struct thread_args *args = arg;
261 +
262 + THREAD_STATS *stats = args->stats;
263 + WAITING_QUEUE *wq = args->wq;
264 + bool with_sleep = args->with_sleep;
265 + bool *stop_flag = args->stop_flag;
266 +
267 + while(!__atomic_load_n(stop_flag, __ATOMIC_ACQUIRE)) {
268 + usec_t wait_time = waiting_queue_wait(wq, stats->priority);
269 + stats->executions++;
270 + stats->total_wait_time += wait_time;
271 + if(wait_time > stats->max_wait_time)
272 + stats->max_wait_time = wait_time;
273 +
274 + if(with_sleep)
275 + tinysleep();
276 +
277 + waiting_queue_done(wq);
278 + }
279 +
280 + return NULL;
281 +}
282 +
283 +static void print_thread_stats(THREAD_STATS *stats, size_t count, usec_t duration) {
284 + fprintf(stderr, "\n%-8s %12s %12s %12s %12s %12s\n",
285 + "PRIORITY", "EXECUTIONS", "EXEC/SEC", "AVG WAIT", "MAX WAIT", "% WAITING");
286 +
287 + size_t total_execs = 0;
288 + for(size_t i = 0; i < count; i++)
289 + total_execs += stats[i].executions;
290 +
291 + double total_time_sec = duration / (double)USEC_PER_SEC;
292 +
293 + for(size_t i = 0; i < count; i++) {
294 + double execs_per_sec = stats[i].executions / total_time_sec;
295 + double avg_wait = stats[i].executions ? (double)stats[i].total_wait_time / stats[i].executions : 0;
296 + double percent_waiting = stats[i].total_wait_time * 100.0 / duration;
297 +
298 + fprintf(stderr, "%-8s %12zu %12.1f %12.1f %12"PRIu64" %12.1f%%\n",
299 + priority_to_string(stats[i].priority),
300 + stats[i].executions,
301 + execs_per_sec,
302 + avg_wait,
303 + stats[i].max_wait_time,
304 + percent_waiting);
305 + }
306 +}
307 +
308 +#define THREADS_PER_PRIORITY 2
309 +#define TEST_DURATION_SEC 5
310 +
311 +static int unittest_stress(void) {
312 + int errors = 0;
313 + fprintf(stderr, "\nStress testing waiting queue...\n");
314 +
315 + WAITING_QUEUE *wq = waiting_queue_create();
316 + const size_t num_priorities = 4;
317 + const size_t total_threads = num_priorities * THREADS_PER_PRIORITY;
318 +
319 + // Test both with and without sleep
320 + for(int test = 0; test < 2; test++) {
321 + bool with_sleep = (test == 1);
322 + bool stop_flag = false;
323 +
324 + fprintf(stderr, "\nRunning %ds stress test %s sleep:\n",
325 + TEST_DURATION_SEC, with_sleep ? "with" : "without");
326 +
327 + // Prepare thread stats and args
328 + THREAD_STATS stats[total_threads];
329 + struct thread_args thread_args[total_threads];
330 + ND_THREAD *threads[total_threads];
331 +
332 + fprintf(stderr, "Starting %zu threads for %ds test %s sleep...\n",
333 + total_threads,
334 + TEST_DURATION_SEC,
335 + with_sleep ? "with" : "without");
336 +
337 + // Initialize stats and create threads
338 + size_t thread_idx = 0;
339 + for(int prio = WAITING_QUEUE_PRIO_URGENT; prio <= WAITING_QUEUE_PRIO_LOW; prio++) {
340 + for(int t = 0; t < THREADS_PER_PRIORITY; t++) {
341 + stats[thread_idx] = (THREAD_STATS){
342 + .priority = prio,
343 + .executions = 0,
344 + .total_wait_time = 0,
345 + .max_wait_time = 0
346 + };
347 + thread_args[thread_idx] = (struct thread_args){
348 + .stats = &stats[thread_idx], // Pass pointer to stats
349 + .wq = wq,
350 + .with_sleep = with_sleep,
351 + .stop_flag = &stop_flag
352 + };
353 +
354 + char thread_name[32];
355 + snprintf(thread_name, sizeof(thread_name), "STRESS%d-%d", prio, t);
356 + threads[thread_idx] = nd_thread_create(
357 + thread_name,
358 + NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE,
359 + stress_thread,
360 + &thread_args[thread_idx]);
361 + thread_idx++;
362 + }
363 + }
364 +
365 + // Let it run
366 + time_t start = now_monotonic_sec();
367 + fprintf(stderr, "Running...");
368 + while(now_monotonic_sec() - start < TEST_DURATION_SEC) {
369 + fprintf(stderr, ".");
370 + sleep_usec(500000); // Print a dot every 0.5 seconds
371 + }
372 + fprintf(stderr, "\n");
373 +
374 +
375 + fprintf(stderr, "Stopping threads...\n");
376 + __atomic_store_n(&stop_flag, true, __ATOMIC_RELEASE);
377 +
378 + // Wait for threads and collect stats
379 + fprintf(stderr, "Waiting for %zu threads to finish...\n", total_threads);
380 + for(size_t i = 0; i < total_threads; i++)
381 + nd_thread_join(threads[i]);
382 +
383 + // Print stats
384 + print_thread_stats(stats, total_threads, TEST_DURATION_SEC * USEC_PER_SEC);
385 +
386 +// // Basic validation
387 +// for(size_t i = 0; i < total_threads - THREADS_PER_PRIORITY; i++) {
388 +// if(stats[i].executions < stats[i + THREADS_PER_PRIORITY].executions) {
389 +// fprintf(stderr, "ERROR: Higher priority thread got fewer executions!\n");
390 +// errors++;
391 +// }
392 +// }
393 +//
394 +// // Check fairness within same priority
395 +// for(size_t i = 0; i < total_threads; i += THREADS_PER_PRIORITY) {
396 +// for(size_t j = i + 1; j < i + THREADS_PER_PRIORITY; j++) {
397 +// double diff = (double)(stats[i].executions - stats[j].executions) /
398 +// (double)(stats[i].executions + stats[j].executions);
399 +// if(fabs(diff) > 0.1) { // allow 10% difference
400 +// fprintf(stderr, "ERROR: Unfair distribution within same priority!\n");
401 +// errors++;
402 +// }
403 +// }
404 +// }
405 + }
406 +
407 + waiting_queue_destroy(wq);
408 + return errors;
409 +}
410 +
411 +int unittest_waiting_queue(void) {
412 + int errors = unittest_functional();
413 + errors += unittest_stress();
414 +
415 + return errors;
416 +}
src/libnetdata/waiting-queue/waiting-queue.h new
+58
@@ -0,0 +1,58 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_WAITING_QUEUE_H
4 +#define NETDATA_WAITING_QUEUE_H
5 +
6 +#include "libnetdata/libnetdata.h"
7 +#include <uv.h>
8 +
9 +/*
10 + * WAITING QUEUE
11 + * Like a mutex, or a spinlock, but:
12 + *
13 + * 1. Waiters get a sequence number (FIFO)
14 + * 2. FIFO is respected within each priority
15 + * 3. Higher priority threads get in first
16 + * 4. No wasting of resources, there are no spins
17 + *
18 + * When there are no other waiters, this is equivalent to 2 atomic
19 + * operations for lock, and 2 for unlock.
20 + *
21 + * As lightweight and fast as it can be.
22 + * About 0.5M thread switches/s per WAITING QUEUE, on modern hardware.
23 + *
24 + * Be careful: higher priority threads can starve the rest!
25 + *
26 + */
27 +
28 +typedef struct waiting_queue WAITING_QUEUE;
29 +
30 +typedef enum __attribute__((packed)) {
31 + WAITING_QUEUE_PRIO_URGENT = 0, // will be first
32 + WAITING_QUEUE_PRIO_HIGH, // will be second
33 + WAITING_QUEUE_PRIO_NORMAL, // will be third
34 + WAITING_QUEUE_PRIO_LOW, // will be last
35 +
36 + // terminator
37 + WAITING_QUEUE_PRIO_MAX,
38 +} WAITING_QUEUE_PRIORITY;
39 +
40 +// Initialize a waiting queue
41 +WAITING_QUEUE *waiting_queue_create(void);
42 +
43 +// Destroy a waiting queue - must be empty
44 +void waiting_queue_destroy(WAITING_QUEUE *wq);
45 +
46 +// Returns when it is our turn to run
47 +// Returns time spent waiting in microseconds
48 +usec_t waiting_queue_wait(WAITING_QUEUE *wq, WAITING_QUEUE_PRIORITY priority);
49 +
50 +// Mark that we are done - wakes up the next in line
51 +void waiting_queue_done(WAITING_QUEUE *wq);
52 +
53 +// Return the number of threads currently waiting
54 +size_t waiting_queue_waiting(WAITING_QUEUE *wq);
55 +
56 +int unittest_waiting_queue(void);
57 +
58 +#endif // NETDATA_WAITING_QUEUE_H
\ No newline at end of file
src/libnetdata/worker_utilization/worker_utilization.c
+6 -2
@@ -118,7 +118,9 @@ void worker_register(const char *name) {
118
119 workers_globals.memory += sizeof(struct worker) + strlen(worker->tag) + 1 + strlen(worker->workname) + 1;
120
121 + JudyAllocThreadPulseReset();
122 Pvoid_t *PValue = JudyHSIns(&workers_globals.worknames_JudyHS, (void *)name, name_size, PJE0);
123 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
124
125 struct workers_workname *workname = *PValue;
126 if(!workname) {
@@ -127,7 +129,7 @@ void worker_register(const char *name) {
129 workname->base = NULL;
130 *PValue = workname;
131
130 - workers_globals.memory += sizeof(struct workers_workname) + JUDYHS_INDEX_SIZE_ESTIMATE(name_size);
132 + workers_globals.memory = (int64_t)workers_globals.memory + (int64_t)sizeof(struct workers_workname) + judy_mem;
133 }
134
135 spinlock_lock(&workname->spinlock);
@@ -176,9 +178,11 @@ void worker_unregister(void) {
178 spinlock_unlock(&workname->spinlock);
179
180 if(!workname->base) {
181 + JudyAllocThreadPulseReset();
182 JudyHSDel(&workers_globals.worknames_JudyHS, (void *) worker->workname, workname_size, PJE0);
183 + int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
184 freez(workname);
181 - workers_globals.memory -= sizeof(struct workers_workname) + JUDYHS_INDEX_SIZE_ESTIMATE(workname_size);
185 + workers_globals.memory = (int64_t)workers_globals.memory - (int64_t)sizeof(struct workers_workname) + judy_mem;
186 }
187 }
188 workers_globals.memory -= sizeof(struct worker) + strlen(worker->tag) + 1 + strlen(worker->workname) + 1;
src/ml/ml_config.cc
+1 -1
@@ -45,7 +45,7 @@ void ml_config_load(ml_config_t *cfg) {
45 std::string anomaly_detection_grouping_method = config_get(config_section_ml, "anomaly detection grouping method", "average");
46 time_t anomaly_detection_query_duration = config_get_duration_seconds(config_section_ml, "anomaly detection grouping duration", 5 * 60);
47
48 - size_t num_worker_threads = stream_conf_is_parent(false) ? netdata_conf_cpus() / 4 : 1;
48 + size_t num_worker_threads = netdata_conf_is_parent() ? netdata_conf_cpus() / 4 : 1;
49 if (num_worker_threads < 1) num_worker_threads = 1;
50 else if (num_worker_threads > 256) num_worker_threads = 256;
51 num_worker_threads = config_get_number(config_section_ml, "num training threads", num_worker_threads);
src/plugins.d/gperf-config.txt
+13 -13
@@ -73,36 +73,36 @@ HOST_LABEL, PLUGINSD_KEYWORD_ID_HOST_LABEL, PARSER_INIT_PLUGINSD|PARSE
73 #
74 # Common keywords
75 #
76 -BEGIN, PLUGINSD_KEYWORD_ID_BEGIN, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 8
77 -CHART, PLUGINSD_KEYWORD_ID_CHART, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 9
76 +BEGIN, PLUGINSD_KEYWORD_ID_BEGIN, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 8
77 +CHART, PLUGINSD_KEYWORD_ID_CHART, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA|PARSER_REP_REPLICATION, WORKER_PARSER_FIRST_JOB + 9
78 CLABEL, PLUGINSD_KEYWORD_ID_CLABEL, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 10
79 CLABEL_COMMIT, PLUGINSD_KEYWORD_ID_CLABEL_COMMIT, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 11
80 DIMENSION, PLUGINSD_KEYWORD_ID_DIMENSION, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 12
81 -END, PLUGINSD_KEYWORD_ID_END, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 13
81 +END, PLUGINSD_KEYWORD_ID_END, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 13
82 FUNCTION, PLUGINSD_KEYWORD_ID_FUNCTION, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 14
83 FUNCTION_RESULT_BEGIN, PLUGINSD_KEYWORD_ID_FUNCTION_RESULT_BEGIN, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 15
84 FUNCTION_PROGRESS, PLUGINSD_KEYWORD_ID_FUNCTION_PROGRESS, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 16
85 LABEL, PLUGINSD_KEYWORD_ID_LABEL, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 17
86 OVERWRITE, PLUGINSD_KEYWORD_ID_OVERWRITE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 18
87 -SET, PLUGINSD_KEYWORD_ID_SET, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 19
87 +SET, PLUGINSD_KEYWORD_ID_SET, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 19
88 VARIABLE, PLUGINSD_KEYWORD_ID_VARIABLE, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 20
89 CONFIG, PLUGINSD_KEYWORD_ID_CONFIG, PARSER_INIT_PLUGINSD|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 21
90 #
91 # Streaming only keywords
92 #
93 CLAIMED_ID, PLUGINSD_KEYWORD_ID_CLAIMED_ID, PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 22
94 -BEGIN2, PLUGINSD_KEYWORD_ID_BEGIN2, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 23
95 -SET2, PLUGINSD_KEYWORD_ID_SET2, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 24
96 -END2, PLUGINSD_KEYWORD_ID_END2, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 25
94 +BEGIN2, PLUGINSD_KEYWORD_ID_BEGIN2, PARSER_INIT_STREAMING|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 23
95 +SET2, PLUGINSD_KEYWORD_ID_SET2, PARSER_INIT_STREAMING|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 24
96 +END2, PLUGINSD_KEYWORD_ID_END2, PARSER_INIT_STREAMING|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 25
97 #
98 # Streaming Replication keywords
99 #
100 -CHART_DEFINITION_END, PLUGINSD_KEYWORD_ID_CHART_DEFINITION_END, PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 26
101 -RBEGIN, PLUGINSD_KEYWORD_ID_RBEGIN, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 27
102 -RDSTATE, PLUGINSD_KEYWORD_ID_RDSTATE, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 28
103 -REND, PLUGINSD_KEYWORD_ID_REND, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 29
104 -RSET, PLUGINSD_KEYWORD_ID_RSET, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 30
105 -RSSTATE, PLUGINSD_KEYWORD_ID_RSSTATE, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 31
100 +CHART_DEFINITION_END, PLUGINSD_KEYWORD_ID_CHART_DEFINITION_END, PARSER_INIT_STREAMING|PARSER_REP_REPLICATION|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 26
101 +RBEGIN, PLUGINSD_KEYWORD_ID_RBEGIN, PARSER_INIT_STREAMING|PARSER_REP_REPLICATION|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 27
102 +RSET, PLUGINSD_KEYWORD_ID_RSET, PARSER_INIT_STREAMING|PARSER_REP_REPLICATION|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 28
103 +REND, PLUGINSD_KEYWORD_ID_REND, PARSER_INIT_STREAMING|PARSER_REP_REPLICATION|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 29
104 +RDSTATE, PLUGINSD_KEYWORD_ID_RDSTATE, PARSER_INIT_STREAMING|PARSER_REP_REPLICATION|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 30
105 +RSSTATE, PLUGINSD_KEYWORD_ID_RSSTATE, PARSER_INIT_STREAMING|PARSER_REP_REPLICATION|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 31
106 #
107 # JSON
108 #
src/plugins.d/gperf-hashtable.h
+16 -16
@@ -1,5 +1,5 @@
1 /* ANSI-C code produced by gperf version 3.1 */
2 -/* Command-line: gperf --multiple-iterations=1000 --output-file=gperf-hashtable.h gperf-config.txt */
2 +/* Command-line: gperf --multiple-iterations=1000 --output-file=gperf-hashtable.h -r gperf-config.txt */
3 /* Computed positions: -k'1-2' */
4
5 #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
@@ -135,11 +135,11 @@ static const PARSER_KEYWORD gperf_keywords[] =
135 #line 69 "gperf-config.txt"
136 {"HOST", PLUGINSD_KEYWORD_ID_HOST, PARSER_INIT_PLUGINSD|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 4},
137 #line 103 "gperf-config.txt"
138 - {"REND", PLUGINSD_KEYWORD_ID_REND, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 29},
138 + {"REND", PLUGINSD_KEYWORD_ID_REND, PARSER_INIT_STREAMING|PARSER_REP_REPLICATION|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 29},
139 #line 68 "gperf-config.txt"
140 {"EXIT", PLUGINSD_KEYWORD_ID_EXIT, PARSER_INIT_PLUGINSD, WORKER_PARSER_FIRST_JOB + 3},
141 #line 77 "gperf-config.txt"
142 - {"CHART", PLUGINSD_KEYWORD_ID_CHART, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 9},
142 + {"CHART", PLUGINSD_KEYWORD_ID_CHART, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA|PARSER_REP_REPLICATION, WORKER_PARSER_FIRST_JOB + 9},
143 #line 89 "gperf-config.txt"
144 {"CONFIG", PLUGINSD_KEYWORD_ID_CONFIG, PARSER_INIT_PLUGINSD|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 21},
145 #line 86 "gperf-config.txt"
@@ -148,8 +148,8 @@ static const PARSER_KEYWORD gperf_keywords[] =
148 {"HOST_LABEL", PLUGINSD_KEYWORD_ID_HOST_LABEL, PARSER_INIT_PLUGINSD|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 7},
149 #line 70 "gperf-config.txt"
150 {"HOST_DEFINE", PLUGINSD_KEYWORD_ID_HOST_DEFINE, PARSER_INIT_PLUGINSD|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 5},
151 -#line 102 "gperf-config.txt"
152 - {"RDSTATE", PLUGINSD_KEYWORD_ID_RDSTATE, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 28},
151 +#line 104 "gperf-config.txt"
152 + {"RDSTATE", PLUGINSD_KEYWORD_ID_RDSTATE, PARSER_INIT_STREAMING|PARSER_REP_REPLICATION|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 30},
153 {(char*)0,0,PARSER_INIT_PLUGINSD,0},
154 #line 118 "gperf-config.txt"
155 {"DELETE_JOB", PLUGINSD_KEYWORD_ID_DELETE_JOB, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 38},
@@ -162,17 +162,17 @@ static const PARSER_KEYWORD gperf_keywords[] =
162 #line 117 "gperf-config.txt"
163 {"REPORT_JOB_STATUS", PLUGINSD_KEYWORD_ID_REPORT_JOB_STATUS, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 37},
164 #line 87 "gperf-config.txt"
165 - {"SET", PLUGINSD_KEYWORD_ID_SET, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 19},
165 + {"SET", PLUGINSD_KEYWORD_ID_SET, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 19},
166 #line 95 "gperf-config.txt"
167 - {"SET2", PLUGINSD_KEYWORD_ID_SET2, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 24},
168 -#line 104 "gperf-config.txt"
169 - {"RSET", PLUGINSD_KEYWORD_ID_RSET, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 30},
167 + {"SET2", PLUGINSD_KEYWORD_ID_SET2, PARSER_INIT_STREAMING|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 24},
168 +#line 102 "gperf-config.txt"
169 + {"RSET", PLUGINSD_KEYWORD_ID_RSET, PARSER_INIT_STREAMING|PARSER_REP_REPLICATION|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 28},
170 #line 100 "gperf-config.txt"
171 - {"CHART_DEFINITION_END", PLUGINSD_KEYWORD_ID_CHART_DEFINITION_END, PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 26},
171 + {"CHART_DEFINITION_END", PLUGINSD_KEYWORD_ID_CHART_DEFINITION_END, PARSER_INIT_STREAMING|PARSER_REP_REPLICATION|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 26},
172 #line 115 "gperf-config.txt"
173 {"DYNCFG_REGISTER_JOB", PLUGINSD_KEYWORD_ID_DYNCFG_REGISTER_JOB, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 35},
174 #line 105 "gperf-config.txt"
175 - {"RSSTATE", PLUGINSD_KEYWORD_ID_RSSTATE, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 31},
175 + {"RSSTATE", PLUGINSD_KEYWORD_ID_RSSTATE, PARSER_INIT_STREAMING|PARSER_REP_REPLICATION|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 31},
176 #line 78 "gperf-config.txt"
177 {"CLABEL", PLUGINSD_KEYWORD_ID_CLABEL, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 10},
178 #line 114 "gperf-config.txt"
@@ -184,17 +184,17 @@ static const PARSER_KEYWORD gperf_keywords[] =
184 #line 93 "gperf-config.txt"
185 {"CLAIMED_ID", PLUGINSD_KEYWORD_ID_CLAIMED_ID, PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 22},
186 #line 81 "gperf-config.txt"
187 - {"END", PLUGINSD_KEYWORD_ID_END, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 13},
187 + {"END", PLUGINSD_KEYWORD_ID_END, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 13},
188 #line 96 "gperf-config.txt"
189 - {"END2", PLUGINSD_KEYWORD_ID_END2, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 25},
189 + {"END2", PLUGINSD_KEYWORD_ID_END2, PARSER_INIT_STREAMING|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 25},
190 #line 79 "gperf-config.txt"
191 {"CLABEL_COMMIT", PLUGINSD_KEYWORD_ID_CLABEL_COMMIT, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 11},
192 #line 76 "gperf-config.txt"
193 - {"BEGIN", PLUGINSD_KEYWORD_ID_BEGIN, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 8},
193 + {"BEGIN", PLUGINSD_KEYWORD_ID_BEGIN, PARSER_INIT_PLUGINSD|PARSER_INIT_STREAMING|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 8},
194 #line 94 "gperf-config.txt"
195 - {"BEGIN2", PLUGINSD_KEYWORD_ID_BEGIN2, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 23},
195 + {"BEGIN2", PLUGINSD_KEYWORD_ID_BEGIN2, PARSER_INIT_STREAMING|PARSER_REP_DATA, WORKER_PARSER_FIRST_JOB + 23},
196 #line 101 "gperf-config.txt"
197 - {"RBEGIN", PLUGINSD_KEYWORD_ID_RBEGIN, PARSER_INIT_STREAMING, WORKER_PARSER_FIRST_JOB + 27},
197 + {"RBEGIN", PLUGINSD_KEYWORD_ID_RBEGIN, PARSER_INIT_STREAMING|PARSER_REP_REPLICATION|PARSER_REP_METADATA, WORKER_PARSER_FIRST_JOB + 27},
198 #line 67 "gperf-config.txt"
199 {"DISABLE", PLUGINSD_KEYWORD_ID_DISABLE, PARSER_INIT_PLUGINSD, WORKER_PARSER_FIRST_JOB + 2},
200 #line 84 "gperf-config.txt"
src/plugins.d/pluginsd_parser.c
+4 -86
@@ -1,9 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "pluginsd_internals.h"
4 -#include "streaming/replication.h"
5 -#include "streaming/stream-waiting-list.h"
6 -#include "web/api/queries/backfill.h"
4 +#include "streaming/stream-replication-receiver.h"
5
6 static inline PARSER_RC pluginsd_set(char **words, size_t num_words, PARSER *parser) {
7 int idx = 1;
@@ -67,7 +65,7 @@ static inline PARSER_RC pluginsd_begin(char **words, size_t num_words, PARSER *p
65 rrdhost_hostname(host), rrdset_id(st),
66 st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec,
67 st->last_updated.tv_sec * USEC_PER_SEC + st->last_updated.tv_usec,
70 - microseconds
68 + (long long unsigned)microseconds
69 );
70 }
71 #endif
@@ -189,7 +187,7 @@ static inline PARSER_RC pluginsd_host_define_end(char **words __maybe_unused, si
187 netdata_configured_utc_offset,
188 program_name,
189 NETDATA_VERSION,
192 - default_rrd_update_every,
190 + nd_profile.update_every,
191 default_rrd_history_entries,
192 default_rrd_memory_mode,
193 health_plugin_enabled(),
@@ -205,7 +203,7 @@ static inline PARSER_RC pluginsd_host_define_end(char **words __maybe_unused, si
203
204 rrdhost_option_set(host, RRDHOST_OPTION_VIRTUAL_HOST);
205 rrdhost_flag_set(host, RRDHOST_FLAG_COLLECTOR_ONLINE);
208 - rrdhost_state_connected(host);
206 + object_state_activate(&host->state_id);
207 ml_host_start(host);
208 dyncfg_host_init(host);
209
@@ -376,79 +374,6 @@ static inline PARSER_RC pluginsd_chart(char **words, size_t num_words, PARSER *p
374 return PARSER_RC_OK;
375 }
376
379 -static bool backfill_callback(size_t successful_dims __maybe_unused, size_t failed_dims __maybe_unused, struct backfill_request_data *brd) {
380 - if(!rrdhost_state_acquire(brd->host, brd->rrdhost_receiver_state_id))
381 - return false;
382 -
383 - bool rc = replicate_chart_request(send_to_plugin, brd->parser, brd->host, brd->st,
384 - brd->first_entry_child, brd->last_entry_child, brd->child_wall_clock_time,
385 - 0, 0);
386 - if (rc) {
387 - rrdset_flag_set(brd->st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS);
388 - rrdset_flag_clear(brd->st, RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED);
389 - rrdhost_receiver_replicating_charts_plus_one(brd->st->rrdhost);
390 - }
391 - else {
392 - netdata_log_error(
393 - "PLUGINSD: 'host:%s' failed to initiate replication for 'chart:%s'",
394 - rrdhost_hostname(brd->host),
395 - rrdset_id(brd->st));
396 - }
397 -
398 - rrdhost_state_release(brd->host);
399 - return rc;
400 -}
401 -
402 -static inline PARSER_RC pluginsd_chart_definition_end(char **words, size_t num_words, PARSER *parser) {
403 - const char *first_entry_txt = get_word(words, num_words, 1);
404 - const char *last_entry_txt = get_word(words, num_words, 2);
405 - const char *wall_clock_time_txt = get_word(words, num_words, 3);
406 -
407 - RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_CHART_DEFINITION_END);
408 - if(!host) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
409 -
410 - RRDSET *st = pluginsd_require_scope_chart(parser, PLUGINSD_KEYWORD_CHART_DEFINITION_END, PLUGINSD_KEYWORD_CHART);
411 - if(!st) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
412 -
413 - time_t first_entry_child = (first_entry_txt && *first_entry_txt) ? (time_t)str2ul(first_entry_txt) : 0;
414 - time_t last_entry_child = (last_entry_txt && *last_entry_txt) ? (time_t)str2ul(last_entry_txt) : 0;
415 - time_t child_wall_clock_time = (wall_clock_time_txt && *wall_clock_time_txt) ? (time_t)str2ul(wall_clock_time_txt) : now_realtime_sec();
416 -
417 - bool ok = true;
418 - if(!rrdset_flag_check(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS)) {
419 -
420 -#ifdef NETDATA_LOG_REPLICATION_REQUESTS
421 - st->replay.start_streaming = false;
422 - st->replay.after = 0;
423 - st->replay.before = 0;
424 -#endif
425 -
426 - struct backfill_request_data brd = {
427 - .rrdhost_receiver_state_id = rrdhost_state_id(host),
428 - .parser = parser,
429 - .host = host,
430 - .st = st,
431 - .first_entry_child = first_entry_child,
432 - .last_entry_child = last_entry_child,
433 - .child_wall_clock_time = child_wall_clock_time,
434 - };
435 -
436 - ok = backfill_request_add(st, backfill_callback, &brd);
437 - if(!ok)
438 - ok = backfill_callback(0, 0, &brd);
439 - }
440 -#ifdef NETDATA_LOG_REPLICATION_REQUESTS
441 - else {
442 - internal_error(true, "REPLAY: 'host:%s/chart:%s' not sending duplicate replication request",
443 - rrdhost_hostname(st->rrdhost), rrdset_id(st));
444 - }
445 -#endif
446 -
447 - stream_thread_received_metadata();
448 -
449 - return ok ? PARSER_RC_OK : PARSER_RC_ERROR;
450 -}
451 -
377 static inline PARSER_RC pluginsd_dimension(char **words, size_t num_words, PARSER *parser) {
378 int idx = 1;
379 ssize_t slot = pluginsd_parse_rrd_slot(words, num_words);
@@ -1179,13 +1104,6 @@ void pluginsd_process_cleanup(PARSER *parser) {
1104 pluginsd_cleanup_v2(parser);
1105 pluginsd_host_define_cleanup(parser);
1106
1182 -#ifdef NETDATA_LOG_STREAM_RECEIVE
1183 - if(parser->user.stream_log_fp) {
1184 - fclose(parser->user.stream_log_fp);
1185 - parser->user.stream_log_fp = NULL;
1186 - }
1187 -#endif
1188 -
1107 parser_destroy(parser);
1108 }
1109
src/plugins.d/pluginsd_parser.h
+16 -11
@@ -5,6 +5,10 @@
5
6 #include "daemon/common.h"
7
8 +#ifdef NETDATA_LOG_STREAM_RECEIVER
9 +#include "streaming/stream-receiver-internals.h"
10 +#endif
11 +
12 #define WORKER_PARSER_FIRST_JOB 36
13
14 // this has to be in-sync with the same at stream-thread.c
@@ -31,7 +35,9 @@ typedef enum __attribute__ ((__packed__)) parser_input_type {
35 typedef enum __attribute__ ((__packed__)) {
36 PARSER_INIT_PLUGINSD = (1 << 1),
37 PARSER_INIT_STREAMING = (1 << 2),
34 - PARSER_REP_METADATA = (1 << 3),
38 + PARSER_REP_REPLICATION = (1 << 3),
39 + PARSER_REP_METADATA = (1 << 4),
40 + PARSER_REP_DATA = (1 << 5),
41 } PARSER_REPERTOIRE;
42
43 struct parser;
@@ -56,9 +62,8 @@ typedef struct parser_user_object {
62 size_t data_collections_count;
63 int enabled;
64
59 -#ifdef NETDATA_LOG_STREAM_RECEIVE
60 - FILE *stream_log_fp;
61 - PARSER_REPERTOIRE stream_log_repertoire;
65 +#ifdef NETDATA_LOG_STREAM_RECEIVER
66 + void *rpt;
67 #endif
68
69 STREAM_CAPABILITIES capabilities; // receiver capabilities
@@ -172,8 +177,8 @@ bool parser_reconstruct_instance(BUFFER *wb, void *ptr);
177 bool parser_reconstruct_context(BUFFER *wb, void *ptr);
178
179 static inline int parser_action(PARSER *parser, char *input) {
175 -#ifdef NETDATA_LOG_STREAM_RECEIVE
176 - static __thread char line[PLUGINSD_LINE_MAX + 1];
180 +#ifdef NETDATA_LOG_STREAM_RECEIVER
181 + char line[1024];
182 strncpyz(line, input, sizeof(line) - 1);
183 #endif
184
@@ -222,11 +227,6 @@ static inline int parser_action(PARSER *parser, char *input) {
227 if(likely(parser->keyword)) {
228 worker_is_busy(parser->keyword->worker_job_id);
229
225 -#ifdef NETDATA_LOG_STREAM_RECEIVE
226 - if(parser->user.stream_log_fp && parser->keyword->repertoire & parser->user.stream_log_repertoire)
227 - fprintf(parser->user.stream_log_fp, "%s", line);
228 -#endif
229 -
230 rc = parser_execute(parser, parser->keyword, parser->line.words, parser->line.num_words);
231 // rc = (*t->func)(words, num_words, parser);
232 worker_is_idle();
@@ -241,6 +241,11 @@ static inline int parser_action(PARSER *parser, char *input) {
241 command, parser->line.count, buffer_tostring(wb));
242 }
243
244 +#ifdef NETDATA_LOG_STREAM_RECEIVER
245 + if((parser->keyword->repertoire & PARSER_REP_REPLICATION) && !(parser->keyword->repertoire & PARSER_REP_DATA))
246 + stream_receiver_log_payload(parser->user.rpt, line, STREAM_TRAFFIC_TYPE_REPLICATION, true);
247 +#endif
248 +
249 line_splitter_reset(&parser->line);
250 return (rc == PARSER_RC_ERROR || rc == PARSER_RC_STOP);
251 }
src/plugins.d/pluginsd_replication.c
+143 -31
@@ -2,8 +2,109 @@
2
3 #include "pluginsd_replication.h"
4 #include "streaming/stream-receiver-internals.h"
5 -#include "streaming/replication.h"
5 +#include "streaming/stream-replication-receiver.h"
6 #include "streaming/stream-waiting-list.h"
7 +#include "web/api/queries/backfill.h"
8 +
9 +static bool backfill_callback(size_t successful_dims __maybe_unused, size_t failed_dims __maybe_unused, struct backfill_request_data *brd) {
10 + if(!object_state_acquire(&brd->host->state_id, brd->host_state_id)) {
11 + // this may happen because the host got reconnected
12 +
13 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
14 + "PLUGINSD REPLAY ERROR: 'host:%s' failed to acquire host for sending replication"
15 + " command for 'chart:%s'",
16 + rrdhost_hostname(brd->host),
17 + rrdset_id(brd->st));
18 +
19 + return false;
20 + }
21 +
22 + __atomic_sub_fetch(&brd->host->stream.rcv.status.replication.backfill_pending, 1, __ATOMIC_RELAXED);
23 +
24 + bool rc = replicate_chart_request(send_to_plugin, brd->parser, brd->host, brd->st,
25 + brd->first_entry_child, brd->last_entry_child, brd->child_wall_clock_time,
26 + 0, 0);
27 +
28 + if (!rc) {
29 + nd_log(NDLS_DAEMON, NDLP_ERR,
30 + "PLUGINSD REPLAY ERROR: 'host:%s' failed to initiate replication for 'chart:%s' - replication may not proceed for this instance.",
31 + rrdhost_hostname(brd->host),
32 + rrdset_id(brd->st));
33 + }
34 +
35 + object_state_release(&brd->host->state_id);
36 + return rc;
37 +}
38 +
39 +PARSER_RC pluginsd_chart_definition_end(char **words, size_t num_words, PARSER *parser) {
40 + const char *first_entry_txt = get_word(words, num_words, 1);
41 + const char *last_entry_txt = get_word(words, num_words, 2);
42 + const char *wall_clock_time_txt = get_word(words, num_words, 3);
43 +
44 + RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_CHART_DEFINITION_END);
45 + if(!host) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
46 +
47 + RRDSET *st = pluginsd_require_scope_chart(parser, PLUGINSD_KEYWORD_CHART_DEFINITION_END, PLUGINSD_KEYWORD_CHART);
48 + if(!st) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
49 +
50 + time_t first_entry_child = (first_entry_txt && *first_entry_txt) ? (time_t)str2ul(first_entry_txt) : 0;
51 + time_t last_entry_child = (last_entry_txt && *last_entry_txt) ? (time_t)str2ul(last_entry_txt) : 0;
52 + time_t child_wall_clock_time = (wall_clock_time_txt && *wall_clock_time_txt) ? (time_t)str2ul(wall_clock_time_txt) : now_realtime_sec();
53 +
54 + bool ok = true;
55 + RRDSET_FLAGS old = rrdset_flag_set_and_clear(
56 + st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS, RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED);
57 +
58 + if(!(old & RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS)) {
59 + rrdhost_receiver_replicating_charts_plus_one(st->rrdhost);
60 + __atomic_add_fetch(&host->stream.rcv.status.replication.counter_in, 1, __ATOMIC_RELAXED);
61 +
62 +#ifdef REPLICATION_TRACKING
63 + st->stream.rcv.who = REPLAY_WHO_ME;
64 +#endif
65 +
66 +#ifdef NETDATA_LOG_REPLICATION_REQUESTS
67 + st->replay.start_streaming = false;
68 + st->replay.after = 0;
69 + st->replay.before = 0;
70 +#endif
71 +
72 + struct backfill_request_data brd = {
73 + .host_state_id = object_state_id(&host->state_id),
74 + .parser = parser,
75 + .host = host,
76 + .st = st,
77 + .first_entry_child = first_entry_child,
78 + .last_entry_child = last_entry_child,
79 + .child_wall_clock_time = child_wall_clock_time,
80 + };
81 +
82 + __atomic_add_fetch(&host->stream.rcv.status.replication.backfill_pending, 1, __ATOMIC_RELAXED);
83 +
84 + if(!rrdset_flag_check(st, RRDSET_FLAG_BACKFILLED_HIGH_TIERS)) {
85 + ok = backfill_request_add(st, backfill_callback, &brd);
86 + if (!ok)
87 + ok = backfill_callback(0, 0, &brd);
88 + else
89 + rrdset_flag_set(st, RRDSET_FLAG_BACKFILLED_HIGH_TIERS);
90 + }
91 + else
92 + ok = backfill_callback(0, 0, &brd);
93 + }
94 + else {
95 + // this is normal, since dimensions may be added to a chart,
96 + // and the child will send another CHART_DEFINITION_END command.
97 +
98 +#ifdef NETDATA_LOG_REPLICATION_REQUESTS
99 + internal_error(true, "REPLAY: 'host:%s/chart:%s' not sending duplicate replication request",
100 + rrdhost_hostname(st->rrdhost), rrdset_id(st));
101 +#endif
102 + }
103 +
104 + stream_thread_received_metadata();
105 +
106 + return ok ? PARSER_RC_OK : PARSER_RC_ERROR;
107 +}
108
109 PARSER_RC pluginsd_replay_begin(char **words, size_t num_words, PARSER *parser) {
110 int idx = 1;
@@ -89,12 +190,13 @@ PARSER_RC pluginsd_replay_begin(char **words, size_t num_words, PARSER *parser)
190 return PARSER_RC_OK;
191 }
192
92 - netdata_log_error("PLUGINSD REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN
93 - " from %ld to %ld, but timestamps are invalid "
94 - "(now is %ld [%s], tolerance %ld). Ignoring " PLUGINSD_KEYWORD_REPLAY_SET,
95 - rrdhost_hostname(st->rrdhost), rrdset_id(st), start_time, end_time,
96 - wall_clock_time, wall_clock_comes_from_child ? "child wall clock" : "parent wall clock",
97 - tolerance);
193 + nd_log(NDLS_DAEMON, NDLP_ERR,
194 + "PLUGINSD REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN
195 + " from %ld to %ld, but timestamps are invalid "
196 + "(now is %ld [%s], tolerance %ld). Ignoring " PLUGINSD_KEYWORD_REPLAY_SET,
197 + rrdhost_hostname(st->rrdhost), rrdset_id(st), start_time, end_time,
198 + wall_clock_time, wall_clock_comes_from_child ? "child wall clock" : "parent wall clock",
199 + tolerance);
200 }
201
202 // the child sends an RBEGIN without any parameters initially
@@ -127,7 +229,7 @@ PARSER_RC pluginsd_replay_set(char **words, size_t num_words, PARSER *parser) {
229 if(!parser->user.replay.rset_enabled) {
230 nd_log_limit_static_thread_var(erl, 1, 0);
231 nd_log_limit(&erl, NDLS_COLLECTORS, NDLP_ERR,
130 - "PLUGINSD: 'host:%s/chart:%s' got a %s but it is disabled by %s errors",
232 + "PLUGINSD REPLAY ERROR: 'host:%s/chart:%s' got a %s but it is disabled by %s errors",
233 rrdhost_hostname(host), rrdset_id(st), PLUGINSD_KEYWORD_REPLAY_SET, PLUGINSD_KEYWORD_REPLAY_BEGIN);
234
235 // we have to return OK here
@@ -140,14 +242,13 @@ PARSER_RC pluginsd_replay_set(char **words, size_t num_words, PARSER *parser) {
242 st->pluginsd.set = true;
243
244 if (unlikely(!parser->user.replay.start_time || !parser->user.replay.end_time)) {
143 - netdata_log_error("PLUGINSD: 'host:%s/chart:%s/dim:%s' got a %s with invalid timestamps %ld to %ld from a %s. Disabling it.",
144 - rrdhost_hostname(host),
145 - rrdset_id(st),
146 - dimension,
147 - PLUGINSD_KEYWORD_REPLAY_SET,
148 - parser->user.replay.start_time,
149 - parser->user.replay.end_time,
150 - PLUGINSD_KEYWORD_REPLAY_BEGIN);
245 +
246 + nd_log(NDLS_DAEMON, NDLP_ERR,
247 + "PLUGINSD REPLAY ERROR: 'host:%s/chart:%s/dim:%s' got a %s with "
248 + "invalid timestamps %ld to %ld from a %s. Disabling it.",
249 + rrdhost_hostname(host), rrdset_id(st), dimension, PLUGINSD_KEYWORD_REPLAY_SET,
250 + parser->user.replay.start_time, parser->user.replay.end_time, PLUGINSD_KEYWORD_REPLAY_BEGIN);
251 +
252 return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
253 }
254
@@ -177,7 +278,7 @@ PARSER_RC pluginsd_replay_set(char **words, size_t num_words, PARSER *parser) {
278 else {
279 nd_log_limit_static_global_var(erl, 1, 0);
280 nd_log_limit(&erl, NDLS_COLLECTORS, NDLP_WARNING,
180 - "PLUGINSD: 'host:%s/chart:%s/dim:%s' has the ARCHIVED flag set, but it is replicated. "
281 + "PLUGINSD REPLAY ERROR: 'host:%s/chart:%s/dim:%s' has the ARCHIVED flag set, but it is replicated. "
282 "Ignoring data.",
283 rrdhost_hostname(st->rrdhost), rrdset_id(st), rrddim_name(rd));
284 }
@@ -219,7 +320,7 @@ PARSER_RC pluginsd_replay_rrddim_collection_state(char **words, size_t num_words
320 usec_t last_collected_ut = last_collected_ut_str ? str2ull_encoded(last_collected_ut_str) : 0;
321 if(last_collected_ut > dim_last_collected_ut) {
322 rd->collector.last_collected_time.tv_sec = (time_t)(last_collected_ut / USEC_PER_SEC);
222 - rd->collector.last_collected_time.tv_usec = (last_collected_ut % USEC_PER_SEC);
323 + rd->collector.last_collected_time.tv_usec = (suseconds_t)(last_collected_ut % USEC_PER_SEC);
324 }
325
326 rd->collector.last_collected_value = last_collected_value_str ? str2ll_encoded(last_collected_value_str) : 0;
@@ -247,14 +348,14 @@ PARSER_RC pluginsd_replay_rrdset_collection_state(char **words, size_t num_words
348 usec_t last_collected_ut = last_collected_ut_str ? str2ull_encoded(last_collected_ut_str) : 0;
349 if(last_collected_ut > chart_last_collected_ut) {
350 st->last_collected_time.tv_sec = (time_t)(last_collected_ut / USEC_PER_SEC);
250 - st->last_collected_time.tv_usec = (last_collected_ut % USEC_PER_SEC);
351 + st->last_collected_time.tv_usec = (suseconds_t)(last_collected_ut % USEC_PER_SEC);
352 }
353
354 usec_t chart_last_updated_ut = (usec_t)st->last_updated.tv_sec * USEC_PER_SEC + (usec_t)st->last_updated.tv_usec;
355 usec_t last_updated_ut = last_updated_ut_str ? str2ull_encoded(last_updated_ut_str) : 0;
356 if(last_updated_ut > chart_last_updated_ut) {
357 st->last_updated.tv_sec = (time_t)(last_updated_ut / USEC_PER_SEC);
257 - st->last_updated.tv_usec = (last_updated_ut % USEC_PER_SEC);
358 + st->last_updated.tv_usec = (suseconds_t)(last_updated_ut % USEC_PER_SEC);
359 }
360
361 st->counter++;
@@ -265,7 +366,7 @@ PARSER_RC pluginsd_replay_rrdset_collection_state(char **words, size_t num_words
366
367 PARSER_RC pluginsd_replay_end(char **words, size_t num_words, PARSER *parser) {
368 if (num_words < 7) { // accepts 7, but the 7th is optional
268 - netdata_log_error("REPLAY: malformed " PLUGINSD_KEYWORD_REPLAY_END " command");
369 + nd_log(NDLS_DAEMON, NDLP_ERR, "REPLAY: malformed " PLUGINSD_KEYWORD_REPLAY_END " command");
370 return PARSER_RC_ERROR;
371 }
372
@@ -291,6 +392,7 @@ PARSER_RC pluginsd_replay_end(char **words, size_t num_words, PARSER *parser) {
392
393 RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_REPLAY_END);
394 if(!host) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
395 + __atomic_add_fetch(&host->stream.rcv.status.replication.counter_in, 1, __ATOMIC_RELAXED);
396
397 RRDSET *st = pluginsd_require_scope_chart(parser, PLUGINSD_KEYWORD_REPLAY_END, PLUGINSD_KEYWORD_REPLAY_BEGIN);
398 if(!st) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
@@ -310,7 +412,7 @@ PARSER_RC pluginsd_replay_end(char **words, size_t num_words, PARSER *parser) {
412
413 if(parser->user.replay.rset_enabled && st->rrdhost->receiver) {
414 time_t now = now_realtime_sec();
313 - time_t started = st->rrdhost->receiver->replication_first_time_t;
415 + time_t started = st->rrdhost->receiver->replication.first_time_s;
416 time_t current = parser->user.replay.end_time;
417
418 if(started && current > started) {
@@ -340,20 +442,25 @@ PARSER_RC pluginsd_replay_end(char **words, size_t num_words, PARSER *parser) {
442 #endif
443
444 if (start_streaming) {
445 +#ifdef REPLICATION_TRACKING
446 + st->stream.rcv.who = REPLAY_WHO_FINISHED;
447 +#endif
448 +
449 if (st->update_every != update_every_child)
450 rrdset_set_update_every_s(st, update_every_child);
451
346 - if(rrdset_flag_check(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS)) {
347 - rrdset_flag_set(st, RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED);
348 - rrdset_flag_clear(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS);
349 - rrdset_flag_clear(st, RRDSET_FLAG_SYNC_CLOCK);
452 + RRDSET_FLAGS old = rrdset_flag_set_and_clear(
453 + st, RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED,
454 + RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS | RRDSET_FLAG_SYNC_CLOCK);
455 +
456 + if(!(old & RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED))
457 rrdhost_receiver_replicating_charts_minus_one(st->rrdhost);
351 - }
352 -#ifdef NETDATA_LOG_REPLICATION_REQUESTS
458 +
459 else
354 - internal_error(true, "REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_END " with enable_streaming = true, but there is no replication in progress for this chart.",
355 - rrdhost_hostname(host), rrdset_id(st));
356 -#endif
460 + nd_log(NDLS_DAEMON, NDLP_WARNING,
461 + "PLUGINSD REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_END " "
462 + "with enable_streaming = true, but there was no replication in progress for this chart.",
463 + rrdhost_hostname(host), rrdset_id(st));
464
465 pluginsd_clear_scope_chart(parser, PLUGINSD_KEYWORD_REPLAY_END);
466
@@ -365,6 +472,10 @@ PARSER_RC pluginsd_replay_end(char **words, size_t num_words, PARSER *parser) {
472 return PARSER_RC_OK;
473 }
474
475 +#ifdef REPLICATION_TRACKING
476 + st->stream.rcv.who = REPLAY_WHO_ME;
477 +#endif
478 +
479 pluginsd_clear_scope_chart(parser, PLUGINSD_KEYWORD_REPLAY_END);
480
481 rrdcontext_updated_retention_rrdset(st);
@@ -372,5 +483,6 @@ PARSER_RC pluginsd_replay_end(char **words, size_t num_words, PARSER *parser) {
483 bool ok = replicate_chart_request(send_to_plugin, parser, host, st,
484 first_entry_child, last_entry_child, child_world_time,
485 first_entry_requested, last_entry_requested);
486 +
487 return ok ? PARSER_RC_OK : PARSER_RC_ERROR;
488 }
src/plugins.d/pluginsd_replication.h
+1
@@ -10,5 +10,6 @@ PARSER_RC pluginsd_replay_set(char **words, size_t num_words, PARSER *parser);
10 PARSER_RC pluginsd_replay_rrddim_collection_state(char **words, size_t num_words, PARSER *parser);
11 PARSER_RC pluginsd_replay_rrdset_collection_state(char **words, size_t num_words, PARSER *parser);
12 PARSER_RC pluginsd_replay_end(char **words, size_t num_words, PARSER *parser);
13 +PARSER_RC pluginsd_chart_definition_end(char **words, size_t num_words, PARSER *parser);
14
15 #endif //NETDATA_PLUGINSD_REPLICATION_H
src/registry/registry_init.c
+4 -4
@@ -149,28 +149,28 @@ int registry_init(void) {
149 &registry.aral_stats,
150 "registry_persons",
151 &netdata_configured_cache_dir,
152 - use_mmap, true);
152 + use_mmap, true, true);
153
154 registry.machines_aral = aral_create("registry_machines", sizeof(REGISTRY_MACHINE),
155 min_page_size / sizeof(REGISTRY_MACHINE), max_page_size,
156 &registry.aral_stats,
157 "registry_machines",
158 &netdata_configured_cache_dir,
159 - use_mmap, true);
159 + use_mmap, true, true);
160
161 registry.person_urls_aral = aral_create("registry_person_urls", sizeof(REGISTRY_PERSON_URL),
162 min_page_size / sizeof(REGISTRY_PERSON_URL), max_page_size,
163 &registry.aral_stats,
164 "registry_person_urls",
165 &netdata_configured_cache_dir,
166 - use_mmap, true);
166 + use_mmap, true, true);
167
168 registry.machine_urls_aral = aral_create("registry_machine_urls", sizeof(REGISTRY_MACHINE_URL),
169 min_page_size / sizeof(REGISTRY_MACHINE_URL), max_page_size,
170 &registry.aral_stats,
171 "registry_machine_urls",
172 &netdata_configured_cache_dir,
173 - use_mmap, true);
173 + use_mmap, true, true);
174
175 registry_log_open();
176 registry_db_load();
src/streaming/protocol/command-chart-definition.c
+3 -4
@@ -116,11 +116,10 @@ bool stream_sender_send_rrdset_definition(BUFFER *wb, RRDSET *st) {
116 (unsigned long long)db_last_time_t,
117 (unsigned long long)now);
118
119 - if(!rrdset_flag_check(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS)) {
120 - rrdset_flag_set(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS);
121 - rrdset_flag_clear(st, RRDSET_FLAG_SENDER_REPLICATION_FINISHED);
119 + RRDSET_FLAGS old = rrdset_flag_set_and_clear(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS, RRDSET_FLAG_SENDER_REPLICATION_FINISHED);
120 + if(!(old & RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS))
121 rrdhost_sender_replicating_charts_plus_one(st->rrdhost);
123 - }
122 +
123 replication_progress = true;
124
125 #ifdef NETDATA_LOG_REPLICATION_REQUESTS
src/streaming/protocol/command-nodeid.c
+6 -6
@@ -52,7 +52,7 @@ void stream_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
52 if (uuid_parse(claim_id_str ? claim_id_str : "", claim_id.uuid) != 0) {
53 nd_log(NDLS_DAEMON, NDLP_ERR,
54 "STREAM SND '%s' [to %s] received invalid claim id '%s'",
55 - rrdhost_hostname(s->host), s->connected_to,
55 + rrdhost_hostname(s->host), s->remote_ip,
56 claim_id_str ? claim_id_str : "(unset)");
57 return;
58 }
@@ -61,7 +61,7 @@ void stream_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
61 if(uuid_parse(node_id_str ? node_id_str : "", node_id.uuid) != 0) {
62 nd_log(NDLS_DAEMON, NDLP_ERR,
63 "STREAM SND '%s' [to %s] received an invalid node id '%s'",
64 - rrdhost_hostname(s->host), s->connected_to,
64 + rrdhost_hostname(s->host), s->remote_ip,
65 node_id_str ? node_id_str : "(unset)");
66 return;
67 }
@@ -69,14 +69,14 @@ void stream_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
69 if (!UUIDiszero(s->host->aclk.claim_id_of_parent) && !UUIDeq(s->host->aclk.claim_id_of_parent, claim_id))
70 nd_log(NDLS_DAEMON, NDLP_INFO,
71 "STREAM SND '%s' [to %s] changed parent's claim id to %s",
72 - rrdhost_hostname(s->host), s->connected_to,
72 + rrdhost_hostname(s->host), s->remote_ip,
73 claim_id_str ? claim_id_str : "(unset)");
74
75 if(!UUIDiszero(s->host->node_id) && !UUIDeq(s->host->node_id, node_id)) {
76 if(claimed) {
77 nd_log(NDLS_DAEMON, NDLP_WARNING,
78 "STREAM SND '%s' [to %s] parent reports different node id '%s', but we are claimed. Ignoring it.",
79 - rrdhost_hostname(s->host), s->connected_to,
79 + rrdhost_hostname(s->host), s->remote_ip,
80 node_id_str ? node_id_str : "(unset)");
81 return;
82 }
@@ -84,7 +84,7 @@ void stream_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
84 update_node_id = true;
85 nd_log(NDLS_DAEMON, NDLP_WARNING,
86 "STREAM SND '%s' [to %s] changed node id to %s",
87 - rrdhost_hostname(s->host), s->connected_to,
87 + rrdhost_hostname(s->host), s->remote_ip,
88 node_id_str ? node_id_str : "(unset)");
89 }
90 }
@@ -92,7 +92,7 @@ void stream_sender_get_node_and_claim_id_from_parent(struct sender_state *s) {
92 if(!url || !*url) {
93 nd_log(NDLS_DAEMON, NDLP_ERR,
94 "STREAM SND '%s' [to %s] received an invalid cloud URL '%s'",
95 - rrdhost_hostname(s->host), s->connected_to,
95 + rrdhost_hostname(s->host), s->remote_ip,
96 url ? url : "(unset)");
97 return;
98 }
src/streaming/replication.h deleted
-47
@@ -1,47 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef REPLICATION_H
4 -#define REPLICATION_H
5 -
6 -#include "daemon/common.h"
7 -#include "stream-circular-buffer.h"
8 -
9 -#ifdef __cplusplus
10 -extern "C" {
11 -#endif
12 -
13 -struct parser;
14 -
15 -struct replication_query_statistics {
16 - SPINLOCK spinlock;
17 - size_t queries_started;
18 - size_t queries_finished;
19 - size_t points_read;
20 - size_t points_generated;
21 -};
22 -
23 -struct replication_query_statistics replication_get_query_statistics(void);
24 -
25 -bool replicate_chart_response(RRDHOST *rh, RRDSET *rs, bool start_streaming, time_t after, time_t before);
26 -
27 -typedef ssize_t (*send_command)(const char *txt, struct parser *parser, STREAM_TRAFFIC_TYPE type);
28 -
29 -bool replicate_chart_request(send_command callback, struct parser *parser,
30 - RRDHOST *rh, RRDSET *rs,
31 - time_t child_first_entry, time_t child_last_entry, time_t child_wall_clock_time,
32 - time_t response_first_start_time, time_t response_last_end_time);
33 -
34 -void replication_sender_init(struct sender_state *sender);
35 -void replication_cleanup_sender(struct sender_state *sender);
36 -void replication_sender_delete_pending_requests(struct sender_state *sender);
37 -void replication_add_request(struct sender_state *sender, const char *chart_id, time_t after, time_t before, bool start_streaming);
38 -void replication_recalculate_buffer_used_ratio_unsafe(struct sender_state *s);
39 -
40 -size_t replication_allocated_memory(void);
41 -size_t replication_allocated_buffers(void);
42 -
43 -#ifdef __cplusplus
44 -}
45 -#endif
46 -
47 -#endif /* REPLICATION_H */
src/streaming/stream-capabilities.c
+4 -2
@@ -81,7 +81,7 @@ void log_receiver_capabilities(struct receiver_state *rpt) {
81 stream_capabilities_to_string(wb, rpt->capabilities);
82
83 nd_log_daemon(NDLP_INFO, "STREAM RCV '%s' [from [%s]:%s]: established link with negotiated capabilities: %s",
84 - rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, buffer_tostring(wb));
84 + rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port, buffer_tostring(wb));
85
86 buffer_free(wb);
87 }
@@ -91,7 +91,7 @@ void log_sender_capabilities(struct sender_state *s) {
91 stream_capabilities_to_string(wb, s->capabilities);
92
93 nd_log_daemon(NDLP_INFO, "STREAM SND '%s' [to %s]: established link with negotiated capabilities: %s",
94 - rrdhost_hostname(s->host), s->connected_to, buffer_tostring(wb));
94 + rrdhost_hostname(s->host), s->remote_ip, buffer_tostring(wb));
95
96 buffer_free(wb);
97 }
@@ -174,4 +174,6 @@ void check_local_streaming_capabilities(void) {
174 ieee754_doubles = is_system_ieee754_double();
175 if(!ieee754_doubles)
176 globally_disabled_capabilities |= STREAM_CAP_IEEE754;
177 + else
178 + globally_disabled_capabilities &= ~STREAM_CAP_IEEE754;
179 }
src/streaming/stream-circular-buffer.c
+21 -9
@@ -6,7 +6,9 @@
6 struct stream_circular_buffer {
7 struct circular_buffer *cb;
8 STREAM_CIRCULAR_BUFFER_STATS stats;
9 - usec_t last_reset_ut;
9 +
10 + usec_t last_recreate_ut; // recreates are only used to shrink the buffer, they are normal during operation
11 + usec_t last_sent_ut; // the last time we removed or flushed data from the buffer
12
13 struct {
14 // the current max size of the buffer
@@ -17,7 +19,7 @@ struct stream_circular_buffer {
19
20 // the last time we flushed the buffer
21 // by monitoring this we can know if the system was reconnected
20 - usec_t since_ut;
22 + usec_t last_flush_ut;
23 } atomic;
24 };
25
@@ -52,9 +54,11 @@ bool stream_circular_buffer_set_max_size_unsafe(STREAM_CIRCULAR_BUFFER *scb, siz
54 }
55
56 void stream_circular_buffer_flush_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t buffer_max_size) {
55 - __atomic_store_n(&((scb)->atomic.since_ut), now_monotonic_usec(), __ATOMIC_RELAXED);
57 + usec_t now_ut = now_monotonic_usec();
58 + __atomic_store_n(&scb->atomic.last_flush_ut, now_ut, __ATOMIC_RELAXED);
59
60 // flush the output buffer from any data it may have
61 + scb->last_sent_ut = now_ut;
62 cbuffer_flush(scb->cb);
63 memset(&scb->stats, 0, sizeof(scb->stats));
64 stream_circular_buffer_set_max_size_unsafe(scb, buffer_max_size, true);
@@ -62,7 +66,7 @@ void stream_circular_buffer_flush_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t buf
66 }
67
68 inline size_t stream_sender_get_buffer_used_percent(STREAM_CIRCULAR_BUFFER *scb) {
65 - return __atomic_load_n(&((scb)->atomic.buffer_ratio), __ATOMIC_RELAXED);
69 + return __atomic_load_n(&scb->atomic.buffer_ratio, __ATOMIC_RELAXED);
70 }
71
72 size_t stream_circular_buffer_get_max_size(STREAM_CIRCULAR_BUFFER *scb) {
@@ -70,10 +74,10 @@ size_t stream_circular_buffer_get_max_size(STREAM_CIRCULAR_BUFFER *scb) {
74 }
75
76 void stream_circular_buffer_recreate_timed_unsafe(STREAM_CIRCULAR_BUFFER *scb, usec_t now_ut, bool force) {
73 - if(!force && (scb->stats.bytes_outstanding || now_ut - scb->last_reset_ut < 300 * USEC_PER_SEC))
77 + if(!force && (scb->stats.bytes_outstanding || now_ut - scb->last_recreate_ut < 300 * USEC_PER_SEC))
78 return;
79
76 - scb->last_reset_ut = now_ut;
80 + scb->last_recreate_ut = now_ut;
81
82 scb->stats.recreates++; // we increase even if we don't do it, to have sender_start() recreate its buffers
83
@@ -84,11 +88,18 @@ void stream_circular_buffer_recreate_timed_unsafe(STREAM_CIRCULAR_BUFFER *scb, u
88 }
89 }
90
87 -inline usec_t stream_circular_buffer_get_since_ut(STREAM_CIRCULAR_BUFFER *scb) {
88 - return __atomic_load_n(&((scb)->atomic.since_ut), __ATOMIC_RELAXED);
91 +inline usec_t stream_circular_buffer_last_flush_ut(STREAM_CIRCULAR_BUFFER *scb) {
92 + return __atomic_load_n(&((scb)->atomic.last_flush_ut), __ATOMIC_RELAXED);
93 +}
94 +
95 +inline usec_t stream_circular_buffer_last_sent_ut(STREAM_CIRCULAR_BUFFER *scb) {
96 + // this is ok without locks and atomics, since only the stream threads
97 + // can actually remove data and call this
98 + return scb->last_sent_ut;
99 }
100
101 void stream_circular_buffer_destroy(STREAM_CIRCULAR_BUFFER *scb) {
102 + if(!scb) return;
103 cbuffer_free(scb->cb);
104 freez(scb);
105 }
@@ -118,7 +129,8 @@ size_t stream_circular_buffer_get_unsafe(STREAM_CIRCULAR_BUFFER *scb, char **chu
129 }
130
131 // removes data from the beginning of the circular buffer
121 -void stream_circular_buffer_del_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t bytes) {
132 +void stream_circular_buffer_del_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t bytes, usec_t now_ut) {
133 + scb->last_sent_ut = now_ut ? now_ut : now_monotonic_usec();
134 scb->stats.sends++;
135 scb->stats.bytes_sent += bytes;
136 cbuffer_remove_unsafe(scb->cb, bytes);
src/streaming/stream-circular-buffer.h
+5 -2
@@ -70,7 +70,10 @@ size_t stream_circular_buffer_get_max_size(STREAM_CIRCULAR_BUFFER *scb);
70 size_t stream_sender_get_buffer_used_percent(STREAM_CIRCULAR_BUFFER *scb);
71
72 // return the monotonic timestamp of the last time the buffer was created
73 -usec_t stream_circular_buffer_get_since_ut(STREAM_CIRCULAR_BUFFER *scb);
73 +usec_t stream_circular_buffer_last_flush_ut(STREAM_CIRCULAR_BUFFER *scb);
74 +
75 +// return the monotonic timestamp of the last time we removed data from the buffer
76 +usec_t stream_circular_buffer_last_sent_ut(STREAM_CIRCULAR_BUFFER *scb);
77
78 // --------------------------------------------------------------------------------------------------------------------
79 // data operations (add, get, remove data from/to the buffer)
@@ -86,7 +89,7 @@ size_t stream_circular_buffer_get_unsafe(STREAM_CIRCULAR_BUFFER *scb, char **chu
89
90 // removes data from the beginning of circular buffer
91 // it updates the statistics
89 -void stream_circular_buffer_del_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t bytes);
92 +void stream_circular_buffer_del_unsafe(STREAM_CIRCULAR_BUFFER *scb, size_t bytes, usec_t now_ut);
93
94 #ifdef __cplusplus
95 }
src/streaming/stream-conf.c
+9 -5
@@ -90,11 +90,12 @@ bool stream_conf_receiver_needs_dbengine(void) {
90 }
91
92 void stream_conf_load() {
93 - static bool loaded = false;
94 - if(loaded) return;
95 - loaded = true;
93 + static bool run = false;
94 + if(run) return;
95 + run = true;
96
97 stream_conf_load_internal();
98 + check_local_streaming_capabilities();
99
100 stream_send.enabled =
101 appconfig_get_boolean(&stream_config, CONFIG_SECTION_STREAM, "enabled", stream_send.enabled);
@@ -199,6 +200,10 @@ bool stream_conf_is_parent(bool recheck) {
200 return rc;
201 }
202
203 +bool stream_conf_is_child(void) {
204 + return stream_send.enabled;
205 +}
206 +
207 void stream_conf_receiver_config(struct receiver_state *rpt, struct stream_receiver_config *config, const char *api_key, const char *machine_guid) {
208 config->mode = rrd_memory_mode_id(
209 appconfig_get(&stream_config, machine_guid, "db",
@@ -209,8 +214,7 @@ void stream_conf_receiver_config(struct receiver_state *rpt, struct stream_recei
214 netdata_log_error("STREAM RCV '%s' [from [%s]:%s]: "
215 "dbengine is not enabled, falling back to default."
216 , rpt->hostname
212 - , rpt->client_ip, rpt->client_port
213 - );
217 + , rpt->remote_ip, rpt->remote_port);
218 config->mode = default_rrd_memory_mode;
219 }
220
src/streaming/stream-conf.h
+1
@@ -86,6 +86,7 @@ void stream_conf_receiver_config(struct receiver_state *rpt, struct stream_recei
86 void stream_conf_load();
87 bool stream_conf_receiver_needs_dbengine();
88 bool stream_conf_is_parent(bool recheck);
89 +bool stream_conf_is_child(void);
90
91 bool stream_conf_is_key_type(const char *api_key, const char *type);
92 bool stream_conf_api_key_is_enabled(const char *api_key, bool enabled);
src/streaming/stream-connector.c
+10 -18
@@ -261,7 +261,7 @@ stream_connect_validate_first_response(RRDHOST *host, struct sender_state *s, ch
261
262 nd_log(NDLS_DAEMON, priority,
263 "STREAM CONNECT '%s' [to %s]: %s - will retry in %d secs, at %s",
264 - rrdhost_hostname(host), s->connected_to, error, delay, buf);
264 + rrdhost_hostname(host), s->remote_ip, error, delay, buf);
265
266 return false;
267 }
@@ -282,7 +282,7 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
282
283 if(!stream_parent_connect_to_one(
284 &s->sock, host, default_port, timeout,
285 - s->connected_to, sizeof(s->connected_to) - 1,
285 + s->remote_ip, sizeof(s->remote_ip) - 1,
286 &host->stream.snd.parents.current)) {
287
288 if(s->sock.error != ND_SOCK_ERR_NO_DESTINATION_AVAILABLE)
@@ -305,7 +305,7 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
305 buffer_key_value_urlencode(wb, "&hostname", rrdhost_hostname(host));
306 buffer_key_value_urlencode(wb, "&registry_hostname", rrdhost_registry_hostname(host));
307 buffer_key_value_urlencode(wb, "&machine_guid", host->machine_guid);
308 - buffer_sprintf(wb, "&update_every=%d", default_rrd_update_every);
308 + buffer_sprintf(wb, "&update_every=%d", (int)nd_profile.update_every);
309 buffer_key_value_urlencode(wb, "&os", rrdhost_os(host));
310 buffer_key_value_urlencode(wb, "&timezone", rrdhost_timezone(host));
311 buffer_key_value_urlencode(wb, "&abbrev_timezone", rrdhost_abbrev_timezone(host));
@@ -346,7 +346,7 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
346
347 nd_log(NDLS_DAEMON, NDLP_ERR,
348 "STREAM CONNECT '%s' [to %s]: failed to send HTTP header to remote netdata.",
349 - rrdhost_hostname(host), s->connected_to);
349 + rrdhost_hostname(host), s->remote_ip);
350
351 stream_parent_set_reconnect_delay(
352 host->stream.snd.parents.current, STREAM_HANDSHAKE_ERROR_SEND_TIMEOUT, 60);
@@ -368,7 +368,7 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
368
369 nd_log(NDLS_DAEMON, NDLP_ERR,
370 "STREAM CONNECT '%s' [to %s]: remote netdata does not respond.",
371 - rrdhost_hostname(host), s->connected_to);
371 + rrdhost_hostname(host), s->remote_ip);
372
373 stream_parent_set_reconnect_delay(
374 host->stream.snd.parents.current, STREAM_HANDSHAKE_ERROR_RECEIVE_TIMEOUT, 30);
@@ -377,18 +377,6 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
377 }
378 response[bytes] = '\0';
379
380 - if(sock_setnonblock(s->sock.fd) < 0)
381 - nd_log(NDLS_DAEMON, NDLP_WARNING,
382 - "STREAM CONNECT '%s' [to %s]: cannot set non-blocking mode for socket.",
383 - rrdhost_hostname(host), s->connected_to);
384 -
385 - sock_setcloexec(s->sock.fd);
386 -
387 - if(sock_enlarge_out(s->sock.fd) < 0)
388 - nd_log(NDLS_DAEMON, NDLP_WARNING,
389 - "STREAM CONNECT '%s' [to %s]: cannot enlarge the socket buffer.",
390 - rrdhost_hostname(host), s->connected_to);
391 -
380 if(!stream_connect_validate_first_response(host, s, response, bytes)) {
381 nd_sock_close(&s->sock);
382 return false;
@@ -406,7 +394,7 @@ bool stream_connect(struct sender_state *s, uint16_t default_port, time_t timeou
394
395 nd_log(NDLS_DAEMON, NDLP_DEBUG,
396 "STREAM CONNECT '%s' [to %s]: connected to parent...",
409 - rrdhost_hostname(host), s->connected_to);
397 + rrdhost_hostname(host), s->remote_ip);
398
399 return true;
400 }
@@ -512,6 +500,10 @@ static void stream_connector_remove(struct sender_state *s) {
500 struct connector *sc = stream_connector_get(s);
501 __atomic_sub_fetch(&sc->nodes, 1, __ATOMIC_RELAXED);
502
503 + nd_log(NDLS_DAEMON, NDLP_NOTICE,
504 + "STREAM CNT '%s' [to %s]: streaming connector removed host: %s (signaled to stop)",
505 + rrdhost_hostname(s->host), s->remote_ip, stream_handshake_error_to_string(s->exit.reason));
506 +
507 stream_sender_remove(s);
508 }
509
src/streaming/stream-control.c
+5 -18
@@ -2,26 +2,13 @@
2
3 #include "stream-control.h"
4 #include "stream.h"
5 -#include "replication.h"
5 +#include "stream-replication-sender.h"
6
7 static struct {
8 - CACHE_LINE_PADDING();
9 -
10 - uint32_t backfill_runners;
11 -
12 - CACHE_LINE_PADDING();
13 -
14 - uint32_t replication_runners;
15 -
16 - CACHE_LINE_PADDING();
17 -
18 - uint32_t user_data_queries_runners;
19 -
20 - CACHE_LINE_PADDING();
21 -
22 - uint32_t user_weights_queries_runners;
23 -
24 - CACHE_LINE_PADDING();
8 + PAD64(uint32_t) backfill_runners;
9 + PAD64(uint32_t) replication_runners;
10 + PAD64(uint32_t) user_data_queries_runners;
11 + PAD64(uint32_t) user_weights_queries_runners;
12 } sc;
13
14 // --------------------------------------------------------------------------------------------------------------------
src/streaming/stream-handshake.c
+1
@@ -51,6 +51,7 @@ static struct {
51 {STREAM_HANDSHAKE_CONNECTED, "CONNECTED"},
52 {STREAM_HANDSHAKE_EXITING, "EXITING"},
53 {STREAM_HANDSHAKE_NO_STREAM_INFO, "NO STREAM INFO"},
54 + {STREAM_HANDSHAKE_REPLICATION_STALLED, "REPLICATION STALLED"},
55 { 0, NULL },
56 };
57
src/streaming/stream-handshake.h
+1
@@ -88,6 +88,7 @@ typedef enum {
88 STREAM_HANDSHAKE_CONNECTED = -39,
89 STREAM_HANDSHAKE_EXITING = -40,
90 STREAM_HANDSHAKE_NO_STREAM_INFO = -41,
91 + STREAM_HANDSHAKE_REPLICATION_STALLED = -42,
92
93 } STREAM_HANDSHAKE;
94
src/streaming/stream-parents.c
+3 -3
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "stream-sender-internals.h"
4 -#include "replication.h"
4 +#include "stream-replication-sender.h"
5
6 #define TIME_TO_CONSIDER_PARENTS_SIMILAR 120
7
@@ -607,7 +607,7 @@ bool stream_parent_connect_to_one_unsafe(
607 d->since_ut = now_ut;
608 d->banned_permanently = true;
609 skipped_not_useful++;
610 - nd_log(NDLS_DAEMON, NDLP_NOTICE,
610 + nd_log(NDLS_DAEMON, NDLP_INFO,
611 "STREAM PARENTS '%s': destination '%s' is banned permanently because it is the origin server",
612 rrdhost_hostname(host), string2str(d->destination));
613 continue;
@@ -635,7 +635,7 @@ bool stream_parent_connect_to_one_unsafe(
635 d->since_ut = now_ut;
636 d->banned_for_this_session = true;
637 skipped_not_useful++;
638 - nd_log(NDLS_DAEMON, NDLP_NOTICE,
638 + nd_log(NDLS_DAEMON, NDLP_INFO,
639 "STREAM PARENTS '%s': destination '%s' is banned for this session, because it is in our path before us.",
640 rrdhost_hostname(host), string2str(d->destination));
641 continue;
src/streaming/stream-receiver-connection.c
+38 -35
@@ -4,7 +4,7 @@
4 #include "stream-thread.h"
5 #include "stream-receiver-internals.h"
6 #include "web/server/h2o/http_server.h"
7 -#include "replication.h"
7 +#include "stream-replication-sender.h"
8
9 // --------------------------------------------------------------------------------------------------------------------
10
@@ -12,8 +12,8 @@ void stream_receiver_log_status(struct receiver_state *rpt, const char *msg, con
12 // this function may be called BEFORE we spawn the receiver thread
13 // so, we need to add the fields again (it does not harm)
14 ND_LOG_STACK lgs[] = {
15 - ND_LOG_FIELD_TXT(NDF_SRC_IP, rpt->client_ip),
16 - ND_LOG_FIELD_TXT(NDF_SRC_PORT, rpt->client_port),
15 + ND_LOG_FIELD_TXT(NDF_SRC_IP, rpt->remote_ip),
16 + ND_LOG_FIELD_TXT(NDF_SRC_PORT, rpt->remote_port),
17 ND_LOG_FIELD_TXT(NDF_NIDL_NODE, (rpt->hostname && *rpt->hostname) ? rpt->hostname : ""),
18 ND_LOG_FIELD_TXT(NDF_RESPONSE_CODE, status),
19 ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_from_child_msgid),
@@ -28,8 +28,7 @@ void stream_receiver_log_status(struct receiver_state *rpt, const char *msg, con
28
29 nd_log(NDLS_DAEMON, priority, "STREAM RCV '%s' [from [%s]:%s]: %s %s%s%s"
30 , (rpt->hostname && *rpt->hostname) ? rpt->hostname : ""
31 - , rpt->client_ip, rpt->client_port
32 - , msg
31 + , rpt->remote_ip, rpt->remote_port, msg
32 , rpt->exit.reason != STREAM_HANDSHAKE_NEVER?" (":""
33 , stream_handshake_error_to_string(rpt->exit.reason)
34 , rpt->exit.reason != STREAM_HANDSHAKE_NEVER?")":""
@@ -54,18 +53,31 @@ void stream_receiver_free(struct receiver_state *rpt) {
53 freez(rpt->os);
54 freez(rpt->timezone);
55 freez(rpt->abbrev_timezone);
57 - freez(rpt->client_ip);
58 - freez(rpt->client_port);
56 + freez(rpt->remote_ip);
57 + freez(rpt->remote_port);
58 freez(rpt->program_name);
59 freez(rpt->program_version);
60
61 + string_freez(rpt->config.send.api_key);
62 + string_freez(rpt->config.send.parents);
63 + string_freez(rpt->config.send.charts_matching);
64 +
65 + buffer_free(rpt->thread.line_buffer);
66 + rpt->thread.line_buffer = NULL;
67 +
68 freez(rpt->thread.compressed.buf);
69 rpt->thread.compressed.buf = NULL;
70 rpt->thread.compressed.size = 0;
71
66 - string_freez(rpt->config.send.api_key);
67 - string_freez(rpt->config.send.parents);
68 - string_freez(rpt->config.send.charts_matching);
72 + rpt->thread.send_to_child.msg.session = 0;
73 + rpt->thread.send_to_child.msg.meta = NULL;
74 + stream_circular_buffer_destroy(rpt->thread.send_to_child.scb);
75 + rpt->thread.send_to_child.scb = NULL;
76 +
77 +#ifdef NETDATA_LOG_STREAM_RECEIVER
78 + if(rpt->log.fp)
79 + fclose(rpt->log.fp);
80 +#endif
81
82 freez(rpt);
83 }
@@ -89,7 +101,7 @@ static int stream_receiver_response_too_busy_now(struct web_client *w) {
101 }
102
103 static void stream_receiver_takeover_web_connection(struct web_client *w, struct receiver_state *rpt) {
92 - rpt->sock.fd = w->ifd;
104 + rpt->sock.fd = w->fd;
105 rpt->sock.ssl = w->ssl;
106
107 w->ssl = NETDATA_SSL_UNSET_CONNECTION;
@@ -99,12 +111,8 @@ static void stream_receiver_takeover_web_connection(struct web_client *w, struct
111 if(web_server_mode == WEB_SERVER_MODE_STATIC_THREADED) {
112 web_client_flag_set(w, WEB_CLIENT_FLAG_DONT_CLOSE_SOCKET);
113 }
102 - else {
103 - if(w->ifd == w->ofd)
104 - w->ifd = w->ofd = -1;
105 - else
106 - w->ifd = -1;
107 - }
114 + else
115 + w->fd = -1;
116
117 buffer_flush(w->response.data);
118 }
@@ -189,9 +197,7 @@ static bool stream_receiver_send_first_response(struct receiver_state *rpt) {
197 "client willing to stream metrics for host '%s' with machine_guid '%s': "
198 "update every = %d, history = %d, memory mode = %s, health %s,%s"
199 , rpt->hostname
192 - , rpt->client_ip
193 - , rpt->client_port
194 - , rrdhost_hostname(rpt->host)
200 + , rpt->remote_ip, rpt->remote_port, rrdhost_hostname(rpt->host)
201 , rpt->host->machine_guid
202 , rpt->host->rrd_update_every
203 , rpt->host->rrd_history_entries
@@ -231,10 +237,10 @@ static bool stream_receiver_send_first_response(struct receiver_state *rpt) {
237 #endif
238 {
239 // remove the non-blocking flag from the socket
234 - if(sock_delnonblock(rpt->sock.fd) < 0)
240 + if(sock_setnonblock(rpt->sock.fd, false) != 0)
241 nd_log(NDLS_DAEMON, NDLP_ERR,
242 "STREAM RCV '%s' [from [%s]:%s]: cannot remove the non-blocking flag from socket %d",
237 - rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, rpt->sock.fd);
243 + rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port, rpt->sock.fd);
244
245 struct timeval timeout;
246 timeout.tv_sec = 600;
@@ -242,10 +248,10 @@ static bool stream_receiver_send_first_response(struct receiver_state *rpt) {
248 if (unlikely(setsockopt(rpt->sock.fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout) != 0))
249 nd_log(NDLS_DAEMON, NDLP_ERR,
250 "STREAM RCV '%s' [from [%s]:%s]: cannot set timeout for socket %d",
245 - rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, rpt->sock.fd);
251 + rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port, rpt->sock.fd);
252 }
253
248 - netdata_log_debug(D_STREAM, "Initial response to %s: %s", rpt->client_ip, initial_response);
254 + netdata_log_debug(D_STREAM, "Initial response to %s: %s", rpt->remote_ip, initial_response);
255 #ifdef ENABLE_H2O
256 if (is_h2o_rrdpush(rpt)) {
257 h2o_stream_write(rpt->h2o_ctx, initial_response, strlen(initial_response));
@@ -279,7 +285,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
285 rpt->thread.compressed.size = COMPRESSION_MAX_CHUNK;
286 rpt->thread.compressed.buf = mallocz(rpt->thread.compressed.size);
287 rpt->connected_since_s = now_realtime_sec();
282 - rpt->last_msg_t = now_monotonic_sec();
288 + rpt->thread.last_traffic_ut = now_monotonic_usec();
289 rpt->hops = 1;
290
291 rpt->capabilities = STREAM_CAP_INVALID;
@@ -294,10 +300,10 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
300 rrdhost_system_info_hops_set(rpt->system_info, rpt->hops);
301
302 nd_sock_init(&rpt->sock, netdata_ssl_web_server_ctx, false);
297 - rpt->client_ip = strdupz(w->client_ip);
298 - rpt->client_port = strdupz(w->client_port);
303 + rpt->remote_ip = strdupz(w->client_ip);
304 + rpt->remote_port = strdupz(w->client_port);
305
300 - rpt->config.update_every = default_rrd_update_every;
306 + rpt->config.update_every = nd_profile.update_every;
307
308 // parse the parameters and fill rpt and rpt->system_info
309
@@ -380,8 +386,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
386 nd_log_daemon(NDLP_NOTICE, "STREAM RCV '%s' [from [%s]:%s]: "
387 "request has parameter '%s' = '%s', which is not used."
388 , (rpt->hostname && *rpt->hostname) ? rpt->hostname : "-"
383 - , rpt->client_ip, rpt->client_port
384 - , name, value);
389 + , rpt->remote_ip, rpt->remote_port, name, value);
390 }
391 }
392 }
@@ -540,8 +545,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
545 (ssize_t)strlen(initial_response)) {
546
547 nd_log_daemon(NDLP_ERR, "STREAM RCV '%s' [from [%s]:%s]: failed to reply.",
543 - rpt->hostname, rpt->client_ip, rpt->client_port
544 - );
548 + rpt->hostname, rpt->remote_ip, rpt->remote_port);
549 }
550
551 stream_receiver_free(rpt);
@@ -598,7 +602,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
602 if (host) {
603 rrdhost_receiver_lock(host);
604 if (host->receiver) {
601 - age = now_monotonic_sec() - host->receiver->last_msg_t;
605 + age =(time_t)((now_monotonic_usec() - host->receiver->thread.last_traffic_ut) / USEC_PER_SEC);
606
607 if (age < 30)
608 receiver_working = true;
@@ -618,8 +622,7 @@ int stream_receiver_accept_connection(struct web_client *w, char *decoded_query_
622 nd_log_daemon(NDLP_NOTICE, "STREAM '%s' [receive from [%s]:%s]: "
623 "stopped previous stale receiver to accept this one."
624 , rpt->hostname
621 - , rpt->client_ip, rpt->client_port
622 - );
625 + , rpt->remote_ip, rpt->remote_port);
626 }
627
628 if (receiver_working || receiver_stale) {
src/streaming/stream-receiver-internals.h
+41 -11
@@ -3,6 +3,16 @@
3 #ifndef NETDATA_STREAM_RECEIVER_INTERNALS_H
4 #define NETDATA_STREAM_RECEIVER_INTERNALS_H
5
6 +#include "libnetdata/libnetdata.h"
7 +
8 +#ifdef NETDATA_LOG_STREAM_RECEIVER
9 +#include "stream-traffic-types.h"
10 +struct receiver_state;
11 +void stream_receiver_log_payload(struct receiver_state *rpt, const char *payload, STREAM_TRAFFIC_TYPE type, bool inbound);
12 +#else
13 +#define stream_receiver_log_payload(s, payload, type, inbound) debug_dummy()
14 +#endif
15 +
16 #include "stream.h"
17 #include "stream-thread.h"
18 #include "stream-conf.h"
@@ -22,29 +32,24 @@ struct receiver_state {
32 char *registry_hostname;
33 char *machine_guid;
34 char *os;
25 - char *timezone; // Unused?
35 + char *timezone; // Unused?
36 char *abbrev_timezone;
27 - char *client_ip; // Duplicated in pluginsd
28 - char *client_port; // Duplicated in pluginsd
29 - char *program_name; // Duplicated in pluginsd
37 + char *remote_ip; // Duplicated in pluginsd
38 + char *remote_port; // Duplicated in pluginsd
39 + char *program_name; // Duplicated in pluginsd
40 char *program_version;
41 struct rrdhost_system_info *system_info;
32 - time_t last_msg_t;
42 time_t connected_since_s;
43
35 - struct buffered_reader reader;
36 -
44 struct {
38 - bool draining_input; // used exclusively by the stream thread
39 -
45 // The parser pointer is safe to read and use, only when having the host receiver lock.
46 // Without this lock, the data pointed by the pointer may vanish randomly.
47 // Also, since the receiver sets it when it starts, it should be read with
48 // an atomic read.
49 struct parser *parser;
50 struct plugind cd;
46 - BUFFER *buffer;
51
52 + // compressed data input
53 struct {
54 bool enabled;
55 size_t start;
@@ -54,6 +59,12 @@ struct receiver_state {
59 struct decompressor_state decompressor;
60 } compressed;
61
62 + // uncompressed data input (either directly or via the decompressor)
63 + struct buffered_reader uncompressed;
64 +
65 + // a single line of input (composed via uncompressed buffer input)
66 + BUFFER *line_buffer;
67 +
68 struct {
69 SPINLOCK spinlock;
70 struct stream_opcode msg;
@@ -61,9 +72,19 @@ struct receiver_state {
72 STREAM_CIRCULAR_BUFFER *scb;
73 } send_to_child;
74
75 + nd_poll_event_t wanted;
76 + usec_t last_traffic_ut;
77 struct pollfd_meta meta;
78 } thread;
79
80 + struct {
81 + uint32_t last_counter_in; // copy from the host, to detect progress
82 + uint32_t last_counter_out; // copy from the host, to detect progress
83 + usec_t last_progress_ut; // last time we found some progress (monotonic)
84 +
85 + time_t first_time_s;
86 + } replication;
87 +
88 struct {
89 bool shutdown; // signal the streaming parser to exit
90 STREAM_HANDSHAKE reason;
@@ -71,7 +92,13 @@ struct receiver_state {
92
93 struct stream_receiver_config config;
94
74 - time_t replication_first_time_t;
95 +#ifdef NETDATA_LOG_STREAM_RECEIVER
96 + struct {
97 + struct timespec first_call;
98 + SPINLOCK spinlock;
99 + FILE *fp;
100 + } log;
101 +#endif
102
103 #ifdef ENABLE_H2O
104 void *h2o_ctx;
@@ -93,4 +120,7 @@ bool stream_receiver_signal_to_stop_and_wait(RRDHOST *host, STREAM_HANDSHAKE rea
120 void stream_receiver_send_opcode(struct receiver_state *rpt, struct stream_opcode msg);
121 void stream_receiver_handle_op(struct stream_thread *sth, struct receiver_state *rpt, struct stream_opcode *msg);
122
123 +void stream_receiver_check_all_nodes_from_poll(struct stream_thread *sth, usec_t now_ut);
124 +void stream_receiver_replication_check_from_poll(struct stream_thread *sth, usec_t now_ut);
125 +
126 #endif //NETDATA_STREAM_RECEIVER_INTERNALS_H
src/streaming/stream-receiver.c
+556 -248
@@ -5,6 +5,66 @@
5 #include "stream-receiver-internals.h"
6 #include "web/server/h2o/http_server.h"
7
8 +#ifdef NETDATA_LOG_STREAM_RECEIVER
9 +void stream_receiver_log_payload(struct receiver_state *rpt, const char *payload, STREAM_TRAFFIC_TYPE type __maybe_unused, bool inbound) {
10 + if (!rpt || type != STREAM_TRAFFIC_TYPE_REPLICATION) return; // not a streaming parser
11 +
12 + spinlock_lock(&rpt->log.spinlock);
13 +
14 + if (!rpt->log.fp) {
15 + char filename[FILENAME_MAX + 1];
16 + snprintfz(
17 + filename, FILENAME_MAX, "/tmp/stream-receiver-%s.txt", rpt->host ? rrdhost_hostname(rpt->host) : "unknown");
18 +
19 + rpt->log.fp = fopen(filename, "w");
20 +
21 + // Align first_call to wall clock time
22 + clock_gettime(CLOCK_REALTIME, &rpt->log.first_call);
23 + rpt->log.first_call.tv_nsec = 0; // Align to the start of the second
24 + }
25 +
26 + if (rpt->log.fp) {
27 + struct timespec now;
28 + clock_gettime(CLOCK_REALTIME, &now);
29 +
30 + time_t elapsed_sec = now.tv_sec - rpt->log.first_call.tv_sec;
31 + long elapsed_nsec = now.tv_nsec - rpt->log.first_call.tv_nsec;
32 +
33 + if (elapsed_nsec < 0) {
34 + elapsed_sec--;
35 + elapsed_nsec += 1000000000;
36 + }
37 +
38 + uint16_t days = elapsed_sec / 86400;
39 + uint8_t hours = (elapsed_sec % 86400) / 3600;
40 + uint8_t minutes = (elapsed_sec % 3600) / 60;
41 + uint8_t seconds = elapsed_sec % 60;
42 + uint16_t milliseconds = elapsed_nsec / 1000000;
43 +
44 + char prefix[30];
45 + snprintf(prefix, sizeof(prefix), "%03ud.%02u:%02u:%02u.%03u ",
46 + days, hours, minutes, seconds, milliseconds);
47 +
48 + const char *line_start = payload;
49 + const char *line_end;
50 +
51 + while (line_start && *line_start) {
52 + line_end = strchr(line_start, '\n');
53 + if (line_end) {
54 + fprintf(rpt->log.fp, "%s%s%.*s\n", prefix, inbound ? "> " : "< ", (int)(line_end - line_start), line_start);
55 + line_start = line_end + 1;
56 + } else {
57 + fprintf(rpt->log.fp, "%s%s%s\n", prefix, inbound ? "> " : "< ", line_start);
58 + break;
59 + }
60 + }
61 + }
62 +
63 + fflush(rpt->log.fp);
64 + spinlock_unlock(&rpt->log.spinlock);
65 +}
66 +#endif
67 +
68 static void stream_receiver_remove(struct stream_thread *sth, struct receiver_state *rpt, const char *why);
69
70 // When a child disconnects this is the maximum we will wait
@@ -97,16 +157,16 @@ static inline ssize_t read_stream(struct receiver_state *r, char* buffer, size_t
157 // --------------------------------------------------------------------------------------------------------------------
158
159 static inline ssize_t receiver_read_uncompressed(struct receiver_state *r) {
100 - internal_fatal(r->reader.read_buffer[r->reader.read_len] != '\0',
160 + internal_fatal(r->thread.uncompressed.read_buffer[r->thread.uncompressed.read_len] != '\0',
161 "%s: read_buffer does not start with zero #2", __FUNCTION__ );
162
103 - ssize_t bytes = read_stream(r, r->reader.read_buffer + r->reader.read_len, sizeof(r->reader.read_buffer) - r->reader.read_len - 1);
163 + ssize_t bytes = read_stream(r, r->thread.uncompressed.read_buffer + r->thread.uncompressed.read_len, sizeof(r->thread.uncompressed.read_buffer) - r->thread.uncompressed.read_len - 1);
164 if(bytes > 0) {
165 worker_set_metric(WORKER_RECEIVER_JOB_BYTES_READ, (NETDATA_DOUBLE)bytes);
166 worker_set_metric(WORKER_RECEIVER_JOB_BYTES_UNCOMPRESSED, (NETDATA_DOUBLE)bytes);
167
108 - r->reader.read_len += bytes;
109 - r->reader.read_buffer[r->reader.read_len] = '\0';
168 + r->thread.uncompressed.read_len += bytes;
169 + r->thread.uncompressed.read_buffer[r->thread.uncompressed.read_len] = '\0';
170 }
171
172 return bytes;
@@ -149,7 +209,7 @@ static inline decompressor_status_t receiver_feed_decompressor(struct receiver_s
209 if (unlikely(!compressed_message_size)) {
210 nd_log(NDLS_DAEMON, NDLP_ERR,
211 "STREAM RCV[x] '%s' [from [%s]:%s]: multiplexed uncompressed data in compressed stream!",
152 - rrdhost_hostname(r->host), r->client_ip, r->client_port);
212 + rrdhost_hostname(r->host), r->remote_ip, r->remote_port);
213 return DECOMPRESS_FAILED;
214 }
215
@@ -158,7 +218,7 @@ static inline decompressor_status_t receiver_feed_decompressor(struct receiver_s
218 "STREAM RCV[x] '%s' [from [%s]:%s]: received a compressed message of %zu bytes, "
219 "which is bigger than the max compressed message "
220 "size supported of %zu. Ignoring message.",
161 - rrdhost_hostname(r->host), r->client_ip, r->client_port,
221 + rrdhost_hostname(r->host), r->remote_ip, r->remote_port,
222 compressed_message_size, (size_t)COMPRESSION_MAX_MSG_SIZE);
223 return DECOMPRESS_FAILED;
224 }
@@ -175,7 +235,7 @@ static inline decompressor_status_t receiver_feed_decompressor(struct receiver_s
235 if (unlikely(!bytes_to_parse)) {
236 nd_log(NDLS_DAEMON, NDLP_ERR,
237 "STREAM RCV[x] '%s' [from [%s]:%s]: no bytes to decompress.",
178 - rrdhost_hostname(r->host), r->client_ip, r->client_port);
238 + rrdhost_hostname(r->host), r->remote_ip, r->remote_port);
239 return DECOMPRESS_FAILED;
240 }
241
@@ -191,20 +251,20 @@ static inline decompressor_status_t receiver_get_decompressed(struct receiver_st
251 if (unlikely(!stream_decompressed_bytes_in_buffer(&r->thread.compressed.decompressor)))
252 return DECOMPRESS_NEED_MORE_DATA;
253
194 - size_t available = sizeof(r->reader.read_buffer) - r->reader.read_len - 1;
254 + size_t available = sizeof(r->thread.uncompressed.read_buffer) - r->thread.uncompressed.read_len - 1;
255 if (likely(available)) {
256 size_t len = stream_decompressor_get(
197 - &r->thread.compressed.decompressor, r->reader.read_buffer + r->reader.read_len, available);
257 + &r->thread.compressed.decompressor, r->thread.uncompressed.read_buffer + r->thread.uncompressed.read_len, available);
258 if (unlikely(!len)) {
259 internal_error(true, "decompressor returned zero length #1");
260 return DECOMPRESS_FAILED;
261 }
262
203 - r->reader.read_len += (int)len;
204 - r->reader.read_buffer[r->reader.read_len] = '\0';
263 + r->thread.uncompressed.read_len += (int)len;
264 + r->thread.uncompressed.read_buffer[r->thread.uncompressed.read_len] = '\0';
265 }
266 else {
207 - internal_fatal(true, "The line to read is too big! Already have %zd bytes in read_buffer.", r->reader.read_len);
267 + internal_fatal(true, "The line to read is too big! Already have %zd bytes in read_buffer.", r->thread.uncompressed.read_len);
268 return DECOMPRESS_FAILED;
269 }
270
@@ -213,7 +273,7 @@ static inline decompressor_status_t receiver_get_decompressed(struct receiver_st
273
274 static inline ssize_t receiver_read_compressed(struct receiver_state *r) {
275
216 - internal_fatal(r->reader.read_buffer[r->reader.read_len] != '\0',
276 + internal_fatal(r->thread.uncompressed.read_buffer[r->thread.uncompressed.read_len] != '\0',
277 "%s: read_buffer does not start with zero #2", __FUNCTION__ );
278
279 ssize_t bytes_read = read_stream(r, r->thread.compressed.buf + r->thread.compressed.used,
@@ -248,11 +308,10 @@ static inline bool receiver_should_stop(struct receiver_state *rpt) {
308 void stream_receiver_handle_op(struct stream_thread *sth, struct receiver_state *rpt, struct stream_opcode *msg) {
309 ND_LOG_STACK lgs[] = {
310 ND_LOG_FIELD_STR(NDF_NIDL_NODE, rpt->host->hostname),
251 - ND_LOG_FIELD_TXT(NDF_SRC_IP, rpt->client_ip),
252 - ND_LOG_FIELD_TXT(NDF_SRC_PORT, rpt->client_port),
311 + ND_LOG_FIELD_TXT(NDF_SRC_IP, rpt->remote_ip),
312 + ND_LOG_FIELD_TXT(NDF_SRC_PORT, rpt->remote_port),
313 ND_LOG_FIELD_CB(NDF_SRC_TRANSPORT, stream_receiver_log_transport, rpt),
314 ND_LOG_FIELD_CB(NDF_SRC_CAPABILITIES, stream_receiver_log_capabilities, rpt),
255 - ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_to_parent_msgid),
315 ND_LOG_FIELD_END(),
316 };
317 ND_LOG_STACK_PUSH(lgs);
@@ -267,7 +326,7 @@ void stream_receiver_handle_op(struct stream_thread *sth, struct receiver_state
326 nd_log(NDLS_DAEMON, NDLP_ERR,
327 "STREAM RCV[%zu] '%s' [from [%s]:%s]: send buffer is full (buffer size %u, max %u, used %u, available %u). "
328 "Restarting connection.",
270 - sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port,
329 + sth->id, rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port,
330 stats.bytes_size, stats.bytes_max_size, stats.bytes_outstanding, stats.bytes_available);
331
332 stream_receiver_remove(sth, rpt, "receiver send buffer overflow");
@@ -297,8 +356,12 @@ static ssize_t send_to_child(const char *txt, void *data, STREAM_TRAFFIC_TYPE ty
356 msg.opcode = STREAM_OPCODE_RECEIVER_BUFFER_OVERFLOW;
357 rc = -1;
358 }
300 - else if(was_empty)
301 - msg.opcode = STREAM_OPCODE_RECEIVER_POLLOUT;
359 + else {
360 + stream_receiver_log_payload(rpt, txt, type, false);
361 +
362 + if(was_empty)
363 + msg.opcode = STREAM_OPCODE_RECEIVER_POLLOUT;
364 + }
365
366 spinlock_unlock(&rpt->thread.send_to_child.spinlock);
367
@@ -308,81 +371,6 @@ static ssize_t send_to_child(const char *txt, void *data, STREAM_TRAFFIC_TYPE ty
371 return rc;
372 }
373
311 -static void streaming_parser_init(struct receiver_state *rpt) {
312 - rpt->thread.cd = (struct plugind){
313 - .update_every = default_rrd_update_every,
314 - .unsafe = {
315 - .spinlock = SPINLOCK_INITIALIZER,
316 - .running = true,
317 - .enabled = true,
318 - },
319 - .started_t = now_realtime_sec(),
320 - };
321 -
322 - // put the client IP and port into the buffers used by plugins.d
323 - {
324 - char buf[CONFIG_MAX_NAME];
325 - snprintfz(buf, sizeof(buf), "[%s]:%s", rpt->client_ip, rpt->client_port);
326 - string_freez(rpt->thread.cd.id);
327 - rpt->thread.cd.id = string_strdupz(buf);
328 -
329 - string_freez(rpt->thread.cd.filename);
330 - rpt->thread.cd.filename = string_strdupz(buf);
331 -
332 - string_freez(rpt->thread.cd.fullfilename);
333 - rpt->thread.cd.fullfilename = string_strdupz(buf);
334 -
335 - string_freez(rpt->thread.cd.cmd);
336 - rpt->thread.cd.cmd = string_strdupz(buf);
337 - }
338 -
339 - PARSER *parser = NULL;
340 - {
341 - PARSER_USER_OBJECT user = {
342 - .enabled = plugin_is_enabled(&rpt->thread.cd),
343 - .host = rpt->host,
344 - .opaque = rpt,
345 - .cd = &rpt->thread.cd,
346 - .trust_durations = 1,
347 - .capabilities = rpt->capabilities,
348 - };
349 -
350 - parser = parser_init(&user, -1, -1, PARSER_INPUT_SPLIT, &rpt->sock);
351 - parser->send_to_plugin_data = rpt;
352 - parser->send_to_plugin_cb = send_to_child;
353 - }
354 -
355 -#ifdef ENABLE_H2O
356 - parser->h2o_ctx = rpt->h2o_ctx;
357 -#endif
358 -
359 - pluginsd_keywords_init(parser, PARSER_INIT_STREAMING);
360 -
361 - rpt->thread.compressed.start = 0;
362 - rpt->thread.compressed.used = 0;
363 - rpt->thread.compressed.enabled = stream_decompression_initialize(rpt);
364 - buffered_reader_init(&rpt->reader);
365 -
366 -#ifdef NETDATA_LOG_STREAM_RECEIVE
367 - {
368 - char filename[FILENAME_MAX + 1];
369 - snprintfz(filename, FILENAME_MAX, "/tmp/stream-receiver-%s.txt", rpt->host ? rrdhost_hostname(
370 - rpt->host) : "unknown"
371 - );
372 - parser->user.stream_log_fp = fopen(filename, "w");
373 - parser->user.stream_log_repertoire = PARSER_REP_METADATA;
374 - }
375 -#endif
376 -
377 - __atomic_store_n(&rpt->thread.parser, parser, __ATOMIC_RELAXED);
378 - stream_receiver_send_node_and_claim_id_to_child(rpt->host);
379 -
380 - rpt->thread.buffer = buffer_create(sizeof(rpt->reader.read_buffer), NULL);
381 -
382 - // help rrdset_push_metric_initialize() select the right buffer
383 - rpt->host->stream.snd.commit.receiver_tid = gettid_cached();
384 -}
385 -
374 // --------------------------------------------------------------------------------------------------------------------
375
376 static void stream_receive_log_database_gap(struct receiver_state *rpt) {
@@ -398,14 +386,14 @@ static void stream_receive_log_database_gap(struct receiver_state *rpt) {
386 if(!last_db_entry) {
387 nd_log(NDLS_DAEMON, NDLP_NOTICE,
388 "STREAM RCV '%s' [from [%s]:%s]: node connected; for the first time!",
401 - rrdhost_hostname(host), rpt->client_ip, rpt->client_port);
389 + rrdhost_hostname(host), rpt->remote_ip, rpt->remote_port);
390 }
391 else {
392 char buf[128];
393 duration_snprintf(buf, sizeof(buf), now - last_db_entry, "s", true);
394 nd_log(NDLS_DAEMON, NDLP_NOTICE,
395 "STREAM RCV '%s' [from [%s]:%s]: node connected; last sample in the database %s ago",
408 - rrdhost_hostname(host), rpt->client_ip, rpt->client_port, buf);
396 + rrdhost_hostname(host), rpt->remote_ip, rpt->remote_port, buf);
397 }
398 }
399
@@ -416,14 +404,23 @@ void stream_receiver_move_to_running_unsafe(struct stream_thread *sth, struct re
404
405 ND_LOG_STACK lgs[] = {
406 ND_LOG_FIELD_STR(NDF_NIDL_NODE, rpt->host->hostname),
419 - ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_to_parent_msgid),
407 + ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_from_child_msgid),
408 ND_LOG_FIELD_END(),
409 };
410 ND_LOG_STACK_PUSH(lgs);
411
412 nd_log(NDLS_DAEMON, NDLP_DEBUG,
413 "STREAM RCV[%zu] '%s' [from [%s]:%s]: moving host from receiver queue to receiver running...",
426 - sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port);
414 + sth->id, rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port);
415 +
416 + sock_setcloexec(rpt->sock.fd, true);
417 + sock_enlarge_rcv_buf(rpt->sock.fd);
418 + sock_enlarge_snd_buf(rpt->sock.fd);
419 + sock_setcork(rpt->sock.fd, false);
420 + if(sock_setnonblock(rpt->sock.fd, true) != 1)
421 + nd_log(NDLS_DAEMON, NDLP_ERR,
422 + "STREAM RCV '%s' [from [%s]:%s]: failed to set non-blocking mode on socket %d",
423 + rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port, rpt->sock.fd);
424
425 rpt->host->stream.rcv.status.tid = gettid_cached();
426 rpt->thread.meta.type = POLLFD_TYPE_RECEIVER;
@@ -439,22 +436,83 @@ void stream_receiver_move_to_running_unsafe(struct stream_thread *sth, struct re
436 internal_fatal(META_GET(&sth->run.meta, (Word_t)&rpt->thread.meta) != NULL, "Receiver to be added is already in the list of receivers");
437 META_SET(&sth->run.meta, (Word_t)&rpt->thread.meta, &rpt->thread.meta);
438
442 - if(sock_setnonblock(rpt->sock.fd) < 0)
443 - nd_log(NDLS_DAEMON, NDLP_ERR,
444 - "STREAM RCV '%s' [from [%s]:%s]: cannot set the non-blocking flag from socket %d",
445 - rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, rpt->sock.fd);
446 -
447 - if(!nd_poll_add(sth->run.ndpl, rpt->sock.fd, ND_POLL_READ, &rpt->thread.meta))
439 + rpt->thread.wanted = ND_POLL_READ;
440 + if(!nd_poll_add(sth->run.ndpl, rpt->sock.fd, rpt->thread.wanted, &rpt->thread.meta))
441 nd_log(NDLS_DAEMON, NDLP_ERR,
442 "STREAM RCV[%zu] '%s' [from [%s]:%s]:"
443 "Failed to add receiver socket to nd_poll()",
451 - sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port);
444 + sth->id, rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port);
445 +
446 + // put the client IP and port into the buffers used by plugins.d
447 + {
448 + char buf[CONFIG_MAX_NAME];
449 + snprintfz(buf, sizeof(buf), "[%s]:%s", rpt->remote_ip, rpt->remote_port);
450 + string_freez(rpt->thread.cd.id);
451 + rpt->thread.cd.id = string_strdupz(buf);
452 +
453 + string_freez(rpt->thread.cd.filename);
454 + rpt->thread.cd.filename = string_strdupz(buf);
455 +
456 + string_freez(rpt->thread.cd.fullfilename);
457 + rpt->thread.cd.fullfilename = string_strdupz(buf);
458 +
459 + string_freez(rpt->thread.cd.cmd);
460 + rpt->thread.cd.cmd = string_strdupz(buf);
461 + }
462 +
463 + rpt->thread.compressed.start = 0;
464 + rpt->thread.compressed.used = 0;
465 + rpt->thread.compressed.enabled = stream_decompression_initialize(rpt);
466 + buffered_reader_init(&rpt->thread.uncompressed);
467 +
468 + rpt->thread.line_buffer = buffer_create(sizeof(rpt->thread.uncompressed.read_buffer), NULL);
469 +
470 + // help preferred_sender_buffer() select the right buffer
471 + rpt->host->stream.snd.commit.receiver_tid = gettid_cached();
472 +
473 + rpt->replication.last_progress_ut = now_monotonic_usec();
474 +
475 + PARSER *parser = NULL;
476 + {
477 + rpt->thread.cd = (struct plugind){
478 + .update_every = nd_profile.update_every,
479 + .unsafe = {
480 + .spinlock = SPINLOCK_INITIALIZER,
481 + .running = true,
482 + .enabled = true,
483 + },
484 + .started_t = now_realtime_sec(),
485 + };
486 +
487 + PARSER_USER_OBJECT user = {
488 + .enabled = plugin_is_enabled(&rpt->thread.cd),
489 + .host = rpt->host,
490 + .opaque = rpt,
491 + .cd = &rpt->thread.cd,
492 + .trust_durations = 1,
493 + .capabilities = rpt->capabilities,
494 +#ifdef NETDATA_LOG_STREAM_RECEIVER
495 + .rpt = rpt,
496 +#endif
497 + };
498 +
499 + parser = parser_init(&user, -1, -1, PARSER_INPUT_SPLIT, &rpt->sock);
500 + parser->send_to_plugin_data = rpt;
501 + parser->send_to_plugin_cb = send_to_child;
502 +
503 + pluginsd_keywords_init(parser, PARSER_INIT_STREAMING);
504 +
505 + __atomic_store_n(&rpt->thread.parser, parser, __ATOMIC_RELAXED);
506 + }
507 +
508 +#ifdef ENABLE_H2O
509 + parser->h2o_ctx = rpt->h2o_ctx;
510 +#endif
511
512 stream_receive_log_database_gap(rpt);
454 - rrdhost_state_connected(rpt->host);
513
456 - // keep this last, since it sends commands back to the child
457 - streaming_parser_init(rpt);
514 + // keep this last - it needs everything ready since to sends data to the child
515 + stream_receiver_send_node_and_claim_id_to_child(rpt->host);
516 }
517
518 void stream_receiver_move_entire_queue_to_running_unsafe(struct stream_thread *sth) {
@@ -473,6 +531,17 @@ void stream_receiver_move_entire_queue_to_running_unsafe(struct stream_thread *s
531 static void stream_receiver_remove(struct stream_thread *sth, struct receiver_state *rpt, const char *why) {
532 internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
533
534 + ND_LOG_STACK lgs[] = {
535 + ND_LOG_FIELD_STR(NDF_NIDL_NODE, rpt->host->hostname),
536 + ND_LOG_FIELD_TXT(NDF_SRC_IP, rpt->remote_ip),
537 + ND_LOG_FIELD_TXT(NDF_SRC_PORT, rpt->remote_port),
538 + ND_LOG_FIELD_CB(NDF_SRC_TRANSPORT, stream_receiver_log_transport, rpt),
539 + ND_LOG_FIELD_CB(NDF_SRC_CAPABILITIES, stream_receiver_log_capabilities, rpt),
540 + ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_from_child_msgid),
541 + ND_LOG_FIELD_END(),
542 + };
543 + ND_LOG_STACK_PUSH(lgs);
544 +
545 PARSER *parser = __atomic_load_n(&rpt->thread.parser, __ATOMIC_RELAXED);
546 size_t count = parser ? parser->user.data_collections_count : 0;
547
@@ -482,45 +551,36 @@ static void stream_receiver_remove(struct stream_thread *sth, struct receiver_st
551 "receiver disconnected (after %zu received messages): %s"
552 , sth->id
553 , rpt->hostname ? rpt->hostname : "-"
485 - , rpt->client_ip ? rpt->client_ip : "-"
486 - , rpt->client_port ? rpt->client_port : "-"
554 + , rpt->remote_ip ? rpt->remote_ip : "-"
555 + , rpt->remote_port ? rpt->remote_port : "-"
556 , count
557 , why ? why : "");
558
490 - rrdhost_state_disconnected(rpt->host);
559 + internal_fatal(META_GET(&sth->run.meta, (Word_t)&rpt->thread.meta) == NULL,
560 + "Receiver to be removed is not found in the list of receivers");
561
492 - internal_fatal(META_GET(&sth->run.meta, (Word_t)&rpt->thread.meta) == NULL, "Receiver to be removed is not found in the list of receivers");
562 META_DEL(&sth->run.meta, (Word_t)&rpt->thread.meta);
563
495 - if(!nd_poll_del(sth->run.ndpl, rpt->sock.fd))
564 + rpt->thread.wanted = 0;
565 + if(!nd_poll_del(sth->run.ndpl, rpt->sock.fd, &rpt->thread.meta))
566 nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to delete receiver socket from nd_poll()");
567
568 rpt->host->stream.rcv.status.tid = 0;
569
500 - spinlock_lock(&rpt->thread.send_to_child.spinlock);
501 - rpt->thread.send_to_child.msg.session = 0;
502 - rpt->thread.send_to_child.msg.meta = NULL;
503 - stream_circular_buffer_destroy(rpt->thread.send_to_child.scb);
504 - rpt->thread.send_to_child.scb = NULL;
505 - spinlock_unlock(&rpt->thread.send_to_child.spinlock);
506 -
507 - stream_thread_node_removed(rpt->host);
508 -
509 - buffer_free(rpt->thread.buffer);
510 - rpt->thread.buffer = NULL;
511 -
570 + // make sure send_to_plugin() will not write any data to the socket (or wait for it to finish)
571 if(parser) {
513 - parser->user.v2.stream_buffer.wb = NULL;
514 -
515 - // make sure send_to_plugin() will not write any data to the socket
572 spinlock_lock(&parser->writer.spinlock);
573 parser->fd_input = -1;
574 parser->fd_output = -1;
575 parser->sock = NULL;
576 spinlock_unlock(&parser->writer.spinlock);
577 +
578 + parser->user.v2.stream_buffer.wb = NULL;
579 }
580
523 - // the parser stopped
581 + stream_thread_node_removed(rpt->host);
582 +
583 + // set a default exit reason, if not set
584 receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_PARSER_EXIT, false);
585
586 // in case we are connected to netdata cloud,
@@ -535,9 +595,29 @@ static void stream_receiver_remove(struct stream_thread *sth, struct receiver_st
595 // DO NOT USE rpt after this point
596 }
597
598 +static bool stream_receiver_dequeue_senders(struct stream_thread *sth, struct receiver_state *rpt, usec_t now_ut) {
599 + internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__);
600 +
601 + // re-check if we need to send data after reading - if we do, try now
602 + if(rpt->thread.wanted & ND_POLL_WRITE) {
603 + worker_is_busy(WORKER_STREAM_JOB_SOCKET_SEND);
604 + if(!stream_receiver_send_data(sth, rpt, now_ut, false))
605 + return false;
606 + }
607 +
608 + if(rpt->host->sender && // the host has a sender
609 + rpt->host->stream.snd.status.tid == gettid_cached() && // the sender is mine
610 + (rpt->host->sender->thread.wanted & ND_POLL_WRITE)) // the sender needs to send data
611 + if(!stream_sender_send_data(sth, rpt->host->sender, now_ut, false))
612 + return false;
613 +
614 + return true;
615 +}
616 +
617 static ssize_t
539 -stream_receive_and_process(struct stream_thread *sth, struct receiver_state *rpt, PARSER *parser, bool *removed) {
618 +stream_receive_and_process(struct stream_thread *sth, struct receiver_state *rpt, PARSER *parser, usec_t now_ut __maybe_unused, bool *removed) {
619 internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__);
620 + *removed = false;
621
622 ssize_t rc;
623 if(rpt->thread.compressed.enabled) {
@@ -559,16 +639,16 @@ stream_receive_and_process(struct stream_thread *sth, struct receiver_state *rpt
639 if (likely(decompress_rc == DECOMPRESS_OK)) {
640 // loop through all the complete lines found in the uncompressed buffer
641
562 - while (buffered_reader_next_line(&rpt->reader, rpt->thread.buffer)) {
563 - if (unlikely(parser_action(parser, rpt->thread.buffer->buffer))) {
642 + while (buffered_reader_next_line(&rpt->thread.uncompressed, rpt->thread.line_buffer)) {
643 + if (unlikely(parser_action(parser, rpt->thread.line_buffer->buffer))) {
644 receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_PARSER_FAILED, false);
645 stream_receiver_remove(sth, rpt, "parser action failed");
646 *removed = true;
647 return -1;
648 }
649
570 - rpt->thread.buffer->len = 0;
571 - rpt->thread.buffer->buffer[0] = '\0';
650 + rpt->thread.line_buffer->len = 0;
651 + rpt->thread.line_buffer->buffer[0] = '\0';
652 }
653 }
654 else if (decompress_rc == DECOMPRESS_NEED_MORE_DATA)
@@ -601,41 +681,203 @@ stream_receive_and_process(struct stream_thread *sth, struct receiver_state *rpt
681 }
682 else {
683 rc = receiver_read_uncompressed(rpt);
604 - if(rc <= 0) return rc;
684 + if(rc <= 0)
685 + return rc;
686
606 - while(buffered_reader_next_line(&rpt->reader, rpt->thread.buffer)) {
607 - if(unlikely(parser_action(parser, rpt->thread.buffer->buffer))) {
687 + while(buffered_reader_next_line(&rpt->thread.uncompressed, rpt->thread.line_buffer)) {
688 + if(unlikely(parser_action(parser, rpt->thread.line_buffer->buffer))) {
689 receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_PARSER_FAILED, false);
690 stream_receiver_remove(sth, rpt, "parser action failed");
691 *removed = true;
692 return -1;
693 }
694
614 - rpt->thread.buffer->len = 0;
615 - rpt->thread.buffer->buffer[0] = '\0';
695 + rpt->thread.line_buffer->len = 0;
696 + rpt->thread.line_buffer->buffer[0] = '\0';
697 }
698 }
699
700 return rc;
701 }
702
703 +bool stream_receiver_send_data(struct stream_thread *sth, struct receiver_state *rpt, usec_t now_ut, bool process_opcodes_and_enable_removal) {
704 + internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
705 +
706 + EVLOOP_STATUS status = EVLOOP_STATUS_CONTINUE;
707 + while(status == EVLOOP_STATUS_CONTINUE) {
708 + if (!spinlock_trylock(&rpt->thread.send_to_child.spinlock)) {
709 + status = EVLOOP_STATUS_CANT_GET_LOCK;
710 + break;
711 + }
712 +
713 + char *chunk;
714 + STREAM_CIRCULAR_BUFFER *scb = rpt->thread.send_to_child.scb;
715 + STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(scb);
716 + size_t outstanding = stream_circular_buffer_get_unsafe(scb, &chunk);
717 +
718 + if(!outstanding) {
719 + status = EVLOOP_STATUS_NO_MORE_DATA;
720 + spinlock_unlock(&rpt->thread.send_to_child.spinlock);
721 + continue;
722 + }
723 +
724 + ssize_t rc = write_stream(rpt, chunk, outstanding);
725 + if (likely(rc > 0)) {
726 + rpt->thread.last_traffic_ut = now_ut;
727 + stream_circular_buffer_del_unsafe(scb, rc, now_ut);
728 + if (!stats->bytes_outstanding) {
729 + rpt->thread.wanted = ND_POLL_READ;
730 + if (!nd_poll_upd(sth->run.ndpl, rpt->sock.fd, rpt->thread.wanted, &rpt->thread.meta))
731 + nd_log(NDLS_DAEMON, NDLP_ERR,
732 + "STREAM RCV[%zu] '%s' [from [%s]:%s]: cannot update nd_poll()",
733 + sth->id, rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port);
734 +
735 + // recreate the circular buffer if we have to
736 + stream_circular_buffer_recreate_timed_unsafe(rpt->thread.send_to_child.scb, now_ut, false);
737 + status = EVLOOP_STATUS_NO_MORE_DATA;
738 + }
739 + }
740 + else if (rc == 0 || errno == ECONNRESET)
741 + status = EVLOOP_STATUS_SOCKET_CLOSED;
742 +
743 + else if (rc < 0) {
744 + if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
745 + status = EVLOOP_STATUS_SOCKET_FULL;
746 + else
747 + status = EVLOOP_STATUS_SOCKET_ERROR;
748 + }
749 +
750 + spinlock_unlock(&rpt->thread.send_to_child.spinlock);
751 +
752 + if (status == EVLOOP_STATUS_SOCKET_ERROR || status == EVLOOP_STATUS_SOCKET_CLOSED) {
753 + const char *disconnect_reason;
754 + STREAM_HANDSHAKE reason;
755 +
756 + if(status == EVLOOP_STATUS_SOCKET_ERROR) {
757 + worker_is_busy(WORKER_STREAM_JOB_DISCONNECT_SEND_ERROR);
758 + disconnect_reason = "socket reports error while writing";
759 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_WRITE_FAILED;
760 + }
761 + else /* if(status == EVLOOP_STATUS_SOCKET_CLOSED) */ {
762 + worker_is_busy(WORKER_STREAM_JOB_DISCONNECT_REMOTE_CLOSED);
763 + disconnect_reason = "socket reports EOF (closed by child)";
764 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END;
765 + }
766 +
767 + nd_log(NDLS_DAEMON, NDLP_ERR,
768 + "STREAM RCV[%zu] '%s' [from [%s]:%s]: %s (%zd, on fd %d) - closing receiver connection - "
769 + "we have sent %zu bytes in %zu operations.",
770 + sth->id, rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port,
771 + disconnect_reason, rc, rpt->sock.fd, stats->bytes_sent, stats->sends);
772 +
773 + receiver_set_exit_reason(rpt, reason, false);
774 +
775 + if(process_opcodes_and_enable_removal) {
776 + // this is not executed from the opcode handling mechanism
777 + // so we can safely remove the receiver.
778 + stream_receiver_remove(sth, rpt, disconnect_reason);
779 + }
780 + else {
781 + // protection against this case:
782 + //
783 + // 1. receiver gets a replication request
784 + // 2. parser processes the request
785 + // 3. parser decides to send back a message to the child (REPLAY_CHART)
786 + // 4. send_to_child appends the data to the sending circular buffer
787 + // 5. send_to_child sends opcode to enable sending
788 + // 6. opcode bypasses the signal and runs this function inline to dispatch immediately
789 + // 7. sending fails (child disconnected)
790 + // 8. receiver is removed
791 + //
792 + // Point 2 above crashes. The parser is no longer there (freed at point 7)
793 + // and there is no way for point 2 to know...
794 + }
795 + }
796 + else if(process_opcodes_and_enable_removal &&
797 + status == EVLOOP_STATUS_CONTINUE &&
798 + stream_thread_process_opcodes(sth, &rpt->thread.meta))
799 + status = EVLOOP_STATUS_OPCODE_ON_ME;
800 + }
801 +
802 + return EVLOOP_STATUS_STILL_ALIVE(status);
803 +}
804 +
805 +bool stream_receiver_receive_data(struct stream_thread *sth, struct receiver_state *rpt, usec_t now_ut, bool process_opcodes) {
806 + internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
807 +
808 + PARSER *parser = __atomic_load_n(&rpt->thread.parser, __ATOMIC_RELAXED);
809 + ND_LOG_STACK lgs[] = {
810 + ND_LOG_FIELD_CB(NDF_REQUEST, line_splitter_reconstruct_line, &parser->line),
811 + ND_LOG_FIELD_CB(NDF_NIDL_NODE, parser_reconstruct_node, parser),
812 + ND_LOG_FIELD_CB(NDF_NIDL_INSTANCE, parser_reconstruct_instance, parser),
813 + ND_LOG_FIELD_CB(NDF_NIDL_CONTEXT, parser_reconstruct_context, parser),
814 + ND_LOG_FIELD_END(),
815 + };
816 + ND_LOG_STACK_PUSH(lgs);
817 +
818 + EVLOOP_STATUS status = EVLOOP_STATUS_CONTINUE;
819 + while(status == EVLOOP_STATUS_CONTINUE) {
820 + bool removed = false;
821 + ssize_t rc = stream_receive_and_process(sth, rpt, parser, now_ut, &removed);
822 + if(unlikely(removed))
823 + status = EVLOOP_STATUS_PARSER_FAILED;
824 +
825 + else if (likely(rc > 0)) {
826 + rpt->thread.last_traffic_ut = now_ut;
827 +
828 + if(!stream_receiver_dequeue_senders(sth, rpt, now_ut))
829 + status = EVLOOP_STATUS_SOCKET_ERROR;
830 + }
831 + else if (rc == 0 || errno == ECONNRESET) {
832 + status = EVLOOP_STATUS_SOCKET_CLOSED;
833 + }
834 + else if (rc < 0) {
835 + if ((errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR))
836 + status = EVLOOP_STATUS_SOCKET_FULL;
837 + else
838 + status = EVLOOP_STATUS_SOCKET_ERROR;
839 + }
840 +
841 + if(status == EVLOOP_STATUS_SOCKET_ERROR || status == EVLOOP_STATUS_SOCKET_CLOSED) {
842 + const char *disconnect_reason;
843 + STREAM_HANDSHAKE reason;
844 +
845 + if(status == EVLOOP_STATUS_SOCKET_ERROR) {
846 + worker_is_busy(WORKER_STREAM_JOB_DISCONNECT_RECEIVE_ERROR);
847 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_READ_FAILED;
848 + disconnect_reason = "error during receive";
849 + }
850 + else /* if(status == EVLOOP_STATUS_SOCKET_CLOSED) */ {
851 + worker_is_busy(WORKER_STREAM_JOB_DISCONNECT_REMOTE_CLOSED);
852 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END;
853 + disconnect_reason = "socket reports EOF (closed by child)";
854 + }
855 +
856 + nd_log(NDLS_DAEMON, NDLP_ERR,
857 + "STREAM RCV[%zu] '%s' [from [%s]:%s]: %s (fd %d) - closing receiver connection.",
858 + sth->id, rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port, disconnect_reason, rpt->sock.fd);
859 +
860 + receiver_set_exit_reason(rpt, reason, false);
861 + stream_receiver_remove(sth, rpt, disconnect_reason);
862 + }
863 + else if(status == EVLOOP_STATUS_CONTINUE && process_opcodes && stream_thread_process_opcodes(sth, &rpt->thread.meta))
864 + status = EVLOOP_STATUS_OPCODE_ON_ME;
865 + }
866 +
867 + return EVLOOP_STATUS_STILL_ALIVE(status);
868 +}
869 +
870 // process poll() events for streaming receivers
871 // returns true when the receiver is still there, false if it removed it
624 -bool stream_receive_process_poll_events(struct stream_thread *sth, struct receiver_state *rpt, nd_poll_event_t events, usec_t now_ut)
625 -{
872 +bool stream_receive_process_poll_events(struct stream_thread *sth, struct receiver_state *rpt, nd_poll_event_t events, usec_t now_ut) {
873 internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__);
874
628 - PARSER *parser = __atomic_load_n(&rpt->thread.parser, __ATOMIC_RELAXED);
875 ND_LOG_STACK lgs[] = {
630 - ND_LOG_FIELD_TXT(NDF_SRC_IP, rpt->client_ip),
631 - ND_LOG_FIELD_TXT(NDF_SRC_PORT, rpt->client_port),
876 + ND_LOG_FIELD_TXT(NDF_SRC_IP, rpt->remote_ip),
877 + ND_LOG_FIELD_TXT(NDF_SRC_PORT, rpt->remote_port),
878 ND_LOG_FIELD_TXT(NDF_NIDL_NODE, rpt->hostname),
879 ND_LOG_FIELD_CB(NDF_SRC_TRANSPORT, stream_receiver_log_transport, rpt),
880 ND_LOG_FIELD_CB(NDF_SRC_CAPABILITIES, stream_receiver_log_capabilities, rpt),
635 - ND_LOG_FIELD_CB(NDF_REQUEST, line_splitter_reconstruct_line, &parser->line),
636 - ND_LOG_FIELD_CB(NDF_NIDL_NODE, parser_reconstruct_node, parser),
637 - ND_LOG_FIELD_CB(NDF_NIDL_INSTANCE, parser_reconstruct_instance, parser),
638 - ND_LOG_FIELD_CB(NDF_NIDL_CONTEXT, parser_reconstruct_context, parser),
881 ND_LOG_FIELD_END(),
882 };
883 ND_LOG_STACK_PUSH(lgs);
@@ -664,7 +906,7 @@ bool stream_receive_process_poll_events(struct stream_thread *sth, struct receiv
906
907 nd_log(NDLS_DAEMON, NDLP_ERR,
908 "STREAM RCV[%zu] '%s' [from [%s]:%s]: %s - closing connection",
667 - sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, error);
909 + sth->id, rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port, error);
910
911 receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_SOCKET_ERROR, false);
912 stream_receiver_remove(sth, rpt, error);
@@ -673,111 +915,154 @@ bool stream_receive_process_poll_events(struct stream_thread *sth, struct receiv
915
916 if (events & ND_POLL_WRITE) {
917 worker_is_busy(WORKER_STREAM_JOB_SOCKET_SEND);
918 + if(!stream_receiver_send_data(sth, rpt, now_ut, true))
919 + return false;
920 + }
921
677 - bool stop = false;
678 - while(!stop) {
679 - if (spinlock_trylock(&rpt->thread.send_to_child.spinlock)) {
680 - const char *disconnect_reason = NULL;
681 - STREAM_HANDSHAKE reason;
682 -
683 - char *chunk;
684 - STREAM_CIRCULAR_BUFFER *scb = rpt->thread.send_to_child.scb;
685 - STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(scb);
686 - size_t outstanding = stream_circular_buffer_get_unsafe(scb, &chunk);
687 - ssize_t rc = write_stream(rpt, chunk, outstanding);
688 - if (likely(rc > 0)) {
689 - stream_circular_buffer_del_unsafe(scb, rc);
690 - if (!stats->bytes_outstanding) {
691 - if (!nd_poll_upd(sth->run.ndpl, rpt->sock.fd, ND_POLL_READ, &rpt->thread.meta))
692 - nd_log(NDLS_DAEMON, NDLP_ERR,
693 - "STREAM RCV[%zu] '%s' [from [%s]:%s]: cannot update nd_poll()",
694 - sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port);
695 -
696 - // recreate the circular buffer if we have to
697 - stream_circular_buffer_recreate_timed_unsafe(rpt->thread.send_to_child.scb, now_ut, false);
698 - stop = true;
699 - }
700 - else if(stream_thread_process_opcodes(sth, &rpt->thread.meta))
701 - stop = true;
702 - }
703 - else if (rc == 0 || errno == ECONNRESET) {
704 - disconnect_reason = "socket reports EOF (closed by child)";
705 - reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END;
706 - }
707 - else if (rc < 0) {
708 - if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
709 - // will try later
710 - stop = true;
711 - else {
712 - disconnect_reason = "socket reports error while writing";
713 - reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_WRITE_FAILED;
714 - }
715 - }
716 - spinlock_unlock(&rpt->thread.send_to_child.spinlock);
922 + if (events & ND_POLL_READ) {
923 + worker_is_busy(WORKER_STREAM_JOB_SOCKET_RECEIVE);
924 + if(!stream_receiver_receive_data(sth, rpt, now_ut, true))
925 + return false;
926 + }
927
718 - if (disconnect_reason) {
719 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR);
720 - nd_log(NDLS_DAEMON, NDLP_ERR,
721 - "STREAM RCV[%zu] '%s' [from [%s]:%s]: %s (%zd, on fd %d) - closing connection - "
722 - "we have sent %zu bytes in %zu operations.",
723 - sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port,
724 - disconnect_reason, rc, rpt->sock.fd, stats->bytes_sent, stats->sends);
725 -
726 - receiver_set_exit_reason(rpt, reason, false);
727 - stream_receiver_remove(sth, rpt, disconnect_reason);
728 - return false;
729 - }
730 - }
731 - else
732 - break;
928 + return true;
929 +}
930 +
931 +void stream_receiver_check_all_nodes_from_poll(struct stream_thread *sth, usec_t now_ut) {
932 + internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
933 +
934 + NETDATA_DOUBLE overall_buffer_ratio = 0.0;
935 +
936 + Word_t idx = 0;
937 + for(struct pollfd_meta *m = META_FIRST(&sth->run.meta, &idx);
938 + m;
939 + m = META_NEXT(&sth->run.meta, &idx)) {
940 + if (m->type != POLLFD_TYPE_RECEIVER) continue;
941 + struct receiver_state *rpt = m->rpt;
942 +
943 + spinlock_lock(&rpt->thread.send_to_child.spinlock);
944 + STREAM_CIRCULAR_BUFFER_STATS stats = *stream_circular_buffer_stats_unsafe(rpt->thread.send_to_child.scb);
945 + spinlock_unlock(&rpt->thread.send_to_child.spinlock);
946 +
947 + if (stats.buffer_ratio > overall_buffer_ratio)
948 + overall_buffer_ratio = stats.buffer_ratio;
949 +
950 + time_t timeout_s = 600;
951 + if(unlikely(rpt->thread.last_traffic_ut + timeout_s * USEC_PER_SEC < now_ut &&
952 + !rrdhost_receiver_replicating_charts(rpt->host))) {
953 +
954 + ND_LOG_STACK lgs[] = {
955 + ND_LOG_FIELD_TXT(NDF_SRC_IP, rpt->remote_ip),
956 + ND_LOG_FIELD_TXT(NDF_SRC_PORT, rpt->remote_port),
957 + ND_LOG_FIELD_TXT(NDF_NIDL_NODE, rpt->hostname),
958 + ND_LOG_FIELD_CB(NDF_SRC_TRANSPORT, stream_receiver_log_transport, rpt),
959 + ND_LOG_FIELD_CB(NDF_SRC_CAPABILITIES, stream_receiver_log_capabilities, rpt),
960 + ND_LOG_FIELD_END(),
961 + };
962 + ND_LOG_STACK_PUSH(lgs);
963 +
964 + worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT);
965 +
966 + char duration[RFC3339_MAX_LENGTH];
967 + duration_snprintf(duration, sizeof(duration), (int64_t)(now_monotonic_usec() - rpt->thread.last_traffic_ut), "us", true);
968 +
969 + char pending[64] = "0";
970 + if(stats.bytes_outstanding)
971 + size_snprintf(pending, sizeof(pending), stats.bytes_outstanding, "B", false);
972 +
973 + nd_log(NDLS_DAEMON, NDLP_ERR,
974 + "STREAM RCV[%zu] '%s' [from %s]: there was not traffic for %ld seconds - closing connection - "
975 + "we have sent %zu bytes in %zu operations, it is idle for %s, and we have %s pending to send "
976 + "(buffer is used %.2f%%).",
977 + sth->id, rrdhost_hostname(rpt->host), rpt->remote_ip, timeout_s,
978 + stats.bytes_sent, stats.sends, duration, pending, stats.buffer_ratio);
979 +
980 + receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_SOCKET_TIMEOUT, false);
981 + stream_receiver_remove(sth, rpt, "timeout");
982 + continue;
983 }
984 +
985 + rpt->thread.wanted = ND_POLL_READ | (stats.bytes_outstanding ? ND_POLL_WRITE : 0);
986 + if(!nd_poll_upd(sth->run.ndpl, rpt->sock.fd, rpt->thread.wanted, &rpt->thread.meta))
987 + nd_log(NDLS_DAEMON, NDLP_ERR,
988 + "STREAM RCV[%zu] '%s' [from %s]: failed to update nd_poll().",
989 + sth->id, rrdhost_hostname(rpt->host), rpt->remote_ip);
990 +
991 + }
992 +}
993 +
994 +static bool stream_receiver_did_replication_progress(struct receiver_state *rpt) {
995 + RRDHOST *host = rpt->host;
996 +
997 + size_t my_counter_in = __atomic_load_n(&rpt->replication.last_counter_in, __ATOMIC_RELAXED);
998 + size_t my_counter_out = __atomic_load_n(&rpt->replication.last_counter_out, __ATOMIC_RELAXED);
999 + size_t host_counter_in = __atomic_load_n(&host->stream.rcv.status.replication.counter_in, __ATOMIC_RELAXED);
1000 + size_t host_counter_out = __atomic_load_n(&host->stream.rcv.status.replication.counter_out, __ATOMIC_RELAXED);
1001 + if(my_counter_in != host_counter_in || my_counter_out != host_counter_out) {
1002 + // there has been some progress
1003 + __atomic_store_n(&rpt->replication.last_counter_in, __atomic_load_n(&host->stream.rcv.status.replication.counter_in, __ATOMIC_RELAXED), __ATOMIC_RELAXED);
1004 + __atomic_store_n(&rpt->replication.last_counter_out, __atomic_load_n(&host->stream.rcv.status.replication.counter_out, __ATOMIC_RELAXED), __ATOMIC_RELAXED);
1005 + rpt->replication.last_progress_ut = now_monotonic_usec();
1006 + return true;
1007 }
1008
736 - if (!(events & ND_POLL_READ))
1009 + if(!my_counter_in || !my_counter_out)
1010 + // we have not started yet
1011 return true;
1012
739 - // we can receive data from this socket
1013 + if(__atomic_load_n(&host->stream.rcv.status.replication.backfill_pending, __ATOMIC_RELAXED))
1014 + // we still have requests to execute
1015 + return true;
1016
741 - worker_is_busy(WORKER_STREAM_JOB_SOCKET_RECEIVE);
742 - bool removed = false, stop = false;
743 - size_t iterations = 0;
744 - while(!removed && !stop && iterations++ < MAX_IO_ITERATIONS_PER_EVENT) {
745 - ssize_t rc = stream_receive_and_process(sth, rpt, parser, &removed);
746 - if (likely(rc > 0)) {
747 - rpt->last_msg_t = (time_t)(now_ut / USEC_PER_SEC);
1017 + return (now_monotonic_usec() - rpt->replication.last_progress_ut < 5ULL * 60 * USEC_PER_SEC);
1018 +}
1019
749 - if(stream_thread_process_opcodes(sth, &rpt->thread.meta))
750 - stop = true;
751 - }
752 - else if (rc == 0 || errno == ECONNRESET) {
753 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_REMOTE_CLOSED);
754 - nd_log(NDLS_DAEMON, NDLP_ERR,
755 - "STREAM RCV[%zu] '%s' [from [%s]:%s]: socket %d reports EOF (closed by child).",
756 - sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, rpt->sock.fd);
757 - receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END, false);
758 - stream_receiver_remove(sth, rpt, "socket reports EOF (closed by child)");
759 - return false;
760 - }
761 - else if (rc < 0) {
762 - if(removed)
763 - return false;
1020 +void stream_receiver_replication_check_from_poll(struct stream_thread *sth, usec_t now_ut __maybe_unused) {
1021 + internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__);
1022
765 - else if ((errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR))
766 - // will try later
767 - stop = true;
768 - else {
769 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR);
770 - nd_log(NDLS_DAEMON, NDLP_ERR,
771 - "STREAM RCV[%zu] '%s' [from [%s]:%s]: error during receive (%zd, on fd %d) - closing connection.",
772 - sth->id, rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, rc, rpt->sock.fd);
773 - receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_DISCONNECT_SOCKET_READ_FAILED, false);
774 - stream_receiver_remove(sth, rpt, "error during receive");
775 - return false;
776 - }
1023 + Word_t idx = 0;
1024 + for(struct pollfd_meta *m = META_FIRST(&sth->run.meta, &idx);
1025 + m;
1026 + m = META_NEXT(&sth->run.meta, &idx)) {
1027 + if (m->type != POLLFD_TYPE_RECEIVER) continue;
1028 + struct receiver_state *rpt = m->rpt;
1029 + RRDHOST *host = rpt->host;
1030 +
1031 +
1032 + if(stream_receiver_did_replication_progress(rpt))
1033 + continue;
1034 +
1035 + size_t exceptions = 0;
1036 + RRDSET *st;
1037 + rrdset_foreach_read(st, rpt->host) {
1038 + RRDSET_FLAGS st_flags = rrdset_flag_get(st);
1039 + if(st_flags & (RRDSET_FLAG_OBSOLETE | RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED))
1040 + continue;
1041 +
1042 + const char *status = (st_flags & RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS) ? "has not finished" : "has not started";
1043 +
1044 + nd_log(NDLS_DAEMON, NDLP_WARNING,
1045 + "STREAM RCV[%zu] '%s' [from %s]: REPLICATION EXCEPTIONS: instance '%s' %s replication yet.",
1046 + sth->id, rrdhost_hostname(host), rpt->remote_ip,
1047 + rrdset_id(st), status);
1048 +
1049 + exceptions++;
1050 + }
1051 + rrdset_foreach_done(st);
1052 +
1053 + if(exceptions && !stream_receiver_did_replication_progress(rpt)) {
1054 + nd_log(NDLS_DAEMON, NDLP_WARNING,
1055 + "STREAM RCV[%zu] '%s' [from %s]: REPLICATION EXCEPTIONS SUMMARY: node has %zu stalled replication requests. "
1056 + "We have received %u and sent %u replication commands. "
1057 + "Disconnecting node to restore streaming.",
1058 + sth->id, rrdhost_hostname(rpt->host), rpt->remote_ip, exceptions,
1059 + __atomic_load_n(&host->stream.rcv.status.replication.counter_in, __ATOMIC_RELAXED),
1060 + __atomic_load_n(&host->stream.rcv.status.replication.counter_out, __ATOMIC_RELAXED));
1061 +
1062 + receiver_set_exit_reason(rpt, STREAM_HANDSHAKE_REPLICATION_STALLED, false);
1063 + stream_receiver_remove(sth, rpt, "replication reception stalled");
1064 }
1065 }
779 -
780 - return !removed;
1066 }
1067
1068 void stream_receiver_cleanup(struct stream_thread *sth) {
@@ -795,11 +1080,27 @@ void stream_receiver_cleanup(struct stream_thread *sth) {
1080 static void stream_receiver_replication_reset(RRDHOST *host) {
1081 RRDSET *st;
1082 rrdset_foreach_read(st, host) {
798 - rrdset_flag_clear(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS);
799 - rrdset_flag_set(st, RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED);
1083 + RRDSET_FLAGS old = rrdset_flag_set_and_clear(st, RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS);
1084 + if(!(old & RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED))
1085 + rrdhost_receiver_replicating_charts_minus_one(host);
1086 +
1087 +#ifdef REPLICATION_TRACKING
1088 + st->stream.rcv.who = REPLAY_WHO_UNKNOWN;
1089 +#endif
1090 }
1091 rrdset_foreach_done(st);
802 - rrdhost_receiver_replicating_charts_zero(host);
1092 +
1093 + if(rrdhost_receiver_replicating_charts(host) != 0) {
1094 + nd_log(NDLS_DAEMON, NDLP_WARNING,
1095 + "STREAM REPLAY ERROR: receiver replication instances counter should be zero, but it is %u"
1096 + " - resetting it to zero",
1097 + rrdhost_receiver_replicating_charts(host));
1098 +
1099 + rrdhost_receiver_replicating_charts_zero(host);
1100 + }
1101 +
1102 + __atomic_store_n(&host->stream.rcv.status.replication.counter_in, 0, __ATOMIC_RELAXED);
1103 + __atomic_store_n(&host->stream.rcv.status.replication.counter_out, 0, __ATOMIC_RELAXED);
1104 }
1105
1106 bool rrdhost_set_receiver(RRDHOST *host, struct receiver_state *rpt) {
@@ -809,7 +1110,10 @@ bool rrdhost_set_receiver(RRDHOST *host, struct receiver_state *rpt) {
1110 rrdhost_receiver_lock(host);
1111
1112 if (!host->receiver) {
1113 + object_state_activate(&host->state_id);
1114 +
1115 rrdhost_flag_clear(host, RRDHOST_FLAG_ORPHAN);
1116 + rrdhost_set_health_evloop_iteration(host);
1117
1118 host->stream.rcv.status.connections++;
1119 streaming_receiver_connected();
@@ -829,7 +1133,7 @@ bool rrdhost_set_receiver(RRDHOST *host, struct receiver_state *rpt) {
1133 nd_log(NDLS_DAEMON, NDLP_DEBUG,
1134 "STREAM RCV '%s' [from [%s]:%s]: "
1135 "Postponing health checks for %" PRId64 " seconds, because it was just connected.",
832 - rrdhost_hostname(host), rpt->client_ip, rpt->client_port,
1136 + rrdhost_hostname(host), rpt->remote_ip, rpt->remote_port,
1137 (int64_t) rpt->config.health.delay);
1138 }
1139 }
@@ -875,12 +1179,15 @@ void rrdhost_clear_receiver(struct receiver_state *rpt) {
1179
1180 rrdhost_receiver_unlock(host);
1181 {
1182 + // this will wait until all workers finish
1183 + object_state_deactivate(&host->state_id);
1184 +
1185 // run all these without having the receiver lock
1186
1187 + rrdhost_set_health_evloop_iteration(host);
1188 ml_host_stop(host);
1189 stream_path_child_disconnected(host);
1190 stream_sender_signal_to_stop_and_wait(host, STREAM_HANDSHAKE_DISCONNECT_RECEIVER_LEFT, false);
883 - stream_receiver_replication_reset(host);
1191 rrdcontext_host_child_disconnected(host);
1192
1193 if (rpt->config.health.enabled)
@@ -892,6 +1199,7 @@ void rrdhost_clear_receiver(struct receiver_state *rpt) {
1199
1200 // now we have the lock again
1201
1202 + stream_receiver_replication_reset(host);
1203 streaming_receiver_disconnected();
1204
1205 __atomic_store_n(&host->receiver->exit.shutdown, false, __ATOMIC_RELAXED);
@@ -940,7 +1248,7 @@ bool stream_receiver_signal_to_stop_and_wait(RRDHOST *host, STREAM_HANDSHAKE rea
1248 netdata_log_error("STREAM RCV[x] '%s' [from [%s]:%s]: "
1249 "streaming thread takes too long to stop, giving up..."
1250 , rrdhost_hostname(host)
943 - , host->receiver->client_ip, host->receiver->client_port);
1251 + , host->receiver->remote_ip, host->receiver->remote_port);
1252 else
1253 ret = true;
1254
src/streaming/stream-replication-receiver.c new
+261
@@ -0,0 +1,261 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "stream-replication-receiver.h"
4 +#include "stream-receiver-internals.h"
5 +
6 +struct replication_request_details {
7 + struct {
8 + send_command callback;
9 + struct parser *parser;
10 + } caller;
11 +
12 + RRDHOST *host;
13 + RRDSET *st;
14 +
15 + struct {
16 + time_t first_entry_t; // the first entry time the child has
17 + time_t last_entry_t; // the last entry time the child has
18 + time_t wall_clock_time; // the current time of the child
19 + bool fixed_last_entry; // when set we set the last entry to wall clock time
20 + } child_db;
21 +
22 + struct {
23 + time_t first_entry_t; // the first entry time we have
24 + time_t last_entry_t; // the last entry time we have
25 + time_t wall_clock_time; // the current local world clock time
26 + } local_db;
27 +
28 + struct {
29 + time_t from; // the starting time of the entire gap we have
30 + time_t to; // the ending time of the entire gap we have
31 + } gap;
32 +
33 + struct {
34 + time_t after; // the start time we requested previously from this child
35 + time_t before; // the end time we requested previously from this child
36 + } last_request;
37 +
38 + struct {
39 + time_t after; // the start time of this replication request - the child will add 1 second
40 + time_t before; // the end time of this replication request
41 + bool start_streaming; // true when we want the child to send anything remaining and start streaming - the child will overwrite 'before'
42 + } wanted;
43 +};
44 +
45 +static void replicate_log_request(struct replication_request_details *r, const char *msg) {
46 +#ifdef NETDATA_INTERNAL_CHECKS
47 + internal_error(true,
48 +#else
49 + nd_log_limit_static_global_var(erl, 1, 0);
50 + nd_log_limit(&erl, NDLS_DAEMON, NDLP_NOTICE,
51 +#endif
52 + "STREAM SND REPLAY ERROR: 'host:%s/chart:%s' child sent: "
53 + "db from %ld to %ld%s, wall clock time %ld, "
54 + "last request from %ld to %ld, "
55 + "issue: %s - "
56 + "sending replication request from %ld to %ld, start streaming %s",
57 + rrdhost_hostname(r->st->rrdhost), rrdset_id(r->st),
58 + r->child_db.first_entry_t,
59 + r->child_db.last_entry_t, r->child_db.fixed_last_entry ? " (fixed)" : "",
60 + r->child_db.wall_clock_time,
61 + r->last_request.after,
62 + r->last_request.before,
63 + msg,
64 + r->wanted.after,
65 + r->wanted.before,
66 + r->wanted.start_streaming ? "true" : "false");
67 +}
68 +
69 +static bool send_replay_chart_cmd(struct replication_request_details *r, const char *msg, bool log) {
70 + RRDSET *st = r->st;
71 +
72 + if(log)
73 + replicate_log_request(r, msg);
74 +
75 + if(st->rrdhost->receiver && (!st->rrdhost->receiver->replication.first_time_s || r->wanted.after < st->rrdhost->receiver->replication.first_time_s))
76 + st->rrdhost->receiver->replication.first_time_s = r->wanted.after;
77 +
78 +#ifdef NETDATA_LOG_REPLICATION_REQUESTS
79 + st->replay.log_next_data_collection = true;
80 +
81 + char wanted_after_buf[LOG_DATE_LENGTH + 1] = "", wanted_before_buf[LOG_DATE_LENGTH + 1] = "";
82 +
83 + if(r->wanted.after)
84 + log_date(wanted_after_buf, LOG_DATE_LENGTH, r->wanted.after);
85 +
86 + if(r->wanted.before)
87 + log_date(wanted_before_buf, LOG_DATE_LENGTH, r->wanted.before);
88 +
89 + internal_error(true,
90 + "STREAM SND REPLAY: 'host:%s/chart:%s' sending replication request %ld [%s] to %ld [%s], start streaming '%s': %s: "
91 + "last[%ld - %ld] child[%ld - %ld, now %ld %s] local[%ld - %ld, now %ld] gap[%ld - %ld %s] %s"
92 + , rrdhost_hostname(r->host), rrdset_id(r->st)
93 + , r->wanted.after, wanted_after_buf
94 + , r->wanted.before, wanted_before_buf
95 + , r->wanted.start_streaming ? "YES" : "NO"
96 + , msg
97 + , r->last_request.after, r->last_request.before
98 + , r->child_db.first_entry_t, r->child_db.last_entry_t
99 + , r->child_db.wall_clock_time, (r->child_db.wall_clock_time == r->local_db.wall_clock_time) ? "SAME" : (r->child_db.wall_clock_time < r->local_db.wall_clock_time) ? "BEHIND" : "AHEAD"
100 + , r->local_db.first_entry_t, r->local_db.last_entry_t
101 + , r->local_db.wall_clock_time
102 + , r->gap.from, r->gap.to
103 + , (r->gap.from == r->wanted.after) ? "FULL" : "PARTIAL"
104 + , (st->replay.after != 0 || st->replay.before != 0) ? "OVERLAPPING" : ""
105 + );
106 +
107 + st->replay.start_streaming = r->wanted.start_streaming;
108 + st->replay.after = r->wanted.after;
109 + st->replay.before = r->wanted.before;
110 +#endif // NETDATA_LOG_REPLICATION_REQUESTS
111 +
112 + char buffer[2048 + 1];
113 + snprintfz(buffer, sizeof(buffer) - 1, PLUGINSD_KEYWORD_REPLAY_CHART " \"%s\" \"%s\" %llu %llu\n",
114 + rrdset_id(st), r->wanted.start_streaming ? "true" : "false",
115 + (unsigned long long)r->wanted.after, (unsigned long long)r->wanted.before);
116 +
117 + ssize_t ret = r->caller.callback(buffer, r->caller.parser, STREAM_TRAFFIC_TYPE_REPLICATION);
118 + if (ret < 0) {
119 + netdata_log_error("STREAM SND REPLAY ERROR: 'host:%s/chart:%s' failed to send replication request to child (error %zd)",
120 + rrdhost_hostname(r->host), rrdset_id(r->st), ret);
121 + return false;
122 + }
123 +
124 + __atomic_add_fetch(&st->rrdhost->stream.rcv.status.replication.counter_out, 1, __ATOMIC_RELAXED);
125 +
126 +#ifdef REPLICATION_TRACKING
127 + st->stream.rcv.who = REPLAY_WHO_THEM;
128 +#endif
129 +
130 + return true;
131 +}
132 +
133 +bool replicate_chart_request(send_command callback, struct parser *parser, RRDHOST *host, RRDSET *st,
134 + time_t child_first_entry, time_t child_last_entry, time_t child_wall_clock_time,
135 + time_t prev_first_entry_wanted, time_t prev_last_entry_wanted)
136 +{
137 + struct replication_request_details r = {
138 + .caller = {
139 + .callback = callback,
140 + .parser = parser,
141 + },
142 +
143 + .host = host,
144 + .st = st,
145 +
146 + .child_db = {
147 + .first_entry_t = child_first_entry,
148 + .last_entry_t = child_last_entry,
149 + .wall_clock_time = child_wall_clock_time,
150 + .fixed_last_entry = false,
151 + },
152 +
153 + .local_db = {
154 + .first_entry_t = 0,
155 + .last_entry_t = 0,
156 + .wall_clock_time = now_realtime_sec(),
157 + },
158 +
159 + .last_request = {
160 + .after = prev_first_entry_wanted,
161 + .before = prev_last_entry_wanted,
162 + },
163 +
164 + .wanted = {
165 + .after = 0,
166 + .before = 0,
167 + .start_streaming = true,
168 + },
169 + };
170 +
171 + if(r.child_db.last_entry_t > r.child_db.wall_clock_time) {
172 + replicate_log_request(&r, "child's db last entry > child's wall clock time");
173 + r.child_db.last_entry_t = r.child_db.wall_clock_time;
174 + r.child_db.fixed_last_entry = true;
175 + }
176 +
177 + rrdset_get_retention_of_tier_for_collected_chart(r.st, &r.local_db.first_entry_t, &r.local_db.last_entry_t, r.local_db.wall_clock_time, 0);
178 +
179 + // let's find the GAP we have
180 + if(!r.last_request.after || !r.last_request.before) {
181 + // there is no previous request
182 +
183 + if(r.local_db.last_entry_t)
184 + // we have some data, let's continue from the last point we have
185 + r.gap.from = r.local_db.last_entry_t;
186 + else
187 + // we don't have any data, the gap is the max timeframe we are allowed to replicate
188 + r.gap.from = r.local_db.wall_clock_time - r.host->stream.replication.period;
189 +
190 + }
191 + else {
192 + // we had sent a request - let's continue at the point we left it
193 + // for this we don't take into account the actual data in our db
194 + // because the child may also have gaps, and we need to get over it
195 + r.gap.from = r.last_request.before;
196 + }
197 +
198 + // we want all the data up to now
199 + r.gap.to = r.local_db.wall_clock_time;
200 +
201 + // The gap is now r.gap.from -> r.gap.to
202 +
203 + if (unlikely(!rrdhost_option_check(host, RRDHOST_OPTION_REPLICATION)))
204 + return send_replay_chart_cmd(&r, "sending empty replication request, replication is disabled", false);
205 +
206 + if (unlikely(!rrdset_number_of_dimensions(st)))
207 + return send_replay_chart_cmd(&r, "sending empty replication request, chart has no dimensions", false);
208 +
209 + if (unlikely(!r.child_db.first_entry_t || !r.child_db.last_entry_t))
210 + return send_replay_chart_cmd(&r, "sending empty replication request, child has no stored data", false);
211 +
212 + if (unlikely(r.child_db.first_entry_t < 0 || r.child_db.last_entry_t < 0))
213 + return send_replay_chart_cmd(&r, "sending empty replication request, child db timestamps are invalid", true);
214 +
215 + if (unlikely(r.child_db.first_entry_t > r.child_db.wall_clock_time))
216 + return send_replay_chart_cmd(&r, "sending empty replication request, child db first entry is after its wall clock time", true);
217 +
218 + if (unlikely(r.child_db.first_entry_t > r.child_db.last_entry_t))
219 + return send_replay_chart_cmd(&r, "sending empty replication request, child timings are invalid (first entry > last entry)", true);
220 +
221 + if (unlikely(r.local_db.last_entry_t > r.child_db.last_entry_t))
222 + return send_replay_chart_cmd(&r, "sending empty replication request, local last entry is later than the child one", false);
223 +
224 + // let's find what the child can provide to fill that gap
225 +
226 + if(r.child_db.first_entry_t > r.gap.from)
227 + // the child does not have all the data - let's get what it has
228 + r.wanted.after = r.child_db.first_entry_t;
229 + else
230 + // ok, the child can fill the entire gap we have
231 + r.wanted.after = r.gap.from;
232 +
233 + if(r.gap.to - r.wanted.after > host->stream.replication.step)
234 + // the duration is too big for one request - let's take the first step
235 + r.wanted.before = r.wanted.after + host->stream.replication.step;
236 + else
237 + // wow, we can do it in one request
238 + r.wanted.before = r.gap.to;
239 +
240 + // don't ask from the child more than it has
241 + if(r.wanted.before > r.child_db.last_entry_t)
242 + r.wanted.before = r.child_db.last_entry_t;
243 +
244 + if(r.wanted.after > r.wanted.before) {
245 + r.wanted.after = 0;
246 + r.wanted.before = 0;
247 + r.wanted.start_streaming = true;
248 + return send_replay_chart_cmd(&r, "sending empty replication request, because wanted 'after' computed bigger than wanted 'before'", true);
249 + }
250 +
251 + // the child should start streaming immediately if the wanted duration is small, or we reached the last entry of the child
252 + r.wanted.start_streaming = (r.local_db.wall_clock_time - r.wanted.after <= host->stream.replication.step ||
253 + r.wanted.before >= r.child_db.last_entry_t ||
254 + r.wanted.before >= r.child_db.wall_clock_time ||
255 + r.wanted.before >= r.local_db.wall_clock_time);
256 +
257 + // the wanted timeframe is now r.wanted.after -> r.wanted.before
258 + // send it
259 + return send_replay_chart_cmd(&r, "OK", false);
260 +}
261 +
src/streaming/stream-replication-receiver.h new
+27
@@ -0,0 +1,27 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_STREAM_REPLICATION_RECEIVER_H
4 +#define NETDATA_STREAM_REPLICATION_RECEIVER_H
5 +
6 +#include "libnetdata/libnetdata.h"
7 +#include "stream-traffic-types.h"
8 +
9 +#ifdef __cplusplus
10 +extern "C" {
11 +#endif
12 +
13 +struct parser;
14 +struct rrdhost;
15 +struct rrdset;
16 +
17 +typedef ssize_t (*send_command)(const char *txt, struct parser *parser, STREAM_TRAFFIC_TYPE type);
18 +bool replicate_chart_request(send_command callback, struct parser *parser,
19 + struct rrdhost *rh, struct rrdset *rs,
20 + time_t child_first_entry, time_t child_last_entry, time_t child_wall_clock_time,
21 + time_t response_first_start_time, time_t response_last_end_time);
22 +
23 +#ifdef __cplusplus
24 +}
25 +#endif
26 +
27 +#endif //NETDATA_STREAM_REPLICATION_RECEIVER_H
src/streaming/stream-replication-sender.c renamed
+172 -330
@@ -1,9 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#include "stream-receiver-internals.h"
3 #include "stream-sender-internals.h"
5 -#include "replication.h"
6 -#include "Judy.h"
4 +#include "stream-replication-sender.h"
5
6 #define MAX_REPLICATION_MESSAGE_PERCENT_SENDER_BUFFER 25ULL
7 #define MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED 50ULL
@@ -33,7 +31,7 @@
31 #define SECONDS_TO_RESET_POINT_IN_TIME 10
32
33 #define MAX_REPLICATION_THREADS 256
36 -#define REQUESTS_AHEAD_PER_THREAD 1 // 1 = enable synchronous queries
34 +#define REQUESTS_AHEAD_PER_THREAD 0 // 0 = dynamic, 1 = enable synchronous queries, > 1 static
35
36 static struct replication_query_statistics replication_queries = {
37 .spinlock = SPINLOCK_INITIALIZER,
@@ -52,7 +50,7 @@ struct replication_query_statistics replication_get_query_statistics(void) {
50
51 static size_t replication_buffers_allocated = 0;
52
55 -size_t replication_allocated_buffers(void) {
53 +size_t replication_sender_allocated_buffers(void) {
54 return __atomic_load_n(&replication_buffers_allocated, __ATOMIC_RELAXED);
55 }
56
@@ -189,8 +187,7 @@ static struct replication_query *replication_query_prepare(
187 d->rda = dictionary_acquired_item_dup(rd_dfe.dict, rd_dfe.item);
188 d->rd = rd;
189
192 - STORAGE_PRIORITY priority = q->query.locked_data_collection ? STORAGE_PRIORITY_HIGH : STORAGE_PRIORITY_LOW;
193 - if(synchronous) priority = STORAGE_PRIORITY_SYNCHRONOUS_FIRST;
190 + STORAGE_PRIORITY priority = (synchronous) ? STORAGE_PRIORITY_SYNCHRONOUS_FIRST : STORAGE_PRIORITY_LOW;
191
192 stream_control_replication_query_started();
193 storage_engine_query_init(q->backend, rd->tiers[0].smh, &d->handle,
@@ -622,14 +619,15 @@ static struct replication_query *replication_response_prepare(
619 wall_clock_time, capabilities, synchronous);
620 }
621
625 -void replication_response_cancel_and_finalize(struct replication_query *q) {
622 +static void replication_response_cancel_and_finalize(struct replication_query *q) {
623 if(!q) return;
624 replication_query_finalize(NULL, q, false);
625 }
626
627 static bool sender_is_still_connected_for_this_request(struct replication_request *rq);
628 +static void replication_replied_add(void);
629
632 -bool replication_response_execute_and_finalize(struct replication_query *q, size_t max_msg_size, bool workers) {
630 +bool replication_response_execute_finalize_and_send(struct replication_query *q, size_t max_msg_size, bool workers) {
631 bool with_slots = (q->query.capabilities & STREAM_CAP_SLOTS) ? true : false;
632 NUMBER_ENCODING integer_encoding = (q->query.capabilities & STREAM_CAP_IEEE754) ? NUMBER_ENCODING_BASE64 : NUMBER_ENCODING_DECIMAL;
633 struct replication_request *rq = q->rq;
@@ -693,15 +691,20 @@ bool replication_response_execute_and_finalize(struct replication_query *q, size
691 if(workers) worker_is_busy(WORKER_JOB_BUFFER_COMMIT);
692 sender_commit(host->sender, wb, STREAM_TRAFFIC_TYPE_REPLICATION);
693 if(workers) worker_is_busy(WORKER_JOB_CLEANUP);
694 + __atomic_add_fetch(&host->stream.snd.status.replication.counter_out, 1, __ATOMIC_RELAXED);
695 + replication_replied_add();
696
697 if(enable_streaming) {
698 +#ifdef REPLICATION_TRACKING
699 + st->stream.snd.who = REPLAY_WHO_FINISHED;
700 +#endif
701 +
702 if(sender_is_still_connected_for_this_request(rq)) {
703 // enable normal streaming if we have to
704 // but only if the sender buffer has not been flushed since we started
705
702 - if(rrdset_flag_check(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS)) {
703 - rrdset_flag_clear(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS);
704 - rrdset_flag_set(st, RRDSET_FLAG_SENDER_REPLICATION_FINISHED);
706 + RRDSET_FLAGS old = rrdset_flag_set_and_clear(st, RRDSET_FLAG_SENDER_REPLICATION_FINISHED, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS);
707 + if(!(old & RRDSET_FLAG_SENDER_REPLICATION_FINISHED)) {
708 rrdhost_sender_replicating_charts_minus_one(st->rrdhost);
709
710 if(!finished_with_gap)
@@ -713,12 +716,18 @@ bool replication_response_execute_and_finalize(struct replication_query *q, size
716 #endif
717 }
718 else
716 - internal_error(true,
717 - "STREAM SND REPLAY ERROR: 'host:%s/chart:%s' "
718 - "received start streaming command, but the chart is not in progress replicating",
719 - rrdhost_hostname(st->rrdhost), rrdset_id(st));
719 + internal_error(
720 + true,
721 + "STREAM SND REPLAY ERROR: 'host:%s/chart:%s' "
722 + "received start streaming command, but the chart was not in progress replicating",
723 + rrdhost_hostname(st->rrdhost), rrdset_id(st));
724 }
725 }
726 + else {
727 +#ifdef REPLICATION_TRACKING
728 + st->stream.snd.who = REPLAY_WHO_THEM;
729 +#endif
730 + }
731
732 if(locked_data_collection)
733 spinlock_unlock(&st->data_collection_lock);
@@ -726,259 +735,6 @@ bool replication_response_execute_and_finalize(struct replication_query *q, size
735 return enable_streaming;
736 }
737
729 -// ----------------------------------------------------------------------------
730 -// sending replication requests
731 -
732 -struct replication_request_details {
733 - struct {
734 - send_command callback;
735 - struct parser *parser;
736 - } caller;
737 -
738 - RRDHOST *host;
739 - RRDSET *st;
740 -
741 - struct {
742 - time_t first_entry_t; // the first entry time the child has
743 - time_t last_entry_t; // the last entry time the child has
744 - time_t wall_clock_time; // the current time of the child
745 - bool fixed_last_entry; // when set we set the last entry to wall clock time
746 - } child_db;
747 -
748 - struct {
749 - time_t first_entry_t; // the first entry time we have
750 - time_t last_entry_t; // the last entry time we have
751 - time_t wall_clock_time; // the current local world clock time
752 - } local_db;
753 -
754 - struct {
755 - time_t from; // the starting time of the entire gap we have
756 - time_t to; // the ending time of the entire gap we have
757 - } gap;
758 -
759 - struct {
760 - time_t after; // the start time we requested previously from this child
761 - time_t before; // the end time we requested previously from this child
762 - } last_request;
763 -
764 - struct {
765 - time_t after; // the start time of this replication request - the child will add 1 second
766 - time_t before; // the end time of this replication request
767 - bool start_streaming; // true when we want the child to send anything remaining and start streaming - the child will overwrite 'before'
768 - } wanted;
769 -};
770 -
771 -static void replicate_log_request(struct replication_request_details *r, const char *msg) {
772 -#ifdef NETDATA_INTERNAL_CHECKS
773 - internal_error(true,
774 -#else
775 - nd_log_limit_static_global_var(erl, 1, 0);
776 - nd_log_limit(&erl, NDLS_DAEMON, NDLP_NOTICE,
777 -#endif
778 - "STREAM SND REPLAY ERROR: 'host:%s/chart:%s' child sent: "
779 - "db from %ld to %ld%s, wall clock time %ld, "
780 - "last request from %ld to %ld, "
781 - "issue: %s - "
782 - "sending replication request from %ld to %ld, start streaming %s",
783 - rrdhost_hostname(r->st->rrdhost), rrdset_id(r->st),
784 - r->child_db.first_entry_t,
785 - r->child_db.last_entry_t, r->child_db.fixed_last_entry ? " (fixed)" : "",
786 - r->child_db.wall_clock_time,
787 - r->last_request.after,
788 - r->last_request.before,
789 - msg,
790 - r->wanted.after,
791 - r->wanted.before,
792 - r->wanted.start_streaming ? "true" : "false");
793 -}
794 -
795 -static bool send_replay_chart_cmd(struct replication_request_details *r, const char *msg, bool log) {
796 - RRDSET *st = r->st;
797 -
798 - if(log)
799 - replicate_log_request(r, msg);
800 -
801 - if(st->rrdhost->receiver && (!st->rrdhost->receiver->replication_first_time_t || r->wanted.after < st->rrdhost->receiver->replication_first_time_t))
802 - st->rrdhost->receiver->replication_first_time_t = r->wanted.after;
803 -
804 -#ifdef NETDATA_LOG_REPLICATION_REQUESTS
805 - st->replay.log_next_data_collection = true;
806 -
807 - char wanted_after_buf[LOG_DATE_LENGTH + 1] = "", wanted_before_buf[LOG_DATE_LENGTH + 1] = "";
808 -
809 - if(r->wanted.after)
810 - log_date(wanted_after_buf, LOG_DATE_LENGTH, r->wanted.after);
811 -
812 - if(r->wanted.before)
813 - log_date(wanted_before_buf, LOG_DATE_LENGTH, r->wanted.before);
814 -
815 - internal_error(true,
816 - "STREAM SND REPLAY: 'host:%s/chart:%s' sending replication request %ld [%s] to %ld [%s], start streaming '%s': %s: "
817 - "last[%ld - %ld] child[%ld - %ld, now %ld %s] local[%ld - %ld, now %ld] gap[%ld - %ld %s] %s"
818 - , rrdhost_hostname(r->host), rrdset_id(r->st)
819 - , r->wanted.after, wanted_after_buf
820 - , r->wanted.before, wanted_before_buf
821 - , r->wanted.start_streaming ? "YES" : "NO"
822 - , msg
823 - , r->last_request.after, r->last_request.before
824 - , r->child_db.first_entry_t, r->child_db.last_entry_t
825 - , r->child_db.wall_clock_time, (r->child_db.wall_clock_time == r->local_db.wall_clock_time) ? "SAME" : (r->child_db.wall_clock_time < r->local_db.wall_clock_time) ? "BEHIND" : "AHEAD"
826 - , r->local_db.first_entry_t, r->local_db.last_entry_t
827 - , r->local_db.wall_clock_time
828 - , r->gap.from, r->gap.to
829 - , (r->gap.from == r->wanted.after) ? "FULL" : "PARTIAL"
830 - , (st->replay.after != 0 || st->replay.before != 0) ? "OVERLAPPING" : ""
831 - );
832 -
833 - st->replay.start_streaming = r->wanted.start_streaming;
834 - st->replay.after = r->wanted.after;
835 - st->replay.before = r->wanted.before;
836 -#endif // NETDATA_LOG_REPLICATION_REQUESTS
837 -
838 - char buffer[2048 + 1];
839 - snprintfz(buffer, sizeof(buffer) - 1, PLUGINSD_KEYWORD_REPLAY_CHART " \"%s\" \"%s\" %llu %llu\n",
840 - rrdset_id(st), r->wanted.start_streaming ? "true" : "false",
841 - (unsigned long long)r->wanted.after, (unsigned long long)r->wanted.before);
842 -
843 - ssize_t ret = r->caller.callback(buffer, r->caller.parser, STREAM_TRAFFIC_TYPE_REPLICATION);
844 - if (ret < 0) {
845 - netdata_log_error("STREAM SND REPLAY ERROR: 'host:%s/chart:%s' failed to send replication request to child (error %zd)",
846 - rrdhost_hostname(r->host), rrdset_id(r->st), ret);
847 - return false;
848 - }
849 -
850 - return true;
851 -}
852 -
853 -bool replicate_chart_request(send_command callback, struct parser *parser, RRDHOST *host, RRDSET *st,
854 - time_t child_first_entry, time_t child_last_entry, time_t child_wall_clock_time,
855 - time_t prev_first_entry_wanted, time_t prev_last_entry_wanted)
856 -{
857 - struct replication_request_details r = {
858 - .caller = {
859 - .callback = callback,
860 - .parser = parser,
861 - },
862 -
863 - .host = host,
864 - .st = st,
865 -
866 - .child_db = {
867 - .first_entry_t = child_first_entry,
868 - .last_entry_t = child_last_entry,
869 - .wall_clock_time = child_wall_clock_time,
870 - .fixed_last_entry = false,
871 - },
872 -
873 - .local_db = {
874 - .first_entry_t = 0,
875 - .last_entry_t = 0,
876 - .wall_clock_time = now_realtime_sec(),
877 - },
878 -
879 - .last_request = {
880 - .after = prev_first_entry_wanted,
881 - .before = prev_last_entry_wanted,
882 - },
883 -
884 - .wanted = {
885 - .after = 0,
886 - .before = 0,
887 - .start_streaming = true,
888 - },
889 - };
890 -
891 - if(r.child_db.last_entry_t > r.child_db.wall_clock_time) {
892 - replicate_log_request(&r, "child's db last entry > child's wall clock time");
893 - r.child_db.last_entry_t = r.child_db.wall_clock_time;
894 - r.child_db.fixed_last_entry = true;
895 - }
896 -
897 - rrdset_get_retention_of_tier_for_collected_chart(r.st, &r.local_db.first_entry_t, &r.local_db.last_entry_t, r.local_db.wall_clock_time, 0);
898 -
899 - // let's find the GAP we have
900 - if(!r.last_request.after || !r.last_request.before) {
901 - // there is no previous request
902 -
903 - if(r.local_db.last_entry_t)
904 - // we have some data, let's continue from the last point we have
905 - r.gap.from = r.local_db.last_entry_t;
906 - else
907 - // we don't have any data, the gap is the max timeframe we are allowed to replicate
908 - r.gap.from = r.local_db.wall_clock_time - r.host->stream.replication.period;
909 -
910 - }
911 - else {
912 - // we had sent a request - let's continue at the point we left it
913 - // for this we don't take into account the actual data in our db
914 - // because the child may also have gaps, and we need to get over it
915 - r.gap.from = r.last_request.before;
916 - }
917 -
918 - // we want all the data up to now
919 - r.gap.to = r.local_db.wall_clock_time;
920 -
921 - // The gap is now r.gap.from -> r.gap.to
922 -
923 - if (unlikely(!rrdhost_option_check(host, RRDHOST_OPTION_REPLICATION)))
924 - return send_replay_chart_cmd(&r, "empty replication request, replication is disabled", false);
925 -
926 - if (unlikely(!rrdset_number_of_dimensions(st)))
927 - return send_replay_chart_cmd(&r, "empty replication request, chart has no dimensions", false);
928 -
929 - if (unlikely(!r.child_db.first_entry_t || !r.child_db.last_entry_t))
930 - return send_replay_chart_cmd(&r, "empty replication request, child has no stored data", false);
931 -
932 - if (unlikely(r.child_db.first_entry_t < 0 || r.child_db.last_entry_t < 0))
933 - return send_replay_chart_cmd(&r, "empty replication request, child db timestamps are invalid", true);
934 -
935 - if (unlikely(r.child_db.first_entry_t > r.child_db.wall_clock_time))
936 - return send_replay_chart_cmd(&r, "empty replication request, child db first entry is after its wall clock time", true);
937 -
938 - if (unlikely(r.child_db.first_entry_t > r.child_db.last_entry_t))
939 - return send_replay_chart_cmd(&r, "empty replication request, child timings are invalid (first entry > last entry)", true);
940 -
941 - if (unlikely(r.local_db.last_entry_t > r.child_db.last_entry_t))
942 - return send_replay_chart_cmd(&r, "empty replication request, local last entry is later than the child one", false);
943 -
944 - // let's find what the child can provide to fill that gap
945 -
946 - if(r.child_db.first_entry_t > r.gap.from)
947 - // the child does not have all the data - let's get what it has
948 - r.wanted.after = r.child_db.first_entry_t;
949 - else
950 - // ok, the child can fill the entire gap we have
951 - r.wanted.after = r.gap.from;
952 -
953 - if(r.gap.to - r.wanted.after > host->stream.replication.step)
954 - // the duration is too big for one request - let's take the first step
955 - r.wanted.before = r.wanted.after + host->stream.replication.step;
956 - else
957 - // wow, we can do it in one request
958 - r.wanted.before = r.gap.to;
959 -
960 - // don't ask from the child more than it has
961 - if(r.wanted.before > r.child_db.last_entry_t)
962 - r.wanted.before = r.child_db.last_entry_t;
963 -
964 - if(r.wanted.after > r.wanted.before) {
965 - r.wanted.after = 0;
966 - r.wanted.before = 0;
967 - r.wanted.start_streaming = true;
968 - return send_replay_chart_cmd(&r, "empty replication request, wanted 'after' computed bigger than wanted 'before'", true);
969 - }
970 -
971 - // the child should start streaming immediately if the wanted duration is small, or we reached the last entry of the child
972 - r.wanted.start_streaming = (r.local_db.wall_clock_time - r.wanted.after <= host->stream.replication.step ||
973 - r.wanted.before >= r.child_db.last_entry_t ||
974 - r.wanted.before >= r.child_db.wall_clock_time ||
975 - r.wanted.before >= r.local_db.wall_clock_time);
976 -
977 - // the wanted timeframe is now r.wanted.after -> r.wanted.before
978 - // send it
979 - return send_replay_chart_cmd(&r, "OK", false);
980 -}
981 -
738 // ----------------------------------------------------------------------------
739 // replication thread
740
@@ -990,7 +746,7 @@ struct replication_request {
746 time_t after; // the start time of the query (maybe zero) key for sorting (JudyL)
747 time_t before; // the end time of the query (maybe zero)
748
993 - usec_t sender_circular_buffer_since_ut; // the timestamp of the sender, at the time we indexed this request
749 + usec_t sender_circular_buffer_last_flush_ut; // the timestamp of the sender, at the time we indexed this request
750 Word_t unique_id; // auto-increment, later requests have bigger
751
752 bool start_streaming; // true, when the parent wants to send the rest of the data (before is overwritten) and enable normal streaming
@@ -1040,13 +796,24 @@ static struct replication_thread {
796
797 struct {
798 Word_t unique_id; // the last unique id we gave to a request (auto-increment, starting from 1)
799 + size_t received; // the number of replication requests received
800 size_t executed; // the number of replication requests executed
801 + size_t replied;
802 + size_t error_not_found; // the number of replication requests ignored because the chart was not found
803 + size_t error_duplicate; // the number of replication requests found duplicate (same chart)
804 + size_t error_flushed; // the number of replication requests deleted due to disconnections
805 size_t latest_first_time; // the 'after' timestamp of the last request we executed
806 size_t memory; // the total memory allocated by replication
807 } atomic; // access should be with atomic operations
808
809 struct {
1049 - size_t last_executed; // caching of the atomic.executed to report number of requests executed since last time
810 + // same as the atomic versions, for finding the delta over time
811 + size_t last_received;
812 + size_t last_executed;
813 + size_t last_replied;
814 + size_t last_error_flushed;
815 + size_t last_error_duplicate;
816 + size_t last_error_not_found;
817
818 ND_THREAD **threads_ptrs;
819 size_t threads;
@@ -1085,7 +852,7 @@ static struct replication_thread {
852 },
853 };
854
1088 -size_t replication_allocated_memory(void) {
855 +size_t replication_sender_allocated_memory(void) {
856 return __atomic_load_n(&replication_globals.atomic.memory, __ATOMIC_RELAXED);
857 }
858
@@ -1378,6 +1145,8 @@ static bool replication_request_conflict_callback(const DICTIONARY_ITEM *item __
1145 struct replication_request *rq = old_value; (void)rq;
1146 struct replication_request *rq_new = new_value;
1147
1148 + __atomic_add_fetch(&replication_globals.atomic.error_duplicate, 1, __ATOMIC_RELAXED);
1149 +
1150 replication_recursive_lock();
1151
1152 if(!rq->indexed_in_judy && rq->not_indexed_buffer_full && !rq->not_indexed_preprocessing) {
@@ -1385,7 +1154,7 @@ static bool replication_request_conflict_callback(const DICTIONARY_ITEM *item __
1154 internal_error(
1155 true,
1156 "STREAM SND '%s' [to %s]: REPLAY: 'host:%s/chart:%s' replacing duplicate replication command received (existing from %llu to %llu [%s], new from %llu to %llu [%s])",
1388 - rrdhost_hostname(s->host), s->connected_to, rrdhost_hostname(s->host), dictionary_acquired_item_name(item),
1157 + rrdhost_hostname(s->host), s->remote_ip, rrdhost_hostname(s->host), dictionary_acquired_item_name(item),
1158 (unsigned long long)rq->after, (unsigned long long)rq->before, rq->start_streaming ? "true" : "false",
1159 (unsigned long long)rq_new->after, (unsigned long long)rq_new->before, rq_new->start_streaming ? "true" : "false");
1160
@@ -1398,7 +1167,7 @@ static bool replication_request_conflict_callback(const DICTIONARY_ITEM *item __
1167 internal_error(
1168 true,
1169 "STREAM SND '%s' [to %s]: REPLAY: 'host:%s/chart:%s' adding duplicate replication command received (existing from %llu to %llu [%s], new from %llu to %llu [%s])",
1401 - rrdhost_hostname(s->host), s->connected_to, rrdhost_hostname(s->host), dictionary_acquired_item_name(item),
1170 + rrdhost_hostname(s->host), s->remote_ip, rrdhost_hostname(s->host), dictionary_acquired_item_name(item),
1171 (unsigned long long)rq->after, (unsigned long long)rq->before, rq->start_streaming ? "true" : "false",
1172 (unsigned long long)rq_new->after, (unsigned long long)rq_new->before, rq_new->start_streaming ? "true" : "false");
1173 }
@@ -1406,7 +1175,7 @@ static bool replication_request_conflict_callback(const DICTIONARY_ITEM *item __
1175 internal_error(
1176 true,
1177 "STREAM SND '%s' [to %s]: REPLAY: 'host:%s/chart:%s' ignoring duplicate replication command received (existing from %llu to %llu [%s], new from %llu to %llu [%s])",
1409 - rrdhost_hostname(s->host), s->connected_to, rrdhost_hostname(s->host),
1178 + rrdhost_hostname(s->host), s->remote_ip, rrdhost_hostname(s->host),
1179 dictionary_acquired_item_name(item),
1180 (unsigned long long) rq->after, (unsigned long long) rq->before, rq->start_streaming ? "true" : "false",
1181 (unsigned long long) rq_new->after, (unsigned long long) rq_new->before, rq_new->start_streaming ? "true" : "false");
@@ -1437,7 +1206,7 @@ static void replication_request_delete_callback(const DICTIONARY_ITEM *item __ma
1206 }
1207
1208 static bool sender_is_still_connected_for_this_request(struct replication_request *rq) {
1440 - return rq->sender_circular_buffer_since_ut == stream_circular_buffer_get_since_ut(rq->sender->scb);
1209 + return rq->sender_circular_buffer_last_flush_ut == stream_circular_buffer_last_flush_ut(rq->sender->scb);
1210 }
1211
1212 static bool replication_execute_request(struct replication_request *rq, bool workers) {
@@ -1446,13 +1215,13 @@ static bool replication_execute_request(struct replication_request *rq, bool wor
1215 if(!rq->st) {
1216 if(likely(workers)) worker_is_busy(WORKER_JOB_FIND_CHART);
1217 rq->st = rrdset_find(rq->sender->host, string2str(rq->chart_id));
1449 - }
1450 -
1451 - if(!rq->st) {
1452 - internal_error(true, "STREAM SND REPLAY ERROR: 'host:%s/chart:%s' not found",
1453 - rrdhost_hostname(rq->sender->host), string2str(rq->chart_id));
1454 -
1455 - goto cleanup;
1218 + if(!rq->st) {
1219 + __atomic_add_fetch(&replication_globals.atomic.error_not_found, 1, __ATOMIC_RELAXED);
1220 + nd_log(NDLS_DAEMON, NDLP_ERR,
1221 + "STREAM SND REPLAY ERROR: 'host:%s/chart:%s' not found",
1222 + rrdhost_hostname(rq->sender->host), string2str(rq->chart_id));
1223 + goto cleanup;
1224 + }
1225 }
1226
1227 if(!rq->q) {
@@ -1468,22 +1237,17 @@ static bool replication_execute_request(struct replication_request *rq, bool wor
1237 if(likely(workers)) worker_is_busy(WORKER_JOB_QUERYING);
1238
1239 // send the replication data
1240 + size_t max_msg_size = (size_t)((unsigned long long)stream_circular_buffer_get_max_size(rq->sender->scb) * MAX_REPLICATION_MESSAGE_PERCENT_SENDER_BUFFER / 100ULL);
1241 rq->q->rq = rq;
1472 - replication_response_execute_and_finalize(
1473 - rq->q,
1474 - (size_t)((unsigned long long)stream_circular_buffer_get_max_size(rq->sender->scb) * MAX_REPLICATION_MESSAGE_PERCENT_SENDER_BUFFER / 100ULL), workers);
1475 -
1242 + replication_response_execute_finalize_and_send(rq->q, max_msg_size, workers);
1243 rq->q = NULL;
1244
1245 __atomic_add_fetch(&replication_globals.atomic.executed, 1, __ATOMIC_RELAXED);
1479 -
1246 ret = true;
1247
1248 cleanup:
1483 - if(rq->q) {
1484 - replication_response_cancel_and_finalize(rq->q);
1485 - rq->q = NULL;
1486 - }
1249 + replication_response_cancel_and_finalize(rq->q);
1250 + rq->q = NULL;
1251
1252 string_freez(rq->chart_id);
1253 worker_is_idle();
@@ -1493,14 +1257,14 @@ cleanup:
1257 // ----------------------------------------------------------------------------
1258 // public API
1259
1496 -void replication_add_request(struct sender_state *sender, const char *chart_id, time_t after, time_t before, bool start_streaming) {
1260 +void replication_sender_request_add(struct sender_state *sender, const char *chart_id, time_t after, time_t before, bool start_streaming) {
1261 struct replication_request rq = {
1262 .sender = sender,
1263 .chart_id = string_strdupz(chart_id),
1264 .after = after,
1265 .before = before,
1266 .start_streaming = start_streaming,
1503 - .sender_circular_buffer_since_ut = stream_circular_buffer_get_since_ut(sender->scb),
1267 + .sender_circular_buffer_last_flush_ut = stream_circular_buffer_last_flush_ut(sender->scb),
1268 .indexed_in_judy = false,
1269 .not_indexed_buffer_full = false,
1270 .not_indexed_preprocessing = false,
@@ -1510,10 +1274,12 @@ void replication_add_request(struct sender_state *sender, const char *chart_id,
1274 sender->replication.oldest_request_after_t = rq.after;
1275
1276 dictionary_set(sender->replication.requests, chart_id, &rq, sizeof(struct replication_request));
1277 + __atomic_add_fetch(&replication_globals.atomic.received, 1, __ATOMIC_RELAXED);
1278 }
1279
1280 void replication_sender_delete_pending_requests(struct sender_state *sender) {
1281 // allow the dictionary destructor to go faster on locks
1282 + __atomic_add_fetch(&replication_globals.atomic.error_flushed, dictionary_entries(sender->replication.requests), __ATOMIC_RELAXED);
1283 dictionary_flush(sender->replication.requests);
1284 sender->replication.oldest_request_after_t = 0;
1285 }
@@ -1527,14 +1293,18 @@ void replication_sender_init(struct sender_state *sender) {
1293 dictionary_register_delete_callback(sender->replication.requests, replication_request_delete_callback, sender);
1294 }
1295
1530 -void replication_cleanup_sender(struct sender_state *sender) {
1296 +void replication_sender_cleanup(struct sender_state *sender) {
1297 // allow the dictionary destructor to go faster on locks
1298 replication_recursive_lock();
1299 dictionary_destroy(sender->replication.requests);
1300 replication_recursive_unlock();
1301 }
1302
1537 -void replication_recalculate_buffer_used_ratio_unsafe(struct sender_state *s) {
1303 +static void replication_replied_add(void) {
1304 + __atomic_add_fetch(&replication_globals.atomic.replied, 1, __ATOMIC_RELAXED);
1305 +}
1306 +
1307 +void replication_sender_recalculate_buffer_used_ratio_unsafe(struct sender_state *s) {
1308 size_t percentage = stream_sender_get_buffer_used_percent(s->scb);
1309
1310 if(unlikely(percentage > MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED && !stream_sender_replication_buffer_full_get(s))) {
@@ -1574,22 +1344,30 @@ void replication_recalculate_buffer_used_ratio_unsafe(struct sender_state *s) {
1344
1345 static size_t verify_host_charts_are_streaming_now(RRDHOST *host) {
1346 internal_error(
1577 - host->sender &&
1578 - !stream_sender_pending_replication_requests(host->sender) &&
1579 - dictionary_entries(host->sender->replication.requests) != 0,
1580 - "STREAM SND REPLAY SUMMARY: 'host:%s' reports %zu pending replication requests, "
1581 - "but its chart replication index says there are %zu charts pending replication",
1582 - rrdhost_hostname(host),
1347 + host->sender &&
1348 + !stream_sender_pending_replication_requests(host->sender) &&
1349 + dictionary_entries(host->sender->replication.requests) != 0,
1350 + "STREAM SND REPLAY SUMMARY: 'host:%s' reports %zu pending replication requests, "
1351 + "but its chart replication index says there are %zu charts pending replication",
1352 + rrdhost_hostname(host),
1353 stream_sender_pending_replication_requests(host->sender),
1584 - dictionary_entries(host->sender->replication.requests)
1585 - );
1354 + dictionary_entries(host->sender->replication.requests)
1355 + );
1356
1357 size_t ok = 0;
1358 size_t errors = 0;
1359 + size_t ignored = 0;
1360
1361 RRDSET *st;
1362 rrdset_foreach_read(st, host) {
1592 - RRDSET_FLAGS flags = rrdset_flag_check(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS | RRDSET_FLAG_SENDER_REPLICATION_FINISHED);
1363 + RRDSET_FLAGS flags = rrdset_flag_check(
1364 + st, RRDSET_FLAG_OBSOLETE | RRDSET_FLAG_UPSTREAM_IGNORE |
1365 + RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS | RRDSET_FLAG_SENDER_REPLICATION_FINISHED);
1366 +
1367 + if(flags & (RRDSET_FLAG_OBSOLETE | RRDSET_FLAG_UPSTREAM_IGNORE)) {
1368 + ignored++;
1369 + continue;
1370 + }
1371
1372 bool is_error = false;
1373
@@ -1619,8 +1397,10 @@ static size_t verify_host_charts_are_streaming_now(RRDHOST *host) {
1397 rrdset_foreach_done(st);
1398
1399 internal_error(errors,
1622 - "STREAM SND REPLAY SUMMARY: 'host:%s' finished replicating %zu charts, but %zu charts are still in progress although replication finished",
1623 - rrdhost_hostname(host), ok, errors);
1400 + "STREAM SND REPLAY SUMMARY: 'host:%s' finished replicating %zu charts, "
1401 + "but %zu charts are still in progress although replication finished "
1402 + "(%zu charts are not streamed - obsolete or excluded)",
1403 + rrdhost_hostname(host), ok, errors, ignored);
1404
1405 return errors;
1406 }
@@ -1628,16 +1408,65 @@ static size_t verify_host_charts_are_streaming_now(RRDHOST *host) {
1408 static void verify_all_hosts_charts_are_streaming_now(void) {
1409 worker_is_busy(WORKER_JOB_CHECK_CONSISTENCY);
1410
1631 - size_t errors = 0;
1411 + size_t charts_flagged_pending = 0, entries_in_dictionaries = 0;
1412 RRDHOST *host;
1633 - dfe_start_read(rrdhost_root_index, host)
1634 - errors += verify_host_charts_are_streaming_now(host);
1413 + dfe_start_read(rrdhost_root_index, host) {
1414 + charts_flagged_pending += verify_host_charts_are_streaming_now(host);
1415 +
1416 + if(host->sender)
1417 + entries_in_dictionaries += dictionary_entries(host->sender->replication.requests);
1418 + }
1419 dfe_done(host);
1420
1421 + size_t flushed = __atomic_load_n(&replication_globals.atomic.error_flushed, __ATOMIC_RELAXED);
1422 + size_t duplicate = __atomic_load_n(&replication_globals.atomic.error_duplicate, __ATOMIC_RELAXED);
1423 + size_t not_found = __atomic_load_n(&replication_globals.atomic.error_not_found, __ATOMIC_RELAXED);
1424 + size_t received = __atomic_load_n(&replication_globals.atomic.received, __ATOMIC_RELAXED);
1425 size_t executed = __atomic_load_n(&replication_globals.atomic.executed, __ATOMIC_RELAXED);
1638 - netdata_log_info("REPLICATION SUMMARY: finished, executed %zu replication requests, %zu charts pending replication",
1639 - executed - replication_globals.main_thread.last_executed, errors);
1426 + size_t replied = __atomic_load_n(&replication_globals.atomic.replied, __ATOMIC_RELAXED);
1427 +
1428 + CLEAN_BUFFER *wb = buffer_create(0, NULL);
1429 +
1430 + if(entries_in_dictionaries) {
1431 + if(buffer_strlen(wb)) buffer_strcat(wb, ", ");
1432 + buffer_sprintf(wb, "%zu requests pending",
1433 + entries_in_dictionaries);
1434 + }
1435 + if(charts_flagged_pending) {
1436 + if(buffer_strlen(wb)) buffer_strcat(wb, ", ");
1437 + buffer_sprintf(wb, "%zu instances waiting parent",
1438 + charts_flagged_pending);
1439 + }
1440 + if(not_found - replication_globals.main_thread.last_error_not_found) {
1441 + if(buffer_strlen(wb)) buffer_strcat(wb, ", ");
1442 + buffer_sprintf(wb, "%zu ignored-not-found",
1443 + not_found - replication_globals.main_thread.last_error_not_found);
1444 + }
1445 + if(duplicate - replication_globals.main_thread.last_error_duplicate) {
1446 + if(buffer_strlen(wb)) buffer_strcat(wb, ", ");
1447 + buffer_sprintf(wb, "%zu ignored-merged",
1448 + duplicate - replication_globals.main_thread.last_error_duplicate);
1449 + }
1450 + if(flushed - replication_globals.main_thread.last_error_flushed) {
1451 + if(buffer_strlen(wb)) buffer_strcat(wb, ", ");
1452 + buffer_sprintf(wb, "%zu were flushed",
1453 + flushed - replication_globals.main_thread.last_error_flushed);
1454 + }
1455 +
1456 + nd_log(NDLS_DAEMON, NDLP_NOTICE,
1457 + "REPLICATION SEND SUMMARY: all senders finished replication. "
1458 + "Received %zu, executed %zu and replied to %zu requests. %s",
1459 + received - replication_globals.main_thread.last_received,
1460 + executed - replication_globals.main_thread.last_executed,
1461 + replied - replication_globals.main_thread.last_replied,
1462 + buffer_tostring(wb));
1463 +
1464 + replication_globals.main_thread.last_error_flushed = flushed;
1465 + replication_globals.main_thread.last_error_duplicate = duplicate;
1466 + replication_globals.main_thread.last_error_not_found = not_found;
1467 + replication_globals.main_thread.last_received = received;
1468 replication_globals.main_thread.last_executed = executed;
1469 + replication_globals.main_thread.last_replied = replied;
1470 }
1471
1472 static void replication_initialize_workers(bool master) {
@@ -1722,7 +1551,18 @@ static int replication_pipeline_execute_next(void) {
1551 struct replication_request *rq;
1552
1553 if(unlikely(!rtp.rqs)) {
1554 +#if REQUESTS_AHEAD_PER_THREAD == 0
1555 + rtp.max_requests_ahead = (int)netdata_conf_cpus() / 2;
1556 +
1557 + if (rtp.max_requests_ahead > libuv_worker_threads * 2)
1558 + rtp.max_requests_ahead = libuv_worker_threads * 2;
1559 +
1560 + if (rtp.max_requests_ahead < 5)
1561 + rtp.max_requests_ahead = 5;
1562 +#else
1563 rtp.max_requests_ahead = REQUESTS_AHEAD_PER_THREAD;
1564 +#endif
1565 +
1566 rtp.rqs = callocz(rtp.max_requests_ahead, sizeof(struct replication_request));
1567 __atomic_add_fetch(&replication_buffers_allocated, rtp.max_requests_ahead * sizeof(struct replication_request), __ATOMIC_RELAXED);
1568 }
@@ -1742,20 +1582,22 @@ static int replication_pipeline_execute_next(void) {
1582 rq = &rtp.rqs[rtp.rqs_last_prepared];
1583
1584 if(rq->found) {
1745 - if (!rq->st) {
1746 - worker_is_busy(WORKER_JOB_FIND_CHART);
1747 - rq->st = rrdset_find(rq->sender->host, string2str(rq->chart_id));
1748 - }
1585 + if(!rq->start_streaming) {
1586 + if (!rq->st) {
1587 + worker_is_busy(WORKER_JOB_FIND_CHART);
1588 + rq->st = rrdset_find(rq->sender->host, string2str(rq->chart_id));
1589 + }
1590
1750 - if (rq->st && !rq->q) {
1751 - worker_is_busy(WORKER_JOB_PREPARE_QUERY);
1752 - rq->q = replication_response_prepare(
1753 - rq->st,
1754 - rq->start_streaming,
1755 - rq->after,
1756 - rq->before,
1757 - rq->sender->capabilities,
1758 - rtp.max_requests_ahead == 1);
1591 + if (rq->st && !rq->q) {
1592 + worker_is_busy(WORKER_JOB_PREPARE_QUERY);
1593 + rq->q = replication_response_prepare(
1594 + rq->st,
1595 + rq->start_streaming,
1596 + rq->after,
1597 + rq->before,
1598 + rq->sender->capabilities,
1599 + rtp.max_requests_ahead == 1);
1600 + }
1601 }
1602
1603 rq->executed = false;
@@ -1773,7 +1615,7 @@ static int replication_pipeline_execute_next(void) {
1615 if(rq->found) {
1616 internal_fatal(rq->executed, "REPLAY FATAL: query has already been executed!");
1617
1776 - if (rq->sender_circular_buffer_since_ut != stream_circular_buffer_get_since_ut(rq->sender->scb)) {
1618 + if (rq->sender_circular_buffer_last_flush_ut != stream_circular_buffer_last_flush_ut(rq->sender->scb)) {
1619 // the sender has reconnected since this request was queued,
1620 // we can safely throw it away, since the parent will resend it
1621 replication_response_cancel_and_finalize(rq->q);
@@ -1842,7 +1684,7 @@ static void *replication_worker_thread(void *ptr __maybe_unused) {
1684 }
1685
1686 if (unlikely(replication_pipeline_execute_next() == REQUEST_QUEUE_EMPTY)) {
1845 - sender_commit_thread_buffer_free();
1687 + sender_thread_buffer_free();
1688 worker_is_busy(WORKER_JOB_WAIT);
1689 worker_is_idle();
1690 sleep_usec(1 * USEC_PER_SEC);
@@ -1889,7 +1731,7 @@ void *replication_thread_main(void *ptr) {
1731
1732 replication_initialize_workers(true);
1733
1892 - size_t threads = stream_conf_is_parent(false) ? (netdata_conf_cpus() / 2) : 1;
1734 + size_t threads = netdata_conf_is_parent() ? (netdata_conf_cpus() / 3) : 1;
1735 if (threads < 1) threads = 1;
1736 else if (threads > MAX_REPLICATION_THREADS) threads = MAX_REPLICATION_THREADS;
1737
@@ -1941,7 +1783,7 @@ void *replication_thread_main(void *ptr) {
1783
1784 // statistics
1785 usec_t now_mono_ut = now_monotonic_usec();
1944 - if(unlikely(now_mono_ut - last_now_mono_ut > default_rrd_update_every * USEC_PER_SEC)) {
1786 + if(unlikely(now_mono_ut - last_now_mono_ut > nd_profile.update_every * USEC_PER_SEC)) {
1787 last_now_mono_ut = now_mono_ut;
1788
1789 worker_is_busy(WORKER_JOB_STATISTICS);
@@ -2009,7 +1851,7 @@ void *replication_thread_main(void *ptr) {
1851 if(slow) {
1852 // no work to be done, wait for a request to come in
1853 timeout = 1000 * USEC_PER_MS;
2012 - sender_commit_thread_buffer_free();
1854 + sender_thread_buffer_free();
1855 }
1856
1857 else if(replication_globals.unsafe.pending > 0) {
src/streaming/stream-replication-sender.h new
+38
@@ -0,0 +1,38 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef REPLICATION_H
4 +#define REPLICATION_H
5 +
6 +#include "daemon/common.h"
7 +#include "stream-circular-buffer.h"
8 +
9 +#ifdef __cplusplus
10 +extern "C" {
11 +#endif
12 +
13 +struct parser;
14 +
15 +struct replication_query_statistics {
16 + SPINLOCK spinlock;
17 + size_t queries_started;
18 + size_t queries_finished;
19 + size_t points_read;
20 + size_t points_generated;
21 +};
22 +
23 +struct replication_query_statistics replication_get_query_statistics(void);
24 +
25 +void replication_sender_init(struct sender_state *sender);
26 +void replication_sender_cleanup(struct sender_state *sender);
27 +void replication_sender_delete_pending_requests(struct sender_state *sender);
28 +void replication_sender_request_add(struct sender_state *sender, const char *chart_id, time_t after, time_t before, bool start_streaming);
29 +void replication_sender_recalculate_buffer_used_ratio_unsafe(struct sender_state *s);
30 +
31 +size_t replication_sender_allocated_memory(void);
32 +size_t replication_sender_allocated_buffers(void);
33 +
34 +#ifdef __cplusplus
35 +}
36 +#endif
37 +
38 +#endif /* REPLICATION_H */
src/streaming/stream-replication-tracking.c new
+58
@@ -0,0 +1,58 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "stream-replication-tracking.h"
4 +#include "database/rrd.h"
5 +#include "stream-receiver-internals.h"
6 +
7 +#ifdef REPLICATION_TRACKING
8 +
9 +void replication_tracking_counters(struct rrdhost *host, struct replay_who_counters *c) {
10 + if(!rrdhost_flag_check(host, RRDHOST_FLAG_COLLECTOR_ONLINE))
11 + return;
12 +
13 + bool is_host_local = host == localhost || rrdhost_option_check(host, RRDHOST_OPTION_VIRTUAL_HOST);
14 + bool is_host_sending = rrdhost_flag_check(host, RRDHOST_FLAG_STREAM_SENDER_READY_4_METRICS);
15 +
16 + RRDSET *st;
17 + rrdset_foreach_read(st, host) {
18 + RRDSET_FLAGS st_flags = rrdset_flag_get(st);
19 +
20 + if(st_flags & RRDSET_FLAG_OBSOLETE)
21 + continue;
22 +
23 + if(!is_host_local && !(st_flags & RRDSET_FLAG_ANOMALY_DETECTION)) {
24 + REPLAY_WHO rcv = st->stream.rcv.who;
25 + if (rcv <= 0 || rcv >= REPLAY_WHO_MAX)
26 + rcv = REPLAY_WHO_UNKNOWN;
27 + c->rcv[rcv]++;
28 +
29 +#ifdef NETDATA_LOG_STREAM_RECEIVER
30 + if(rcv == REPLAY_WHO_ME || rcv == REPLAY_WHO_THEM) {
31 + char buf[1024];
32 + snprintfz(buf, sizeof(buf), "### REPLICATION RECEIVE waits on %s for chart '%s'\n",
33 + rcv == REPLAY_WHO_ME ? "me" : "them", rrdset_id(st));
34 + stream_receiver_log_payload(host->receiver, buf, STREAM_TRAFFIC_TYPE_METADATA, rcv == REPLAY_WHO_THEM);
35 + }
36 +#endif
37 + }
38 +
39 + if(is_host_sending && (st_flags & RRDSET_FLAG_UPSTREAM_SEND) && !(st_flags & RRDSET_FLAG_UPSTREAM_IGNORE)) {
40 + REPLAY_WHO snd = st->stream.snd.who;
41 + if (snd <= 0 || snd >= REPLAY_WHO_MAX)
42 + snd = REPLAY_WHO_UNKNOWN;
43 + c->snd[snd]++;
44 +
45 +#ifdef NETDATA_LOG_STREAM_SENDER
46 + if(snd == REPLAY_WHO_ME || snd == REPLAY_WHO_THEM) {
47 + char buf[1024];
48 + snprintfz(buf, sizeof(buf), "### REPLICATION SEND waits on %s for chart '%s'\n",
49 + snd == REPLAY_WHO_ME ? "me" : "them", rrdset_id(st));
50 + stream_receiver_log_payload(host->receiver, buf, STREAM_TRAFFIC_TYPE_METADATA, snd == REPLAY_WHO_THEM);
51 + }
52 +#endif
53 + }
54 + }
55 + rrdset_foreach_done(st);
56 +}
57 +
58 +#endif
src/streaming/stream-replication-tracking.h new
+32
@@ -0,0 +1,32 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_STREAM_REPLICATION_TRACKING_H
4 +#define NETDATA_STREAM_REPLICATION_TRACKING_H
5 +
6 +#include "libnetdata/libnetdata.h"
7 +
8 +// #define REPLICATION_TRACKING 1
9 +
10 +#ifdef REPLICATION_TRACKING
11 +
12 +typedef enum __attribute__((packed)) {
13 + REPLAY_WHO_UNKNOWN = 0, // default value
14 + REPLAY_WHO_ME, // I have to respond
15 + REPLAY_WHO_THEM, // they have to respond
16 + REPLAY_WHO_FINISHED, // replication finished
17 +
18 + // terminator
19 + REPLAY_WHO_MAX,
20 +} REPLAY_WHO;
21 +
22 +struct replay_who_counters {
23 + size_t rcv[REPLAY_WHO_MAX];
24 + size_t snd[REPLAY_WHO_MAX];
25 +};
26 +
27 +struct rrdhost;
28 +void replication_tracking_counters(struct rrdhost *host, struct replay_who_counters *c);
29 +
30 +#endif
31 +
32 +#endif //NETDATA_STREAM_REPLICATION_TRACKING_H
src/streaming/stream-sender-api.c
+5 -2
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "stream-sender-internals.h"
4 -#include "replication.h"
4 +#include "stream-replication-sender.h"
5
6 bool stream_sender_has_capabilities(struct rrdhost *host, STREAM_CAPABILITIES capabilities) {
7 return host && stream_has_capability(host->sender, capabilities);
@@ -34,6 +34,7 @@ void stream_sender_structures_init(RRDHOST *host, bool stream, STRING *parents,
34 host->sender->connector.id = -1;
35 host->sender->host = host;
36 host->sender->scb = stream_circular_buffer_create();
37 + host->sender->wait_queue = waiting_queue_create();
38 host->sender->capabilities = stream_our_capabilities(host, true);
39
40 nd_sock_init(&host->sender->sock, netdata_ssl_streaming_sender_ctx, netdata_ssl_validate_certificate_sender);
@@ -64,9 +65,11 @@ void stream_sender_structures_free(struct rrdhost *host) {
65 stream_sender_signal_to_stop_and_wait(host, STREAM_HANDSHAKE_DISCONNECT_HOST_CLEANUP, true);
66 stream_circular_buffer_destroy(host->sender->scb);
67 host->sender->scb = NULL;
68 + waiting_queue_destroy(host->sender->wait_queue);
69 + host->sender->wait_queue = NULL;
70 stream_compressor_destroy(&host->sender->compressor);
71
69 - replication_cleanup_sender(host->sender);
72 + replication_sender_cleanup(host->sender);
73
74 __atomic_sub_fetch(&netdata_buffers_statistics.rrdhost_senders, sizeof(*host->sender), __ATOMIC_RELAXED);
75
src/streaming/stream-sender-commit.c
+29 -18
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "stream-thread.h"
4 -#include "replication.h"
4 +#include "stream-replication-sender.h"
5
6 static __thread struct sender_buffer commit___thread = { 0 };
7
@@ -14,7 +14,7 @@ void sender_buffer_destroy(struct sender_buffer *commit) {
14 commit->last_function = NULL;
15 }
16
17 -void sender_commit_thread_buffer_free(void) {
17 +void sender_thread_buffer_free(void) {
18 sender_buffer_destroy(&commit___thread);
19 }
20
@@ -23,14 +23,14 @@ BUFFER *sender_commit_start_with_trace(struct sender_state *s __maybe_unused, st
23 if(unlikely(commit->used))
24 fatal("STREAM SND '%s' [to %s]: thread buffer is used multiple times concurrently (%u). "
25 "It is already being used by '%s()', and now is called by '%s()'",
26 - rrdhost_hostname(s->host), s->connected_to,
26 + rrdhost_hostname(s->host), s->remote_ip,
27 (unsigned)commit->used,
28 commit->last_function ? commit->last_function : "(null)",
29 func ? func : "(null)");
30
31 if(unlikely(commit->receiver_tid && commit->receiver_tid != gettid_cached()))
32 fatal("STREAM SND '%s' [to %s]: thread buffer is reserved for tid %d, but it used by thread %d function '%s()'.",
33 - rrdhost_hostname(s->host), s->connected_to,
33 + rrdhost_hostname(s->host), s->remote_ip,
34 commit->receiver_tid, gettid_cached(), func ? func : "(null)");
35
36 if(unlikely(commit->wb &&
@@ -68,6 +68,9 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
68 if (unlikely(!src || !src_len))
69 return;
70
71 + waiting_queue_wait(s->wait_queue, (s->host->stream.rcv.status.tid == gettid_cached()
72 + || s->host->stream.snd.status.tid == gettid_cached()) ?
73 + WAITING_QUEUE_PRIO_HIGH : WAITING_QUEUE_PRIO_NORMAL);
74 stream_sender_lock(s);
75
76 // copy the sequence number of sender buffer recreates, while having our lock
@@ -77,9 +80,12 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
80
81 if (!s->thread.msg.session) {
82 // the dispatcher is not there anymore - ignore these data
80 - stream_sender_unlock(s);
83 +
84 if(commit)
85 sender_buffer_destroy(commit);
86 +
87 + stream_sender_unlock(s);
88 + waiting_queue_done(s->wait_queue);
89 return;
90 }
91
@@ -88,11 +94,14 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
94 // adaptive sizing of the circular buffer
95 nd_log(NDLS_DAEMON, NDLP_NOTICE,
96 "STREAM SND '%s' [to %s]: Increased max buffer size to %u (message size %zu).",
91 - rrdhost_hostname(s->host), s->connected_to, stats->bytes_max_size, src_len + 1);
97 + rrdhost_hostname(s->host), s->remote_ip, stats->bytes_max_size, src_len + 1);
98 }
99
100 stream_sender_log_payload(s, wb, type, false);
101
102 + // if there are data already in the buffer, we don't need to send an opcode
103 + bool enable_sending = stats->bytes_outstanding == 0;
104 +
105 if (s->compressor.initialized) {
106 // compressed traffic
107 if(rrdhost_is_this_a_stream_thread(s->host))
@@ -127,7 +136,7 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
136 if (!dst_len) {
137 nd_log(NDLS_DAEMON, NDLP_ERR,
138 "STREAM SND '%s' [to %s]: COMPRESSION failed. Resetting compressor and re-trying",
130 - rrdhost_hostname(s->host), s->connected_to);
139 + rrdhost_hostname(s->host), s->remote_ip);
140
141 stream_compression_initialize(s);
142 dst_len = stream_compress(&s->compressor, src, size_to_compress, &dst);
@@ -144,7 +153,7 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
153 fatal(
154 "STREAM SND '%s' [to %s]: invalid signature, original payload %zu bytes, "
155 "compressed payload length %zu bytes, but signature says payload is %zu bytes",
147 - rrdhost_hostname(s->host), s->connected_to,
156 + rrdhost_hostname(s->host), s->remote_ip,
157 size_to_compress, dst_len, decoded_dst_len);
158 #endif
159
@@ -166,13 +175,13 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
175 goto overflow_with_lock;
176 }
177
169 - bool enable_sending = stats->bytes_outstanding == 0;
170 - replication_recalculate_buffer_used_ratio_unsafe(s);
178 + replication_sender_recalculate_buffer_used_ratio_unsafe(s);
179
180 if (enable_sending)
181 msg = s->thread.msg;
182
183 stream_sender_unlock(s);
184 + waiting_queue_done(s->wait_queue);
185
186 if (enable_sending) {
187 msg.opcode = STREAM_OPCODE_SENDER_POLLOUT;
@@ -184,14 +193,15 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
193 overflow_with_lock: {
194 msg = s->thread.msg;
195 stream_sender_unlock(s);
196 + waiting_queue_done(s->wait_queue);
197 msg.opcode = STREAM_OPCODE_SENDER_BUFFER_OVERFLOW;
198 stream_sender_send_opcode(s, msg);
199 nd_log_limit_static_global_var(erl, 1, 0);
200 nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
191 - "STREAM SND '%s' [to %s]: buffer overflow (buffer size %u, max size %u, used %u, available %u). "
201 + "STREAM SND '%s' [to %s]: buffer overflow (buffer size %u, max size %u, available %u). "
202 "Restarting connection.",
193 - rrdhost_hostname(s->host), s->connected_to,
194 - stats->bytes_size, stats->bytes_max_size, stats->bytes_outstanding, stats->bytes_available);
203 + rrdhost_hostname(s->host), s->remote_ip,
204 + stats->bytes_size, stats->bytes_max_size, stats->bytes_available);
205 return;
206 }
207
@@ -199,26 +209,27 @@ compression_failed_with_lock: {
209 stream_compression_deactivate(s);
210 msg = s->thread.msg;
211 stream_sender_unlock(s);
212 + waiting_queue_done(s->wait_queue);
213 msg.opcode = STREAM_OPCODE_SENDER_RECONNECT_WITHOUT_COMPRESSION;
214 stream_sender_send_opcode(s, msg);
215 nd_log_limit_static_global_var(erl, 1, 0);
216 nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
217 "STREAM SND '%s' [to %s]: COMPRESSION failed (twice). "
218 "Deactivating compression and restarting connection.",
208 - rrdhost_hostname(s->host), s->connected_to);
219 + rrdhost_hostname(s->host), s->remote_ip);
220 }
221 }
222
212 -void sender_thread_commit(struct sender_state *s, BUFFER *wb, STREAM_TRAFFIC_TYPE type, const char *func) {
213 - struct sender_buffer *commit = (wb == commit___thread.wb) ? & commit___thread : &s->host->stream.snd.commit;
223 +void sender_thread_commit_with_trace(struct sender_state *s, BUFFER *wb, STREAM_TRAFFIC_TYPE type, const char *func) {
224 + struct sender_buffer *commit = (wb == commit___thread.wb) ? &commit___thread : &s->host->stream.snd.commit;
225
226 if (unlikely(wb != commit->wb))
227 fatal("STREAM SND '%s' [to %s]: function '%s()' is trying to commit an unknown commit buffer.",
217 - rrdhost_hostname(s->host), s->connected_to, func);
228 + rrdhost_hostname(s->host), s->remote_ip, func);
229
230 if (unlikely(!commit->used))
231 fatal("STREAM SND '%s' [to %s]: function '%s()' is committing a sender buffer twice.",
221 - rrdhost_hostname(s->host), s->connected_to, func);
232 + rrdhost_hostname(s->host), s->remote_ip, func);
233
234 commit->used = false;
235 commit->last_function = NULL;
src/streaming/stream-sender-commit.h
+11 -4
@@ -22,18 +22,25 @@ void sender_buffer_destroy(struct sender_buffer *commit);
22
23 // thread buffer for sending data upstream (to a parent)
24
25 +void sender_thread_buffer_free(void);
26 BUFFER *sender_thread_buffer_with_trace(struct sender_state *s, const char *func);
27 #define sender_thread_buffer(s) sender_thread_buffer_with_trace(s, __FUNCTION__)
28
29 +// commit the global host buffer
30 +// this is the preferred buffer for stream threads (unified receiver / sender threads)
31 +// these threads require a buffer that can remain intact while switching hosts
32 BUFFER *sender_host_buffer_with_trace(struct rrdhost *host, const char *func);
33 #define sender_host_buffer(host) sender_host_buffer_with_trace(host, __FUNCTION__)
34
31 -void sender_thread_commit(struct sender_state *s, BUFFER *wb, STREAM_TRAFFIC_TYPE type, const char *func);
32 -#define sender_commit(s, wb, type) sender_thread_commit(s, wb, type, __FUNCTION__)
35 +// commit a buffer acquired with sender_thread_buffer()
36 +// this is the preferred buffer for dedicated workers sending a lot of messages (like replication)
37 +// these threads need to maintain enough allocation for repeated use of the buffer
38 +void sender_thread_commit_with_trace(struct sender_state *s, BUFFER *wb, STREAM_TRAFFIC_TYPE type, const char *func);
39 +#define sender_commit(s, wb, type) sender_thread_commit_with_trace(s, wb, type, __FUNCTION__)
40
41 +// commit any buffer
42 +// this is the preferred buffer for occasional senders, as it avoids constant buffer allocations
43 void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buffer *commit, STREAM_TRAFFIC_TYPE type);
44 #define sender_commit_clean_buffer(s, wb, type) sender_buffer_commit(s, wb, NULL, type)
45
37 -void sender_commit_thread_buffer_free(void);
38 -
46 #endif //NETDATA_STREAM_SENDER_COMMIT_H
src/streaming/stream-sender-execute.c
+17 -12
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "stream-thread.h"
4 -#include "replication.h"
4 +#include "stream-replication-sender.h"
5
6 struct inflight_stream_function {
7 struct sender_state *sender;
@@ -27,7 +27,7 @@ static void stream_execute_function_callback(BUFFER *func_wb, int code, void *da
27 sender_commit_clean_buffer(s, wb, STREAM_TRAFFIC_TYPE_FUNCTIONS);
28
29 internal_error(true, "STREAM SND '%s' [to %s]: FUNCTION transaction %s sending back response (%zu bytes, %"PRIu64" usec).",
30 - rrdhost_hostname(s->host), s->connected_to,
30 + rrdhost_hostname(s->host), s->remote_ip,
31 string2str(tmp->transaction),
32 buffer_strlen(func_wb),
33 now_realtime_usec() - tmp->received_ut);
@@ -58,7 +58,7 @@ static void execute_commands_function(struct sender_state *s, const char *comman
58
59 if(!transaction || !*transaction || !timeout_s || !*timeout_s || !function || !*function) {
60 netdata_log_error("STREAM SND '%s' [to %s]: %s execution command is incomplete (transaction = '%s', timeout = '%s', function = '%s'). Ignoring it.",
61 - rrdhost_hostname(s->host), s->connected_to,
61 + rrdhost_hostname(s->host), s->remote_ip,
62 command,
63 transaction?transaction:"(unset)",
64 timeout_s?timeout_s:"(unset)",
@@ -112,7 +112,7 @@ static void execute_deferred_json(struct sender_state *s, void *data) {
112 else
113 nd_log(NDLS_DAEMON, NDLP_ERR,
114 "STREAM SND '%s' [to %s]: unknown JSON keyword '%s' with payload: %s",
115 - rrdhost_hostname(s->host), s->connected_to,
115 + rrdhost_hostname(s->host), s->remote_ip,
116 keyword, buffer_tostring(s->defer.payload));
117 }
118
@@ -268,6 +268,8 @@ void stream_sender_execute_commands(struct sender_state *s) {
268 stream_sender_log_payload(s, s->log.received, STREAM_TRAFFIC_TYPE_REPLICATION, true);
269 #endif
270
271 + __atomic_add_fetch(&s->host->stream.snd.status.replication.counter_in, 1, __ATOMIC_RELAXED);
272 +
273 // do not log replication commands received - way too many!
274 // nd_log(NDLS_ACCESS, NDLP_DEBUG, NULL);
275
@@ -277,9 +279,9 @@ void stream_sender_execute_commands(struct sender_state *s) {
279 const char *before = get_word(s->rbuf.line.words, s->rbuf.line.num_words, 4);
280
281 if (!chart_id || !start_streaming || !after || !before) {
280 - netdata_log_error("STREAM SND '%s' [to %s] %s command is incomplete"
282 + netdata_log_error("STREAM REPLAY ERROR '%s' [send to %s] %s command is incomplete"
283 " (chart=%s, start_streaming=%s, after=%s, before=%s)",
282 - rrdhost_hostname(s->host), s->connected_to,
284 + rrdhost_hostname(s->host), s->remote_ip,
285 command,
286 chart_id ? chart_id : "(unset)",
287 start_streaming ? start_streaming : "(unset)",
@@ -287,11 +289,14 @@ void stream_sender_execute_commands(struct sender_state *s) {
289 before ? before : "(unset)");
290 }
291 else {
290 - replication_add_request(s, chart_id,
291 - strtoll(after, NULL, 0),
292 - strtoll(before, NULL, 0),
293 - !strcmp(start_streaming, "true")
294 - );
292 +#ifdef REPLICATION_TRACKING
293 + RRDSET *st = rrdset_find(s->host, chart_id);
294 + if(st)
295 + st->stream.snd.who = REPLAY_WHO_ME;
296 +#endif
297 +
298 + replication_sender_request_add(
299 + s, chart_id, strtoll(after, NULL, 0), strtoll(before, NULL, 0), !strcmp(start_streaming, "true"));
300 }
301 }
302 else if(command && strcmp(command, PLUGINSD_KEYWORD_NODE_ID) == 0) {
@@ -314,7 +319,7 @@ void stream_sender_execute_commands(struct sender_state *s) {
319 }
320 else {
321 netdata_log_error("STREAM SND '%s' [to %s] received unknown command over connection: %s",
317 - rrdhost_hostname(s->host), s->connected_to, s->rbuf.line.words[0]?s->rbuf.line.words[0]:"(unset)");
322 + rrdhost_hostname(s->host), s->remote_ip, s->rbuf.line.words[0]?s->rbuf.line.words[0]:"(unset)");
323 }
324
325 line_splitter_reset(&s->rbuf.line);
src/streaming/stream-sender-internals.h
+8 -4
@@ -40,8 +40,6 @@ struct sender_state {
40 ND_SOCK sock;
41
42 struct {
43 - bool draining_input; // used exclusively by the stream thread
44 -
43 struct stream_opcode msg; // the template for sending a message to the dispatcher - protected by sender_lock()
44
45 // this is a property of stream_sender_send_msg_to_dispatcher()
@@ -49,8 +47,8 @@ struct sender_state {
47 // DO NOT READ OR WRITE ANYWHERE
48 uint32_t msg_slot; // ensures a opcode queue that can never get full
49
50 + nd_poll_event_t wanted;
51 usec_t last_traffic_ut;
53 -
52 struct pollfd_meta meta;
53 } thread;
54
@@ -58,9 +56,10 @@ struct sender_state {
56 int8_t id; // the connector id - protected by sender_lock()
57 } connector;
58
61 - char connected_to[CONNECTED_TO_SIZE + 1]; // We don't know which proxy we connect to, passed back from socket.c
59 + char remote_ip[CONNECTED_TO_SIZE + 1]; // We don't know which proxy we connect to, passed back from socket.c
60 time_t last_state_since_t; // the timestamp of the last state (online/offline) change
61
62 + WAITING_QUEUE *wait_queue;
63 STREAM_CIRCULAR_BUFFER *scb;
64
65 struct {
@@ -74,6 +73,7 @@ struct sender_state {
73 #ifdef NETDATA_LOG_STREAM_SENDER
74 struct {
75 SPINLOCK spinlock;
76 + struct timespec first_call;
77 BUFFER *received;
78 FILE *fp;
79 } log;
@@ -85,6 +85,10 @@ struct sender_state {
85 } exit;
86
87 struct {
88 + uint32_t last_counter_in; // copy from the host, to detect progress
89 + uint32_t last_counter_out; // copy from the host, to detect progress
90 + usec_t last_progress_ut; // last time we found some progress (monotonic)
91 +
92 DICTIONARY *requests; // de-duplication of replication requests, per chart
93 time_t oldest_request_after_t; // the timestamp of the oldest replication request
94 time_t latest_completed_before_t; // the timestamp of the latest replication request
src/streaming/stream-sender.c
+363 -173
@@ -2,7 +2,7 @@
2
3 #include "stream-thread.h"
4 #include "stream-sender-internals.h"
5 -#include "replication.h"
5 +#include "stream-replication-sender.h"
6
7 static void stream_sender_move_running_to_connector_or_remove(struct stream_thread *sth, struct sender_state *s, STREAM_HANDSHAKE reason, bool reconnect);
8
@@ -18,25 +18,50 @@ void stream_sender_log_payload(struct sender_state *s, BUFFER *payload, STREAM_T
18 filename, FILENAME_MAX, "/tmp/stream-sender-%s.txt", s->host ? rrdhost_hostname(s->host) : "unknown");
19
20 s->log.fp = fopen(filename, "w");
21 - }
21
23 - if(inbound) {
24 - fprintf(
25 - s->log.fp,
26 - "\n--- RECEIVE MESSAGE START: %s => %s ----\n"
27 - "%s"
28 - "--- RECEIVE MESSAGE END ----------------------------------------\n",
29 - s->connected_to, rrdhost_hostname(s->host), buffer_tostring(payload));
22 + // Align first_call to wall clock time
23 + clock_gettime(CLOCK_REALTIME, &s->log.first_call);
24 + s->log.first_call.tv_nsec = 0; // Align to the start of the second
25 }
31 - else {
32 - fprintf(
33 - s->log.fp,
34 - "\n--- SEND MESSAGE START: %s => %s ----\n"
35 - "%s"
36 - "--- SEND MESSAGE END ----------------------------------------\n",
37 - rrdhost_hostname(s->host), s->connected_to, buffer_tostring(payload));
26 +
27 + if (s->log.fp) {
28 + struct timespec now;
29 + clock_gettime(CLOCK_REALTIME, &now);
30 +
31 + time_t elapsed_sec = now.tv_sec - s->log.first_call.tv_sec;
32 + long elapsed_nsec = now.tv_nsec - s->log.first_call.tv_nsec;
33 +
34 + if (elapsed_nsec < 0) {
35 + elapsed_sec--;
36 + elapsed_nsec += 1000000000;
37 + }
38 +
39 + uint16_t days = elapsed_sec / 86400;
40 + uint8_t hours = (elapsed_sec % 86400) / 3600;
41 + uint8_t minutes = (elapsed_sec % 3600) / 60;
42 + uint8_t seconds = elapsed_sec % 60;
43 + uint16_t milliseconds = elapsed_nsec / 1000000;
44 +
45 + char prefix[30];
46 + snprintf(prefix, sizeof(prefix), "%03ud.%02u:%02u:%02u.%03u ",
47 + days, hours, minutes, seconds, milliseconds);
48 +
49 + const char *line_start = buffer_tostring(payload);
50 + const char *line_end;
51 +
52 + while (line_start && *line_start) {
53 + line_end = strchr(line_start, '\n');
54 + if (line_end) {
55 + fprintf(s->log.fp, "%s%s%.*s\n", prefix, inbound ? "> " : "< ", (int)(line_end - line_start), line_start);
56 + line_start = line_end + 1;
57 + } else {
58 + fprintf(s->log.fp, "%s%s%s\n", prefix, inbound ? "> " : "< ", line_start);
59 + break;
60 + }
61 + }
62 }
63
64 + // fflush(s->log.fp);
65 spinlock_unlock(&s->log.spinlock);
66 }
67 #endif
@@ -50,8 +75,13 @@ static void stream_sender_charts_and_replication_reset(struct sender_state *s) {
75 // reset the state of all charts
76 RRDSET *st;
77 rrdset_foreach_read(st, s->host) {
53 - rrdset_flag_clear(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS);
54 - rrdset_flag_set(st, RRDSET_FLAG_SENDER_REPLICATION_FINISHED);
78 + RRDSET_FLAGS old = rrdset_flag_set_and_clear(st, RRDSET_FLAG_SENDER_REPLICATION_FINISHED, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS);
79 + if(!(old & RRDSET_FLAG_SENDER_REPLICATION_FINISHED))
80 + rrdhost_sender_replicating_charts_minus_one(st->rrdhost);
81 +
82 +#ifdef REPLICATION_TRACKING
83 + st->stream.snd.who = REPLAY_WHO_UNKNOWN;
84 +#endif
85
86 st->stream.snd.resync_time_s = 0;
87
@@ -64,8 +94,19 @@ static void stream_sender_charts_and_replication_reset(struct sender_state *s) {
94 }
95 rrdset_foreach_done(st);
96
67 - rrdhost_sender_replicating_charts_zero(s->host);
97 + if(rrdhost_sender_replicating_charts(s->host) != 0) {
98 + nd_log(NDLS_DAEMON, NDLP_WARNING,
99 + "STREAM REPLAY ERROR: sender replicating instances counter should be zero, but it is %u"
100 + " - resetting it to zero",
101 + rrdhost_sender_replicating_charts(s->host));
102 +
103 + rrdhost_sender_replicating_charts_zero(s->host);
104 + }
105 +
106 stream_sender_replicating_charts_zero(s);
107 +
108 + __atomic_store_n(&s->host->stream.snd.status.replication.counter_in, 0, __ATOMIC_RELAXED);
109 + __atomic_store_n(&s->host->stream.snd.status.replication.counter_out, 0, __ATOMIC_RELAXED);
110 }
111
112 // --------------------------------------------------------------------------------------------------------------------
@@ -169,7 +210,6 @@ void stream_sender_handle_op(struct stream_thread *sth, struct sender_state *s,
210 ND_LOG_FIELD_CB(NDF_DST_PORT, stream_sender_log_dst_port, s),
211 ND_LOG_FIELD_CB(NDF_DST_TRANSPORT, stream_sender_log_transport, s),
212 ND_LOG_FIELD_CB(NDF_DST_CAPABILITIES, stream_sender_log_capabilities, s),
172 - ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_to_parent_msgid),
213 ND_LOG_FIELD_END(),
214 };
215 ND_LOG_STACK_PUSH(lgs);
@@ -184,7 +224,7 @@ void stream_sender_handle_op(struct stream_thread *sth, struct sender_state *s,
224 nd_log(NDLS_DAEMON, NDLP_ERR,
225 "STREAM SND[%zu] '%s' [to %s]: send buffer is full (buffer size %u, max %u, used %u, available %u). "
226 "Restarting connection.",
187 - sth->id, rrdhost_hostname(s->host), s->connected_to,
227 + sth->id, rrdhost_hostname(s->host), s->remote_ip,
228 stats.bytes_size, stats.bytes_max_size, stats.bytes_outstanding, stats.bytes_available);
229
230 stream_sender_move_running_to_connector_or_remove(
@@ -204,7 +244,7 @@ void stream_sender_handle_op(struct stream_thread *sth, struct sender_state *s,
244 errno_clear();
245 nd_log(NDLS_DAEMON, NDLP_ERR,
246 "STREAM SND[%zu] '%s' [to %s]: restarting connection without compression.",
207 - sth->id, rrdhost_hostname(s->host), s->connected_to);
247 + sth->id, rrdhost_hostname(s->host), s->remote_ip);
248
249 stream_sender_move_running_to_connector_or_remove(
250 sth, s, STREAM_HANDSHAKE_DISCONNECT_NOT_SUFFICIENT_SENDER_COMPRESSION_FAILED, true);
@@ -239,14 +279,27 @@ void stream_sender_move_queue_to_running_unsafe(struct stream_thread *sth) {
279
280 ND_LOG_STACK lgs[] = {
281 ND_LOG_FIELD_STR(NDF_NIDL_NODE, s->host->hostname),
242 - ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_to_parent_msgid),
282 + ND_LOG_FIELD_CB(NDF_DST_IP, stream_sender_log_dst_ip, s),
283 + ND_LOG_FIELD_CB(NDF_DST_PORT, stream_sender_log_dst_port, s),
284 + ND_LOG_FIELD_CB(NDF_DST_TRANSPORT, stream_sender_log_transport, s),
285 + ND_LOG_FIELD_CB(NDF_DST_CAPABILITIES, stream_sender_log_capabilities, s),
286 ND_LOG_FIELD_END(),
287 };
288 ND_LOG_STACK_PUSH(lgs);
289
290 nd_log(NDLS_DAEMON, NDLP_DEBUG,
291 "STREAM SND[%zu] '%s' [to %s]: moving host from dispatcher queue to dispatcher running...",
249 - sth->id, rrdhost_hostname(s->host), s->connected_to);
292 + sth->id, rrdhost_hostname(s->host), s->remote_ip);
293 +
294 + if(sock_setnonblock(s->sock.fd, true) != 1)
295 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
296 + "STREAM SND[%zu] '%s' [to %s]: failed to set non-blocking mode on socket %d",
297 + sth->id, rrdhost_hostname(s->host), s->remote_ip, s->sock.fd);
298 +
299 + sock_setcloexec(s->sock.fd, true);
300 + sock_enlarge_rcv_buf(s->sock.fd);
301 + sock_enlarge_snd_buf(s->sock.fd);
302 + sock_setcork(s->sock.fd, false);
303
304 stream_sender_lock(s);
305 s->thread.meta.type = POLLFD_TYPE_SENDER;
@@ -260,17 +313,20 @@ void stream_sender_move_queue_to_running_unsafe(struct stream_thread *sth) {
313 s->host->stream.snd.status.connections++;
314 s->last_state_since_t = now_realtime_sec();
315
316 + s->replication.last_progress_ut = now_monotonic_usec();
317 +
318 stream_circular_buffer_flush_unsafe(s->scb, stream_send.buffer_max_size);
264 - replication_recalculate_buffer_used_ratio_unsafe(s);
319 + replication_sender_recalculate_buffer_used_ratio_unsafe(s);
320 stream_sender_unlock(s);
321
322 internal_fatal(META_GET(&sth->run.meta, (Word_t)&s->thread.meta) != NULL, "Sender already exists in meta list");
323 META_SET(&sth->run.meta, (Word_t)&s->thread.meta, &s->thread.meta);
324
270 - if(!nd_poll_add(sth->run.ndpl, s->sock.fd, ND_POLL_READ, &s->thread.meta))
325 + s->thread.wanted = ND_POLL_READ;
326 + if(!nd_poll_add(sth->run.ndpl, s->sock.fd, s->thread.wanted, &s->thread.meta))
327 nd_log(NDLS_DAEMON, NDLP_ERR,
328 "STREAM SND[%zu] '%s' [to %s]: failed to add sender socket to nd_poll()",
273 - sth->id, rrdhost_hostname(s->host), s->connected_to);
329 + sth->id, rrdhost_hostname(s->host), s->remote_ip);
330
331 stream_sender_on_ready_to_dispatch(s);
332 }
@@ -280,10 +336,6 @@ void stream_sender_remove(struct sender_state *s) {
336 // THIS FUNCTION IS USED BY THE CONNECTOR TOO
337 // when it gives up on a certain node
338
283 - nd_log(NDLS_DAEMON, NDLP_NOTICE,
284 - "STREAM SND '%s' [to %s]: streaming sender removed host: %s",
285 - rrdhost_hostname(s->host), s->connected_to, stream_handshake_error_to_string(s->exit.reason));
286 -
339 stream_sender_lock(s);
340
341 __atomic_store_n(&s->exit.shutdown, false, __ATOMIC_RELAXED);
@@ -311,16 +363,39 @@ void stream_sender_remove(struct sender_state *s) {
363 #endif
364 }
365
366 +static void stream_sender_log_disconnection(struct stream_thread *sth, struct sender_state *s, STREAM_HANDSHAKE reason) {
367 + ND_LOG_STACK lgs[] = {
368 + ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_to_parent_msgid),
369 + ND_LOG_FIELD_END(),
370 + };
371 + ND_LOG_STACK_PUSH(lgs);
372 +
373 + nd_log(NDLS_DAEMON, NDLP_NOTICE,
374 + "STREAM SND[%zu] '%s' [to %s]: sender disconnected from parent, reason: %s",
375 + sth->id, rrdhost_hostname(s->host), s->remote_ip, stream_handshake_error_to_string(reason));
376 +}
377 +
378 static void stream_sender_move_running_to_connector_or_remove(struct stream_thread *sth, struct sender_state *s, STREAM_HANDSHAKE reason, bool reconnect) {
379 internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
380
381 + ND_LOG_STACK lgs[] = {
382 + ND_LOG_FIELD_STR(NDF_NIDL_NODE, s->host->hostname),
383 + ND_LOG_FIELD_CB(NDF_DST_IP, stream_sender_log_dst_ip, s),
384 + ND_LOG_FIELD_CB(NDF_DST_PORT, stream_sender_log_dst_port, s),
385 + ND_LOG_FIELD_CB(NDF_DST_TRANSPORT, stream_sender_log_transport, s),
386 + ND_LOG_FIELD_CB(NDF_DST_CAPABILITIES, stream_sender_log_capabilities, s),
387 + ND_LOG_FIELD_END(),
388 + };
389 + ND_LOG_STACK_PUSH(lgs);
390 +
391 internal_fatal(META_GET(&sth->run.meta, (Word_t)&s->thread.meta) == NULL, "Sender to be removed is not in the list of senders");
392 META_DEL(&sth->run.meta, (Word_t)&s->thread.meta);
393
320 - if(!nd_poll_del(sth->run.ndpl, s->sock.fd))
394 + s->thread.wanted = 0;
395 + if(!nd_poll_del(sth->run.ndpl, s->sock.fd, &s->thread.meta))
396 nd_log(NDLS_DAEMON, NDLP_ERR,
397 "STREAM SND[%zu] '%s' [to %s]: failed to delete sender socket from nd_poll()",
323 - sth->id, rrdhost_hostname(s->host), s->connected_to);
398 + sth->id, rrdhost_hostname(s->host), s->remote_ip);
399
400 // clear this flag asap, to stop other threads from pushing metrics for this node
401 rrdhost_flag_clear(s->host, RRDHOST_FLAG_STREAM_SENDER_CONNECTED | RRDHOST_FLAG_STREAM_SENDER_READY_4_METRICS);
@@ -334,9 +409,7 @@ static void stream_sender_move_running_to_connector_or_remove(struct stream_thre
409 s->host->stream.snd.status.tid = 0;
410 stream_sender_unlock(s);
411
337 - nd_log(NDLS_DAEMON, NDLP_NOTICE,
338 - "STREAM SND[%zu] '%s' [to %s]: sender disconnected from parent, reason: %s",
339 - sth->id, rrdhost_hostname(s->host), s->connected_to, stream_handshake_error_to_string(reason));
412 + stream_sender_log_disconnection(sth, s, reason);
413
414 nd_sock_close(&s->sock);
415
@@ -378,16 +451,14 @@ void stream_sender_check_all_nodes_from_poll(struct stream_thread *sth, usec_t n
451 if(unlikely(stats.bytes_outstanding &&
452 s->thread.last_traffic_ut + stream_send.parents.timeout_s * USEC_PER_SEC < now_ut &&
453 !stream_sender_pending_replication_requests(s) &&
381 - !stream_sender_replicating_charts(s)
382 - )) {
454 + !stream_sender_replicating_charts(s))) {
455
456 ND_LOG_STACK lgs[] = {
457 ND_LOG_FIELD_STR(NDF_NIDL_NODE, s->host->hostname),
458 ND_LOG_FIELD_CB(NDF_DST_IP, stream_sender_log_dst_ip, s),
459 ND_LOG_FIELD_CB(NDF_DST_PORT, stream_sender_log_dst_port, s),
460 ND_LOG_FIELD_CB(NDF_DST_TRANSPORT, stream_sender_log_transport, s),
389 - ND_LOG_FIELD_CB(NDF_SRC_CAPABILITIES, stream_sender_log_capabilities, s),
390 - ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_to_parent_msgid),
461 + ND_LOG_FIELD_CB(NDF_DST_CAPABILITIES, stream_sender_log_capabilities, s),
462 ND_LOG_FIELD_END(),
463 };
464 ND_LOG_STACK_PUSH(lgs);
@@ -402,12 +473,12 @@ void stream_sender_check_all_nodes_from_poll(struct stream_thread *sth, usec_t n
473 size_snprintf(pending, sizeof(pending), stats.bytes_outstanding, "B", false);
474
475 nd_log(NDLS_DAEMON, NDLP_ERR,
405 - "STREAM SND[%zu] '%s' [to %s]: could not send data for %ld seconds - closing connection - "
476 + "STREAM SND[%zu] '%s' [to %s]: there was not traffic for %ld seconds - closing connection - "
477 "we have sent %zu bytes in %zu operations, it is idle for %s, and we have %s pending to send "
478 "(buffer is used %.2f%%).",
408 - sth->id, rrdhost_hostname(s->host), s->connected_to, stream_send.parents.timeout_s,
479 + sth->id, rrdhost_hostname(s->host), s->remote_ip, stream_send.parents.timeout_s,
480 stats.bytes_sent, stats.sends,
410 - duration, pending, stats.buffer_ratio);
481 + duration, pending, stats.buffer_ratio);
482
483 stream_sender_move_running_to_connector_or_remove(sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_TIMEOUT, true);
484 continue;
@@ -416,10 +487,11 @@ void stream_sender_check_all_nodes_from_poll(struct stream_thread *sth, usec_t n
487 bytes_compressed += stats.bytes_added;
488 bytes_uncompressed += stats.bytes_uncompressed;
489
419 - if(!nd_poll_upd(sth->run.ndpl, s->sock.fd, ND_POLL_READ | (stats.bytes_outstanding ? ND_POLL_WRITE : 0), &s->thread.meta))
490 + s->thread.wanted = ND_POLL_READ | (stats.bytes_outstanding ? ND_POLL_WRITE : 0);
491 + if(!nd_poll_upd(sth->run.ndpl, s->sock.fd, s->thread.wanted, &s->thread.meta))
492 nd_log(NDLS_DAEMON, NDLP_ERR,
493 "STREAM SND[%zu] '%s' [to %s]: failed to update nd_poll().",
422 - sth->id, rrdhost_hostname(s->host), s->connected_to);
494 + sth->id, rrdhost_hostname(s->host), s->remote_ip);
495 }
496
497 if (bytes_compressed && bytes_uncompressed) {
@@ -432,6 +504,243 @@ void stream_sender_check_all_nodes_from_poll(struct stream_thread *sth, usec_t n
504 worker_set_metric(WORKER_SENDER_JOB_BUFFER_RATIO, overall_buffer_ratio);
505 }
506
507 +static bool stream_sender_did_replication_progress(struct sender_state *s) {
508 + RRDHOST *host = s->host;
509 +
510 + size_t my_counter_in = __atomic_load_n(&s->replication.last_counter_in, __ATOMIC_RELAXED);
511 + size_t my_counter_out = __atomic_load_n(&s->replication.last_counter_out, __ATOMIC_RELAXED);
512 + size_t host_counter_in = __atomic_load_n(&host->stream.snd.status.replication.counter_in, __ATOMIC_RELAXED);
513 + size_t host_counter_out = __atomic_load_n(&host->stream.snd.status.replication.counter_out, __ATOMIC_RELAXED);
514 + if(my_counter_in != host_counter_in || my_counter_out != host_counter_out) {
515 + // there has been some progress
516 + __atomic_store_n(&s->replication.last_counter_in, __atomic_load_n(&host->stream.snd.status.replication.counter_in, __ATOMIC_RELAXED), __ATOMIC_RELAXED);
517 + __atomic_store_n(&s->replication.last_counter_out, __atomic_load_n(&host->stream.snd.status.replication.counter_out, __ATOMIC_RELAXED), __ATOMIC_RELAXED);
518 + s->replication.last_progress_ut = now_monotonic_usec();
519 + return true;
520 + }
521 +
522 + if(!my_counter_in || !my_counter_out)
523 + // we have not started yet
524 + return true;
525 +
526 + if(dictionary_entries(s->replication.requests))
527 + // we still have requests to execute
528 + return true;
529 +
530 + return (now_monotonic_usec() - s->replication.last_progress_ut < 5ULL * 60 * USEC_PER_SEC);
531 +}
532 +
533 +void stream_sender_replication_check_from_poll(struct stream_thread *sth, usec_t now_ut __maybe_unused) {
534 + internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__);
535 +
536 + Word_t idx = 0;
537 + for(struct pollfd_meta *m = META_FIRST(&sth->run.meta, &idx);
538 + m;
539 + m = META_NEXT(&sth->run.meta, &idx)) {
540 + if (m->type != POLLFD_TYPE_SENDER) continue;
541 + struct sender_state *s = m->s;
542 + RRDHOST *host = s->host;
543 +
544 + if(stream_sender_did_replication_progress(s))
545 + continue;
546 +
547 + ND_LOG_STACK lgs[] = {
548 + ND_LOG_FIELD_STR(NDF_NIDL_NODE, host->hostname),
549 + ND_LOG_FIELD_CB(NDF_DST_IP, stream_sender_log_dst_ip, s),
550 + ND_LOG_FIELD_CB(NDF_DST_PORT, stream_sender_log_dst_port, s),
551 + ND_LOG_FIELD_CB(NDF_DST_TRANSPORT, stream_sender_log_transport, s),
552 + ND_LOG_FIELD_CB(NDF_DST_CAPABILITIES, stream_sender_log_capabilities, s),
553 + ND_LOG_FIELD_END(),
554 + };
555 + ND_LOG_STACK_PUSH(lgs);
556 +
557 + size_t exceptions = 0;
558 + RRDSET *st;
559 + rrdset_foreach_read(st, host) {
560 + RRDSET_FLAGS st_flags = rrdset_flag_get(st);
561 + if(st_flags & (RRDSET_FLAG_OBSOLETE | RRDSET_FLAG_UPSTREAM_IGNORE | RRDSET_FLAG_SENDER_REPLICATION_FINISHED))
562 + continue;
563 +
564 + const char *status = (st_flags & RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS) ? "has not finished" : "has not started";
565 +
566 + nd_log(NDLS_DAEMON, NDLP_WARNING,
567 + "STREAM SND[%zu] '%s' [to %s]: REPLICATION STALLED: instance '%s' %s replication yet.",
568 + sth->id, rrdhost_hostname(host), s->remote_ip,
569 + rrdset_id(st), status);
570 +
571 + exceptions++;
572 + }
573 + rrdset_foreach_done(st);
574 +
575 + if(exceptions && !stream_sender_did_replication_progress(s)) {
576 + nd_log(NDLS_DAEMON, NDLP_ERR,
577 + "STREAM SND[%zu] '%s' [to %s]: REPLICATION EXCEPTIONS SUMMARY: node has %zu stalled replication requests."
578 + "We have received %u and sent %u replication commands. "
579 + "Disconnecting node to restore streaming.",
580 + sth->id, rrdhost_hostname(s->host), s->remote_ip, exceptions,
581 + __atomic_load_n(&host->stream.snd.status.replication.counter_in, __ATOMIC_RELAXED),
582 + __atomic_load_n(&host->stream.snd.status.replication.counter_out, __ATOMIC_RELAXED));
583 +
584 + stream_sender_move_running_to_connector_or_remove(sth, s, STREAM_HANDSHAKE_REPLICATION_STALLED, true);
585 + }
586 + }
587 +}
588 +
589 +bool stream_sender_send_data(struct stream_thread *sth, struct sender_state *s, usec_t now_ut, bool process_opcodes_and_enable_removal) {
590 + internal_fatal(sth->tid != gettid_cached(), "Function %s() should only be used by the dispatcher thread", __FUNCTION__ );
591 +
592 + EVLOOP_STATUS status = EVLOOP_STATUS_CONTINUE;
593 + while(status == EVLOOP_STATUS_CONTINUE) {
594 + if(!stream_sender_trylock(s)) {
595 + sth->snd.send_misses++;
596 + status = EVLOOP_STATUS_CANT_GET_LOCK;
597 + break;
598 + }
599 +
600 + STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(s->scb);
601 + char *chunk;
602 + size_t outstanding = stream_circular_buffer_get_unsafe(s->scb, &chunk);
603 +
604 + if(!outstanding) {
605 + status = EVLOOP_STATUS_NO_MORE_DATA;
606 + stream_sender_unlock(s);
607 + continue;
608 + }
609 +
610 + ssize_t rc = nd_sock_send_nowait(&s->sock, chunk, outstanding);
611 + if (likely(rc > 0)) {
612 + stream_circular_buffer_del_unsafe(s->scb, rc, now_ut);
613 + replication_sender_recalculate_buffer_used_ratio_unsafe(s);
614 + s->thread.last_traffic_ut = now_ut;
615 + sth->snd.bytes_sent += rc;
616 +
617 + if (!stats->bytes_outstanding) {
618 + // we sent them all - remove ND_POLL_WRITE
619 + s->thread.wanted = ND_POLL_READ;
620 + if (!nd_poll_upd(sth->run.ndpl, s->sock.fd, s->thread.wanted, &s->thread.meta))
621 + nd_log(NDLS_DAEMON, NDLP_ERR,
622 + "STREAM SND[%zu] '%s' [to %s]: failed to update nd_poll().",
623 + sth->id, rrdhost_hostname(s->host), s->remote_ip);
624 +
625 + // recreate the circular buffer if we have to
626 + stream_circular_buffer_recreate_timed_unsafe(s->scb, now_ut, false);
627 + status = EVLOOP_STATUS_NO_MORE_DATA;
628 + }
629 + }
630 + else if (rc == 0 || errno == ECONNRESET)
631 + status = EVLOOP_STATUS_SOCKET_CLOSED;
632 +
633 + else if (rc < 0) {
634 + if(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
635 + status = EVLOOP_STATUS_SOCKET_FULL;
636 + else
637 + status = EVLOOP_STATUS_SOCKET_ERROR;
638 + }
639 + stream_sender_unlock(s);
640 +
641 + if (status == EVLOOP_STATUS_SOCKET_ERROR || status == EVLOOP_STATUS_SOCKET_CLOSED) {
642 + const char *disconnect_reason = NULL;
643 + STREAM_HANDSHAKE reason;
644 +
645 + if(status == EVLOOP_STATUS_SOCKET_ERROR) {
646 + worker_is_busy(WORKER_STREAM_JOB_DISCONNECT_SEND_ERROR);
647 + disconnect_reason = "socket reports error while writing";
648 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_WRITE_FAILED;
649 + }
650 + else /* if(status == EVLOOP_STATUS_SOCKET_CLOSED) */ {
651 + worker_is_busy(WORKER_STREAM_JOB_DISCONNECT_REMOTE_CLOSED);
652 + disconnect_reason = "socket reports EOF (closed by parent)";
653 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END;
654 + }
655 +
656 + nd_log(NDLS_DAEMON, NDLP_ERR,
657 + "STREAM SND[%zu] '%s' [to %s]: %s (%zd, on fd %d) - restarting connection - "
658 + "we have sent %zu bytes in %zu operations.",
659 + sth->id, rrdhost_hostname(s->host), s->remote_ip, disconnect_reason, rc, s->sock.fd,
660 + stats->bytes_sent, stats->sends);
661 +
662 + if(process_opcodes_and_enable_removal) {
663 + // this is not executed from the opcode handling mechanism
664 + // so we can safely remove the sender
665 + stream_sender_move_running_to_connector_or_remove(sth, s, reason, true);
666 + }
667 + else {
668 + // protection against this case:
669 + //
670 + // 1. sender gets a function request
671 + // 2. sender executes the request
672 + // 3. response is immediately available
673 + // 4. sender_commit() appends the data to the sending circular buffer
674 + // 5. sender_commit() sends opcode to enable sending
675 + // 6. opcode bypasses the signal and runs this function inline to dispatch immediately
676 + // 7. sending fails (remote disconnected)
677 + // 8. sender is removed
678 + //
679 + // Point 2 above crashes. The sender is no longer there (freed at point 8)
680 + // and there is no way for point 2 to know...
681 + }
682 + }
683 + else if(process_opcodes_and_enable_removal &&
684 + status == EVLOOP_STATUS_CONTINUE &&
685 + stream_thread_process_opcodes(sth, &s->thread.meta))
686 + status = EVLOOP_STATUS_OPCODE_ON_ME;
687 + }
688 +
689 + return EVLOOP_STATUS_STILL_ALIVE(status);
690 +}
691 +
692 +bool stream_sender_receive_data(struct stream_thread *sth, struct sender_state *s, usec_t now_ut, bool process_opcodes) {
693 + EVLOOP_STATUS status = EVLOOP_STATUS_CONTINUE;
694 + while(status == EVLOOP_STATUS_CONTINUE) {
695 + ssize_t rc = nd_sock_revc_nowait(&s->sock, s->rbuf.b + s->rbuf.read_len, sizeof(s->rbuf.b) - s->rbuf.read_len - 1);
696 + if (likely(rc > 0)) {
697 + s->rbuf.read_len += rc;
698 +
699 + s->thread.last_traffic_ut = now_ut;
700 + sth->snd.bytes_received += rc;
701 +
702 + worker_is_busy(WORKER_SENDER_JOB_EXECUTE);
703 + stream_sender_execute_commands(s);
704 + }
705 + else if (rc == 0 || errno == ECONNRESET)
706 + status = EVLOOP_STATUS_SOCKET_CLOSED;
707 +
708 + else if (rc < 0) {
709 + if(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
710 + status = EVLOOP_STATUS_SOCKET_FULL;
711 + else
712 + status = EVLOOP_STATUS_SOCKET_ERROR;
713 + }
714 +
715 + if(status == EVLOOP_STATUS_SOCKET_ERROR || status == EVLOOP_STATUS_SOCKET_CLOSED) {
716 + const char *disconnect_reason;
717 + STREAM_HANDSHAKE reason;
718 +
719 + if(status == EVLOOP_STATUS_SOCKET_ERROR) {
720 + worker_is_busy(WORKER_STREAM_JOB_DISCONNECT_RECEIVE_ERROR);
721 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_READ_FAILED;
722 + disconnect_reason = "error during receive";
723 + }
724 + else /* if(status == EVLOOP_STATUS_SOCKET_CLOSED) */ {
725 + worker_is_busy(WORKER_STREAM_JOB_DISCONNECT_REMOTE_CLOSED);
726 + reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END;
727 + disconnect_reason = "socket reports EOF (closed by parent)";
728 + }
729 +
730 + nd_log(NDLS_DAEMON, NDLP_ERR,
731 + "STREAM SND[%zu] '%s' [to %s]: %s (fd %d) - restarting sender connection.",
732 + sth->id, rrdhost_hostname(s->host), s->remote_ip, disconnect_reason, s->sock.fd);
733 +
734 + stream_sender_move_running_to_connector_or_remove(
735 + sth, s, reason, true);
736 + }
737 + else if(status == EVLOOP_STATUS_CONTINUE && process_opcodes && stream_thread_process_opcodes(sth, &s->thread.meta))
738 + status = EVLOOP_STATUS_OPCODE_ON_ME;
739 + }
740 +
741 + return EVLOOP_STATUS_STILL_ALIVE(status);
742 +}
743 +
744 // process poll() events for streaming senders
745 // returns true when the sender is still there, false if it removed it
746 bool stream_sender_process_poll_events(struct stream_thread *sth, struct sender_state *s, nd_poll_event_t events, usec_t now_ut) {
@@ -442,8 +751,7 @@ bool stream_sender_process_poll_events(struct stream_thread *sth, struct sender_
751 ND_LOG_FIELD_CB(NDF_DST_IP, stream_sender_log_dst_ip, s),
752 ND_LOG_FIELD_CB(NDF_DST_PORT, stream_sender_log_dst_port, s),
753 ND_LOG_FIELD_CB(NDF_DST_TRANSPORT, stream_sender_log_transport, s),
445 - ND_LOG_FIELD_CB(NDF_SRC_CAPABILITIES, stream_sender_log_capabilities, s),
446 - ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_to_parent_msgid),
754 + ND_LOG_FIELD_CB(NDF_DST_CAPABILITIES, stream_sender_log_capabilities, s),
755 ND_LOG_FIELD_END(),
756 };
757 ND_LOG_STACK_PUSH(lgs);
@@ -471,129 +779,22 @@ bool stream_sender_process_poll_events(struct stream_thread *sth, struct sender_
779
780 nd_log(NDLS_DAEMON, NDLP_ERR,
781 "STREAM SND[%zu] '%s' [to %s]: %s restarting connection - %zu bytes transmitted in %zu operations.",
474 - sth->id, rrdhost_hostname(s->host), s->connected_to, error, stats.bytes_sent, stats.sends);
782 + sth->id, rrdhost_hostname(s->host), s->remote_ip, error, stats.bytes_sent, stats.sends);
783
784 stream_sender_move_running_to_connector_or_remove(sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_ERROR, true);
785 return false;
786 }
787
480 - if(events & ND_POLL_WRITE) {
481 - // we can send data on this socket
482 -
483 - bool stop = false;
484 - while(!stop) {
485 - if(stream_sender_trylock(s)) {
486 - worker_is_busy(WORKER_STREAM_JOB_SOCKET_SEND);
487 -
488 - const char *disconnect_reason = NULL;
489 - STREAM_HANDSHAKE reason;
490 -
491 - STREAM_CIRCULAR_BUFFER_STATS *stats = stream_circular_buffer_stats_unsafe(s->scb);
492 - char *chunk;
493 - size_t outstanding = stream_circular_buffer_get_unsafe(s->scb, &chunk);
494 - ssize_t rc = nd_sock_send_nowait(&s->sock, chunk, outstanding);
495 - if (likely(rc > 0)) {
496 - stream_circular_buffer_del_unsafe(s->scb, rc);
497 - replication_recalculate_buffer_used_ratio_unsafe(s);
498 - s->thread.last_traffic_ut = now_ut;
499 - sth->snd.bytes_sent += rc;
500 -
501 - if (!stats->bytes_outstanding) {
502 - // we sent them all - remove ND_POLL_WRITE
503 - if (!nd_poll_upd(sth->run.ndpl, s->sock.fd, ND_POLL_READ, &s->thread.meta))
504 - nd_log(NDLS_DAEMON, NDLP_ERR,
505 - "STREAM SND[%zu] '%s' [to %s]: failed to update nd_poll().",
506 - sth->id, rrdhost_hostname(s->host), s->connected_to);
507 -
508 - // recreate the circular buffer if we have to
509 - stream_circular_buffer_recreate_timed_unsafe(s->scb, now_ut, false);
510 - stop = true;
511 - }
512 - else if(stream_thread_process_opcodes(sth, &s->thread.meta))
513 - stop = true;
514 - }
515 - else if (rc == 0 || errno == ECONNRESET) {
516 - disconnect_reason = "socket reports EOF (closed by parent)";
517 - reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END;
518 - }
519 - else if (rc < 0) {
520 - if(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) {
521 - // will try later
522 - stop = true;
523 - }
524 - else {
525 - disconnect_reason = "socket reports error while writing";
526 - reason = STREAM_HANDSHAKE_DISCONNECT_SOCKET_WRITE_FAILED;
527 - }
528 - }
529 - stream_sender_unlock(s);
530 -
531 - if (disconnect_reason) {
532 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR);
533 - nd_log(NDLS_DAEMON, NDLP_ERR,
534 - "STREAM SND[%zu] '%s' [to %s]: %s (%zd, on fd %d) - restarting connection - "
535 - "we have sent %zu bytes in %zu operations.",
536 - sth->id, rrdhost_hostname(s->host), s->connected_to, disconnect_reason, rc, s->sock.fd,
537 - stats->bytes_sent, stats->sends);
538 -
539 - stream_sender_move_running_to_connector_or_remove(sth, s, reason, true);
540 - return false;
541 - }
542 - }
543 - else {
544 - sth->snd.send_misses++;
545 - break;
546 - }
547 - }
788 + if(events & ND_POLL_READ) {
789 + worker_is_busy(WORKER_STREAM_JOB_SOCKET_RECEIVE);
790 + if(!stream_sender_receive_data(sth, s, now_ut, true))
791 + return false;
792 }
793
550 - if(!(events & ND_POLL_READ))
551 - return true;
552 -
553 - // we can receive data from this socket
554 -
555 - worker_is_busy(WORKER_STREAM_JOB_SOCKET_RECEIVE);
556 - bool stop = false;
557 - size_t iterations = 0;
558 - while(!stop && iterations++ < MAX_IO_ITERATIONS_PER_EVENT) {
559 - // we have to drain the socket!
560 -
561 - ssize_t rc = nd_sock_revc_nowait(&s->sock, s->rbuf.b + s->rbuf.read_len, sizeof(s->rbuf.b) - s->rbuf.read_len - 1);
562 - if (likely(rc > 0)) {
563 - s->rbuf.read_len += rc;
564 -
565 - s->thread.last_traffic_ut = now_ut;
566 - sth->snd.bytes_received += rc;
567 -
568 - worker_is_busy(WORKER_SENDER_JOB_EXECUTE);
569 - stream_sender_execute_commands(s);
570 -
571 - if(stream_thread_process_opcodes(sth, &s->thread.meta))
572 - stop = true;
573 - }
574 - else if (rc == 0 || errno == ECONNRESET) {
575 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_REMOTE_CLOSED);
576 - nd_log(NDLS_DAEMON, NDLP_ERR,
577 - "STREAM SND[%zu] '%s' [to %s]: socket %d reports EOF (closed by parent).",
578 - sth->id, rrdhost_hostname(s->host), s->connected_to, s->sock.fd);
579 - stream_sender_move_running_to_connector_or_remove(
580 - sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_CLOSED_BY_REMOTE_END, true);
794 + if(events & ND_POLL_WRITE) {
795 + worker_is_busy(WORKER_STREAM_JOB_SOCKET_SEND);
796 + if(!stream_sender_send_data(sth, s, now_ut, true))
797 return false;
582 - }
583 - else if (rc < 0) {
584 - if(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
585 - // will try later
586 - stop = true;
587 - else {
588 - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR);
589 - nd_log(NDLS_DAEMON, NDLP_ERR,
590 - "STREAM SND[%zu] '%s' [to %s]: error during receive (%zd, on fd %d) - restarting connection.",
591 - sth->id, rrdhost_hostname(s->host), s->connected_to, rc, s->sock.fd);
592 - stream_sender_move_running_to_connector_or_remove(
593 - sth, s, STREAM_HANDSHAKE_DISCONNECT_SOCKET_READ_FAILED, true);
594 - return false;
595 - }
596 - }
798 }
799
800 return true;
@@ -608,17 +809,6 @@ void stream_sender_cleanup(struct stream_thread *sth) {
809 if(m->type != POLLFD_TYPE_SENDER) continue;
810 struct sender_state *s = m->s;
811
611 - ND_LOG_STACK lgs[] = {
612 - ND_LOG_FIELD_STR(NDF_NIDL_NODE, s->host->hostname),
613 - ND_LOG_FIELD_CB(NDF_DST_IP, stream_sender_log_dst_ip, s),
614 - ND_LOG_FIELD_CB(NDF_DST_PORT, stream_sender_log_dst_port, s),
615 - ND_LOG_FIELD_CB(NDF_DST_TRANSPORT, stream_sender_log_transport, s),
616 - ND_LOG_FIELD_CB(NDF_SRC_CAPABILITIES, stream_sender_log_capabilities, s),
617 - ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &streaming_to_parent_msgid),
618 - ND_LOG_FIELD_END(),
619 - };
620 - ND_LOG_STACK_PUSH(lgs);
621 -
812 s->exit.reason = STREAM_HANDSHAKE_DISCONNECT_SHUTDOWN;
813 s->exit.shutdown = true;
814 stream_sender_move_running_to_connector_or_remove(sth, s, STREAM_HANDSHAKE_DISCONNECT_SHUTDOWN, false);
src/streaming/stream-thread.c
+50 -26
@@ -29,12 +29,18 @@ static void stream_thread_handle_op(struct stream_thread *sth, struct stream_opc
29 {
30 if(m->type == POLLFD_TYPE_SENDER) {
31 if(msg->opcode & STREAM_OPCODE_SENDER_POLLOUT) {
32 - if(!nd_poll_upd(sth->run.ndpl, m->s->sock.fd, ND_POLL_READ|ND_POLL_WRITE, m)) {
32 + m->s->thread.wanted = ND_POLL_READ | ND_POLL_WRITE;
33 + if(!nd_poll_upd(sth->run.ndpl, m->s->sock.fd, m->s->thread.wanted, m)) {
34 nd_log_limit_static_global_var(erl, 1, 0);
35 nd_log_limit(&erl, NDLS_DAEMON, NDLP_DEBUG,
36 "STREAM SND[%zu] '%s' [to %s]: cannot enable output on sender socket %d.",
36 - sth->id, rrdhost_hostname(m->s->host), m->s->connected_to, m->s->sock.fd);
37 + sth->id, rrdhost_hostname(m->s->host), m->s->remote_ip, m->s->sock.fd);
38 }
39 +
40 + if(!stream_sender_send_data(sth, m->s, now_monotonic_usec(), false))
41 + // sender has been removed
42 + return;
43 +
44 msg->opcode &= ~(STREAM_OPCODE_SENDER_POLLOUT);
45 }
46
@@ -43,12 +49,18 @@ static void stream_thread_handle_op(struct stream_thread *sth, struct stream_opc
49 }
50 else if(m->type == POLLFD_TYPE_RECEIVER) {
51 if (msg->opcode & STREAM_OPCODE_RECEIVER_POLLOUT) {
46 - if (!nd_poll_upd(sth->run.ndpl, m->rpt->sock.fd, ND_POLL_READ | ND_POLL_WRITE, m)) {
52 + m->rpt->thread.wanted = ND_POLL_READ | ND_POLL_WRITE;
53 + if (!nd_poll_upd(sth->run.ndpl, m->rpt->sock.fd, m->rpt->thread.wanted, m)) {
54 nd_log_limit_static_global_var(erl, 1, 0);
48 - nd_log_limit(&erl, NDLS_DAEMON, NDLP_DEBUG,
55 + nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
56 "STREAM RCV[%zu] '%s' [from [%s]:%s]: cannot enable output on receiver socket %d.",
50 - sth->id, rrdhost_hostname(m->rpt->host), m->rpt->client_ip, m->rpt->client_port, m->rpt->sock.fd);
57 + sth->id, rrdhost_hostname(m->rpt->host), m->rpt->remote_ip, m->rpt->remote_port, m->rpt->sock.fd);
58 }
59 +
60 + if(!stream_receiver_send_data(sth, m->rpt, now_monotonic_usec(), false))
61 + // receiver has been removed
62 + return;
63 +
64 msg->opcode &= ~(STREAM_OPCODE_RECEIVER_POLLOUT);
65 }
66
@@ -84,19 +96,19 @@ void stream_receiver_send_opcode(struct receiver_state *rpt, struct stream_opcod
96 if(msg.meta != &rpt->thread.meta) {
97 nd_log(NDLS_DAEMON, NDLP_ERR,
98 "STREAM RCV '%s' [from [%s]:%s]: the receiver in the opcode the message does not match this receiver. "
87 - "Ignoring opcode.", rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port);
99 + "Ignoring opcode.", rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port);
100 return;
101 }
102 struct stream_thread *sth = stream_thread_by_slot_id(msg.thread_slot);
103 if(!sth) {
104 nd_log(NDLS_DAEMON, NDLP_ERR,
105 "STREAM RCV '%s' [from [%s]:%s]: the opcode (%u) message cannot be verified. Ignoring it.",
94 - rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, msg.opcode);
106 + rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port, msg.opcode);
107 return;
108 }
109
110 // check if we can execute the message now
99 - if(sth->tid == gettid_cached() && (!rpt->thread.draining_input || msg.opcode == STREAM_OPCODE_RECEIVER_POLLOUT)) {
111 + if(sth->tid == gettid_cached() && msg.opcode == STREAM_OPCODE_RECEIVER_POLLOUT) {
112 // we are running at the stream thread, and the request is about enabling POLLOUT,
113 // we can do this synchronously.
114 // IMPORTANT: DO NOT HANDLE FAILURES THAT REMOVE THE RECEIVER OR THE SENDER THIS WAY
@@ -138,7 +150,7 @@ void stream_receiver_send_opcode(struct receiver_state *rpt, struct stream_opcod
150 #endif
151
152 fatal("STREAM RCV '%s' [from [%s]:%s]: The streaming opcode queue is full, but this should never happen...",
141 - rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port);
153 + rrdhost_hostname(rpt->host), rpt->remote_ip, rpt->remote_port);
154 }
155
156 // let's use a new slot
@@ -164,7 +176,7 @@ void stream_sender_send_opcode(struct sender_state *s, struct stream_opcode msg)
176 if(msg.meta != &s->thread.meta) {
177 nd_log(NDLS_DAEMON, NDLP_ERR,
178 "STREAM SND '%s' [to %s]: the opcode message does not match this sender. "
167 - "Ignoring opcode.", rrdhost_hostname(s->host), s->connected_to);
179 + "Ignoring opcode.", rrdhost_hostname(s->host), s->remote_ip);
180 return;
181 }
182
@@ -172,12 +184,12 @@ void stream_sender_send_opcode(struct sender_state *s, struct stream_opcode msg)
184 if(!sth) {
185 nd_log(NDLS_DAEMON, NDLP_ERR,
186 "STREAM SND[x] '%s' [to %s] the opcode (%u) message cannot be verified. Ignoring it.",
175 - rrdhost_hostname(s->host), s->connected_to, msg.opcode);
187 + rrdhost_hostname(s->host), s->remote_ip, msg.opcode);
188 return;
189 }
190
191 // check if we can execute the message now
180 - if(sth->tid == gettid_cached() && (!s->thread.draining_input || msg.opcode == STREAM_OPCODE_SENDER_POLLOUT)) {
192 + if(sth->tid == gettid_cached() && msg.opcode == STREAM_OPCODE_SENDER_POLLOUT) {
193 // we are running at the stream thread, and the request is about enabling POLLOUT,
194 // we can do this synchronously.
195 // IMPORTANT: DO NOT HANDLE FAILURES THAT REMOVE THE RECEIVER OR THE SENDER THIS WAY
@@ -219,7 +231,7 @@ void stream_sender_send_opcode(struct sender_state *s, struct stream_opcode msg)
231 #endif
232
233 fatal("STREAM SND '%s' [to %s]: The streaming opcode queue is full, but this should never happen...",
222 - rrdhost_hostname(s->host), s->connected_to);
234 + rrdhost_hostname(s->host), s->remote_ip);
235 }
236
237 // let's use a new slot
@@ -326,18 +338,19 @@ static bool stream_thread_process_poll_slot(struct stream_thread *sth, nd_poll_r
338 switch(m->type) {
339 case POLLFD_TYPE_SENDER: {
340 struct sender_state *s = m->s;
329 - s->thread.draining_input = true;
330 - if(stream_sender_process_poll_events(sth, s, ev->events, now_ut))
331 - s->thread.draining_input = false;
332 - *replay_entries += dictionary_entries(s->replication.requests);
341 + if(stream_sender_process_poll_events(sth, s, ev->events, now_ut)) {
342 + // the sender is still there
343 + *replay_entries += dictionary_entries(s->replication.requests);
344 + }
345 break;
346 }
347
348 case POLLFD_TYPE_RECEIVER: {
349 struct receiver_state *rpt = m->rpt;
338 - rpt->thread.draining_input = true;
339 - if(stream_receive_process_poll_events(sth, rpt, ev->events, now_ut))
340 - rpt->thread.draining_input = false;
350 + if(stream_receive_process_poll_events(sth, rpt, ev->events, now_ut)) {
351 + // the receiver is still there
352 + ;
353 + }
354 break;
355 }
356
@@ -394,9 +407,9 @@ void *stream_thread(void *ptr) {
407 worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_OVERFLOW, "disconnect overflow");
408 worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT, "disconnect timeout");
409 worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_SOCKET_ERROR, "disconnect socket error");
397 - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_REMOTE_CLOSED, "disconnect remote closed");
398 - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR, "disconnect receive error");
399 - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR, "disconnect send error");
410 + worker_register_job_name(WORKER_STREAM_JOB_DISCONNECT_REMOTE_CLOSED, "disconnect remote closed");
411 + worker_register_job_name(WORKER_STREAM_JOB_DISCONNECT_RECEIVE_ERROR, "disconnect receive error");
412 + worker_register_job_name(WORKER_STREAM_JOB_DISCONNECT_SEND_ERROR, "disconnect send error");
413 worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_COMPRESSION_ERROR, "disconnect compression error");
414 worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_RECEIVER_LEFT, "disconnect receiver left");
415 worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_HOST_CLEANUP, "disconnect host cleanup");
@@ -471,8 +484,8 @@ void *stream_thread(void *ptr) {
484 sth->pipe.size = set_pipe_size(sth->pipe.fds[PIPE_READ], 65536 * sizeof(*sth->pipe.buffer)) / sizeof(*sth->pipe.buffer);
485 sth->pipe.buffer = mallocz(sth->pipe.size * sizeof(*sth->pipe.buffer));
486
474 - usec_t last_check_all_nodes_ut = 0;
475 - usec_t last_dequeue_ut = 0;
487 + usec_t last_check_replication_ut, last_check_all_nodes_ut, last_dequeue_ut;
488 + last_check_replication_ut = last_check_all_nodes_ut = last_dequeue_ut = now_monotonic_usec();
489
490 sth->run.pipe = (struct pollfd_meta){
491 .type = POLLFD_TYPE_PIPE,
@@ -515,12 +528,14 @@ void *stream_thread(void *ptr) {
528 last_dequeue_ut = now_ut;
529 }
530
518 - if(now_ut - last_check_all_nodes_ut >= USEC_PER_SEC) {
531 + if(now_ut - last_check_all_nodes_ut >= nd_profile.update_every * USEC_PER_SEC) {
532 worker_is_busy(WORKER_STREAM_JOB_LIST);
533
534 // periodically check the entire list of nodes
535 // this detects unresponsive parents too (timeout)
536 stream_sender_check_all_nodes_from_poll(sth, now_ut);
537 + stream_receiver_check_all_nodes_from_poll(sth, now_ut);
538 +
539 worker_set_metric(WORKER_SENDER_JOB_MESSAGES, (NETDATA_DOUBLE)(sth->messages.processed));
540 worker_set_metric(WORKER_STREAM_METRIC_NODES, (NETDATA_DOUBLE)sth->nodes_count);
541
@@ -537,6 +552,15 @@ void *stream_thread(void *ptr) {
552 last_check_all_nodes_ut = now_ut;
553 }
554
555 + if(now_ut - last_check_replication_ut >= 10 * 60 * USEC_PER_SEC) {
556 + worker_is_busy(WORKER_STREAM_JOB_LIST);
557 +
558 + stream_sender_replication_check_from_poll(sth, now_ut);
559 + stream_receiver_replication_check_from_poll(sth, now_ut);
560 +
561 + last_check_replication_ut = now_ut;
562 + }
563 +
564 worker_is_idle();
565
566 nd_poll_result_t ev;
src/streaming/stream-thread.h
+22 -4
@@ -64,9 +64,9 @@ struct stream_opcode {
64 #define WORKER_SENDER_JOB_DISCONNECT_OVERFLOW 16
65 #define WORKER_SENDER_JOB_DISCONNECT_TIMEOUT 17
66 #define WORKER_SENDER_JOB_DISCONNECT_SOCKET_ERROR 18
67 -#define WORKER_SENDER_JOB_DISCONNECT_REMOTE_CLOSED 19
68 -#define WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR 20
69 -#define WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR 21
67 +#define WORKER_STREAM_JOB_DISCONNECT_REMOTE_CLOSED 19
68 +#define WORKER_STREAM_JOB_DISCONNECT_RECEIVE_ERROR 20
69 +#define WORKER_STREAM_JOB_DISCONNECT_SEND_ERROR 21
70 #define WORKER_SENDER_JOB_DISCONNECT_COMPRESSION_ERROR 22
71 #define WORKER_SENDER_JOB_DISCONNECT_RECEIVER_LEFT 23
72 #define WORKER_SENDER_JOB_DISCONNECT_HOST_CLEANUP 24
@@ -95,7 +95,18 @@ struct stream_opcode {
95 #define STREAM_MAX_THREADS 2048
96 #define THREAD_TAG_STREAM "STREAM"
97
98 -#define MAX_IO_ITERATIONS_PER_EVENT 65536 // drain the input, take it all
98 +typedef enum {
99 + EVLOOP_STATUS_CONTINUE,
100 + EVLOOP_STATUS_SOCKET_FULL,
101 + EVLOOP_STATUS_SOCKET_CLOSED,
102 + EVLOOP_STATUS_SOCKET_ERROR,
103 + EVLOOP_STATUS_NO_MORE_DATA,
104 + EVLOOP_STATUS_OPCODE_ON_ME,
105 + EVLOOP_STATUS_CANT_GET_LOCK,
106 + EVLOOP_STATUS_PARSER_FAILED,
107 +} EVLOOP_STATUS;
108 +
109 +#define EVLOOP_STATUS_STILL_ALIVE(status) (status == EVLOOP_STATUS_CONTINUE || status == EVLOOP_STATUS_NO_MORE_DATA || status == EVLOOP_STATUS_SOCKET_FULL || status == EVLOOP_STATUS_CANT_GET_LOCK)
110
111 typedef enum {
112 POLLFD_TYPE_EMPTY,
@@ -193,6 +204,7 @@ extern struct stream_thread_globals stream_thread_globals;
204 void stream_sender_move_queue_to_running_unsafe(struct stream_thread *sth);
205 void stream_receiver_move_entire_queue_to_running_unsafe(struct stream_thread *sth);
206 void stream_sender_check_all_nodes_from_poll(struct stream_thread *sth, usec_t now_ut);
207 +void stream_sender_replication_check_from_poll(struct stream_thread *sth, usec_t now_ut);
208
209 void stream_receiver_add_to_queue(struct receiver_state *rpt);
210 void stream_sender_add_to_connector_queue(struct rrdhost *host);
@@ -214,6 +226,12 @@ bool stream_thread_process_opcodes(struct stream_thread *sth, struct pollfd_meta
226
227 void stream_receiver_move_to_running_unsafe(struct stream_thread *sth, struct receiver_state *rpt);
228
229 +bool stream_sender_receive_data(struct stream_thread *sth, struct sender_state *s, usec_t now_ut, bool process_opcodes);
230 +bool stream_sender_send_data(struct stream_thread *sth, struct sender_state *s, usec_t now_ut, bool process_opcodes_and_enable_removal);
231 +
232 +bool stream_receiver_receive_data(struct stream_thread *sth, struct receiver_state *rpt, usec_t now_ut, bool process_opcodes);
233 +bool stream_receiver_send_data(struct stream_thread *sth, struct receiver_state *rpt, usec_t now_ut, bool process_opcodes_and_enable_removal);
234 +
235 #include "stream-sender-internals.h"
236 #include "stream-receiver-internals.h"
237 #include "plugins.d/pluginsd_parser.h"
src/web/api/formatters/json_wrapper.c
+4 -4
@@ -16,7 +16,7 @@ static void jsonwrap_query_metric_plan(BUFFER *wb, QUERY_METRIC *qm) {
16 buffer_json_array_close(wb);
17
18 buffer_json_member_add_array(wb, "tiers");
19 - for (size_t tier = 0; tier < storage_tiers; tier++) {
19 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
20 buffer_json_add_array_item_object(wb);
21 buffer_json_member_add_uint64(wb, "tr", tier);
22 buffer_json_member_add_time_t(wb, "fe", qm->tiers[tier].db_first_time_s);
@@ -921,7 +921,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb) {
921 buffer_json_member_add_string(wb, "format", rrdr_format_to_string(format));
922
923 buffer_json_member_add_array(wb, "db_points_per_tier");
924 - for(size_t tier = 0; tier < storage_tiers ; tier++)
924 + for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++)
925 buffer_json_add_array_item_uint64(wb, qt->db.tiers[tier].points);
926 buffer_json_array_close(wb);
927
@@ -1500,7 +1500,7 @@ void rrdr_json_wrapper_end2(RRDR *r, BUFFER *wb) {
1500
1501 buffer_json_member_add_object(wb, "db");
1502 {
1503 - buffer_json_member_add_uint64(wb, "tiers", storage_tiers);
1503 + buffer_json_member_add_uint64(wb, "tiers", nd_profile.storage_tiers);
1504 buffer_json_member_add_time_t(wb, "update_every", qt->db.minimum_latest_update_every_s);
1505 buffer_json_member_add_time_t(wb, "first_entry", qt->db.first_time_s);
1506 buffer_json_member_add_time_t(wb, "last_entry", qt->db.last_time_s);
@@ -1515,7 +1515,7 @@ void rrdr_json_wrapper_end2(RRDR *r, BUFFER *wb) {
1515 buffer_json_object_close(wb); // dimensions
1516
1517 buffer_json_member_add_array(wb, "per_tier");
1518 - for(size_t tier = 0; tier < storage_tiers ; tier++) {
1518 + for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
1519 buffer_json_add_array_item_object(wb);
1520 buffer_json_member_add_uint64(wb, "tier", tier);
1521 buffer_json_member_add_uint64(wb, "queries", qt->db.tiers[tier].queries);
src/web/api/formatters/rrd2json.c
+1 -1
@@ -63,7 +63,7 @@ int rrdset2value_api_v1(
63 *db_points_read += r->stats.db_points_read;
64
65 if(db_points_per_tier) {
66 - for(size_t t = 0; t < storage_tiers ;t++)
66 + for(size_t t = 0; t < nd_profile.storage_tiers;t++)
67 db_points_per_tier[t] += r->internal.qt->db.tiers[t].points;
68 }
69
src/web/api/formatters/value/value.c
+1 -1
@@ -133,7 +133,7 @@ QUERY_VALUE rrdmetric2value(RRDHOST *host,
133 storage_point_merge_to(qv.sp, r->internal.qt->query.array[d].query_points);
134 }
135
136 - for(size_t t = 0; t < storage_tiers ;t++)
136 + for(size_t t = 0; t < nd_profile.storage_tiers;t++)
137 qv.storage_points_per_tier[t] = r->internal.qt->db.tiers[t].points;
138
139 long i = (!(options & RRDR_OPTION_REVERSED))?(long)rrdr_rows(r) - 1:0;
src/web/api/queries/backfill.c
+55 -22
@@ -3,11 +3,11 @@
3 #include "backfill.h"
4
5 struct backfill_request {
6 - size_t rrdhost_receiver_state_id;
6 + OBJECT_STATE_ID host_state_id;
7 RRDSET_ACQUIRED *rsa;
8 - uint32_t works;
9 - uint32_t successful;
10 - uint32_t failed;
8 + int32_t works;
9 + int32_t successful;
10 + int32_t failed;
11 backfill_callback_t cb;
12 struct backfill_request_data data;
13 };
@@ -28,6 +28,9 @@ static struct {
28 size_t queue_size;
29 BACKFILL_JudyLSet queue;
30
31 + size_t charts_added;
32 + size_t callbacks_executed;
33 +
34 ARAL *ar_br;
35 ARAL *ar_bdm;
36
@@ -46,9 +49,9 @@ bool backfill_request_add(RRDSET *st, backfill_callback_t cb, struct backfill_re
49 struct backfill_dim_work *array[dimensions];
50
51 if(backfill_globals.running) {
49 - struct backfill_request *br = aral_mallocz(backfill_globals.ar_br);
52 + struct backfill_request *br = aral_callocz(backfill_globals.ar_br);
53 br->data = *data;
51 - br->rrdhost_receiver_state_id = rrdhost_state_id(st->rrdhost);
54 + br->host_state_id = object_state_id(&st->rrdhost->state_id);
55 br->rsa = rrdset_find_and_acquire(st->rrdhost, string2str(st->id));
56 if(br->rsa) {
57 br->cb = cb;
@@ -59,7 +62,7 @@ bool backfill_request_add(RRDSET *st, backfill_callback_t cb, struct backfill_re
62 break;
63
64 if (!rrddim_option_check(rd, RRDDIM_OPTION_BACKFILLED_HIGH_TIERS)) {
62 - struct backfill_dim_work *bdm = aral_mallocz(backfill_globals.ar_bdm);
65 + struct backfill_dim_work *bdm = aral_callocz(backfill_globals.ar_bdm);
66 bdm->rda = (RRDDIM_ACQUIRED *)dictionary_acquired_item_dup(st->rrddim_root_index, rd_dfe.item);
67 bdm->br = br;
68 br->works++;
@@ -69,9 +72,13 @@ bool backfill_request_add(RRDSET *st, backfill_callback_t cb, struct backfill_re
72 rrddim_foreach_done(rd);
73 }
74
75 + internal_fatal((size_t)br->works != added, "works and added are not the same");
76 +
77 if(added) {
78 spinlock_lock(&backfill_globals.spinlock);
79
80 + __atomic_add_fetch(&backfill_globals.charts_added, 1, __ATOMIC_RELAXED);
81 +
82 for(size_t i = 0; i < added ;i++) {
83 backfill_globals.queue_size++;
84 BACKFILL_SET(&backfill_globals.queue, backfill_globals.id++, array[i]);
@@ -95,20 +102,20 @@ bool backfill_request_add(RRDSET *st, backfill_callback_t cb, struct backfill_re
102 bool backfill_execute(struct backfill_dim_work *bdm) {
103 RRDSET *st = rrdset_acquired_to_rrdset(bdm->br->rsa);
104
98 - if(!rrdhost_state_acquire(st->rrdhost, bdm->br->rrdhost_receiver_state_id))
105 + if(!object_state_acquire(&st->rrdhost->state_id, bdm->br->host_state_id))
106 return false;
107
108 size_t success = 0;
109 RRDDIM *rd = rrddim_acquired_to_rrddim(bdm->rda);
110
104 - for (size_t tier = 1; tier < storage_tiers; tier++)
111 + for (size_t tier = 1; tier < nd_profile.storage_tiers; tier++)
112 if (backfill_tier_from_smaller_tiers(rd, tier, now_realtime_sec()))
113 success++;
114
115 if (success > 0)
116 rrddim_option_set(rd, RRDDIM_OPTION_BACKFILLED_HIGH_TIERS);
117
111 - rrdhost_state_release(st->rrdhost);
118 + object_state_release(&st->rrdhost->state_id);
119 return success > 0;
120 }
121
@@ -120,12 +127,20 @@ static void backfill_dim_work_free(bool successful, struct backfill_dim_work *bd
127 else
128 __atomic_add_fetch(&br->failed, 1, __ATOMIC_RELAXED);
129
123 - uint32_t works = __atomic_sub_fetch(&br->works, 1, __ATOMIC_RELAXED);
124 - if(!works) {
125 - if(br->cb)
126 - br->cb(__atomic_load_n(&br->successful, __ATOMIC_RELAXED),
127 - __atomic_load_n(&br->failed, __ATOMIC_RELAXED),
128 - &br->data);
130 + int32_t works = __atomic_sub_fetch(&br->works, 1, __ATOMIC_RELAXED);
131 + internal_fatal(works < 0, "negative backfill jobs");
132 +
133 + if(works == 0) {
134 + // we are the last dimension of the chart
135 +
136 + if(br->cb) {
137 + __atomic_add_fetch(&backfill_globals.callbacks_executed, 1, __ATOMIC_RELAXED);
138 +
139 + br->cb(
140 + __atomic_load_n(&br->successful, __ATOMIC_RELAXED),
141 + __atomic_load_n(&br->failed, __ATOMIC_RELAXED),
142 + &br->data);
143 + }
144
145 rrdset_acquired_release(br->rsa);
146 aral_freez(backfill_globals.ar_br, br);
@@ -135,7 +150,13 @@ static void backfill_dim_work_free(bool successful, struct backfill_dim_work *bd
150 aral_freez(backfill_globals.ar_bdm, bdm);
151 }
152
138 -void *backfill_worker_thread(void *ptr __maybe_unused) {
153 +#define LOG_WARNING_EVERY 10
154 +
155 +void *backfill_worker_thread(void *ptr) {
156 + bool main_thread = (ptr == (void *)0x01);
157 + size_t warning = LOG_WARNING_EVERY;
158 + bool timeout = false;
159 +
160 worker_register("BACKFILL");
161
162 worker_register_job_name(0, "get");
@@ -156,16 +177,31 @@ void *backfill_worker_thread(void *ptr __maybe_unused) {
177 spinlock_unlock(&backfill_globals.spinlock);
178
179 if(bdm) {
180 + warning = LOG_WARNING_EVERY;
181 worker_is_busy(1);
182 bool success = backfill_execute(bdm);
183 backfill_dim_work_free(success, bdm);
184 continue;
185 }
186 + else if(main_thread && timeout) {
187 + size_t added = __atomic_load_n(&backfill_globals.charts_added, __ATOMIC_RELAXED);
188 + size_t executed = __atomic_load_n(&backfill_globals.callbacks_executed, __ATOMIC_RELAXED);
189 +
190 + if(executed != added && --warning == 0) {
191 + warning = LOG_WARNING_EVERY;
192 +
193 + nd_log(NDLS_DAEMON, NDLP_WARNING,
194 + "BACKFILL: the queue is empty, but the commands executed %zu is not equal to the commands added %zu",
195 + executed, added);
196 + }
197 + }
198
199 worker_set_metric(2, (NETDATA_DOUBLE)queue_size);
200
201 worker_is_idle();
168 - job_id = completion_wait_for_a_job_with_timeout(&backfill_globals.completion, job_id, 1000);
202 + size_t new_job_id = completion_wait_for_a_job_with_timeout(&backfill_globals.completion, job_id, 1000);
203 + timeout = new_job_id == job_id;
204 + job_id = new_job_id;
205 }
206
207 worker_unregister();
@@ -199,7 +235,7 @@ void *backfill_thread(void *ptr) {
235 th[t] = nd_thread_create(tag, NETDATA_THREAD_OPTION_JOINABLE, backfill_worker_thread, NULL);
236 }
237
202 - backfill_worker_thread(NULL);
238 + backfill_worker_thread((void *)0x01);
239 static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
240
241 for(size_t t = 0; t < threads - 1 ;t++) {
@@ -227,6 +263,3 @@ void *backfill_thread(void *ptr) {
263 return NULL;
264 }
265
230 -bool backfill_threads_detect_from_stream_conf(void) {
231 - return stream_conf_is_parent(false);
232 -}
src/web/api/queries/backfill.h
+1 -3
@@ -7,7 +7,7 @@
7
8 struct parser;
9 struct backfill_request_data {
10 - size_t rrdhost_receiver_state_id;
10 + OBJECT_STATE_ID host_state_id;
11 struct parser *parser;
12 RRDHOST *host;
13 RRDSET *st;
@@ -21,6 +21,4 @@ typedef bool (*backfill_callback_t)(size_t successful_dims, size_t failed_dims,
21 void *backfill_thread(void *ptr);
22 bool backfill_request_add(RRDSET *st, backfill_callback_t cb, struct backfill_request_data *data);
23
24 -bool backfill_threads_detect_from_stream_conf(void);
25 -
24 #endif //NETDATA_BACKFILL_H
src/web/api/queries/query.c
+17 -18
@@ -957,7 +957,7 @@ static bool query_metric_is_valid_tier(QUERY_METRIC *qm, size_t tier) {
957 }
958
959 static size_t query_metric_first_working_tier(QUERY_METRIC *qm) {
960 - for(size_t tier = 0; tier < storage_tiers ; tier++) {
960 + for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
961
962 // find the db time-range for this tier for all metrics
963 STORAGE_METRIC_HANDLE *smh = qm->tiers[tier].smh;
@@ -1002,7 +1002,7 @@ static long query_plan_points_coverage_weight(time_t db_first_time_s, time_t db_
1002 }
1003
1004 static size_t query_metric_best_tier_for_timeframe(QUERY_METRIC *qm, time_t after_wanted, time_t before_wanted, size_t points_wanted) {
1005 - if(unlikely(storage_tiers < 2))
1005 + if(unlikely(nd_profile.storage_tiers < 2))
1006 return 0;
1007
1008 if(unlikely(after_wanted == before_wanted || points_wanted <= 0))
@@ -1015,7 +1015,7 @@ static size_t query_metric_best_tier_for_timeframe(QUERY_METRIC *qm, time_t afte
1015 time_t min_first_time_s = 0;
1016 time_t max_last_time_s = 0;
1017
1018 - for(size_t tier = 0; tier < storage_tiers ; tier++) {
1018 + for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
1019 time_t first_time_s = qm->tiers[tier].db_first_time_s;
1020 time_t last_time_s = qm->tiers[tier].db_last_time_s;
1021
@@ -1026,7 +1026,7 @@ static size_t query_metric_best_tier_for_timeframe(QUERY_METRIC *qm, time_t afte
1026 max_last_time_s = last_time_s;
1027 }
1028
1029 - for(size_t tier = 0; tier < storage_tiers ; tier++) {
1029 + for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
1030
1031 // find the db time-range for this tier for all metrics
1032 STORAGE_METRIC_HANDLE *smh = qm->tiers[tier].smh;
@@ -1053,7 +1053,7 @@ static size_t query_metric_best_tier_for_timeframe(QUERY_METRIC *qm, time_t afte
1053 }
1054
1055 size_t best_tier = 0;
1056 - for(size_t tier = 1; tier < storage_tiers ; tier++) {
1056 + for(size_t tier = 1; tier < nd_profile.storage_tiers; tier++) {
1057 if(qm->tiers[tier].weight >= qm->tiers[best_tier].weight)
1058 best_tier = tier;
1059 }
@@ -1062,7 +1062,7 @@ static size_t query_metric_best_tier_for_timeframe(QUERY_METRIC *qm, time_t afte
1062 }
1063
1064 static size_t rrddim_find_best_tier_for_timeframe(QUERY_TARGET *qt, time_t after_wanted, time_t before_wanted, size_t points_wanted) {
1065 - if(unlikely(storage_tiers < 2))
1065 + if(unlikely(nd_profile.storage_tiers < 2))
1066 return 0;
1067
1068 if(unlikely(after_wanted == before_wanted || points_wanted <= 0)) {
@@ -1070,9 +1070,9 @@ static size_t rrddim_find_best_tier_for_timeframe(QUERY_TARGET *qt, time_t after
1070 return 0;
1071 }
1072
1073 - long weight[storage_tiers];
1073 + long weight[nd_profile.storage_tiers];
1074
1075 - for(size_t tier = 0; tier < storage_tiers ; tier++) {
1075 + for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
1076
1077 time_t common_first_time_s = 0;
1078 time_t common_last_time_s = 0;
@@ -1109,7 +1109,7 @@ static size_t rrddim_find_best_tier_for_timeframe(QUERY_TARGET *qt, time_t after
1109 }
1110
1111 size_t best_tier = 0;
1112 - for(size_t tier = 1; tier < storage_tiers ; tier++) {
1112 + for(size_t tier = 1; tier < nd_profile.storage_tiers; tier++) {
1113 if(weight[tier] >= weight[best_tier])
1114 best_tier = tier;
1115 }
@@ -1122,13 +1122,13 @@ static size_t rrddim_find_best_tier_for_timeframe(QUERY_TARGET *qt, time_t after
1122
1123 static time_t rrdset_find_natural_update_every_for_timeframe(QUERY_TARGET *qt, time_t after_wanted, time_t before_wanted, size_t points_wanted, RRDR_OPTIONS options, size_t tier) {
1124 size_t best_tier;
1125 - if((options & RRDR_OPTION_SELECTED_TIER) && tier < storage_tiers)
1125 + if((options & RRDR_OPTION_SELECTED_TIER) && tier < nd_profile.storage_tiers)
1126 best_tier = tier;
1127 else
1128 best_tier = rrddim_find_best_tier_for_timeframe(qt, after_wanted, before_wanted, points_wanted);
1129
1130 // find the db minimum update every for this tier for all metrics
1131 - time_t common_update_every_s = default_rrd_update_every;
1131 + time_t common_update_every_s = nd_profile.update_every;
1132 for(size_t i = 0, used = qt->query.used; i < used ; i++) {
1133 QUERY_METRIC *qm = query_metric(qt, i);
1134
@@ -1365,8 +1365,7 @@ static bool query_plan(QUERY_ENGINE_OPS *ops, time_t after_wanted, time_t before
1365 bool switch_tiers = true;
1366
1367 if((ops->r->internal.qt->window.options & RRDR_OPTION_SELECTED_TIER)
1368 - && ops->r->internal.qt->window.tier < storage_tiers
1369 - && query_metric_is_valid_tier(qm, ops->r->internal.qt->window.tier)) {
1368 + && ops->r->internal.qt->window.tier < nd_profile.storage_tiers && query_metric_is_valid_tier(qm, ops->r->internal.qt->window.tier)) {
1369 selected_tier = ops->r->internal.qt->window.tier;
1370 switch_tiers = false;
1371 }
@@ -1396,7 +1395,7 @@ static bool query_plan(QUERY_ENGINE_OPS *ops, time_t after_wanted, time_t before
1395 // check if our selected tier can start the query
1396 if (selected_tier_first_time_s > after_wanted) {
1397 // we need some help from other tiers
1399 - for (size_t tr = (int)selected_tier + 1; tr < storage_tiers && qm->plan.used < QUERY_PLANS_MAX ; tr++) {
1398 + for (size_t tr = (int)selected_tier + 1; tr < nd_profile.storage_tiers && qm->plan.used < QUERY_PLANS_MAX ; tr++) {
1399 if(!query_metric_is_valid_tier(qm, tr))
1400 continue;
1401
@@ -1955,7 +1954,7 @@ static void rrd2rrdr_query_execute(RRDR *r, size_t dim_id_in_rrdr, QUERY_ENGINE_
1954
1955 r->stats.result_points_generated += points_added;
1956 r->stats.db_points_read += ops->db_total_points_read;
1958 - for(size_t tr = 0; tr < storage_tiers ; tr++)
1957 + for(size_t tr = 0; tr < nd_profile.storage_tiers; tr++)
1958 qt->db.tiers[tr].points += ops->db_points_read_per_tier[tr];
1959 }
1960
@@ -1965,7 +1964,7 @@ static void rrd2rrdr_query_execute(RRDR *r, size_t dim_id_in_rrdr, QUERY_ENGINE_
1964 void store_metric_at_tier(RRDDIM *rd, size_t tier, struct rrddim_tier *t, STORAGE_POINT sp, usec_t now_ut);
1965
1966 bool backfill_tier_from_smaller_tiers(RRDDIM *rd, size_t tier, time_t now_s) {
1968 - if(unlikely(tier >= storage_tiers)) return false;
1967 + if(unlikely(tier >= nd_profile.storage_tiers)) return false;
1968 #ifdef ENABLE_DBENGINE
1969 if(default_backfill == RRD_BACKFILL_NONE) return false;
1970 #else
@@ -2232,7 +2231,7 @@ bool query_target_calculate_window(QUERY_TARGET *qt) {
2231 rrdr_relative_window_to_absolute_query(&after_wanted, &before_wanted, NULL, unittest_running);
2232 query_debug_log(":relative2absolute after %ld, before %ld", after_wanted, before_wanted);
2233
2235 - if (natural_points && (options & RRDR_OPTION_SELECTED_TIER) && tier > 0 && storage_tiers > 1) {
2234 + if (natural_points && (options & RRDR_OPTION_SELECTED_TIER) && tier > 0 && nd_profile.storage_tiers > 1) {
2235 update_every = rrdset_find_natural_update_every_for_timeframe(
2236 qt, after_wanted, before_wanted, points_wanted, options, tier);
2237
@@ -3498,7 +3497,7 @@ RRDR *rrd2rrdr(ONEWAYALLOC *owa, QUERY_TARGET *qt) {
3497 if(qt->query.used)
3498 ops = onewayalloc_callocz(owa, qt->query.used, sizeof(QUERY_ENGINE_OPS *));
3499
3501 - size_t capacity = libuv_worker_threads * 10;
3500 + size_t capacity = MAX(netdata_conf_cpus() / 2, 4);
3501 size_t max_queries_to_prepare = (qt->query.used > (capacity - 1)) ? (capacity - 1) : qt->query.used;
3502 size_t queries_prepared = 0;
3503 while(queries_prepared < max_queries_to_prepare) {
src/web/api/queries/weights.c
+5 -5
@@ -147,7 +147,7 @@ static void results_header_to_json(DICTIONARY *results __maybe_unused, BUFFER *w
147
148 buffer_json_member_add_array(wb, "db_points_per_tier");
149 {
150 - for (size_t tier = 0; tier < storage_tiers; tier++)
150 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++)
151 buffer_json_add_array_item_uint64(wb, stats->db_points_per_tier[tier]);
152 }
153 buffer_json_array_close(wb);
@@ -459,7 +459,7 @@ static void results_header_to_json_v2(DICTIONARY *results __maybe_unused, BUFFER
459
460 buffer_json_member_add_array(wb, "db_points_per_tier");
461 {
462 - for (size_t tier = 0; tier < storage_tiers; tier++)
462 + for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++)
463 buffer_json_add_array_item_uint64(wb, stats->db_points_per_tier[tier]);
464 }
465 buffer_json_array_close(wb);
@@ -1295,7 +1295,7 @@ NETDATA_DOUBLE *rrd2rrdr_ks2(
1295 stats->db_queries++;
1296 stats->result_points += r->stats.result_points_generated;
1297 stats->db_points += r->stats.db_points_read;
1298 - for(size_t tr = 0; tr < storage_tiers ; tr++)
1298 + for(size_t tr = 0; tr < nd_profile.storage_tiers; tr++)
1299 stats->db_points_per_tier[tr] += r->internal.qt->db.tiers[tr].points;
1300
1301 if(r->d != 1 || r->internal.qt->query.used != 1) {
@@ -1402,7 +1402,7 @@ static void merge_query_value_to_stats(QUERY_VALUE *qv, WEIGHTS_STATS *stats, si
1402 stats->db_queries += queries;
1403 stats->result_points += qv->result_points;
1404 stats->db_points += qv->points_read;
1405 - for(size_t tier = 0; tier < storage_tiers ; tier++)
1405 + for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++)
1406 stats->db_points_per_tier[tier] += qv->storage_points_per_tier[tier];
1407 }
1408
@@ -1522,7 +1522,7 @@ static void rrdset_weights_multi_dimensional_value(struct query_weights_data *qw
1522 .tier = qwd->qwr->tier,
1523 .timeout_ms = qwd->qwr->timeout_ms,
1524 .query_source = QUERY_SOURCE_API_WEIGHTS,
1525 - .priority = STORAGE_PRIORITY_NORMAL,
1525 + .priority = STORAGE_PRIORITY_SYNCHRONOUS_FIRST,
1526 };
1527
1528 ONEWAYALLOC *owa = onewayalloc_create(16 * 1024);
src/web/api/v1/api_v1_badge/web_buffer_svg.c
+1 -1
@@ -1121,7 +1121,7 @@ int api_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
1121 NULL, &latest_timestamp,
1122 NULL, NULL, NULL,
1123 &value_is_null, NULL, 0, 0,
1124 - QUERY_SOURCE_API_BADGE, STORAGE_PRIORITY_NORMAL);
1124 + QUERY_SOURCE_API_BADGE, STORAGE_PRIORITY_SYNCHRONOUS_FIRST);
1125
1126 // if the value cannot be calculated, show empty badge
1127 if (ret != HTTP_RESP_OK) {
src/web/api/v1/api_v1_data.c
+1 -1
@@ -111,7 +111,7 @@ int api_v1_data(RRDHOST *host, struct web_client *w, char *url) {
111 }
112 else if(!strcmp(name, "tier")) {
113 tier = str2ul(value);
114 - if(tier < storage_tiers)
114 + if(tier < nd_profile.storage_tiers)
115 options |= RRDR_OPTION_SELECTED_TIER;
116 else
117 tier = 0;
src/web/api/v1/api_v1_dbengine.c
+1 -1
@@ -85,7 +85,7 @@ int api_v1_dbengine_stats(RRDHOST *host __maybe_unused, struct web_client *w, ch
85 wb->content_type = CT_APPLICATION_JSON;
86 buffer_no_cacheable(wb);
87 buffer_strcat(wb, "{");
88 - for(size_t tier = 0; tier < storage_tiers ;tier++) {
88 + for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
89 buffer_sprintf(wb, "%s\n\t\"tier%zu\": {", tier?",":"", tier);
90 web_client_api_v1_dbengine_stats_for_tier(wb, tier);
91 buffer_strcat(wb, "\n\t}");
src/web/api/v2/api_v2_data.c
+1 -1
@@ -180,7 +180,7 @@ int api_v2_data(RRDHOST *host __maybe_unused, struct web_client *w, char *url) {
180
181 if(tier_str && *tier_str) {
182 tier = str2ul(tier_str);
183 - if(tier < storage_tiers)
183 + if(tier < nd_profile.storage_tiers)
184 options |= RRDR_OPTION_SELECTED_TIER;
185 else
186 tier = 0;
src/web/api/v2/api_v2_weights.c
+1 -1
@@ -85,7 +85,7 @@ int web_client_api_request_weights(RRDHOST *host, struct web_client *w, char *ur
85
86 else if(!strcmp(name, "tier")) {
87 tier = str2ul(value);
88 - if(tier < storage_tiers)
88 + if(tier < nd_profile.storage_tiers)
89 options |= RRDR_OPTION_SELECTED_TIER;
90 else
91 tier = 0;
src/web/api/web_api.c
+1 -1
@@ -125,7 +125,7 @@ bool web_client_interrupt_callback(void *data) {
125 if(w->interrupt.callback)
126 ret = w->interrupt.callback(w, w->interrupt.callback_data);
127 else
128 - ret = is_socket_closed(w->ofd);
128 + ret = is_socket_closed(w->fd);
129
130 return ret;
131 }
src/web/server/static/static-threaded.c
+35 -180
@@ -29,7 +29,7 @@ static struct web_client *web_client_create_on_fd(POLLINFO *pi) {
29 struct web_client *w;
30
31 w = web_client_get_from_cache();
32 - w->ifd = w->ofd = pi->fd;
32 + w->fd = pi->fd;
33
34 strncpyz(w->client_ip, pi->client_ip, sizeof(w->client_ip) - 1);
35 strncpyz(w->client_port, pi->client_port, sizeof(w->client_port) - 1);
@@ -41,19 +41,18 @@ static struct web_client *web_client_create_on_fd(POLLINFO *pi) {
41
42 int flag = 1;
43 if(unlikely(
44 - web_client_check_conn_tcp(w) && setsockopt(w->ifd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0))
45 - netdata_log_debug(D_WEB_CLIENT, "%llu: failed to enable TCP_NODELAY on socket fd %d.", w->id, w->ifd);
44 + web_client_check_conn_tcp(w) && setsockopt(w->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0))
45 + netdata_log_debug(D_WEB_CLIENT, "%llu: failed to enable TCP_NODELAY on socket fd %d.", w->id, w->fd);
46
47 flag = 1;
48 - if(unlikely(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0))
49 - netdata_log_debug(D_WEB_CLIENT, "%llu: failed to enable SO_KEEPALIVE on socket fd %d.", w->id, w->ifd);
48 + if(unlikely(setsockopt(w->fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0))
49 + netdata_log_debug(D_WEB_CLIENT, "%llu: failed to enable SO_KEEPALIVE on socket fd %d.", w->id, w->fd);
50
51 web_client_update_acl_matches(w);
52 web_client_enable_wait_receive(w);
53
54 web_server_log_connection(w, "CONNECTED");
55
56 - w->pollinfo_slot = pi->slot;
56 return(w);
57 }
58
@@ -73,9 +72,6 @@ struct web_server_static_threaded_worker {
72 volatile size_t receptions;
73 volatile size_t sends;
74 volatile size_t max_concurrent;
76 -
77 - volatile size_t files_read;
78 - volatile size_t file_reads;
75 };
76
77 static long long static_threaded_workers_count = 1;
@@ -92,107 +88,10 @@ static inline int web_server_check_client_status(struct web_client *w) {
88 return 0;
89 }
90
95 -// ----------------------------------------------------------------------------
96 -// web server files
97 -
98 -static void *web_server_file_add_callback(POLLINFO *pi, short int *events, void *data) {
99 - struct web_client *w = (struct web_client *)data;
100 -
101 - worker_is_busy(WORKER_JOB_ADD_FILE);
102 -
103 - worker_private->files_read++;
104 -
105 - netdata_log_debug(D_WEB_CLIENT, "%llu: ADDED FILE READ ON FD %d", w->id, pi->fd);
106 - *events = POLLIN;
107 - pi->data = w;
108 -
109 - worker_is_idle();
110 - return w;
111 -}
112 -
113 -static void web_server_file_del_callback(POLLINFO *pi) {
114 - struct web_client *w = (struct web_client *)pi->data;
115 - netdata_log_debug(D_WEB_CLIENT, "%llu: RELEASE FILE READ ON FD %d", w->id, pi->fd);
116 -
117 - worker_is_busy(WORKER_JOB_DEL_FILE);
118 -
119 - w->pollinfo_filecopy_slot = 0;
120 -
121 - if(unlikely(!w->pollinfo_slot)) {
122 - netdata_log_debug(D_WEB_CLIENT, "%llu: CROSS WEB CLIENT CLEANUP (iFD %d, oFD %d)", w->id, pi->fd, w->ofd);
123 - web_server_log_connection(w, "DISCONNECTED");
124 - web_client_request_done(w);
125 - web_client_release_to_cache(w);
126 - pulse_web_client_disconnected();
127 - }
128 -
129 - worker_is_idle();
130 -}
131 -
132 -static int web_server_file_read_callback(POLLINFO *pi, short int *events) {
133 - int retval = -1;
134 - struct web_client *w = (struct web_client *)pi->data;
135 -
136 - worker_is_busy(WORKER_JOB_READ_FILE);
137 -
138 - // if there is no POLLINFO linked to this, it means the client disconnected
139 - // stop the file reading too
140 - if(unlikely(!w->pollinfo_slot)) {
141 - netdata_log_debug(D_WEB_CLIENT, "%llu: PREVENTED ATTEMPT TO READ FILE ON FD %d, ON CLOSED WEB CLIENT", w->id, pi->fd);
142 - retval = -1;
143 - goto cleanup;
144 - }
145 -
146 - if(unlikely(w->mode != HTTP_REQUEST_MODE_FILECOPY || w->ifd == w->ofd)) {
147 - netdata_log_debug(D_WEB_CLIENT, "%llu: PREVENTED ATTEMPT TO READ FILE ON FD %d, ON NON-FILECOPY WEB CLIENT", w->id, pi->fd);
148 - retval = -1;
149 - goto cleanup;
150 - }
151 -
152 - netdata_log_debug(D_WEB_CLIENT, "%llu: READING FILE ON FD %d", w->id, pi->fd);
153 -
154 - worker_private->file_reads++;
155 - ssize_t ret = unlikely(web_client_read_file(w));
156 -
157 - if(likely(web_client_has_wait_send(w))) {
158 - POLLJOB *p = pi->p; // our POLLJOB
159 - POLLINFO *wpi = pollinfo_from_slot(p, w->pollinfo_slot); // POLLINFO of the client socket
160 -
161 - netdata_log_debug(D_WEB_CLIENT, "%llu: SIGNALING W TO SEND (iFD %d, oFD %d)", w->id, pi->fd, wpi->fd);
162 - p->fds[wpi->slot].events |= POLLOUT;
163 - }
164 -
165 - if(unlikely(ret <= 0 || w->ifd == w->ofd)) {
166 - netdata_log_debug(D_WEB_CLIENT, "%llu: DONE READING FILE ON FD %d", w->id, pi->fd);
167 - retval = -1;
168 - goto cleanup;
169 - }
170 -
171 - *events = POLLIN;
172 - retval = 0;
173 -
174 -cleanup:
175 - worker_is_idle();
176 - return retval;
177 -}
178 -
179 -static int web_server_file_write_callback(POLLINFO *pi, short int *events) {
180 - (void)pi;
181 - (void)events;
182 -
183 - worker_is_busy(WORKER_JOB_WRITE_FILE);
184 - netdata_log_error("Writing to web files is not supported!");
185 - worker_is_idle();
186 -
187 - return -1;
188 -}
189 -
91 // ----------------------------------------------------------------------------
92 // web server clients
93
193 -static void *web_server_add_callback(POLLINFO *pi, short int *events, void *data) {
194 - (void)data; // Suppress warning on unused argument
195 -
94 +static void *web_server_add_callback(POLLINFO *pi, nd_poll_event_t *events, void *data __maybe_unused) {
95 worker_is_busy(WORKER_JOB_ADD_CONNECTION);
96 worker_private->connected++;
97
@@ -200,7 +99,7 @@ static void *web_server_add_callback(POLLINFO *pi, short int *events, void *data
99 if(unlikely(concurrent > worker_private->max_concurrent))
100 worker_private->max_concurrent = concurrent;
101
203 - *events = POLLIN;
102 + *events = ND_POLL_READ;
103
104 netdata_log_debug(D_WEB_CLIENT_ACCESS, "LISTENER on %d: new connection.", pi->fd);
105 struct web_client *w = web_client_create_on_fd(pi);
@@ -212,17 +111,17 @@ static void *web_server_add_callback(POLLINFO *pi, short int *events, void *data
111 }
112
113 if ((web_client_check_conn_tcp(w)) && (netdata_ssl_web_server_ctx)) {
215 - sock_delnonblock(w->ifd);
114 + sock_setnonblock(w->fd, false);
115
116 //Read the first 7 bytes from the message, but the message
117 //is not removed from the queue, because we are using MSG_PEEK
118 char test[8];
220 - if ( recv(w->ifd,test, 7, MSG_PEEK) == 7 ) {
119 + if ( recv(w->fd,test, 7, MSG_PEEK) == 7 ) {
120 test[7] = '\0';
121 }
122 else {
123 // we couldn't read 7 bytes
225 - sock_setnonblock(w->ifd);
124 + sock_setnonblock(w->fd, true);
125 goto cleanup;
126 }
127
@@ -232,11 +131,11 @@ static void *web_server_add_callback(POLLINFO *pi, short int *events, void *data
131 }
132 else {
133 // SSL
235 - if(!netdata_ssl_open(&w->ssl, netdata_ssl_web_server_ctx, w->ifd) || !netdata_ssl_accept(&w->ssl))
134 + if(!netdata_ssl_open(&w->ssl, netdata_ssl_web_server_ctx, w->fd) || !netdata_ssl_accept(&w->ssl))
135 WEB_CLIENT_IS_DEAD(w);
136 }
137
239 - sock_setnonblock(w->ifd);
138 + sock_setnonblock(w->fd, true);
139 }
140
141 netdata_log_debug(D_WEB_CLIENT, "%llu: ADDED CLIENT FD %d", w->id, pi->fd);
@@ -254,28 +153,19 @@ static void web_server_del_callback(POLLINFO *pi) {
153
154 struct web_client *w = (struct web_client *)pi->data;
155
257 - w->pollinfo_slot = 0;
258 - if(unlikely(w->pollinfo_filecopy_slot)) {
259 - POLLINFO *fpi = pollinfo_from_slot(pi->p, w->pollinfo_filecopy_slot); // POLLINFO of the client socket
260 - (void)fpi;
156 + if(web_client_flag_check(w, WEB_CLIENT_FLAG_DONT_CLOSE_SOCKET))
157 + pi->flags |= POLLINFO_FLAG_DONT_CLOSE;
158
262 - netdata_log_debug(D_WEB_CLIENT, "%llu: THE CLIENT WILL BE FRED BY READING FILE JOB ON FD %d", w->id, fpi->fd);
263 - }
264 - else {
265 - if(web_client_flag_check(w, WEB_CLIENT_FLAG_DONT_CLOSE_SOCKET))
266 - pi->flags |= POLLINFO_FLAG_DONT_CLOSE;
267 -
268 - netdata_log_debug(D_WEB_CLIENT, "%llu: CLOSING CLIENT FD %d", w->id, pi->fd);
269 - web_server_log_connection(w, "DISCONNECTED");
270 - web_client_request_done(w);
271 - web_client_release_to_cache(w);
272 - pulse_web_client_disconnected();
273 - }
159 + netdata_log_debug(D_WEB_CLIENT, "%llu: CLOSING CLIENT FD %d", w->id, pi->fd);
160 + web_server_log_connection(w, "DISCONNECTED");
161 + web_client_request_done(w);
162 + web_client_release_to_cache(w);
163 + pulse_web_client_disconnected();
164
165 worker_is_idle();
166 }
167
278 -static int web_server_rcv_callback(POLLINFO *pi, short int *events) {
168 +static int web_server_rcv_callback(POLLINFO *pi, nd_poll_event_t *events) {
169 int ret = -1;
170 worker_is_busy(WORKER_JOB_RCV_DATA);
171
@@ -296,57 +186,21 @@ static int web_server_rcv_callback(POLLINFO *pi, short int *events) {
186 if (unlikely(w->mode == HTTP_REQUEST_MODE_STREAM)) {
187 web_client_send(w);
188 }
189 + else if(unlikely(w->fd == fd && web_client_has_wait_receive(w)))
190 + *events |= ND_POLL_READ;
191
300 - else if(unlikely(w->mode == HTTP_REQUEST_MODE_FILECOPY)) {
301 - if(w->pollinfo_filecopy_slot == 0) {
302 - netdata_log_debug(D_WEB_CLIENT, "%llu: FILECOPY DETECTED ON FD %d", w->id, pi->fd);
303 -
304 - if (unlikely(w->ifd != -1 && w->ifd != w->ofd && w->ifd != fd)) {
305 - // add a new socket to poll_events, with the same
306 - netdata_log_debug(D_WEB_CLIENT, "%llu: CREATING FILECOPY SLOT ON FD %d", w->id, pi->fd);
307 -
308 - POLLINFO *fpi = poll_add_fd(
309 - pi->p
310 - , w->ifd
311 - , pi->port_acl
312 - , 0
313 - , POLLINFO_FLAG_CLIENT_SOCKET
314 - , "FILENAME"
315 - , ""
316 - , ""
317 - , web_server_file_add_callback
318 - , web_server_file_del_callback
319 - , web_server_file_read_callback
320 - , web_server_file_write_callback
321 - , (void *) w
322 - );
323 -
324 - if(fpi)
325 - w->pollinfo_filecopy_slot = fpi->slot;
326 - else {
327 - netdata_log_error("Failed to add filecopy fd. Closing client.");
328 - ret = -1;
329 - goto cleanup;
330 - }
331 - }
332 - }
333 - }
334 - else {
335 - if(unlikely(w->ifd == fd && web_client_has_wait_receive(w)))
336 - *events |= POLLIN;
337 - }
192 + if(unlikely(w->fd == fd && web_client_has_wait_send(w)))
193 + *events |= ND_POLL_WRITE;
194
339 - if(unlikely(w->ofd == fd && web_client_has_wait_send(w)))
340 - *events |= POLLOUT;
195 } else if(unlikely(bytes < 0)) {
196 ret = -1;
197 goto cleanup;
198 } else if (unlikely(bytes == 0)) {
345 - if(unlikely(w->ifd == fd && web_client_has_ssl_wait_receive(w)))
346 - *events |= POLLIN;
199 + if(unlikely(w->fd == fd && web_client_has_ssl_wait_receive(w)))
200 + *events |= ND_POLL_READ;
201
348 - if(unlikely(w->ofd == fd && web_client_has_ssl_wait_send(w)))
349 - *events |= POLLOUT;
202 + if(unlikely(w->fd == fd && web_client_has_ssl_wait_send(w)))
203 + *events |= ND_POLL_WRITE;
204 }
205
206 ret = web_server_check_client_status(w);
@@ -356,7 +210,7 @@ cleanup:
210 return ret;
211 }
212
359 -static int web_server_snd_callback(POLLINFO *pi, short int *events) {
213 +static int web_server_snd_callback(POLLINFO *pi, nd_poll_event_t *events) {
214 int retval = -1;
215 worker_is_busy(WORKER_JOB_SND_DATA);
216
@@ -374,11 +228,11 @@ static int web_server_snd_callback(POLLINFO *pi, short int *events) {
228 goto cleanup;
229 }
230
377 - if(unlikely(w->ifd == fd && web_client_has_wait_receive(w)))
378 - *events |= POLLIN;
231 + if(unlikely(w->fd == fd && web_client_has_wait_receive(w)))
232 + *events |= ND_POLL_READ;
233
380 - if(unlikely(w->ofd == fd && web_client_has_wait_send(w)))
381 - *events |= POLLOUT;
234 + if(unlikely(w->fd == fd && web_client_has_wait_send(w)))
235 + *events |= ND_POLL_WRITE;
236
237 retval = web_server_check_client_status(w);
238
@@ -437,7 +291,8 @@ void *socket_listen_main_static_threaded_worker(void *ptr) {
291 , NULL
292 , web_client_first_request_timeout
293 , web_client_timeout
440 - , default_rrd_update_every * 1000 // timer_milliseconds
294 + ,
295 + nd_profile.update_every * 1000 // timer_milliseconds
296 , ptr // timer_data
297 , worker_private->max_sockets
298 );
src/web/server/web_client.c
+44 -170
@@ -82,22 +82,6 @@ static inline int bad_request_multiple_dashboard_versions(struct web_client *w)
82 return HTTP_RESP_BAD_REQUEST;
83 }
84
85 -static inline int web_client_cork_socket(struct web_client *w __maybe_unused) {
86 -#ifdef TCP_CORK
87 - if(likely(web_client_check_conn_tcp(w) && !w->tcp_cork && w->ofd != -1)) {
88 - w->tcp_cork = true;
89 - if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
90 - netdata_log_error("%llu: failed to enable TCP_CORK on socket.", w->id);
91 -
92 - w->tcp_cork = false;
93 - return -1;
94 - }
95 - }
96 -#endif /* TCP_CORK */
97 -
98 - return 0;
99 -}
100 -
85 static inline void web_client_enable_wait_from_ssl(struct web_client *w) {
86 if (w->ssl.ssl_errno == SSL_ERROR_WANT_READ)
87 web_client_enable_ssl_wait_receive(w);
@@ -109,22 +93,6 @@ static inline void web_client_enable_wait_from_ssl(struct web_client *w) {
93 }
94 }
95
112 -static inline int web_client_uncork_socket(struct web_client *w __maybe_unused) {
113 -#ifdef TCP_CORK
114 - if(likely(w->tcp_cork && w->ofd != -1)) {
115 - w->tcp_cork = false;
116 - if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
117 - netdata_log_error("%llu: failed to disable TCP_CORK on socket.", w->id);
118 - w->tcp_cork = true;
119 - return -1;
120 - }
121 - }
122 -#endif /* TCP_CORK */
123 -
124 - w->tcp_cork = false;
125 - return 0;
126 -}
127 -
96 static inline char *strip_control_characters(char *url) {
97 if(!url) return "";
98
@@ -221,7 +189,7 @@ void web_client_log_completed_request(struct web_client *w, bool update_web_stat
189 struct timeval tv;
190 now_monotonic_high_precision_timeval(&tv);
191
224 - size_t size = (w->mode == HTTP_REQUEST_MODE_FILECOPY) ? w->response.rlen : w->response.data->len;
192 + size_t size = w->response.data->len;
193 size_t sent = w->response.zoutput ? (size_t)w->response.zstream.total_out : size;
194
195 if(update_web_stats)
@@ -276,26 +244,11 @@ void web_client_log_completed_request(struct web_client *w, bool update_web_stat
244 }
245
246 void web_client_request_done(struct web_client *w) {
279 - web_client_uncork_socket(w);
247 + sock_setcork(w->fd, false);
248
249 netdata_log_debug(D_WEB_CLIENT, "%llu: Resetting client.", w->id);
250
251 web_client_log_completed_request(w, true);
284 -
285 - if(unlikely(w->mode == HTTP_REQUEST_MODE_FILECOPY)) {
286 - if(w->ifd != w->ofd) {
287 - netdata_log_debug(D_WEB_CLIENT, "%llu: Closing filecopy input file descriptor %d.", w->id, w->ifd);
288 -
289 - if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
290 - if (w->ifd != -1){
291 - close(w->ifd);
292 - }
293 - }
294 -
295 - w->ifd = w->ofd;
296 - }
297 - }
298 -
252 web_client_reset_allocations(w, false);
253
254 w->mode = HTTP_REQUEST_MODE_GET;
@@ -311,7 +264,6 @@ void web_client_request_done(struct web_client *w) {
264 web_client_disable_wait_send(w);
265
266 w->response.has_cookies = false;
314 - w->response.rlen = 0;
267 w->response.sent = 0;
268 w->response.code = 0;
269 w->response.zoutput = false;
@@ -467,7 +419,7 @@ static bool find_filename_to_serve(const char *filename, char *dst, size_t dst_l
419 return true;
420 }
421
470 -static int mysendfile(struct web_client *w, char *filename) {
422 +static int web_server_static_file(struct web_client *w, char *filename) {
423 netdata_log_debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename);
424
425 if(!http_can_access_dashboard(w))
@@ -511,10 +463,24 @@ static int mysendfile(struct web_client *w, char *filename) {
463 if(is_dir && !web_client_flag_check(w, WEB_CLIENT_FLAG_PATH_HAS_TRAILING_SLASH))
464 return append_slash_to_url_and_redirect(w);
465
466 + buffer_flush(w->response.data);
467 + buffer_need_bytes(w->response.data, (size_t)statbuf.st_size);
468 + w->response.data->len = (size_t)statbuf.st_size;
469 +
470 // open the file
515 - w->ifd = open(web_filename, O_NONBLOCK, O_RDONLY | O_CLOEXEC);
516 - if(w->ifd == -1) {
517 - w->ifd = w->ofd;
471 + int fd = open(web_filename, O_RDONLY | O_CLOEXEC);
472 +
473 + // read the file
474 + if(fd != -1 && read(fd, w->response.data->buffer, statbuf.st_size) != statbuf.st_size) {
475 + // cannot read the whole file
476 + nd_log(NDLS_DAEMON, NDLP_ERR, "Web server failed to read file '%s'", web_filename);
477 + close(fd);
478 + fd = -1;
479 + }
480 +
481 + // check for failures
482 + if(fd == -1) {
483 + buffer_flush(w->response.data);
484
485 if(errno == EBUSY || errno == EAGAIN) {
486 netdata_log_error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, web_filename);
@@ -532,24 +498,23 @@ static int mysendfile(struct web_client *w, char *filename) {
498 return HTTP_RESP_NOT_FOUND;
499 }
500 }
535 -
536 - sock_setnonblock(w->ifd);
501 + else
502 + close(fd);
503
504 w->response.data->content_type = contenttype_for_filename(web_filename);
539 - netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%"PRId64" bytes, ifd %d, ofd %d).", w->id, web_filename, (int64_t)statbuf.st_size, w->ifd, w->ofd);
505 + netdata_log_debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%"PRId64" bytes, fd %d).", w->id, web_filename, (int64_t)statbuf.st_size, w->fd);
506 +
507 + w->mode = HTTP_REQUEST_MODE_GET;
508 + web_client_enable_wait_send(w);
509 + web_client_disable_wait_receive(w);
510
541 - w->mode = HTTP_REQUEST_MODE_FILECOPY;
542 - web_client_enable_wait_receive(w);
543 - web_client_disable_wait_send(w);
544 - buffer_flush(w->response.data);
545 - buffer_need_bytes(w->response.data, (size_t)statbuf.st_size);
546 - w->response.rlen = (size_t)statbuf.st_size;
511 #ifdef __APPLE__
512 w->response.data->date = statbuf.st_mtimespec.tv_sec;
513 #else
514 w->response.data->date = statbuf.st_mtim.tv_sec;
515 #endif
516 w->response.data->expires = now_realtime_sec() + 86400;
517 +
518 buffer_cacheable(w->response.data);
519
520 return HTTP_RESP_OK;
@@ -814,9 +779,9 @@ static inline ssize_t web_client_send_data(struct web_client *w,const void *buf,
779 bytes = netdata_ssl_write(&w->ssl, buf, len);
780 web_client_enable_wait_from_ssl(w);
781 } else
817 - bytes = send(w->ofd, buf, len, flags);
782 + bytes = send(w->fd, buf, len, flags);
783 } else if (web_client_check_conn_tcp(w) || web_client_check_conn_unix(w))
819 - bytes = send(w->ofd, buf, len, flags);
784 + bytes = send(w->fd, buf, len, flags);
785 else
786 bytes = -999;
787
@@ -923,9 +888,9 @@ void web_client_build_http_header(struct web_client *w) {
888 if(likely(w->flags & WEB_CLIENT_CHUNKED_TRANSFER))
889 buffer_strcat(w->response.header_output, "Transfer-Encoding: chunked\r\n");
890 else {
926 - if(likely((w->response.data->len || w->response.rlen))) {
891 + if(likely(w->response.data->len)) {
892 // we know the content length, put it
928 - buffer_sprintf(w->response.header_output, "Content-Length: %zu\r\n", w->response.data->len? w->response.data->len: w->response.rlen);
893 + buffer_sprintf(w->response.header_output, "Content-Length: %zu\r\n", (size_t)w->response.data->len);
894 }
895 else {
896 // we don't know the content length, disable keep-alive
@@ -952,7 +917,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
917 , buffer_tostring(w->response.header_output)
918 );
919
955 - web_client_cork_socket(w);
920 + sock_setcork(w->fd, true);
921
922 size_t count = 0;
923 ssize_t bytes;
@@ -963,7 +928,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
928 web_client_enable_wait_from_ssl(w);
929 }
930 else {
966 - while((bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0)) == -1) {
931 + while((bytes = send(w->fd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0)) == -1) {
932 count++;
933
934 if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
@@ -974,7 +939,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
939 }
940 }
941 else if(web_client_check_conn_tcp(w) || web_client_check_conn_unix(w)) {
977 - while((bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0)) == -1) {
942 + while((bytes = send(w->fd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0)) == -1) {
943 count++;
944
945 if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
@@ -1285,7 +1250,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1250 }
1251
1252 buffer_flush(w->response.data);
1288 - return mysendfile(w, filename);
1253 + return web_server_static_file(w, filename);
1254 }
1255
1256 static bool web_server_log_transport(BUFFER *wb, void *ptr) {
@@ -1365,7 +1330,6 @@ void web_client_process_request_from_web_server(struct web_client *w) {
1330 w->response.code = HTTP_RESP_OK;
1331 break;
1332
1368 - case HTTP_REQUEST_MODE_FILECOPY:
1333 case HTTP_REQUEST_MODE_POST:
1334 case HTTP_REQUEST_MODE_GET:
1335 case HTTP_REQUEST_MODE_PUT:
@@ -1508,28 +1472,6 @@ void web_client_process_request_from_web_server(struct web_client *w) {
1472 w->id, (size_t)w->response.data->len);
1473 break;
1474
1511 - case HTTP_REQUEST_MODE_FILECOPY:
1512 - if(w->response.rlen) {
1513 - netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %zu bytes to client.", w->id, w->response.rlen);
1514 - web_client_enable_wait_receive(w);
1515 -
1516 - /*
1517 - // utilize the kernel sendfile() for copying the file to the socket.
1518 - // this block of code can be commented, without anything missing.
1519 - // when it is commented, the program will copy the data using async I/O.
1520 - {
1521 - long len = sendfile(w->ofd, w->ifd, NULL, w->response.data->rbytes);
1522 - if(len != w->response.data->rbytes)
1523 - netdata_log_error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->response.data->rbytes, len);
1524 - else
1525 - web_client_request_done(w);
1526 - }
1527 - */
1528 - }
1529 - else
1530 - netdata_log_debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
1531 - break;
1532 -
1475 default:
1476 fatal("%llu: Unknown client mode %u.", w->id, w->mode);
1477 break;
@@ -1627,13 +1569,6 @@ ssize_t web_client_send_deflate(struct web_client *w)
1569 if(t < 0) return t;
1570 }
1571
1630 - if(w->mode == HTTP_REQUEST_MODE_FILECOPY && web_client_has_wait_receive(w) && w->response.rlen && w->response.rlen > w->response.data->len) {
1631 - // we have to wait, more data will come
1632 - netdata_log_debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
1633 - web_client_disable_wait_send(w);
1634 - return t;
1635 - }
1636 -
1572 if(unlikely(!web_client_has_keepalive(w))) {
1573 netdata_log_debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
1574 WEB_CLIENT_IS_DEAD(w);
@@ -1669,8 +1604,10 @@ ssize_t web_client_send_deflate(struct web_client *w)
1604
1605 // ask for FINISH if we have all the input
1606 int flush = Z_SYNC_FLUSH;
1672 - if((w->mode == HTTP_REQUEST_MODE_GET || w->mode == HTTP_REQUEST_MODE_POST || w->mode == HTTP_REQUEST_MODE_PUT || w->mode == HTTP_REQUEST_MODE_DELETE)
1673 - || (w->mode == HTTP_REQUEST_MODE_FILECOPY && !web_client_has_wait_receive(w) && w->response.data->len == w->response.rlen)) {
1607 + if((w->mode == HTTP_REQUEST_MODE_GET ||
1608 + w->mode == HTTP_REQUEST_MODE_POST ||
1609 + w->mode == HTTP_REQUEST_MODE_PUT ||
1610 + w->mode == HTTP_REQUEST_MODE_DELETE)) {
1611 flush = Z_FINISH;
1612 netdata_log_debug(D_DEFLATE, "%llu: Requesting Z_FINISH, if possible.", w->id);
1613 }
@@ -1735,13 +1672,6 @@ ssize_t web_client_send(struct web_client *w) {
1672 // A. we have done everything
1673 // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
1674
1738 - if(w->mode == HTTP_REQUEST_MODE_FILECOPY && web_client_has_wait_receive(w) && w->response.rlen && w->response.rlen > w->response.data->len) {
1739 - // we have to wait, more data will come
1740 - netdata_log_debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
1741 - web_client_disable_wait_send(w);
1742 - return 0;
1743 - }
1744 -
1675 if(unlikely(!web_client_has_keepalive(w))) {
1676 netdata_log_debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
1677 WEB_CLIENT_IS_DEAD(w);
@@ -1770,63 +1700,7 @@ ssize_t web_client_send(struct web_client *w) {
1700 return(bytes);
1701 }
1702
1773 -ssize_t web_client_read_file(struct web_client *w)
1774 -{
1775 - if(unlikely(w->response.rlen > w->response.data->size))
1776 - buffer_need_bytes(w->response.data, w->response.rlen - w->response.data->size);
1777 -
1778 - if(unlikely(w->response.rlen <= w->response.data->len))
1779 - return 0;
1780 -
1781 - ssize_t left = (ssize_t)(w->response.rlen - w->response.data->len);
1782 - ssize_t bytes = read(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t)left);
1783 - if(likely(bytes > 0)) {
1784 - size_t old = w->response.data->len;
1785 - (void)old;
1786 -
1787 - w->response.data->len += bytes;
1788 - w->response.data->buffer[w->response.data->len] = '\0';
1789 -
1790 - netdata_log_debug(D_WEB_CLIENT, "%llu: Read %zd bytes.", w->id, bytes);
1791 - netdata_log_debug(D_WEB_DATA, "%llu: Read data: '%s'.", w->id, &w->response.data->buffer[old]);
1792 -
1793 - web_client_enable_wait_send(w);
1794 -
1795 - if(w->response.rlen && w->response.data->len >= w->response.rlen)
1796 - web_client_disable_wait_receive(w);
1797 - }
1798 - else if(likely(bytes == 0)) {
1799 - netdata_log_debug(D_WEB_CLIENT, "%llu: Out of input file data.", w->id);
1800 -
1801 - // if we cannot read, it means we have an error on input.
1802 - // if however, we are copying a file from ifd to ofd, we should not return an error.
1803 - // in this case, the error should be generated when the file has been sent to the client.
1804 -
1805 - // we are copying data from ifd to ofd
1806 - // let it finish copying...
1807 - web_client_disable_wait_receive(w);
1808 -
1809 - netdata_log_debug(D_WEB_CLIENT, "%llu: Read the whole file.", w->id);
1810 -
1811 - if(web_server_mode != WEB_SERVER_MODE_STATIC_THREADED) {
1812 - if (w->ifd != w->ofd) close(w->ifd);
1813 - }
1814 -
1815 - w->ifd = w->ofd;
1816 - }
1817 - else {
1818 - netdata_log_debug(D_WEB_CLIENT, "%llu: read data failed.", w->id);
1819 - WEB_CLIENT_IS_DEAD(w);
1820 - }
1821 -
1822 - return(bytes);
1823 -}
1824 -
1825 -ssize_t web_client_receive(struct web_client *w)
1826 -{
1827 - if(unlikely(w->mode == HTTP_REQUEST_MODE_FILECOPY))
1828 - return web_client_read_file(w);
1829 -
1703 +ssize_t web_client_receive(struct web_client *w) {
1704 ssize_t bytes;
1705
1706 // do we have any space for more data?
@@ -1842,11 +1716,11 @@ ssize_t web_client_receive(struct web_client *w)
1716 web_client_enable_wait_from_ssl(w);
1717 }
1718 else {
1845 - bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
1719 + bytes = recv(w->fd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
1720 }
1721 }
1722 else if(web_client_check_conn_tcp(w) || web_client_check_conn_unix(w)) {
1849 - bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
1723 + bytes = recv(w->fd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
1724 }
1725 else // other connection methods
1726 bytes = -1;
@@ -1940,7 +1814,7 @@ void web_client_reuse_from_cache(struct web_client *w) {
1814 // zero everything
1815 memset(w, 0, sizeof(struct web_client));
1816
1943 - w->ifd = w->ofd = -1;
1817 + w->fd = -1;
1818 w->statistics.memory_accounting = statistics_memory_accounting;
1819 w->use_count = use_count;
1820
src/web/server/web_client.h
+3 -15
@@ -140,15 +140,10 @@ struct response {
140 BUFFER *header; // our response header
141 BUFFER *header_output; // internal use
142 BUFFER *data; // our response data buffer
143 -
143 + size_t sent; // current data length sent to output
144 short int code; // the HTTP response code
145 bool has_cookies;
146 -
147 - size_t rlen; // if non-zero, the excepted size of ifd (input of firecopy)
148 - size_t sent; // current data length sent to output
149 -
150 - bool zoutput; // if set to 1, web_client_send() will send compressed data
151 -
146 + bool zoutput; // if set to 1, web_client_send() will send compressed data
147 bool zinitialized;
148 z_stream zstream; // zlib stream for sending compressed output to client
149 size_t zsent; // the compressed bytes we have sent to the client
@@ -174,9 +169,7 @@ struct web_client {
169 size_t header_parse_tries;
170 size_t header_parse_last_size;
171
177 - bool tcp_cork;
178 - int ifd;
179 - int ofd;
172 + int fd;
173
174 char client_ip[INET6_ADDRSTRLEN]; // Defined buffer sizes include null-terminators
175 char client_port[NI_MAXSERV];
@@ -196,10 +189,6 @@ struct web_client {
189
190 BUFFER *payload; // when this request is a POST, this has the payload
191
199 - // STATIC-THREADED WEB SERVER MEMBERS
200 - size_t pollinfo_slot; // POLLINFO slot of the web client
201 - size_t pollinfo_filecopy_slot; // POLLINFO slot of the file read
202 -
192 NETDATA_SSL ssl;
193
194 struct {
@@ -241,7 +230,6 @@ int web_client_service_unavailable(struct web_client *w);
230
231 ssize_t web_client_send(struct web_client *w);
232 ssize_t web_client_receive(struct web_client *w);
244 -ssize_t web_client_read_file(struct web_client *w);
233
234 void web_client_process_request_from_web_server(struct web_client *w);
235 void web_client_request_done(struct web_client *w);
src/web/server/web_client_cache.c
-1
@@ -125,7 +125,6 @@ struct web_client *web_client_get_from_cache(void) {
125 }
126
127 void web_client_release_to_cache(struct web_client *w) {
128 -
128 netdata_ssl_close(&w->ssl);
129
130 // unlink it from the used
src/web/server/web_server.c
+6 -6
@@ -95,32 +95,32 @@ void web_client_update_acl_matches(struct web_client *w) {
95
96 if(!(w->port_acl & HTTP_ACL_TRANSPORTS_WITHOUT_CLIENT_IP_VALIDATION)) {
97 if (!web_allow_dashboard_from ||
98 - connection_allowed(w->ifd, w->client_ip, w->client_host, sizeof(w->client_host),
98 + connection_allowed(w->fd, w->client_ip, w->client_host, sizeof(w->client_host),
99 web_allow_dashboard_from, "dashboard", web_allow_dashboard_dns))
100 w->acl |= HTTP_ACL_DASHBOARD;
101
102 if (!web_allow_registry_from ||
103 - connection_allowed(w->ifd, w->client_ip, w->client_host, sizeof(w->client_host),
103 + connection_allowed(w->fd, w->client_ip, w->client_host, sizeof(w->client_host),
104 web_allow_registry_from, "registry", web_allow_registry_dns))
105 w->acl |= HTTP_ACL_REGISTRY;
106
107 if (!web_allow_badges_from ||
108 - connection_allowed(w->ifd, w->client_ip, w->client_host, sizeof(w->client_host),
108 + connection_allowed(w->fd, w->client_ip, w->client_host, sizeof(w->client_host),
109 web_allow_badges_from, "badges", web_allow_badges_dns))
110 w->acl |= HTTP_ACL_BADGES;
111
112 if (!web_allow_mgmt_from ||
113 - connection_allowed(w->ifd, w->client_ip, w->client_host, sizeof(w->client_host),
113 + connection_allowed(w->fd, w->client_ip, w->client_host, sizeof(w->client_host),
114 web_allow_mgmt_from, "management", web_allow_mgmt_dns))
115 w->acl |= HTTP_ACL_MANAGEMENT;
116
117 if (!web_allow_streaming_from ||
118 - connection_allowed(w->ifd, w->client_ip, w->client_host, sizeof(w->client_host),
118 + connection_allowed(w->fd, w->client_ip, w->client_host, sizeof(w->client_host),
119 web_allow_streaming_from, "streaming", web_allow_streaming_dns))
120 w->acl |= HTTP_ACL_STREAMING;
121
122 if (!web_allow_netdataconf_from ||
123 - connection_allowed(w->ifd, w->client_ip, w->client_host, sizeof(w->client_host),
123 + connection_allowed(w->fd, w->client_ip, w->client_host, sizeof(w->client_host),
124 web_allow_netdataconf_from, "netdata.conf", web_allow_netdataconf_dns))
125 w->acl |= HTTP_ACL_NETDATACONF;
126 }
system/systemd/netdata.service.in
+1 -1
@@ -1,6 +1,6 @@
1 # SPDX-License-Identifier: GPL-3.0-or-later
2 [Unit]
3 -Description=Real time performance monitoring
3 +Description=infrastructure monitoring and troubleshooting, transformed
4
5 # append here other services you want netdata to wait for them to start
6 After=network.target network-online.target nss-lookup.target
system/systemd/netdata.service.v235.in
+1 -1
@@ -1,6 +1,6 @@
1 # SPDX-License-Identifier: GPL-3.0-or-later
2 [Unit]
3 -Description=Real time performance monitoring
3 +Description=infrastructure monitoring and troubleshooting, transformed
4
5 # append here other services you want netdata to wait for them to start
6 After=network.target network-online.target nss-lookup.target