Skip to content

[ENG-44755] Emit discovery_path label on lakeView_table_discovery_failure - #195

Merged
vishalk9 merged 2 commits into
mainfrom
ENG-44755-emit-table-base-path-discovery-failure
Jul 8, 2026
Merged

[ENG-44755] Emit discovery_path label on lakeView_table_discovery_failure#195
vishalk9 merged 2 commits into
mainfrom
ENG-44755-emit-table-base-path-discovery-failure

Conversation

@vishalk9

@vishalk9 vishalk9 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

metadataExtractorDiscoveryFailure fires per-org on lakeView_table_discovery_failure_total with no table-level dimension — so on-call can only silence the whole org's alert, which then blinds us to any NEW table failure for that org during the silence window. Hit live on ENG-44423 / ENG-44424 (Cardlytics): a customer-side S3 bucket-policy explicit-Deny hit 3 of hundreds of paths under Domain=CustomerOfferEvent/, and the only lever was an org-wide 2-week Alertmanager silence.

Fix

  • Add a table_base_path tag to lakeView_table_discovery_failure, wired at TableDiscoveryService — the per-path .exceptionally is the only failure call site with the failing path in scope.
  • Run-level failure sites (Main, TableDiscoveryAndUploadJob) route through the reason-only overload, which now supplies an "unknown" sentinel so the counter's tag set stays consistent across all call sites (needed for a clean Prometheus label family).
  • Once emitted, an Alertmanager silence can target {orgId, table_base_path} instead of the whole org.

tableId is deliberately not used: discovery is the step that resolves a path into a tableId, so at discovery-failure time no tableId exists yet — the failing base path is the available table-level dimension (matches the ticket's "and/or table base path").

Follow-ups (not in this PR)

  • infra: surface {{ $labels.table_base_path }} on the metadataExtractorDiscoveryFailure alert + update the runbook silence step to {orgId, table_base_path}.
  • Cardinality: table_base_path is emitted only on failure (0 series in steady state) and is naturally bounded by the customer's Deny scope. A defensive per-round cap (collapse to __many__ above K distinct failing paths) is a candidate hardening if a wholesale-denial pathological case shows up — open for reviewer input.

Test plan

  • LakeViewExtractorMetricsTest: reason-only cases now assert the "unknown" sentinel tag; new testIncrementTableDiscoveryFailureCounterWithTableBasePath asserts the real path tag.
  • TableDiscoveryServiceTest.testTableDiscoveryEncountersRateLimitException updated to the path-aware signature.
  • ./gradlew :lakeview:spotlessApply — clean, no rewrites
  • ./gradlew :lakeview:test --tests LakeViewExtractorMetricsTest --tests TableDiscoveryServiceTest — all passed locally

References

🤖 Generated with Claude Code

…ilure

metadataExtractorDiscoveryFailure fires per-org on
lakeView_table_discovery_failure_total with no table-level dimension, so
on-call can only silence the whole org's alert — which blinds us to any
NEW table failure for that org during the silence window (hit live on
ENG-44423 / ENG-44424, Cardlytics: a customer-side S3 bucket-policy Deny
hit 3 of hundreds of paths and the workaround was an org-wide 2-week
silence).

Add a table_base_path tag to the discovery-failure counter, wired at
TableDiscoveryService (the per-path .exceptionally, the only call site
with the failing path in scope). Run-level failure sites route through
the reason-only overload, which now supplies an "unknown" sentinel so
the counter's tag set stays consistent across all call sites (required
for a clean Prometheus label family). Once emitted, an Alertmanager
silence can target {orgId, table_base_path} instead of the whole org.

Note: tableId is not available at discovery-failure time (discovery is
what resolves a path to a tableId), so the failing base path is the
table-level dimension, per the ticket's "and/or table base path".

Test plan:
- LakeViewExtractorMetricsTest: existing reason-only cases now assert the
  "unknown" sentinel tag; new case asserts the real path tag.
- TableDiscoveryServiceTest.testTableDiscoveryEncountersRateLimitException
  updated to the path-aware signature.

Local checks:
- ./gradlew :lakeview:spotlessApply — clean, no rewrites
- ./gradlew :lakeview:test --tests LakeViewExtractorMetricsTest --tests TableDiscoveryServiceTest — all passed

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
getMetadataExtractorFailureReason(
e, MetricsConstants.MetadataUploadFailureReasons.UNKNOWN));
e, MetricsConstants.MetadataUploadFailureReasons.UNKNOWN),
path);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit: path here is whatever directory the failed listAllFilesInDir was on, which is the table root only sometimes. Discovery stops recursing once it sees .hoodie/, so if the Deny lands on a prefix above the tables (e.g. s3://.../Domain=CustomerOfferEvent/) that list fails before we ever descend, and the emitted value is that domain prefix — a container of many tables, not a table base path. The table_base_path label name over-promises for exactly the broad-Deny case this feature targets.

Since the metric is new (no back-compat cost), maybe rename the key to something like discovery_path / scan_path, or add a one-line note that the value is the scan path at failure depth and can be a prefix above the table. Non-blocking.

…ry_path

Review feedback (kroushan-nit): the emitted value is the prefix whose
listing failed in the recursive discovery walk. When a Deny lands on a
parent prefix (e.g. Domain=CustomerOfferEvent/) the list fails before
descending, so the value is a container of many tables, not a table base
path — table_base_path over-promised for exactly the broad-Deny case
this targets. Rename the label + constants to discovery_path before it
becomes a deployed contract (silences / dashboards / runbook reference it).

Behaviour unchanged; label key and identifiers only.

Local checks:
- ./gradlew :lakeview:spotlessApply — clean
- ./gradlew :lakeview:test --tests LakeViewExtractorMetricsTest --tests TableDiscoveryServiceTest — passed

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vishalk9 vishalk9 changed the title [ENG-44755] Emit table_base_path label on lakeView_table_discovery_failure [ENG-44755] Emit discovery_path label on lakeView_table_discovery_failure Jul 8, 2026
@sonarqubecloud

sonarqubecloud Bot commented Jul 8, 2026

Copy link
Copy Markdown

@vishalk9
vishalk9 merged commit 589f99e into main Jul 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants