DLPX-96312 Add InfluxDB/Telegraf infrastructure for Engine Performance Analytics - #119
DLPX-96312 Add InfluxDB/Telegraf infrastructure for Engine Performance Analytics#119dbshah12 wants to merge 4 commits into
Conversation
bb1bd01 to
985a3ac
Compare
6286549 to
2a39e0c
Compare
bad0342 to
df102c9
Compare
34acba1 to
02cc5df
Compare
02cc5df to
7095d33
Compare
304c9a7 to
60660da
Compare
daae5c1 to
9a5175a
Compare
|
All Copilot review comments addressed — stale threads resolved, valid issues fixed or explained inline. |
2e8baa6 to
9bf19d7
Compare
bde1608 to
774cdc1
Compare
…lection gating - Add INFLUXDB_BUCKET_ID and INFLUXDB_SUPPORT_BUCKET_ID to the influxdb_meta heredoc; both variables are already computed earlier in the script but were omitted from the written file (spec gap flagged in PR #119 review) - Clarify comment in telegraf.inputs.playbook: estat_nfs/iscsi/backend-io are intentionally InfluxDB-gated only; collecting without an InfluxDB sink wastes CPU with no benefit Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
ShibasishDelphix
left a comment
There was a problem hiding this comment.
minimal test probe
ShibasishDelphix
left a comment
There was a problem hiding this comment.
Found 1 blocker, 6 warnings, and 2 nits across 8 files — please address before merging.
❌ Testing — Missing
Please add a Jenkins run link to the PR description before merging.
Reviewed with generic rules — no repo-specific config found for delphix/performance-diagnostics.
Attached ab-pre-push and also appliance-build-orchestrator is running by default as well. |
Code Review — performance-diagnostics PR #119PR: DLPX-96312 Add InfluxDB/Telegraf infrastructure for Engine Performance Analytics Summary
Severity mix: 1 High · 6 Medium · 4 Low. No finding is a hard merge-blocker on its own, but the High finding and two of the Mediums defeat crash-safety / recovery guarantees the PR explicitly claims, and should be resolved before this ships to appliances. Because this is a Findings (triaged)🔴 HighH1.
|
| # | Finding (as raised) | Why dismissed |
|---|---|---|
| D1 | chmod 640 on telegraf.conf / telegraf.outputs.influxdb breaks Telegraf if it runs as user telegraf |
Verified: delphix-telegraf.service runs User=root (and so does delphix-influxdb.service). Root reads 640 root-owned files fine. |
| D2 | postinst never enables/starts delphix-influxdb.service |
#DEBHELPER# is present → dh_installsystemd enables and starts WantedBy=delphix.target units. Handled by packaging. |
| D3 | Histogram schema change (merged JSON object → per-bucket le/count) breaks existing Grafana dashboards on upgrade |
InfluxDB is new in this PR — there were no prior InfluxDB-backed dashboards to break. The old JSON-object format only ever fed the removed file outputs. |
| D4 | order=2 Starlark namepass=["estat_*"] also strips microseconds from the hist_estat_* clones (breaking order=3) |
Telegraf glob estat_* is prefix-anchored and does not match hist_estat_*; order=3 still sees microseconds. Edge Hunter self-cleared this on walk-through. |
| D5 | INFLUXD_CONFIG_PATH treated as a directory → influxdb.toml ignored → influxd binds all interfaces |
PR testing evidence shows the bolt/engine paths from influxdb.toml in effect (/var/lib/influxdb/…, not ~/.influxdbv2), i.e. the file is loaded, so http-bind-address = 127.0.0.1:8086 is honored. |
| D6 | Duplicate PartOf= directives in delphix-influxdb.service |
Intentional and documented in PR notes (PartOf=delphix.target for grouping + PartOf=delphix-platform.service for stop propagation), mirroring delphix-telegraf.service. |
| D7 | Playbook estat histogram processing now depends on InfluxDB state (wasted collection when playbook on / InfluxDB off) | Moot: when InfluxDB is disabled the output is [[outputs.discard]], so nothing is stored regardless. Wasted CPU only, no data defect. |
Verification performed during triage
- Cloned
delphix/performance-diagnostics@developand inspected files outside the diff:telegraf/delphix-telegraf.service→User=root(dismisses D1).debian/controlondevelop→ confirmsopensslwas already absent and this PR does not add it (confirms M1).debian/postinst→#DEBHELPER#present (dismisses D2); appliance has no/etc/nginxtree (supports M4).
- Cross-checked both reviewer layers against each other; merged 4 overlapping findings (H1, M3, M4, M6, L3).
Notes / residual verification for the author (not counted as findings)
- M5 and M6 are marked verify-first — they may already be correct by design/tooling, but neither could be confirmed from the diff alone and both carry silent-failure consequences.
- Systemd
StartLimitis not tuned ondelphix-influxdb.service; combined with H1, a repeatedly-failing init will land the unit in a permanentfailedstate (not an infinite spin). Consider whether init failure should really killinfluxd.
|
Thanks for the thorough review. Here's a status update on each finding: Addressed in
|
…ing, nginx
H1 Probe GET /api/v2/setup before POST to detect already-onboarded state
when local state file is absent (crash window / config-volume reset).
M1 Add openssl to Debian Depends (used by init for password generation).
M2 Fix early-exit guard to require both influxdb_meta AND
telegraf.outputs.influxdb; add recovery path that reads tokens from
meta and regenerates the output file without re-running InfluxDB setup.
M3 Replace incremental >> appends to the state file with write_state_file()
helper that always rewrites the full file atomically (mktemp+mv), so a
crash mid-write never leaves a torn line that the resume parser trusts.
M4 Point postinst nginx -t at /opt/delphix/server/etc/nginx/nginx.conf
(the Delphix-managed config that actually includes conf.d/influxdb.conf).
L1 Set proxy_read_timeout 300s on nginx InfluxDB proxy (was 999d).
L2 Validate required keys from influxdb-init.conf after sourcing so a
missing key fails with a clear message rather than a cryptic bash -eu abort.
L3 Change delphix-influxdb.service to Restart=always so a clean influxd
exit (exit 0) also triggers a restart.
L4 Guard int() conversion in histogram Starlark processor; store le tag as
int for correct Grafana heatmap bucket ordering.
Also add INFLUXDB_SUPPORT_WRITE_TOKEN to influxdb_meta so the M2 recovery
path can regenerate both output stanzas from stored credentials.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The nginx systemd unit is masked on the Delphix appliance even though nginx is running. Replace systemctl is-active with pgrep -x nginx so the reload fires correctly after package install. Also guard with command -v nginx in case nginx is absent. Config path stays as /etc/nginx/nginx.conf which includes /opt/delphix/server/etc/nginx/conf.d/*. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Telegraf Starlark requires tags to be strings. Assigning int(parts[0]) to m.tags["le"] caused every hist_estat_* per-bucket row to be dropped with "tag value must be of type 'str'", silently discarding all histogram data. parts[0] is already a string from split() so the int() cast is removed. Addresses review finding L4. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Design Doc
Problem
Telegraf is already collecting engine performance metrics and writing them to local JSON files on the appliance. However, there is no local time-series database to store and serve these metrics, making it difficult for tools like DCT Smart Proxy to query historical performance data from the engine directly.
Additionally, several valuable metrics — per-connection TCP statistics, and storage I/O (NFS, iSCSI, backend disk) — were either not collected or only available when the performance playbook was explicitly enabled.
Storing all metrics in a single bucket would also mix Grafana-dashboard data with low-level diagnostics (aggregates, process counters, TCP internals), inflating storage costs for data that serves no dashboard purpose.
Solution
InfluxDB 2.x infrastructure
Add InfluxDB 2.x to the appliance as the single metrics store, mirroring the existing Telegraf setup pattern:
influxdb/influxdb.toml— InfluxDB daemon config: bound to127.0.0.1:8086, with bolt/engine paths matching the installed package (/var/lib/influxdb/). Named.toml(not.conf) because InfluxDB uses the Viper config library, which determines the file format from the extension —.confis not recognized and is silently ignored, causing influxd to fall back to defaults (~/.influxdbv2/).influxdb/influxdb-init.conf— Tunable init config (org, bucket names, retention period, readiness wait parameters) sourced by the init script. Change values here without touching the script. Bothdefaultandsupport_metricsbuckets use 30-day retention (INFLUXDB_RETENTION_SECONDSandINFLUXDB_SUPPORT_RETENTION_SECONDS = 2592000respectively).influxdb/delphix-influxdb-init— One-time init script that:/etc/influxdb/influxdb_metaand/etc/telegraf/telegraf.outputs.influxdbexist (safe on upgrades and reboots). If meta exists but the output file is missing (e.g. deleted by operator), falls through to a recovery path that reads stored tokens from meta and regenerates the output stanza without repeating any InfluxDB API calls./healthendpoint./api/v2/setupto create the org,defaultbucket, and admin credentials (one-shot; usescurldirectly, noinfluxCLI dependency)./api/v2/setup; each subsequent step (bucket creation, token creation) atomically rewrites the full state file viawrite_state_file()(mktemp+mv) — a crash mid-write leaves the previous complete state rather than a torn line. Checks state on re-run; the entire script is idempotent end-to-end.support_metricsbucket (30-day retention) for diagnostic and aggregate data that is not displayed in Grafana dashboards.default, a read-only token for DCT Smart Proxy →default, and a write-only token for Telegraf →support_metrics.[[outputs.influxdb_v2]]stanzas to/etc/telegraf/telegraf.outputs.influxdb(chmod 640) — see Dual-bucket routing below./etc/telegraf/INFLUXDB_ENABLEDto enable InfluxDB output by default./etc/influxdb/influxdb_meta(chmod 600) containing:INFLUXDB_ORG,INFLUXDB_BUCKET,INFLUXDB_BUCKET_ID,INFLUXDB_SUPPORT_BUCKET,INFLUXDB_SUPPORT_BUCKET_ID,INFLUXDB_ADMIN_USER,INFLUXDB_ADMIN_PASSWORD,INFLUXDB_WRITE_TOKEN,INFLUXDB_READ_TOKEN,INFLUXDB_SUPPORT_WRITE_TOKEN. The support write token is stored so the recovery path can regenerate both[[outputs.influxdb_v2]]stanzas from stored credentials whentelegraf.outputs.influxdbis missing.influxdb/delphix-influxdb-service— Wrapper that startsinfluxdwithINFLUXD_CONFIG_PATH=/etc/influxdb/influxdb.tomlin the background, runs the init script, then waits on the daemon PID.influxdb/delphix-influxdb.service— Systemd unit following the same structure asdelphix-telegraf.service(PartOf=delphix.target,PartOf=delphix-platform.servicefor stop propagation on platform maintenance,Restart=always, runs as root).influxdb/perf_influxdb— Toggle script (mirrorsperf_playbook) to enable/disable InfluxDB metric output from Telegraf without stopping InfluxDB itself. Exits with error ifsystemctl restartfails so the operator is not left silently broken.influxdb/influxdb-nginx.conf— nginx reverse proxy config that exposes InfluxDB externally at/influxdb/.debian/rules— Installs all influxdb files.debian/control— Addedinfluxdb2,curl, andopenssltoDepends.Dual-bucket routing
Metrics are split across two buckets to keep Grafana-facing data separate from diagnostic data:
defaultcpu,disk,diskio,net,zfs,tcp_stats,estat_nfs,estat_iscsi,hist_estat_nfs,hist_estat_iscsi,hist_estat_backend-iosupport_metricsmem,processes,system,procstat,agg_*,nfs_threads,estat_backend-io,estat_zpl/zvol/zio/zio-queue/metaslab-alloc,hist_estat_zpl/zvol/zio/…,docker_container_*Routing uses
namepasson the default bucket stanza (explicit allowlist of the 11 Grafana-facing measurements) andnamedropon the support_metrics stanza (mirrors the allowlist). Any new measurement added in future automatically lands insupport_metrics— it must be explicitly added to thenamepasslist to reach the Grafana-facing bucket. This prevents accidental default-bucket bloat from new collections.Telegraf metric collection changes
telegraf/telegraf.base— Removed all[[outputs.file]]stanzas; InfluxDB is the sole output.percpu=false,tagexclude=["fstype","mode"]on disk,tagdropfor partition devices andtagexclude=["wwid"]on diskio,tagexclude=["cgroup_full"]on procstat. Added[[inputs.execd]]fortcp_statsviaconnstat-stats.sh. Addedarcstats_demand_metadata_hitsandarcstats_demand_metadata_missesto ZFS fieldpass.telegraf/connstat-stats.sh— Aggregates per-connection TCP stats by(laddr, raddr, service). Uses-c 2so awk exits after each 10-second interval, triggering C runtime flush — required because mawk does not flush viafflush()to a Telegraf execd pipe.sleep 5guard prevents tight CPU spin ifconnstatfails immediately.telegraf/telegraf.inputs.storage_io— Always-onestat_nfs,estat_iscsi,estat_backend-iowhen InfluxDB is enabled. Processor pipeline:starlarkdropsname=totalrows →clone(order=1) createshist_estat_*→starlark(order=2) popsmicrosecondsfrom originalestat_*rows →starlark(order=3) expandshist_estat_*into per-bucketle=/countrows. Usesprocessors.starlark(notprocessors.strings) to dropmicrosecondsbecausefieldexcludeinprocessors.stringsonly controls which fields string transforms operate on — it does not remove fields from the metric.telegraf/telegraf.inputs.playbook— Removedestat_nfs/iscsi/backend-io(moved to storage_io). Updatedestat_metaslab-allocto use wrapper script.telegraf/delphix-telegraf-service— Appends storage_io and InfluxDB output stanzas when InfluxDB is enabled; falls back to[[outputs.discard]]otherwise.chmod 640ontelegraf.confapplied unconditionally as hardening.BPF/estat kernel compatibility fixes (DLPX-96701)
Removed conflicting forward declarations and struct typedefs from
bpf/estat/nfs.c,bpf/stbtrace/nfs.st,bpf/estat/zvol.c,bpf/stbtrace/iscsi.st. Added-D__KERNEL__/-D_KERNELBPF compiler flags. Fixedestat zil(zil_commit_waiter_skipkprobe removed,--colldefault added).Complete list of measurements in InfluxDB
cpu[[inputs.cpu]](cpu-total only)defaultdisk[[inputs.disk]](fstype/mode tags excluded)defaultdiskio[[inputs.diskio]](partitions + wwid excluded)defaultnet[[inputs.net]]defaultzfs[[inputs.zfs]]defaulttcp_statsconnstatdefaultmem[[inputs.mem]]support_metricsprocesses[[inputs.processes]]support_metricssystem[[inputs.system]]support_metricsprocstat[[inputs.procstat]](cgroup_full excluded)support_metricsagg_cpu/disk/diskio/mem/net/processes/systemsupport_metricsestat_nfsestat nfsdefaultestat_iscsiestat iscsidefaultestat_backend-ioestat backend-ioscalarssupport_metricshist_estat_nfs,hist_estat_iscsi,hist_estat_backend-iodefaultestat_zpl/zio/zvol/zio-queue/metaslab-allocsupport_metricshist_estat_zpl/zvol/zio/…support_metricsnfs_threadssupport_metricsdocker_container_*support_metricsNotes to Reviewers
Bucket routing: namepass (explicit allowlist), not namedrop
The
defaultbucket stanza usesnamepasswith an explicit list of the 11 Grafana-facing measurements. Any new measurement added in future automatically lands insupport_metricswithout any config change — it must be explicitly promoted to reach Grafana. This prevents accidental default-bucket bloat.Retention policy: both buckets are 30 days
Both
defaultandsupport_metricsuse 30-day retention (INFLUXDB_RETENTION_SECONDS = INFLUXDB_SUPPORT_RETENTION_SECONDS = 2592000), configured ininfluxdb/influxdb-init.conf. This matches the existing Telegraf JSON file retention and provides sufficient history for trend analysis and support investigation.hist_estat_*— whyprocessors.starlark, notprocessors.stringsThe
microsecondsfield is dropped from originalestat_*rows via a[[processors.starlark]]processor callingmetric.fields.pop("microseconds", None).processors.stringswithfieldexcludewas not used becausefieldexcludein that plugin only controls which fields string transforms (replace, trim, etc.) operate on — it does not remove fields from the metric. With no transforms defined, the block is a complete no-op andmicrosecondswould be stored as a raw string in everyestat_*row in InfluxDB (verified: leaked before the Starlark fix was applied).PartOf=delphix-platform.servicein both service unitsBoth
delphix-influxdb.serviceanddelphix-telegraf.servicecarry twoPartOf=directives:PartOf=delphix.target(unit group membership) andPartOf=delphix-platform.service(stop propagation).After=alone establishes only boot ordering — it does not propagate stop/restart events. The secondPartOf=ensures both services stop cleanly when the Delphix platform stops during maintenance.Runtime dependency decisions (
debian/control)openssldelphix-influxdb-initfor password generation; only inBuild-Dependsindelphix-platformpython3python3-minimalcurldelphix-platform'sBuild-DependsWhy
influxdb.tomlinstead ofinfluxdb.confInfluxDB 2.x uses Viper for config parsing — only
.json,.toml,.yaml,.ymlare recognized..confis silently ignored; influxd falls back to~/.influxdbv2/. Verified on v2.8.0.metaslab-alloc-stats.sh— DLPX-88427 garbage name filterMoved to a dedicated PR (#120 / DLPX-88427). Not part of this PR.
Testing Done
ab-pre-push
1000aa9): https://selfservice-jenkins.eng-tools-prd.aws.delphixcloud.com/job/appliance-build-orchestrator-pre-push/13811/ - ✅b6e6d4a/d4f491e/7bccceb(review fixes + Starlark le tag correction)Measurements verified in InfluxDB
defaultbucket (Grafana-facing — dashboard measurements only):support_metricsbucket (everything else):Change-specific verifications
diskiopartition exclusionnvme0n1p*andsda[0-9]*absent; only whole-disk entries presentdiskiowwidtag removalwwidabsent fromdiskiodiskfstype/modetag removalprocstatcgroup_fulltag removalhist_estat_*histogram expansiondefaultwith stringle=<upper_bound_us>tags and integercountfield (Telegraf Starlark requires string tags); rawmicrosecondsstring absenttelegraf.outputs.influxdbon dhruv3.dlpxdc.co, re-ran init; file regenerated frominfluxdb_metatokens, Telegraf restarted cleanly, histogram data resumed within one collection cyclemicrosecondsdropped fromestat_*originalsestat_iscsifields are onlyavg latency(us),iops(/s),stddev(us),throughput(k/s)—microsecondsdoes not exist (confirmed afterprocessors.starlarkfix)tcp_statsindefault— all fieldsconnections,inbytes,outbytes,retranssegs,rtt,cwnd,swnd,rwnd,suna,unsentall present; absent fromsupport_metricsagg_*insupport_metricsonlydefaultmem/processes/system/procstatinsupport_metricsdefaultestat_backend-ioscalars insupport_metricshist_estat_backend-ioindefaulttcp_statsservicetagnfs,https,dlpx-sp)tcp_statsrportabsent(laddr, raddr, service)influxdb_metacompletenessINFLUXDB_ORG,INFLUXDB_BUCKET,INFLUXDB_BUCKET_ID,INFLUXDB_SUPPORT_BUCKET,INFLUXDB_SUPPORT_BUCKET_ID,INFLUXDB_ADMIN_USER,INFLUXDB_ADMIN_PASSWORD,INFLUXDB_WRITE_TOKEN,INFLUXDB_READ_TOKEN,INFLUXDB_SUPPORT_WRITE_TOKEN(DLPX-96313)influxdb_setup_stateandinfluxdb_metaon dhruv3 with InfluxDB already running; re-ran init; script probedGET /api/v2/setup, saw{"allowed":false}, exited with clear manual-recovery message rather than looping on a failingPOSTnginx -t -c /opt/delphix/server/etc/nginx/nginx.confsucceeds on appliance (the Delphix-managed config includesconf.d/influxdb.conf);nginx -s reloadfollows; confirmed on ab-pre-push engineproxy_read_timeoutinfluxdb-nginx.confcontainsproxy_read_timeout 300s; confirmed viagrepon deployed configRestart=alwayson clean exitSIGTERMtoinfluxdPID directly (clean exit 0); systemd restarted the unit within 100ms;systemctl statusshows one restart in the journalconnstatmawk flush fixsleep 5guard confirmedperf_influxdberror propagationsystemctl restartfailure surfaces via `INFLUXDB_RETENTION_SECONDS=2592000andINFLUXDB_SUPPORT_RETENTION_SECONDS=2592000confirmed ininfluxdb-init.confestat nfs/iscsi/backend-iocompile without errorsestat zildefault collection-cflagperf_influxdb enable/disable testing
INFLUXDB_ENABLEDflag exists on fresh boottelegraf.outputs.influxdbwith correct perms (-rw-r-----)influxdb_v2output (2x) on bootperf_influxdb disable→[[outputs.discard]]configperf_influxdb enable→ bothinfluxdb_v2stanzas reloaddefaultandsupport_metrics