Skip to content

feat: Add async REST scan planning poll and plan storage credentials - #3724

Open
lukeFalsina wants to merge 1 commit into
apache:mainfrom
lukeFalsina:feat/async-rest-scan-planning
Open

feat: Add async REST scan planning poll and plan storage credentials#3724
lukeFalsina wants to merge 1 commit into
apache:mainfrom
lukeFalsina:feat/async-rest-scan-planning

Conversation

@lukeFalsina

@lukeFalsina lukeFalsina commented Jul 31, 2026

Copy link
Copy Markdown

Summary

  • Implement async REST fetchPlanningResult / cancelPlanning polling when planTableScan returns status=submitted
  • Apply completed-plan storage-credentials to the scan-scoped FileIO (layered on existing IO properties)
  • Keep public RestCatalog.plan_scan(...) -> list[FileScanTask] unchanged; credentials flow through internal _plan_scan_result / _file_io_from_plan

Related: #2775, #3495
Java reference: apache/iceberg#13400

Rationale

Unblocks REST catalogs that return async plans (for example policy-protected tables). Finishes the unchecked async items from #2775 and the plan-credential gap from #3495.

User-facing

  • table.scan() with scan-planning-mode=server now handles async plans automatically
  • New optional rest-scan-planning.poll-timeout-ms (default 300000)
  • No breaking change to RestCatalog.plan_scan return type

Test plan

  • make lint
  • make test (3805 passed)
  • Unit tests in tests/catalog/test_scan_planning_models.py for poll success / timeout / failed / cancelled and IO property retention
  • Existing REST scan integration suite (CI)
  • Manual validation against an async-capable catalog (Snowflake Horizon Iceberg REST Catalog Scan Plan API)

Made with Cursor

Catalogs that return status=submitted from planTableScan can now be polled via
GET .../plan/{plan-id}, with best-effort cancel and scan-scoped FileIO rebuilt
from plan storage-credentials. Public RestCatalog.plan_scan still returns
list[FileScanTask].

Co-authored-by: Cursor <cursoragent@cursor.com>

@singhpk234 singhpk234 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.

Thanks @lukeFalsina this is really promising, have some suggestions inline

Comment thread mkdocs/docs/api.md
[task.file.file_path for task in scan.plan_files()]
```

When the REST catalog is configured with `scan-planning-mode=server` and advertises the plan endpoint, `plan_files()` / `to_arrow()` use server-side scan planning. Catalogs that return async plans (`status=submitted`) are polled automatically; see [REST Catalog configuration](configuration.md#rest-catalog).

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.

Suggested change
When the REST catalog is configured with `scan-planning-mode=server` and advertises the plan endpoint, `plan_files()` / `to_arrow()` use server-side scan planning. Catalogs that return async plans (`status=submitted`) are polled automatically; see [REST Catalog configuration](configuration.md#rest-catalog).
When the REST catalog returns with `scan-planning-mode=server` and advertises the plan endpoint, `plan_files()` / `to_arrow()` for clients to use server-side scan planning. Catalogs that return async plans (`status=submitted`) are polled automatically till they reach terminal state; see [REST Catalog configuration](configuration.md#rest-catalog).

| snapshot-loading-mode | refs | The snapshots to return in the body of the metadata. Setting the value to `all` would return the full set of snapshots currently valid for the table. Setting the value to `refs` would load all snapshots referenced by branches or tags. |
| `header.X-Iceberg-Access-Delegation` | `vended-credentials` | Signal to the server that the client supports delegated access via a comma-separated list of access mechanisms. The server may choose to supply access via any or none of the requested mechanisms. When using `vended-credentials`, the server provides temporary credentials to the client. When using `remote-signing`, the server signs requests on behalf of the client. (default: `vended-credentials`) |
| view-endpoints-supported | false | For backwards compatibility with older REST servers. Set to `true` if the server supports view endpoints but doesn't send the `endpoints` field in the ConfigResponse. |
| scan-planning-mode | client | When set to `server`, and the catalog advertises the plan-table-scan endpoint, `table.scan()` uses REST server-side scan planning. Async plans (`status=submitted`) are polled via `GET .../plan/{plan-id}` until completion. |

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.

this is something loadTable return too, in case the server wants to ask client for server side scan planning ...

storage_credentials: list[StorageCredential],
location: str | None = None,
) -> FileIO | None:
"""Build a scan-scoped FileIO from plan storage credentials.

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.

in this scenario can there be more than one plan active ?

Comment on lines -588 to -592
# Collect tasks from initial response
for task in response.file_scan_tasks:
tasks.append(FileScanTask.from_rest_response(task, response.delete_files))

# Fetch and collect from additional batches

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.

should we keep the comments still ?

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.

2 participants