-
Notifications
You must be signed in to change notification settings - Fork 116
Stub every page in the listing resume test #871
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0ecf23b
8b0d8a8
2a83e8d
6cef032
5041b76
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,7 @@ | |
| from pyathena.util import RetryConfig | ||
| from tests import ENV | ||
| from tests.pyathena.conftest import connect | ||
| from tests.pyathena.tables import TABLES, VIEWS | ||
| from tests.pyathena.util import succeeded_query_execution, throttle_metadata_api, unreachable_glue | ||
|
|
||
| _logger = logging.getLogger(__name__) | ||
|
|
@@ -1571,34 +1572,57 @@ def test_other_metadata_exception_keeps_its_retries(self, cursor, monkeypatch): | |
| cursor.get_table_metadata("one_row") | ||
| assert calls == ["get_table_metadata"] * 2 | ||
|
|
||
| # Without the Glue fallback, a throttled page is retried, so Athena serves every page. | ||
| @pytest.mark.parametrize("cursor", [{"glue_metadata_fallback": False}], indirect=["cursor"]) | ||
| def test_listing_reads_every_page(self, cursor, monkeypatch): | ||
| client = cursor.connection.client | ||
| list_table_metadata = client.list_table_metadata | ||
| tokens = [] | ||
|
|
||
| def record_token(**kwargs): | ||
| tokens.append(kwargs.get("NextToken")) | ||
| return list_table_metadata(**kwargs) | ||
|
|
||
| monkeypatch.setattr(client, "list_table_metadata", record_token) | ||
| # Other tests add and drop their own tables, so only the session's are compared. | ||
| session_tables = sorted(t.name for t in (*TABLES, *VIEWS)) | ||
|
|
||
| names = [m.name for m in cursor.list_table_metadata(max_results=4)] | ||
|
|
||
| assert sorted(n for n in names if n in session_tables) == session_tables | ||
| # At four per page, the session's tables and views span three pages or more. | ||
| assert len(set(tokens)) >= 3 | ||
|
|
||
| @pytest.mark.parametrize( | ||
| "cursor", [{"retry_config": RetryConfig(attempt=1)}], indirect=["cursor"] | ||
| ) | ||
| def test_listing_resumes_after_a_failed_glue_request(self, cursor, monkeypatch): | ||
| # A page throttled mid-listing is read again, not the pages before it. | ||
| expected = sorted(m.name for m in cursor.list_table_metadata(max_results=2)) | ||
| # The pages are stubbed so that real throttling cannot change the requests. | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Self-review round 2 (claims, callers, AWS operation): FINDINGS, repaired in the PR description
|
||
| pages = { | ||
| None: {"TableMetadataList": [{"Name": "t0"}, {"Name": "t1"}], "NextToken": "1"}, | ||
| "1": {"TableMetadataList": [{"Name": "t2"}, {"Name": "t3"}], "NextToken": "2"}, | ||
| "2": {"TableMetadataList": [{"Name": "t4"}]}, | ||
| } | ||
| client = cursor.connection.client | ||
| list_table_metadata = client.list_table_metadata | ||
| requests = [] | ||
|
|
||
| def throttle_second_page_once(**kwargs): | ||
| requests.append(kwargs.get("NextToken")) | ||
| def throttle_second_request_once(**kwargs): | ||
| token = kwargs.get("NextToken") | ||
| requests.append(token) | ||
| if len(requests) == 2: | ||
| raise ClientError( | ||
| {"Error": {"Code": "ThrottlingException", "Message": "Rate exceeded"}}, | ||
| "ListTableMetadata", | ||
| ) | ||
| return list_table_metadata(**kwargs) | ||
| return pages[token] | ||
|
|
||
| monkeypatch.setattr(client, "list_table_metadata", throttle_second_page_once) | ||
| self._unreachable_glue(cursor.connection, monkeypatch) | ||
| monkeypatch.setattr(client, "list_table_metadata", throttle_second_request_once) | ||
| glue = self._unreachable_glue(cursor.connection, monkeypatch) | ||
|
|
||
| assert sorted(m.name for m in cursor.list_table_metadata(max_results=2)) == expected | ||
| # Every page once, and the throttled second page a second time. | ||
| assert len(expected) > 2 | ||
| assert requests[0] is None | ||
| assert requests[1] == requests[2] | ||
| assert len(requests) == len(set(requests)) + 1 | ||
| assert [m.name for m in cursor.list_table_metadata()] == ["t0", "t1", "t2", "t3", "t4"] | ||
| assert requests == [None, "1", "1", "2"] | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Self-review round 1 (behavior and implementation): CLEAN
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Self-review round 2 after the rebase (claims, callers, AWS operation): CLEAN
|
||
| assert not glue.reachable | ||
|
|
||
| @pytest.mark.parametrize( | ||
| ("cursor", "catalog_name"), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Independent review after the rebase (relayed): FINDINGS → repaired in 5041b76
codex exec, model gpt-6-sol,model_reasoning_effort=high,--sandbox read-only), session01a0f836-8bba-7cf2-b281-fdde829dddeb. Static review only. The prompt omitted the PR number, description, commit messages, and self-review findings. Snapshot at the head, unchanged afterwards.49481d900e9e0098e3edd562f1a858ce91ec0573, head6cef032c4f5c1ee5a0809cc18346e4f14a3d88e0. Covered: both changed tests and neighbouring throttling tests, session tables/views/setup and cursor fixture, Athena listing/retry/Glue fallback paths, conventions.NextToken. A throttled page was answered by Glue's whole listing, and a regression that droppedmax_resultsreturned all nine session entries in one page. Reproduced locally: forcingMaxResultsto 50 passed the old version. Repair:glue_metadata_fallback=False, so a throttled page is retried by the default policy, pluslen(set(tokens)) >= 3from a pass-through recorder; retries repeat a token, so they cannot inflate the count. With the repair, the forced-50 regression fails (assert 1 >= 3) and the first-page-only regression fails (Right contains 5 more items).NextTokenwere an offset. Not fixed. One probe on a leftover schema showed no shift when a table was added or removed before the current page, and a dedicated database would add DDL to every run. The assumption is stated in the PR description.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Independent follow-up on the repair (relayed): CLEAN
codex exec, model gpt-6-sol,model_reasoning_effort=high,--sandbox read-only), session01a0f83d-108b-7e32-ba88-396821237828. Static review of patch6cef032c..5041b763with the full diff49481d90..5041b763for context. Snapshot unchanged afterwards.max_results=4need at least three distinct page requests, so neither a single oversized page nor a listing that stops early satisfies both assertions. Retries reuse a page's token and cannot inflate the count.