Skip to content

fix(ingestion): detect missing Lake Formation grants in Athena test connection - #29515

Merged
harshsoni2024 merged 2 commits into
mainfrom
athena_test_con_improvement
Jul 1, 2026
Merged

fix(ingestion): detect missing Lake Formation grants in Athena test connection#29515
harshsoni2024 merged 2 commits into
mainfrom
athena_test_con_improvement

Conversation

@harshsoni2024

@harshsoni2024 harshsoni2024 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Fixes #29160

Problem

The Athena connector's Test Connection reported all four steps green —
CheckAccess, GetSchemas, GetTables, GetViews — even when the IAM role was
missing the AWS Lake Formation DESCRIBE/SELECT grants needed to read the
target tables. The metadata run then "succeeded" but ingested 0 tables,
giving users no signal that anything was wrong.

Why it passed

  • The test steps only checked that the AWS call didn't raise. AWS Lake
    Formation silently filters results — when grants are missing it returns
    an empty list instead of an error. pyathena reinforces this: its
    list_table_metadata/_get_schemas even convert some ClientErrors into
    []. So inspector.get_table_names(schema) came back empty, the step was
    marked passed, and nothing raised.
  • The old executor probed only the first schema returned (all_schemas[0]),
    not the schemas the configured schemaFilterPattern actually targets.

Catalog vs. schema

catalogId is the AWS data catalog (the level above schemas), injected
into the connection URL as &catalog_name=<catalogId>. pyathena scopes
list_databases/list_table_metadata to it, so get_schema_names() already
returns the databases within the configured catalog and get_table_names()
is already catalog-scoped. A single Athena service maps to a single catalog
(Athena doesn't override get_database_names), so detecting "zero readable
tables across the targeted schemas" via the inspector is a faithful proxy for
the same condition that makes ingestion yield 0 tables.

Fix

  • GetTables now selects the schemas schemaFilterPattern would target
    (reusing filter_by_schema, capped at MAX_SCHEMAS_TO_PROBE = 100 so a
    catalog with many databases can't exhaust the 3-minute timeout) and raises
    when no tables are readable in any of them — with a message naming the catalog
    and pointing the user at Lake Formation DESCRIBE/SELECT grants. It passes
    as soon as any one targeted schema is readable.
  • GetViews uses the same schema selection but never raises on empty
    (views are legitimately often absent; the step is non-mandatory).
  • The Athena test-connection definition's GetTables errorMessage now mentions
    Lake Formation so the user-visible one-liner is actionable.

Type of change:

  • Bug fix
  • Improvement
  • New feature
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation

High-level design:

N/A — small change.

Tests:

Use cases covered

Unit tests

Backend integration tests

Ingestion integration tests

Playwright (UI) tests

Manual testing performed

UI screen recording / screenshots:

Not applicable.

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes <issue-number>: <short explanation>
  • My PR is linked to a GitHub issue via Fixes #<issue-number> above.
  • I have commented on my code, particularly in hard-to-understand areas.
  • For JSON Schema changes: I updated the migration scripts or explained why it is not needed.
  • For UI changes: I attached a screen recording and/or screenshots above.
  • I have added tests (unit / integration / Playwright as applicable) and listed them above.

Greptile Summary

This PR fixes a silent false-positive in the Athena connector's Test Connection flow: AWS Lake Formation returns empty lists (not errors) when grants are missing, so the old executor reported all steps green while ingestion would produce 0 tables. The fix makes GetTables inspect the schemas the configured schemaFilterPattern actually targets and raise a descriptive RuntimeError when no tables are readable across all of them.

  • _get_targeted_schemas() mirrors the ingestion-time schema filter and caps probing at MAX_SCHEMAS_TO_PROBE = 100 to avoid exhausting the 3-minute timeout on wide catalogs.
  • custom_executor_for_table short-circuits as soon as any one targeted schema has at least one readable table; custom_executor_for_view is probe-only and never raises, because views are legitimately absent.
  • Eight new unit tests cover the raise/pass/filter/cap/memoisation behaviours introduced by the fix.

Confidence Score: 5/5

Safe to merge — the change is scoped entirely to the Athena test-connection path and does not touch ingestion runtime, schema listing logic, or any shared utilities.

The implementation correctly mirrors the ingestion-time schema filter, handles the Lake Formation silent-empty-list behaviour, caps probing to avoid timeouts, and memoises schema listing across the two executor steps. All new code paths are covered by targeted unit tests that verify both the raise and the pass conditions. No existing tests are broken and no shared code was modified.

No files require special attention.

Important Files Changed

Filename Overview
ingestion/src/metadata/ingestion/source/database/athena/connection.py Adds _get_targeted_schemas(), rewrites custom_executor_for_table to raise on zero-readable-tables, and updates custom_executor_for_view to probe-only. Logic is correct; minor ordering nit with inspect() before the guard is cosmetic only.
ingestion/tests/unit/source/database/athena/test_connection.py Adds eight focused unit tests covering empty schema list, partial reads, filter pattern, cap at MAX_SCHEMAS_TO_PROBE, and memoisation. Test helper _run_steps_capturing correctly intercepts the framework without coupling to its internals.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant TC as test_connection()
    participant GS as get_targeted_schemas() [memoised]
    participant Insp as SQLAlchemy Inspector
    participant LF as AWS Lake Formation (via pyathena)

    TC->>GS: get_targeted_schemas()
    GS->>Insp: get_schema_names()
    Insp->>LF: list_databases (catalog-scoped)
    LF-->>Insp: ["db1", "db2", ...]
    Insp-->>GS: schema list
    GS->>GS: filter_by_schema + cap at MAX_SCHEMAS_TO_PROBE
    GS-->>TC: targeted_schemas (cached)

    note over TC: GetTables step
    TC->>Insp: get_table_names(schema) for each targeted schema
    Insp->>LF: list_table_metadata (catalog-scoped)
    alt Lake Formation grants missing
        LF-->>Insp: [] (silently filtered)
        Insp-->>TC: []
        TC-->>TC: raise RuntimeError (Lake Formation DESCRIBE/SELECT missing)
    else Grants present
        LF-->>Insp: ["t1", "t2"]
        Insp-->>TC: ["t1", "t2"]
        TC-->>TC: "any() == True → pass"
    end

    note over TC: GetViews step (non-mandatory, never raises)
    TC->>GS: get_targeted_schemas() [cache hit, no new call]
    GS-->>TC: targeted_schemas
    TC->>Insp: get_view_names(schema) for each targeted schema
    Insp-->>TC: views or []
    TC-->>TC: break on first non-empty, or finish quietly
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant TC as test_connection()
    participant GS as get_targeted_schemas() [memoised]
    participant Insp as SQLAlchemy Inspector
    participant LF as AWS Lake Formation (via pyathena)

    TC->>GS: get_targeted_schemas()
    GS->>Insp: get_schema_names()
    Insp->>LF: list_databases (catalog-scoped)
    LF-->>Insp: ["db1", "db2", ...]
    Insp-->>GS: schema list
    GS->>GS: filter_by_schema + cap at MAX_SCHEMAS_TO_PROBE
    GS-->>TC: targeted_schemas (cached)

    note over TC: GetTables step
    TC->>Insp: get_table_names(schema) for each targeted schema
    Insp->>LF: list_table_metadata (catalog-scoped)
    alt Lake Formation grants missing
        LF-->>Insp: [] (silently filtered)
        Insp-->>TC: []
        TC-->>TC: raise RuntimeError (Lake Formation DESCRIBE/SELECT missing)
    else Grants present
        LF-->>Insp: ["t1", "t2"]
        Insp-->>TC: ["t1", "t2"]
        TC-->>TC: "any() == True → pass"
    end

    note over TC: GetViews step (non-mandatory, never raises)
    TC->>GS: get_targeted_schemas() [cache hit, no new call]
    GS-->>TC: targeted_schemas
    TC->>Insp: get_view_names(schema) for each targeted schema
    Insp-->>TC: views or []
    TC-->>TC: break on first non-empty, or finish quietly
Loading

Reviews (2): Last reviewed commit: "refactor(ingestion): address review feed..." | Re-trigger Greptile

…onnection

Athena test connection reported all steps green even when the IAM role
lacked Lake Formation DESCRIBE/SELECT grants, so the run "succeeded" but
ingested 0 tables. AWS Lake Formation silently filters results (pyathena
even converts the underlying ClientError into an empty list), so
inspector.get_table_names() returned empty and GetTables passed. The old
executor also probed only the first schema, not the schemas the configured
schemaFilterPattern targets.

GetTables now probes the schemas schemaFilterPattern would target (capped
at MAX_SCHEMAS_TO_PROBE) and raises when no tables are readable in any of
them, with a message pointing at Lake Formation grants on the catalog. It
passes as soon as any one schema is readable. GetViews uses the same
selection but never raises on empty.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 26, 2026 05:58
@harshsoni2024
harshsoni2024 requested a review from a team as a code owner June 26, 2026 05:58
@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

✅ PR checks passed

The linked issue has a description and all required Shipping project fields set. Thanks!

@github-actions github-actions Bot added Ingestion safe to test Add this label to run secure Github workflows on PRs labels Jun 26, 2026
Comment thread ingestion/src/metadata/ingestion/source/database/athena/connection.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Improves the Athena connector’s Test Connection behavior to detect the “silent empty list” case caused by missing AWS Lake Formation grants, preventing false-green results that later lead to ingesting 0 tables with no signal.

Changes:

  • Added schema targeting logic (honoring schemaFilterPattern) and capped probing to avoid exhausting the test-connection timeout.
  • Updated GetTables to fail when no tables are readable across targeted schemas, and updated GetViews to probe similarly without failing on empty views.
  • Added unit tests covering missing grants behavior, schema filtering, and the probing cap.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
ingestion/src/metadata/ingestion/source/database/athena/connection.py Adds targeted-schema selection + capped probing; makes GetTables raise on “zero readable tables” while keeping GetViews non-failing on empty results.
ingestion/tests/unit/source/database/athena/test_connection.py Adds unit tests to validate the new GetTables/GetViews behaviors, schema filter honoring, and the max-schemas cap.
Comments suppressed due to low confidence (1)

ingestion/src/metadata/ingestion/source/database/athena/connection.py:164

  • The PR description mentions updating the Athena Test Connection definition’s GetTables.errorMessage to mention Lake Formation, but the current definition file still has the generic privilege message. Since the UI result stores the one-liner in TestConnectionStepResult.message (from errorMessage) separately from the exception text, users may still see an unhelpful message unless the definition is updated.
        test_fn = {
            "CheckAccess": partial(test_connection_engine_step, engine),
            "GetSchemas": partial(execute_inspector_func, engine, "get_schema_names"),
            "GetTables": custom_executor_for_table,
            "GetViews": custom_executor_for_view,

Comment thread ingestion/src/metadata/ingestion/source/database/athena/connection.py Outdated
Comment thread ingestion/src/metadata/ingestion/source/database/athena/connection.py Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 63%
63.42% (70688/111446) 45.9% (40625/88504) 47.81% (12369/25866)

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🟡 Playwright Results — all passed (14 flaky)

✅ 4457 passed · ❌ 0 failed · 🟡 14 flaky · ⏭️ 88 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 324 0 1 4
🟡 Shard 2 833 0 4 9
🟡 Shard 3 836 0 3 7
🟡 Shard 4 833 0 3 10
🟡 Shard 5 834 0 1 50
🟡 Shard 6 797 0 2 8
🟡 14 flaky test(s) (passed on retry)
  • Flow/SearchRBAC.spec.ts › the browse tree only shows the asset-type categories a user can access (shard 1, 1 retry)
  • Features/BulkEditEntity.spec.ts › Glossary (shard 2, 1 retry)
  • Features/BulkImport.spec.ts › Database service (shard 2, 1 retry)
  • Features/BulkImport.spec.ts › Database Schema (shard 2, 1 retry)
  • Features/DataQuality/ColumnLevelTests.spec.ts › Column Value Min To Be Between (shard 2, 1 retry)
  • Features/SearchExport.spec.ts › Export queues a background job and downloads from the jobs tray (shard 3, 1 retry)
  • Features/Table.spec.ts › Table pagination with sorting should works (shard 3, 1 retry)
  • Features/TestSuiteMultiPipeline.spec.ts › TestSuite multi pipeline support (shard 3, 1 retry)
  • Pages/CustomProperties.spec.ts › Should display custom properties for apiCollection in right panel (shard 4, 1 retry)
  • Pages/DataContracts.spec.ts › Create Data Contract and validate for Api Collection (shard 4, 1 retry)
  • Pages/DataContracts.spec.ts › Contract Status badge should be visible on condition if Contract Tab is present/hidden by Persona (shard 4, 1 retry)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 5, 1 retry)
  • Pages/GlossaryImportExport.spec.ts › Import partial success - some terms pass, some fail (shard 6, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

Memoize the targeted-schema listing so GetTables and GetViews share a
single get_schema_names() round-trip instead of one each. Stop collecting
schemas once MAX_SCHEMAS_TO_PROBE is reached rather than slicing after the
fact. Replace the any()-for-side-effect view probe with an explicit
for/break loop to avoid the discarded-return anti-pattern (B015). Soften
the GetTables error message to note the catalog may legitimately be empty,
and document the empty/view-only catalog as a by-design failure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@harshsoni2024
harshsoni2024 enabled auto-merge (squash) July 1, 2026 13:15
@harshsoni2024
harshsoni2024 merged commit 10aefa4 into main Jul 1, 2026
63 checks passed
@harshsoni2024
harshsoni2024 deleted the athena_test_con_improvement branch July 1, 2026 13:22
@gitar-bot

gitar-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 2 resolved / 2 findings

Improves Athena connection testing by replacing naive single-schema probes with a filter-aware, capped multi-schema scan. This change explicitly detects missing Lake Formation grants and ensures the test fails when no tables are readable in the targeted schemas.

✅ 2 resolved
Edge Case: Mandatory GetTables now fails for legitimately empty/view-only catalogs

📄 ingestion/src/metadata/ingestion/source/database/athena/connection.py:133-147
custom_executor_for_table now raises (failing the mandatory GetTables step) whenever no targeted schema returns any tables. This is the intended way to surface missing Lake Formation grants, but it also turns previously-passing setups into failures in legitimate cases:

  • A catalog/schema that contains only views and no tables (GetViews never raises, but GetTables will).
  • Brand-new or empty databases that genuinely have zero tables yet.
  • Catalogs with more than MAX_SCHEMAS_TO_PROBE (100) databases where all readable tables happen to live in schemas beyond the cap (the cap is applied to get_schema_names() order, which is not guaranteed to surface non-empty schemas first).

In these cases the test connection will now report a hard failure where it used to pass. Since the empty-result/missing-grant ambiguity is fundamentally undistinguishable (as the docstring notes), consider whether GetTables should remain mandatory, or document this trade-off, so users with legitimately table-less catalogs aren't blocked.

Performance: Redundant get_schema_names() calls across executors

📄 ingestion/src/metadata/ingestion/source/database/athena/connection.py:135 📄 ingestion/src/metadata/ingestion/source/database/athena/connection.py:155 📄 ingestion/src/metadata/ingestion/source/database/athena/connection.py:162
_get_targeted_schemas calls inspector.get_schema_names() (an AWS Athena/Glue API round-trip). It is invoked independently inside both custom_executor_for_table and custom_executor_for_view, and the GetSchemas step also lists schemas separately, so get_schema_names() is executed three times within a single test connection. While not on a hot path, each call counts against the 3-minute test-connection timeout. Consider computing the targeted schema list once and reusing it across the table and view executors.

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ingestion safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Athena: Flag test connection if specific catalog has no permission

3 participants