Skip to content

ci: validate typed Python DataSource failure head - #3

Closed
Robin-Everaars wants to merge 9 commits into
mainfrom
ci/typed-python-datasource-failures-retry
Closed

ci: validate typed Python DataSource failure head#3
Robin-Everaars wants to merge 9 commits into
mainfrom
ci/typed-python-datasource-failures-retry

Conversation

@Robin-Everaars

Copy link
Copy Markdown
Collaborator

Temporary CI-only pull request for exact-head verification of #1.

Do not merge this branch. The integration target remains integration/dp-engine in #1.

Add an optional bearer_access_token_file to the Iceberg REST catalog
config. When the path is set, the token is read from that file and
re-read when the file changes or a short refresh interval elapses, so a
rotated projected service account token is picked up without restarting
the server. The file takes precedence over the static bearer_access_token.

main already resolves credentials per request through
CatalogCredentials::retrieve(), so this adds a FileCatalogCredentials
implementation plus the config field and the wiring in
create_catalog_manager. No provider changes are needed.

Closes lakehq#2287.
An empty or whitespace-only token file now yields an error instead of
caching an empty credential that would be sent as a bare bearer header.
The empty read is never cached, so the next retrieve picks up the token
as soon as the file holds one again. The Iceberg REST credentials
selection moved into iceberg_rest_credentials with behavioral tests for
the file precedence, the static and OAuth fallbacks and the no-token
case, covering the previously untested wiring. The token file path no
longer takes a needless clone.
…e per request

The token file credential kept an in-memory cache guarded by an mtime check,
a TTL and a mutex, yet still stat'd the file on every call, so the cache added
state without a filesystem-free fast path. Drop the cache: retrieve() now reads
the file, trims it and errors on an empty token, so every read is fresh.

Because IcebergRestCatalogProvider resolved the credential once and baked the
Authorization header into a single ApiClient, drop_database(cascade) reused that
client across every list and drop request. A projected service account token
that rotated partway through the cascade kept sending the stale header, and
because the per-object drop errors are ignored the cascade could partially
apply. Add a with_auth_retry helper that builds the client from a freshly read
credential, runs the request and, on a 401, rebuilds the client and retries the
request once. Every catalog operation goes through it, including each request in
the cascade loop, so a mid-cascade rotation is recovered per request. The
one-time startup config fetch is the exception: it caches its result and a 401
there is a hard startup failure that a same-token retry cannot recover. The
shared reqwest::Client and its connection pool are reused across attempts.

Cover the helper with unit tests (retry once on 401, no second retry, no retry
on other errors) and add a mock server regression test where the server rejects
the old token mid-cascade while the token file rotates, and the cascade drop
completes with every request eventually authorized.
Add bearer_access_token_file to the Iceberg REST catalog options and an example,
noting that the token is re-read per request and that a request rejected with a
401 is retried once after reloading the file.
The CASCADE path wrapped the table and view listings in `if let Ok(...)`
and each per-object drop in `let _ =`, so any failure was silently
discarded. A transient error on a table drop or a listing produced a half
applied cascade that still dropped the namespace and reported success.

Every list_tables, list_views, drop_table and drop_view failure now
propagates as an error. Only benign statuses are tolerated: NOT_FOUND on a
per-object drop (a concurrent removal), NOT_FOUND on a listing (the
namespace is already gone, handled by the trailing if_exists drop) and 405
or 501 on list_views (a catalog with no views endpoint, which the old code
also tolerated).

Signed-off-by: HOIST IT B.V. <info@hoist-it.nl>
Signed-off-by: HOIST IT B.V. <info@hoist-it.nl>
Signed-off-by: Robin Everaars <robin.everaars@hoist-it.nl>
Signed-off-by: Robin Everaars <robin.everaars@hoist-it.nl>
Signed-off-by: Robin Everaars <robin.everaars@hoist-it.nl>
@github-actions

Copy link
Copy Markdown

Warning

The baseline was not found.

Ibis Test Report

Commit Information

Commit Revision Branch
After dd68ed0 refs/pull/3/merge
Before dd68ed0 refs/pull/3/merge

Test Summary

Suite Commit Failed Passed Skipped Warnings Time (s)
test-ibis After 31 1538 166 4535 243.84
Before 31 1538 166 4535 243.84

Test Details

Error Counts
           32 Total
           23 Total Unique
-------- ---- ----------------------------------------------------------------------------------------------------------
            5 IllegalArgumentException: invalid argument: found TRUNCATE at 0:8 expected something else, ';', statement, or end of input
            2 AnalysisException: Internal error: Function 'approx_percentile_cont' failed to match any signature, errors: Error during planning: Function 'approx_percentile_cont' requires Float64, but received List...
            2 AssertionError
            2 AssertionError: Series are different
            2 IllegalArgumentException: invalid argument: found PARTITIONS at 5:15 expected 'DATABASES', 'SCHEMAS', 'NAMESPACES', 'CATALOGS', 'TABLES', 'TABLE', 'CREATE', 'COLUMNS', 'VIEWS', 'ALL', 'USER', 'SYSTEM'...
            2 assert ibis.Schema {... float64\n} == ibis.Schema {... float64\n} Full diff: ibis.Schema { carat float64 cut string color string clarity string depth float64 table float64 - price int32 ? ^^ + price i...
            1 AnalysisException: Catalog not found: local
            1 AnalysisException: Database not found: ibis_database_nqous3vw5vgwxhcotjkpqksiky
            1 AssertionError: DataFrame.iloc[:, 0] (column name="id") are different
            1 AssertionError: Series NA mask are different
            1 AssertionError: assert 'ibis_cached_mh7trevlgnechesdhb6d7btqfm' not in ['array_types', 'astronauts', 'awards_players', 'basic_table', 'batting', 'complicated', ...]
            1 AssertionError: assert nan == 22
            1 Failed: DID NOT RAISE <class 'pyspark.errors.exceptions.base.AnalysisException'>
            1 SparkRuntimeException: Cast error: Casting from Date32 to Float64 not supported
            1 SparkRuntimeException: Error during planning: expr type Struct("StructColumn({'x': xs, 'y': ys})": non-null Struct("x": non-null Int32, "y": non-null Int32)) can't cast to Struct("x": Int64, metadata:...
            1 TypeError: Cannot convert pyarrow.lib.ChunkedArray to pyarrow.lib.Array
            1 UnsupportedOperationException: CommandNode::AnalyzeTable
            1 UnsupportedOperationException: Physical plan does not support logical expression AggregateFunction(AggregateFunction { func: AggregateUDF { inner: ArrayAgg { signature: Signature { type_signature: Any...
            1 UnsupportedOperationException: Physical plan does not support logical expression InSubquery(InSubquery { expr: Column(Column { relation: Some(Bare { table: "t0" }), name: "#0" }), subquery: <subquery>...
            1 UnsupportedOperationException: Physical plan does not support logical expression InSubquery(InSubquery { expr: Column(Column { relation: Some(Bare { table: "t0" }), name: "#1" }), subquery: <subquery>...
            1 UnsupportedOperationException: unsupported ALTER TABLE operation
            1 assert frozenset({None}) == frozenset({None, 47}) Extra items in the right set: 47 Full diff: frozenset({ None, - 47, })
            1 assert {0.0, 1.0, 2.0, 3.0} == {1, 2, 3} Extra items in the left set: 0.0 Full diff: { + 0.0, - 1, + 1.0, ? ++ - 2, + 2.0, ? ++ - 3, + 3.0, ? ++ }
Passed Tests Diff

(empty)

Failed Tests
ibis/backends/pyspark/tests/test_basic.py::test_group_by
ibis/backends/pyspark/tests/test_client.py::test_catalog_db_args
ibis/backends/pyspark/tests/test_client.py::test_create_table_with_partition_and_catalog
ibis/backends/pyspark/tests/test_client.py::test_create_table_with_partition_no_catalog
ibis/backends/pyspark/tests/test_ddl.py::test_compute_stats
ibis/backends/pyspark/tests/test_ddl.py::test_drop_non_empty_database
ibis/backends/pyspark/tests/test_ddl.py::test_insert_table
ibis/backends/pyspark/tests/test_ddl.py::test_truncate_table
ibis/backends/tests/test_aggregation.py::test_approx_quantile[pyspark-True-False]
ibis/backends/tests/test_aggregation.py::test_approx_quantile[pyspark-True-True]
ibis/backends/tests/test_aggregation.py::test_date_quantile[pyspark]
ibis/backends/tests/test_aggregation.py::test_group_concat_over_window[pyspark]
ibis/backends/tests/test_client.py::test_insert_overwrite_from_dataframe[pyspark]
ibis/backends/tests/test_client.py::test_insert_overwrite_from_expr[pyspark]
ibis/backends/tests/test_client.py::test_insert_overwrite_from_list[pyspark]
ibis/backends/tests/test_client.py::test_rename_table[pyspark]
ibis/backends/tests/test_expr_caching.py::test_persist_expression_contextmanager[pyspark]
ibis/backends/tests/test_expr_caching.py::test_persist_expression_release[pyspark]
ibis/backends/tests/test_expr_caching.py::test_persist_expression_repeated_cache[pyspark]
ibis/backends/tests/test_generic.py::test_isin_uncorrelated[pyspark]
ibis/backends/tests/test_generic.py::test_isin_uncorrelated_simple[pyspark]
ibis/backends/tests/test_io.py::test_read_csv[pyspark-default]
ibis/backends/tests/test_io.py::test_read_csv[pyspark-file_name]
ibis/backends/tests/test_join.py::test_join_with_pandas[pyspark]
ibis/backends/tests/test_json.py::test_json_getitem_array[pyspark]
ibis/backends/tests/test_struct.py::test_field_overwrite_always_prefers_unpacked[pyspark]
ibis/backends/tests/test_struct.py::test_isin_struct[pyspark]
ibis/backends/tests/test_struct.py::test_single_field[pyspark-a]
ibis/backends/tests/test_struct.py::test_single_field[pyspark-b]
ibis/backends/tests/test_struct.py::test_single_field[pyspark-c]
ibis/backends/tests/test_temporal.py::test_delta[pyspark-time]

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.

1 participant