feat(dhcp): add reconcile observability metrics and structured logs - #172
feat(dhcp): add reconcile observability metrics and structured logs#172dmathur0 wants to merge 1 commit into
Conversation
Diagnosing the EKS-upgrade DHCP recovery failure was hard because operators only had container restart counts and could not tell whether the break was in Kea, config-sync, Redis, or PostgreSQL. This adds an observability surface to the sync reconcile loop: - DHCP_SYNC_FAILURES counter labeled by `operation` (redis_read, config_generation, config_set, hash_get, config_test, postgres) so failures point to WHERE reconciliation broke. - DHCP_CONFIG_HASH_MISMATCHES counter for detected config drift. - DHCP_LAST_SUCCESSFUL_SYNC_TIMESTAMP gauge whose age reveals a stalled sync. - SyncState taxonomy on structured log lines (waiting-for-initial-redis-config, in-sync, drift-detected, applying, dependency-error) that logs desired/running config hashes on drift and recovery while never logging credentials or full config-get responses. Metric/log points are wired at the drift and last-verified-sync locations that exist on main today, with clearly-named MERGE HOOK comments so the hash-based reconcile (Part 1) and heartbeat state (Part 2) merge cleanly. Adds unit tests covering the failure labels, mismatch counter, gauge update, and secret redaction. Signed-off-by: Davesh Mathur <daveshm@nvidia.com>
📝 WalkthroughWalkthroughThe DHCP reconcile loop now uses hashed configuration drift detection, structured sync-state logging, labeled dependency-failure metrics, verified-sync timestamps, and log-safety protections. New tests cover failure accounting, drift handling, waiting states, successful syncs, and secret redaction. ChangesDHCP reconciliation observability
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Redis
participant DHCPReconcileLoop
participant Kea
participant Prometheus
Redis->>DHCPReconcileLoop: load desired configuration
DHCPReconcileLoop->>DHCPReconcileLoop: compare desired and running hashes
DHCPReconcileLoop->>Prometheus: record state or mismatch
DHCPReconcileLoop->>Kea: apply configuration when drift is detected
Kea-->>DHCPReconcileLoop: return application result
DHCPReconcileLoop->>Prometheus: update successful sync timestamp
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/nv_config_manager/dhcp/cli.py (1)
435-443: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAvoid computing the config fingerprint on every "still in sync" tick when it's discarded.
_config_fingerprint(previous_config)(JSON-serializing + SHA-256 hashing the whole config) runs on every loop iteration here, but_mark_verified_syncimmediately discardsrunning_hashwheneverlogis falsy (if not log: return,metrics.py/cli.py_mark_verified_sync). Sincelog=debuganddebugisFalsein production, this is wasted work on every tick of an indefinitely-running loop.⚡ Proposed fix: only compute the fingerprint when it will actually be logged
else: # Still in sync: keep the last-verified-sync gauge fresh # so its age only climbs when reconciliation stalls. _mark_verified_sync( ip_version, - _config_fingerprint(previous_config), + _config_fingerprint(previous_config) if debug else "", recovered=False, log=debug, )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/nv_config_manager/dhcp/cli.py` around lines 435 - 443, In the still-in-sync branch, update the call to _mark_verified_sync so _config_fingerprint(previous_config) is computed only when debug is enabled; otherwise pass the appropriate absent-value accepted by _mark_verified_sync. Preserve the existing recovered=False behavior and keep fingerprint logging unchanged when debug is true.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/nv_config_manager/dhcp/cli.py`:
- Around line 435-443: In the still-in-sync branch, update the call to
_mark_verified_sync so _config_fingerprint(previous_config) is computed only
when debug is enabled; otherwise pass the appropriate absent-value accepted by
_mark_verified_sync. Preserve the existing recovered=False behavior and keep
fingerprint logging unchanged when debug is true.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: b2259520-cfef-46cd-8f45-8a2fb74fb285
📒 Files selected for processing (3)
src/nv_config_manager/dhcp/cli.pysrc/nv_config_manager/dhcp/metrics.pysrc/tests/dhcp/test_cli.py
Summary
Adds enough state to diagnose future DHCP upgrade failures without relying on container restart counts alone — the signal that was missing during the incident. Instruments the sync/refresh loops with 3 metrics and a structured log-state taxonomy, with strict secret redaction.
Metrics (exposed via existing
/metrics)nv_config_manager_dhcp_sync_failures_totaloperation,ip_versionnv_config_manager_dhcp_config_hash_mismatches_totalip_versionnv_config_manager_dhcp_last_successful_sync_timestamp_secondsip_versionoperationvalues:redis_read,config_generation,config_set,hash_get,config_test,postgres— so operators can tell where it broke.time() - gauge).Log-state taxonomy (
sync_statefield)waiting-for-initial-redis-config,in-sync,drift-detected(logsdesired_hash+running_hash),applying(logsdesired_hash),dependency-error(logsoperation+escaped error).Redaction is strict: only truncated SHA-256 hashes are logged — never credentials or full
config-getresponses; structured field values are newline-escaped to prevent log forging.Changes
metrics.py— metric definitions +SyncOperation/SyncStatetaxonomy classescli.py— instrumented_sync_kea_configuration_asyncand_refresh_kea_configuration_asynctest_cli.py— 8 new unit testsTest plan
Part 4 of 5 from the DHCP config-sync recovery proposal, opened independently against
main.Merge note
The
hash_getoperation label and the drift/verified-sync points carryMERGE HOOK (Part 1/2)comments — they slot into #168 (Part 1'sget_config_hash/expected_hash) and #171 (Part 2's last-successful-reconciliation state) without reimplementing that logic.cli.py(_sync_kea_configuration_async) overlaps with #168/#170/#171, so whichever merges later needs a small rebase.Summary by CodeRabbit
New Features
Bug Fixes