feat(signals): auto-refresh stale inbox ranking labels partitions - #111262
trunk-io[bot] merged 2 commits into
Conversation
Stamp FEATURE_SCHEMA_VERSION on each labels object and add an hourly sensor that rewrites labels partitions in the training lookback whose stamp is missing or older, newest first, a few per tick. A label column change then reaches the whole lookback without a manual backfill. Also count the snapshot pairs each head drops for a missing label column on inbox_ranking_examples_built. Refs #111247 Generated-By: PostHog Desktop Task-Id: 715b0db5-3f60-48ca-a00f-5c276d384b74
|
😎 Merged successfully - details. |
|
Risk: No findings This change adds an hourly Dagster sensor that auto-rewrites stale inbox-ranking labels partitions after a FEATURE_SCHEMA_VERSION bump (at most 6 per tick, newest first, deduped via a cursor), stamps the schema version into each Parquet object's S3 metadata, and adds a per-head telemetry counter for snapshot pairs skipped over missing label columns. All inputs to the new code paths are internal (Dagster cursor state, pipeline-written S3 metadata, settings, dates); no untrusted data reaches any dangerous sink, the labels asset's team-scoped SQL guards are untouched, and the partition rewrite path is the already-sanctioned idempotent re-run. No security vulnerabilities found. Sentinel reviewed |
🤖 CI report
|
| File | Comment lines | Added lines |
|---|---|---|
products/signals/dags/inbox_ranking/dataset/dag.py |
5 | 91 |
posthog/settings/object_storage.py |
2 | 3 |
products/signals/dags/inbox_ranking/common.py |
2 | 24 |
products/signals/dags/inbox_ranking/training/examples.py |
2 | 16 |
This check does not block merging. It updates on every push and clears when the share drops.
|
/trunk merge |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Important Review skippedReview was skipped as selected files did not have any reviewable changes. ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThe labels asset now stamps Parquet objects with the feature schema version. An hourly sensor selects and requests stale label partitions, with a per-tick limit and versioned run keys. Training metadata and events now report per-head counts of pairs skipped because label columns are missing. Priority: ➖ Normal Severity of issue fixed: Medium Merge Risk: 🟡 Moderate · up to An invalid refresh limit could leave labels stale or trigger too many refreshes in one tick. Validate the limit before merging. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to Automatic refresh remains confined to the existing labels dataset, with no new user-facing write path identified. Negative refresh-limit values can weaken the intended batch bound. Production concurrency and schema-rollback guarantees remain unverified. Retained concerns
Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 1✅ Passed checks (1 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings
🧪 Generate unit tests (beta)
🛠️ Fix failing CI checks 💡
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
products/signals/dags/inbox_ranking/training/examples.py (1)
261-268: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the nonzero skipped-pair count.
The existing refund test already creates a horizon pair with
refund_countmissing from the scoring snapshot and checks that the examples are skipped. Extend it to check the count as well.Suggested test change
@@ Snapshot, assemble_snapshot, birth_day_positives, build_examples, + build_head_examples, @@ - assert build_examples(snapshots, head, TABULAR_FEATURE_SET).empty + built = build_head_examples(snapshots, head, TABULAR_FEATURE_SET) + assert built.examples.empty + assert built.pairs_skipped_missing_label_columns == 1
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
- Review profile: QUIET
- Plan: Enterprise
- Run ID:
9ee98472-d2bd-44e5-9dae-0ff30ceab3ef
📒 Files selected for processing (10)
posthog/dags/locations/signals.pyposthog/settings/object_storage.pyproducts/signals/dags/inbox_ranking/README.mdproducts/signals/dags/inbox_ranking/common.pyproducts/signals/dags/inbox_ranking/dataset/dag.pyproducts/signals/dags/inbox_ranking/tests/test_dataset.pyproducts/signals/dags/inbox_ranking/tests/test_training.pyproducts/signals/dags/inbox_ranking/training/dag.pyproducts/signals/dags/inbox_ranking/training/examples.pyproducts/signals/dags/inbox_ranking/training/telemetry.py
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 8 remain after this review.
| INBOX_RANKING_PROMOTION_MIN_DAYS = get_from_env("INBOX_RANKING_PROMOTION_MIN_DAYS", 3, type_cast=int) | ||
| # Labels refresh sensor (products/signals/dags/inbox_ranking/dataset): how many stale labels | ||
| # partitions one hourly tick rewrites after a FEATURE_SCHEMA_VERSION bump, newest first. | ||
| INBOX_RANKING_LABELS_REFRESH_MAX_RUNS = get_from_env("INBOX_RANKING_LABELS_REFRESH_MAX_RUNS", 6, type_cast=int) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Require a positive refresh limit.
If INBOX_RANKING_LABELS_REFRESH_MAX_RUNS=0, the sensor requests no stale partitions. If the value is -1, stale_label_partitions uses [:-1] and can request nearly the entire lookback in one tick. Reject non-positive values before the sensor uses the setting.
HostHog preview —
|
…esh-stale-inbox-db1189 Generated-By: PostHog Desktop Task-Id: 97b0a0e3-3143-4257-adbe-8388f6715940
A new stamphog review started for this PR — the fresh verdict replaces this approval.
There was a problem hiding this comment.
Approved.
This is a contained Dagster sensor and telemetry change in the inbox-ranking ML pipeline. It sits outside risky territory, rewrites only idempotent labels partitions in the team's own bucket, and has a current-head human approval. The one open CodeRabbit thread, on validating the refresh-limit setting, is a minor hardening point on an ops-controlled env var.
- andrewm4894 reviewed the current head.
- Optional hardening: validate INBOX_RANKING_LABELS_REFRESH_MAX_RUNS as positive (CodeRabbit inline thread); a 0 or negative value would stall or over-request refreshes.
- On first deploy the sensor rewrites every labels partition in the lookback once, at 6 per hour. This is documented in the PR.
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✓ | no deny categories matched |
| size | ✓ | 170L, 7F substantive, 204L/10F incl. docs/generated/snapshots — within ceiling |
| tier | ✓ | T1-agent / T1c-medium (204L, 10F, two-areas, feat) |
| stamphog 2.3.1 | .stamphog/policy.yml @ 84f7428 · reviewed head 84f7428 |

Problem
action,fixedanddismiss_lowvalueafter the schema v9 head redesign).FEATURE_SCHEMA_VERSIONbump.Closes #111247
Origin
2991bfbChanges
feature-schema-versionin each object's metadata. A partition is stale when its stamp is missing or older thanFEATURE_SCHEMA_VERSION.run_keyalone stalls the backlog. A run that is in flight or failed leaves its partition stale, so the next tick asks for the same 6 partitions again and Dagster dedupes all of them. The sensor cursor records the partitions it already requested at the current version, so each tick moves on to older ones. A failed refresh alerts and needs a person, as the issue asks.pairs_skipped_missing_label_columnsper head oninbox_ranking_examples_builtand on the examples asset metadata. A head with 0 positives now shows why.INBOX_RANKING_LABELS_REFRESH_MAX_RUNS(default 6). The README documents the sensor and the code-location pod memory limit.Note
On first deploy, the sensor (RUNNING in prod US only) rewrites every labels partition in the lookback once, because no object has the stamp yet. Each run sits in the
inbox_ranking_etlpool and uses the default run pod size.How did you test this code?
products/signals/dags/inbox_ranking/tests/locally against Postgres and ClickHouse, plus repo-wide mypy and the dagster paths check.posthog.dags.locations.signalsunder Django. The sensor and the job resolve.Test rationale:
test_stale_label_partitions_newest_first_capped_and_skips_requestedcatches a wrong order, a missing cap, a current partition flagged as stale, or the stall above coming back.test_schema_version_stamp_round_tripscatches a key mismatch between the writer and the reader of the stamp. No existing test coveredwrite_parquetmetadata, so it could not be extended.Release status
Automatic notifications
Docs update
products/signals/dags/inbox_ranking/README.md(Operating it).🤖 Agent context
Autonomy: Fully autonomous
Agent: PostHog Desktop (Claude Code), claude-opus-5-5
/writing-tests,/writing-pr-descriptions.run_key-only dedupe, for the reason given under Changes.Created with PostHog Desktop from this inbox report.
🤖 Generated with Claude Code