DBMON-6747 ClickHouse Async Inserts Monitoring Buffer State Collection - #24517
DBMON-6747 ClickHouse Async Inserts Monitoring Buffer State Collection#24517jenny-chung wants to merge 34 commits into
Conversation
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 069b175 | Docs | Datadog PR Page | Give us feedback! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 918a5c439d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| arbitrary_types_allowed=True, | ||
| frozen=True, | ||
| ) | ||
| asynchronous_insert_buffer_snapshot: Optional[AsynchronousInsertBufferSnapshot] = None |
There was a problem hiding this comment.
Update the defaults test for the new field
Adding this InstanceConfig annotation without adding asynchronous_insert_buffer_snapshot to clickhouse/tests/test_config_defaults.py makes test_all_config_defaults fail: that test compares InstanceConfig.__annotations__ against EXPECTED_DEFAULTS and explicitly fails for any missing field. The ClickHouse unit suite will therefore reject this change even though the runtime default is provided in dict_defaults.py.
Useful? React with 👍 / 👎.
| self.statement_samples = ClickhouseStatementSamples( | ||
| self, self._config.query_samples, self._config.asynchronous_insert_buffer_snapshot | ||
| ) |
There was a problem hiding this comment.
Decouple buffer snapshots from query samples
This wires the buffer snapshot only through ClickhouseStatementSamples, which is constructed under the query_samples.enabled guard immediately above and scheduled at query_samples.collection_interval. If an operator enables asynchronous_insert_buffer_snapshot while disabling query samples, no buffer events are emitted; if they raise the query-samples interval above the buffer interval, snapshots are silently under-collected. Since the new option is documented as its own DBM feature requiring only dbm: true, it needs to be scheduled when the buffer feature is enabled rather than depending on query samples.
Useful? React with 👍 / 👎.
75590a5 to
23635e1
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e3f1f53ed3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if buffer_config.enabled: | ||
| enabled_intervals.append(buffer_config.collection_interval) | ||
| collection_interval = ( | ||
| math.gcd(*(int(i) for i in enabled_intervals)) if enabled_intervals else samples_collection_interval |
There was a problem hiding this comment.
Preserve fractional collection intervals
When an existing user sets query_samples.collection_interval: 0.5 or enables the new buffer snapshot with collection_interval: 0.5, this truncates the positive float to 0, so the subsequent rate_limit=1 / collection_interval raises during check initialization. The config schema accepts number values and validation only rejects intervals <= 0, so positive fractional intervals that previously worked now crash instead of being scheduled at the requested rate.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed division by zero error to preserve fractional collection intervals - c7e90e7
| ## Enable collection of asynchronous insert buffer snapshots from system.asynchronous_inserts. | ||
| ## Requires `dbm: true`. Sends per-buffer records as DBM events (dbm_type="async_inserts_buffer") | ||
| ## including the query text, showing bytes pending, pending entry count, and oldest | ||
| ## entry age per buffer. |
There was a problem hiding this comment.
This says oldest entry age per buffer but looks like we emit flush_deadline_us. Lets confirm the correct behavior with the CH folks and update this description and the field correctly to match what we emit
There was a problem hiding this comment.
Waiting on confirmation with CH team, but description has been updated in the meantime 5d4c294
| raw=True, | ||
| ) | ||
| return [] | ||
| except Exception as e: |
There was a problem hiding this comment.
On ClickHouse versions that predate system.asynchronous_inserts, this query raises "Unknown table" every cycle, which this branch logs as a full ERROR stack trace and counts as async_inserts_buffer.error in every collection_interval. Let's have a version check or detect the "unknown table" case and downgrade it to a single warning + skip.
There was a problem hiding this comment.
Added case to detect unknown table and downgrades it to single warning + skip in df0ee69
There was a problem hiding this comment.
we'd want to skip the collection entirely when we see UNKNOWN_TABLE_ERROR_CODE or if version check is possible to avoid the check when the table is not present that would be even better too
| elapsed_ms, | ||
| ) | ||
|
|
||
| def _collect_buffer_snapshot(self): |
There was a problem hiding this comment.
https://github.com/DataDog/integrations-core/blob/master/AGENTS.md#python-code-style asks that any new code should provide type hints. let's add these to all new methods
|
|
||
| try: | ||
| self._collect_samples() | ||
| if self._config.enabled and time.time() - self._last_samples_time >= self._collection_interval: |
There was a problem hiding this comment.
let's use one time.time() call here
| assert check.statement_samples._expected_collection_interval == 5 | ||
|
|
||
|
|
||
| def test_query_samples_independent_of_buffer(): |
There was a problem hiding this comment.
this is nice, but there's no mirror test for the buffer side - i.e. that _collect_buffer_snapshot only fires once per _buffer_collection_interval via _last_buffer_snapshot_time
There was a problem hiding this comment.
Added mirror test for buffer side - 09c32cc
…r greater than 0 validation
…use-async-inserts # Conflicts: # clickhouse/datadog_checks/clickhouse/statement_samples.py
Validation ReportAll 21 validations passed. Show details
|
What does this PR do?
Add buffer state collection for async inserts in ClickHouse. It collects snapshots from
system.asynchronous_inserts, including pending bytes, pending entry count, active buffer count, and each buffer's scheduled flush deadline. This collection logic shares the existing query samples job instead of running as its own job to avoid the overhead of a new connection.Motivation
Support for diagnosing ClickHouse asynchronous inserts in DBM. We can't tell if a buffer is backing up, how big it's getting, or when its pending data is scheduled to flush, so this gives us a way to actually diagnose these issues instead of manually querying the
system.*tables.Agent payload (in mitm proxy):

Review checklist (to be filled by reviewers)
qa/requiredif this PR needs QA validation, orqa/skip-qaif it does not. Exactly one of the two is required.backport/<branch-name>label to the PR and it will automatically open a backport PR once this one is merged