Skip to content

Add a temporal benchmark shape derived from the snapshot timestamp (GH-1287) - #1290

Merged
laughingman7743 merged 6 commits into
masterfrom
feat/1287-temporal-bench-shape
Oct 18, 2026
Merged

laughingman7743 merged 6 commits into
masterfrom
feat/1287-temporal-bench-shape

Conversation

@laughingman7743

@laughingman7743 laughingman7743 commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

WHAT

Adds a temporal shape to the benchmark harness (benchmarks/pyathena_bench). It adds the following columns, derived from the snapshot's bigint "timestamp" (epoch microseconds), to the flat projection:

Column Expression Athena type
event_time from_unixtime("timestamp" / 1e6) timestamp(3)
event_date CAST(... AS date) date
event_time_utc from_unixtime("timestamp" / 1e6, 'UTC') timestamp(3) with time zone
event_time_zone from_unixtime("timestamp" / 1e6, 'Asia/Tokyo') timestamp(3) with time zone
event_time_offset from_unixtime("timestamp" / 1e6, '+09:00') timestamp(3) with time zone
event_clock CAST(... AS time(3)) time(3)
  • select_sql() now takes the transport and raises ValueError for an unknown transport. For the temporal shape, UNLOAD and CTAS reads project only event_time and event_date, because UNLOAD and CTAS cannot write the other types. The other shapes return the same SQL for every transport. The runner and prepare_fixtures() pass the case's transport.
  • matrix() gives the temporal shape the same cases as flat in the single-query suite. UNLOAD and CTAS stay supported, through the Parquet projection. Like json, the shape has no cases in the concurrent and initialization suites.
  • The zone-name column uses Asia/Tokyo, a zone without daylight saving time. A zone name with an ambiguous fall-back hour, such as America/New_York on 2026-11-01, makes PyAthena raise DataError (checked on Athena), which would turn the CSV cases of a snapshot from such a day into failures.
  • README and DATASET.md describe the shape, and the README gets an example run command.

WHY

Part of #1287 (harness change; the measurement belongs to the combined run of #1288). The existing flat, nested, and json shapes keep "timestamp" as a bigint, so no case measures the temporal conversions that changed in 4.2.0 (#1233, #1234, #1236, #1273, #1277, #1224). The combined run of #1288 needs this shape on the commit planned for 4.2.0. The shape needs no new input data.

TEST

Tested commit: 1086ef7

  • just lint, just benchmark lint, just docs lint: passed.
  • just benchmark test: 251 passed, 1 skipped.
  • New tests:
    • test_temporal_projection_of_parquet_reads_has_no_time_or_time_zone_types: the CSV, UNLOAD, and CTAS SQL of the shape.
    • test_temporal_shape_covers_every_flat_case_in_the_single_query_suite.
    • test_temporal_trials_query_the_projection_of_their_transport: the SQL in the trial payloads of run().
  • Mutation checks: with the transport branch of select_sql() disabled, the projection test fails. With the transport validation removed, the same test fails. With the runner passing "csv" instead of case.transport, the runner test fails. With matrix() returning flat cases for the temporal shape, the matrix test fails.
  • plan --suite single --scale small --shape temporal: 138 cases, the same count as flat. jobs --shape temporal produced job IDs such as single-small-temporal-pandas-sync-unload-native.
  • Checked on Athena with two-row VALUES queries (test work group, objects removed afterwards):
    • from_unixtime(double) returns timestamp(3), and from_unixtime(double, zone) returns timestamp(3) with time zone.
    • UNLOAD and Wrangler CTAS succeed for timestamp(3) and date. They fail for with time zone and time(3) with NOT_SUPPORTED: Unsupported Hive type.
    • All six columns (with Asia/Tokyo) read without errors with Cursor, PandasCursor, ArrowCursor, PolarsCursor, and Wrangler CSV. UNLOAD reads of event_time and event_date succeeded with PandasCursor, ArrowCursor, and PolarsCursor.

Not run: the harness against a benchmark stack (prepare/run), which needs the CloudFormation stack and belongs to the combined run of #1288. The Athena checks above ran the shape's column expressions directly, not the harness's full queries over a snapshot.

🤖 Generated with Claude Code

laughingman7743 and others added 4 commits October 18, 2026 22:19
…H-1287)

The existing shapes keep "timestamp" as a bigint, so no benchmark case
converts a temporal value. The temporal shape adds timestamp(3), date,
timestamp(3) with time zone (UTC, a zone name, an offset), and time(3)
columns derived from it to the flat projection. UNLOAD and CTAS reads
project only the timestamp(3) and date columns, because Athena's Hive
writer rejects time and with time zone types. The shape covers every
family in the single-query suite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…H-1287)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…H-1287)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TEMPORAL_COLUMNS = (
f"{PARQUET_TEMPORAL_COLUMNS}, "
f"from_unixtime({_SECONDS}, 'UTC') AS event_time_utc, "
f"from_unixtime({_SECONDS}, 'Asia/Tokyo') AS event_time_zone, "

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Self-review round one (behavior and implementation), base 7b2a084, initial head 1bf7f02, repaired head 7e6349b.

Covered: select_sql() and its callers (runner trial payloads, prepare_fixtures()), matrix() for every suite, the case/job IDs, the docs, and the new tests. Passes: my own read, then /code-review (high), then /simplify (four angles).

Result: FINDINGS, repaired.

  1. The zone-name column used America/New_York. Athena writes zone-name values without an offset, and PyAthena raises DataError for an ambiguous local time. Checked on Athena: from_unixtime(1793511000, 'America/New_York') gives DataError ... '2026-11-01 01:30:00.000 America/New_York' is ambiguous. Any snapshot that includes a fall-back hour would turn every temporal CSV case into a failure. Repaired in 64a8887: the column now uses Asia/Tokyo, which has no DST. README and DATASET.md were updated too.

}.get(shape)
if columns is None:
raise ValueError(f"Unknown shape: {shape}")
if transport not in {"csv", "unload", "ctas"}:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Round one, findings 2-3 (repaired):

  1. select_sql() treated any transport outside {unload, ctas} as CSV, so a misspelled transport would read the full temporal projection. Repaired in 64a8887: an unknown transport raises ValueError. The test is in test_temporal_projection_of_parquet_reads_has_no_time_or_time_zone_types.
  2. Simplifications from my own pass and /simplify, in 0a96670 and 7e6349b: the shape lookup now runs before the Parquet override, which drops the str | None annotation. After validation, the override checks transport != "csv". The matrix() docstring no longer repeats the projection rule from select_sql().

Deferred, with reasons:

  • Precision 6 for the CSV columns: Benchmark the temporal type conversion changes of 4.2.0 #1287 specifies timestamp(3), the precision of Hive tables.
  • One table for both Parquet-incompatible mechanisms (json marked unsupported vs temporal narrowed): json without its column would repeat flat, while the narrowed temporal projection still has timestamp(3) and date. Unifying them would be an unapproved restructuring.
  • A shared TRANSPORTS constant: the transport literals are also repeated in existing code outside this diff (__main__.py:100, runner.py:557, cases.py).
  • CSV vs UNLOAD reading different column sets in the temporal shape: documented in README; the report already says to compare cases with the same transport.

)


def test_temporal_trials_query_the_projection_of_their_transport(monkeypatch, tmp_path, run_stubs):

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Round one, test coverage: test names, mutation checks, and validation results.

Test names: renamed in 64a8887 from ..._query_no_time_zone_columns, which promised more than it checked. This test covers the wiring of run(). The exact Parquet projection is asserted in test_config.py.

Mutation checks on 1bf7f02:

  • With the temporal branch of select_sql() disabled, test_temporal_projection_... fails.
  • With the runner passing "csv" instead of case.transport, this test fails.

Validation at 7e6349b:

  • just benchmark lint: passed.
  • just benchmark test: 251 passed, 1 skipped.
  • just docs lint: passed, at 64a8887; 7e6349b changes no Markdown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread benchmarks/README.md
The nested workload preserves those structures.
The json workload adds `CAST(details AS JSON) AS details_json` to the flat projection; it covers native pandas and Wrangler reads in the single-query suite, with their UNLOAD and CTAS reads marked unsupported, because Parquet cannot hold the `json` type.
The temporal workload adds columns derived from `"timestamp"` with `from_unixtime()` to the flat projection: `event_time` (`timestamp(3)`), `event_date` (`date`), `event_time_utc`, `event_time_zone`, and `event_time_offset` (`timestamp(3) with time zone` in `UTC`, `Asia/Tokyo`, and `+09:00`), and `event_clock` (`time(3)`).
It covers every family in the single-query suite; its UNLOAD and CTAS reads add only `event_time` and `event_date`, because UNLOAD and CTAS cannot write `time` or `with time zone` types.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Self-review round two (claims, callers, operations), base 7b2a084, initial head 7e6349b, repaired head 998a3b6.

Claims checked: the PR body, README, DATASET.md, the comments and docstrings in config.py and cases.py, and the commit messages.

Result: FINDINGS, repaired.

  1. README and DATASET.md said that Athena cannot write time or with time zone types to Parquet. The evidence covers only UNLOAD and CTAS (NOT_SUPPORTED: Unsupported Hive type); Iceberg Parquet is a separate path that I did not check. Repaired in 998a3b6: the wording now names UNLOAD and CTAS.
  2. The PR body still named America/New_York, tested commit 1bf7f02, and the old test name, and it left out the ValueError for an unknown transport. Repaired in the PR description.

# through Hive, which has no time or with time zone types, so their reads project
# the timestamp and date columns only. The zone has no daylight saving time, whose
# ambiguous local times PyAthena rejects.
_SECONDS = '"timestamp" / 1e6'

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Round two, evidence for the claims:

Rerun on Athena after the zone change (two-row VALUES queries, test work group, probe objects removed):

  • from_unixtime(double) returns timestamp(3).
  • from_unixtime(double, zone) returns timestamp(3) with time zone.
  • UNLOAD and CTAS accept event_time and event_date and reject the other four columns.
  • All six columns, with Asia/Tokyo, read without errors with Cursor, PandasCursor, ArrowCursor, PolarsCursor, and Wrangler CSV.
  • PandasCursor, ArrowCursor, and PolarsCursor read UNLOAD results of the Parquet projection.

Existing callers: the signature change of select_sql() reaches only runner.run() and aws.prepare_fixtures(), and both pass the transport. Queued fleet jobs carry CLI arguments, not SQL, and workers run the stack's checked-out commit.

Operator view: the temporal columns are computed from "timestamp", which the flat projection already reads, so a query scans no extra columns of the snapshot. The shape adds cases only when --shape temporal is selected; plan shows 138 cases at small, the same count as flat.

Evidence: the transport validation was mutation-checked (with it removed, test_temporal_projection_... fails).

Limits:

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread benchmarks/tests/test_config.py Outdated
def test_temporal_shape_covers_every_flat_case_in_the_single_query_suite():
cases = matrix(Settings(), "single", "temporal")
# UNLOAD and CTAS read a Parquet projection instead of being unsupported.
assert [replace(c, shape="flat") for c in cases] == matrix(Settings(), "single", "flat")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Independent review (relayed), static only.

Reviewer: Codex CLI 0.163.0, model gpt-6-astra, reasoning effort max, codex exec --sandbox read-only, session 01a14f34-7a9c-7771-9350-35a3ae161e7b. Base 7b2a084, head 998a3b6, on a detached snapshot (unchanged afterwards). The reviewer got the literal diff, the conventions, and the testing guide. It got no PR text, commit messages, or prior findings. Instructions: no edits, builds, tests, or network access.

Covered: both select_sql() callers, fixture preparation, and transport validation. All four suites; CLI plan/run/jobs; fleet filtering and case identity. PyAthena row/native paths, Wrangler CSV/CTAS/UNLOAD, SQLAlchemy, failure propagation, and reporting. Changed tests against existing coverage and docs/testing.md; README/DATASET accuracy; imports, docstrings, and structural scope.

Result: FINDINGS (1). No runtime defect was identified.

  • P3 — The matrix test masks an incorrect result shape. This line replaces every returned case's shape with "flat" before comparing. A regression that returns flat cases for matrix(Settings(), "single", "temporal") would therefore pass, although run() would select flat SQL and measure no temporal conversion. The new runner test constructs temporal cases directly, so it would also miss that regression.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Repaired in 1086ef7: the test now compares matrix(Settings(), "single", "temporal") with the flat cases converted to shape="temporal", so the actual shape stays in the comparison.

Validation:

  • Mutation check: with matrix() returning flat cases for the temporal shape, this test now fails.
  • just benchmark lint: passed.
  • just benchmark test: 251 passed, 1 skipped.

Self-review of the repair:

  • Round one: behavior and tests. It changes only the assertion. The contract (every flat case, UNLOAD and CTAS included, none unsupported) is unchanged.
  • Round two: claims. The PR description's tested commit and mutation checks were updated. No docs change.

Independent follow-up: Codex CLI 0.163.0, gpt-6-astra, effort max, read-only, session 01a14f39-7cc6-7903-aa3f-576bc5ab161b, on the detached snapshot of 1086ef7 and the diff 998a3b6..1086ef7. Result: CLEAN. "The actual temporal cases remain untouched, so returning flat shapes now fails. Full Case equality preserves comparison of every flat case and option, including UNLOAD, CTAS, and unsupported=None." Static inspection only.

@laughingman7743
laughingman7743 marked this pull request as ready for review October 18, 2026 13:39
@laughingman7743
laughingman7743 merged commit d8526ef into master Oct 18, 2026
14 checks passed
@laughingman7743
laughingman7743 deleted the feat/1287-temporal-bench-shape branch October 18, 2026 14:08
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