Skip to content

DBMON-6748 ClickHouse Async Inserts Monitoring Flush Log Collection#24549

Open
jenny-chung wants to merge 21 commits into
masterfrom
jenny-chung/clickhouse-async-inserts-flush-log
Open

DBMON-6748 ClickHouse Async Inserts Monitoring Flush Log Collection#24549
jenny-chung wants to merge 21 commits into
masterfrom
jenny-chung/clickhouse-async-inserts-flush-log

Conversation

@jenny-chung

@jenny-chung jenny-chung commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds flush log collection for ClickHouse async inserts. The check queries system.asynchronous_insert_log and submits one DBM event per flush record. The collection logic runs in the existing query completions job.

Motivation

DBM currently has no visibility into ClickHouse async insert flush health, so customers have to manually query system tables just to see whether flushes are failing, how fast they're running, or how much data they're moving.

Agent payload (in mitm proxy):
Screenshot 2026-07-15 at 10 08 33 AM

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Add qa/required if this PR needs QA validation, or qa/skip-qa if it does not. Exactly one of the two is required.
  • If you need to backport this PR to another branch, you can add the backport/<branch-name> label to the PR and it will automatically open a backport PR once this one is merged

@jenny-chung jenny-chung changed the title Jenny chung/clickhouse async inserts flush log DBMON-6748 ClickHouse Async Inserts Monitoring Flush Log Collection Jul 14, 2026
@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Jul 14, 2026

Copy link
Copy Markdown

Tests  Code Coverage

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 90.99%
Overall Coverage: 94.02% (+5.75%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 240253e | Docs | Datadog PR Page | Give us feedback!

@jenny-chung jenny-chung added the qa/required QA is required for this PR and will generate a QA card label Jul 14, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d686cbc0b1

ℹ️ 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".

self._config.query_completions.enabled or self._config.asynchronous_insert_flush_log.enabled
):
self.query_completions = ClickhouseQueryCompletions(
self, self._config.query_completions, self._config.asynchronous_insert_flush_log

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enable the shared job when only flush logs are configured

When a user enables asynchronous_insert_flush_log but disables query_completions, this still constructs ClickhouseQueryCompletions with self._config.query_completions; ClickhouseQueryLogJob passes that config's enabled value into DBMAsyncJob, whose run_job_loop returns immediately when _enabled is false. In that configuration self.query_completions exists but run_job() is never called, so _collect_flush() is never reached and no async insert flush events are emitted.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in commits d96c340 and b8e99f1

Comment on lines +86 to +87
{checkpoint_filter}
AND event_time_microseconds <= now64(6)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add the date bound to flush-log collections

On instances that retain more than a few days of system.asynchronous_insert_log, this new query only has the microsecond checkpoint predicate. The existing query-log collectors also bind event_date >= toDate(fromUnixTimestamp64Micro({min_checkpoint_us:UInt64})) so ClickHouse can prune old daily partitions; here _collect_flush_rows discards _min_checkpoint and never adds the date predicate, so every async flush collection can scan historical log partitions that can never match. Please add the same event_date bound and parameter to the flush query.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in commit 24d2d2f


def _flush_error_count(self, error_label):
self._check.count(
"dd.clickhouse.query_completions.error",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a async insert specific metrics name maybe dd.clickhouse.async_inserts_flush.error

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, changed the name to be async insert specific in 52c4b33

"""
Run the async insert flush log collection on its own collection interval
"""
if self._flush_enabled and time.time() - self._last_flush_collection_time >= self._flush_collection_interval:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do one call of time.time(). since between these two calls there will be a few microseconds of elapsed time so the _last_flush_collection_time is later than the value being compared

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense! Addressed in bcce48e

"{checkpoint_filter}", checkpoint_filter
)
params["min_checkpoint_us"] = min_checkpoint
params["max_flush_rows"] = self._max_samples_per_collection

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

max_samples_per_collection seems to be coming from a different config section, we should have a max_flush_rows option under asynchronous_insert_flush_log in the spec.yaml.
This is confusing for anyone who disables query_completions entirely but runs flush.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to use max_flush_rows option in asynchronous_insert_flush_log config - e35973d

'collection_interval': self._flush_collection_interval,
'ddtags': self._tags_no_db,
'timestamp': time.time() * 1000,
'clickhouse_version': self._check.dbms_version,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add service here too 'service': getattr(self._check._config, 'service', None),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added service in 32a158a


event_time_int = self.to_microseconds(event_time_microseconds)
flush_time_int = self.to_microseconds(flush_time_microseconds)
flush_latency_us = flush_time_int - event_time_int if event_time_int and flush_time_int else 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can go negative when the system clocks skew across nodes, might be good to do a flush_latency_us = max(0, flush_time_int - event_time_int) if event_time_int and flush_time_int else 0

@jenny-chung jenny-chung Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2a5ea02

SECURE_FIELD_NAMES = frozenset(['tls_ca_cert'])


class AsynchronousInsertFlushLog(BaseModel):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QQ: I'm guessing this was a generated change but wanted to confirm

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes instance.py is auto generated

type: number
example: 60
fleet_configurable: false
- name: max_flush_rows

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is limit a clearer? Perhaps max_samples_per_collection? We should strive for following existing naming conventions for similar functionality so that we maintain a cohesive customer experience.

@dd-octo-sts

dd-octo-sts Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Validation Report

All 21 validations passed.

Show details
Validation Description Status
agent-reqs Verify check versions match the Agent requirements file
ci Validate CI configuration and code coverage settings
codeowners Validate every integration has a CODEOWNERS entry
config Validate default configuration files against spec.yaml
dep Verify dependency pins are consistent and Agent-compatible
http Validate integrations use the HTTP wrapper correctly
imports Validate check imports do not use deprecated modules
integration-style Validate check code style conventions
jmx-metrics Validate JMX metrics definition files and config
labeler Validate PR labeler config matches integration directories
legacy-signature Validate no integration uses the legacy Agent check signature
license-headers Validate Python files have proper license headers
licenses Validate third-party license attribution list
metadata Validate metadata.csv metric definitions
models Validate configuration data models match spec.yaml
openmetrics Validate OpenMetrics integrations disable the metric limit
package Validate Python package metadata and naming
qa-label Validate the pull request declares whether it needs QA for the next Agent release
readmes Validate README files have required sections
saved-views Validate saved view JSON file structure and fields
version Validate version consistency between package and changelog

View full run

type: boolean
example: false
fleet_configurable: false
- name: asynchronous_insert_flush_log

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This config name is a bit of a mouthful and I'm not sure customers would easily understand what this feature collects. Let's brainstorm on this a bit 🤔

example: false
fleet_configurable: false
- name: asynchronous_insert_flush_log
description: Configure asynchronous insert flush log monitoring

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently this description is re-stating the config block currently. I think we should move parts of the description for enabled up into here to better describe this whole block and shrink down enabled description. The part below about calling out dbm:true requirement is good to keep there

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants