feat(warehouse_sources): add hetzner metrics, pricing and zones tables - #110747
Conversation
Adds server_metrics, load_balancer_metrics, pricing and zones tables to the Hetzner Cloud source, closing four gaps from the endpoint coverage audit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: e57ef1de-e8be-41ac-8116-9f1717f701ef
|
😎 Merged successfully - details. |
|
Risk: No findings This increment replaces the plain tuple yielded by Sentinel reviewed |
🤖 CI report
|
| First copy | Second copy | Lines | Tokens |
|---|---|---|---|
products/warehouse_sources/backend/temporal/data_imports/sources/descope/descope.py:8 |
products/warehouse_sources/backend/temporal/data_imports/sources/hetzner/hetzner.py:9 |
12 | 121 |
✅ Duplication (TypeScript) — clean
New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.
🚨 Comment density — 8% of added code lines are comments (36 of 438)
This section warns when comments are more than 3% of the code lines a PR adds, and alerts above 6%. Before agent-assisted PRs, the typical share was about 2%. Only full-line comments count. Docstrings, generated files, snapshots, migrations, and workflow files are left out.
Comments that restate the code, record how the change came about, or narrate the next line add noise for the next reader. Keep the comments that explain a reason the code cannot show, and remove the rest. See .agents/skills/writing-code-comments/SKILL.md for the house rules.
Files with the most added comment lines:
| File | Comment lines | Added lines |
|---|---|---|
products/warehouse_sources/backend/temporal/data_imports/sources/hetzner/hetzner.py |
16 | 151 |
products/warehouse_sources/backend/temporal/data_imports/sources/hetzner/settings.py |
9 | 57 |
products/warehouse_sources/backend/temporal/data_imports/sources/hetzner/source.py |
4 | 17 |
products/warehouse_sources/backend/temporal/data_imports/sources/hetzner/tests/test_hetzner.py |
4 | 139 |
products/warehouse_sources/backend/temporal/data_imports/sources/hetzner/tests/test_hetzner_source.py |
3 | 22 |
This check does not block merging. It updates on every push and clears when the share drops.
⚠️ Backend coverage — 96.0% of changed backend lines covered — 6 uncovered
🧪 Backend test coverage
Patch coverage — changed backend lines (products + core): ███████████████████░ 96.0% (185 / 191)
| File | Patch | Uncovered changed lines |
|---|---|---|
products/warehouse_sources/backend/temporal/data_imports/sources/hetzner/hetzner.py |
92.4% | 162–163, 167, 198–199, 264 |
🤖 Agents: add a test only if an uncovered line exposes a realistic regression that existing tests miss. Otherwise explain why no new test is needed under "How did you test this code?". Gap list: the patch-coverage artifact on this run (gh run download 47789257735369 -n patch-coverage), or the coverage-data block at the end of this comment.
Per-product line coverage (touched products)
| Product | Coverage | Lines |
|---|---|---|
warehouse_sources |
███████████████████░ 97.3% |
469,840 / 482,775 |
Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.
|
/trunk merge |
Add connect/read timeouts to both Hetzner REST clients and make touched configuration dataclasses explicitly immutable.
A new stamphog review started for this PR — the fresh verdict replaces this approval.
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 🧰 Additional context used📚 Code guidelines (7)No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: PostHog/posthog/.coderabbit.yaml Review profile: QUIET Plan: Enterprise Run ID: 📒 Files selected for processing (1)
Limit details: You’ve used all 12 included reviews currently available. 📝 WalkthroughWalkthroughThe Hetzner source adds zone and pricing endpoints and adds server and load-balancer metrics extraction. Metrics requests use bounded five-minute windows and produce flattened rows. Metrics schemas support incremental sync and route to the metrics source. The coverage checklist marks pricing, zones, and both metrics endpoints complete; zone RRsets remains incomplete. Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to Metrics sync requests now time out rather than waiting indefinitely, and no actionable merge-blocking risk remains in the reviewed change. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The new requests retain the existing fixed provider address, bearer authentication, and redirect blocking. No introduced security vulnerability was established. Remaining uncertainty concerns end-to-end recovery, concurrent ingestion, and shared pagination behavior. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
🚥 Pre-merge checks | ✅ 1✅ Passed checks (1 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Usage-based review receipt
Note This review exceeded your plan’s limits and used usage-based reviews—free during trial. After your trial, your Enterprise plan’s existing billing terms apply. Manage usage-based reviews. Comment |
Bound Hetzner request durations, make configuration immutable, and represent metrics windows with named fields to satisfy the new-code Semgrep guard.
A new stamphog review started for this PR — the fresh verdict replaces this approval.
There was a problem hiding this comment.
Approved.
This is an additive Hetzner warehouse-source change, outside risky territory. The author is on the owning team with STRONG familiarity, tests cover the new paths, and the helper APIs it relies on (merge_only, request_timeout) exist.
- Author wrote 100% of the modified lines and has 50 merged PRs in these paths (familiarity STRONG).
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✓ | no deny categories matched |
| size | ✓ | 330L, 4F substantive, 540L/7F incl. docs/generated/snapshots — within ceiling |
| tier | ✓ | T1-agent / T1d-complex (540L, 7F, single-area, feat) |
| stamphog 2.3.1 | .stamphog/policy.yml @ 4386189 · reviewed head 4386189 |
|
All code checks passed on 🦉 via talyn.dev |
Problem
Changes
server_metrics,load_balancer_metrics,pricingandzones.server_metricsandload_balancer_metricshold one row per sample: resource id, series name (for examplecpuorbandwidth.in), timestamp and value.timestamp, becausestart/endis a real server-side filter. They are merge-only, because each run re-reads the newest sample.sort_mode="desc", because rows are ordered per resource but not across resources. The watermark then commits once, at the end of a successful run, and not after one resource's newest sample.pricingis a single row keyed bycurrency. It uses a single-page paginator, because/pricinghas no pagination metadata and ignorespage.zonesis a plain list table, like the existing ones (id:asc, partitioned oncreated).COVERAGE_GAPS_APPENDIX.md.Verified against the Cloud API spec: all four endpoints exist on
v1, the version the source calls.Note
The audit says
/pricingis "the only way to cost the servers we already sync". That overstates it:server_typesandload_balancer_typesalready carry per-location prices. The new table adds the other prices (volumes, IPs, images, backups), plus the currency and VAT rate.Not done:
GET /zones/{id_or_name}/rrsets, which is in the appendix but was not part of this request. Its box stays open.How did you test this code?
Unit tests only. Nothing ran against a live Hetzner account, because the sandbox has no Hetzner token.
Test rationale:
TestMetricsintest_hetzner.pycatches four regressions: wrong flattening of the series map into rows, gaps or overlaps between retention windows, an incremental run that ignores the watermark, and a deleted resource that aborts the sync.test_pricing_is_one_row_from_one_requestcatches a regression to the page-number paginator, which would request/pricingforever.test_hetzner_source.pynow checks that only the metrics tables advertise incremental sync, replacing the "all endpoints are full refresh" test. It also checks thatsource_for_pipelineroutes metrics schemas to the metrics transport.Release status
Automatic notifications
Docs update
None in this repo. The posthog.com Hetzner doc builds its table list from the public source config API, so the new tables appear there on their own.
🤖 Agent context
Autonomy: Fully autonomous
Agent: Claude Code (PostHog Desktop cloud task), Claude Opus 5.5 (
claude-opus-5-5)/implementing-warehouse-sources,/writing-pr-descriptions.🤖 Generated with Claude Code
Created with PostHog Desktop