Skip to content

Core: Reduce visibility on scan planning response builder for specsById and deleteFiles - #17638

Merged
amogh-jahagirdar merged 1 commit into
apache:mainfrom
dramaticlly:1.12deprecation-scan-planning-visibility
Aug 26, 2026
Merged

Core: Reduce visibility on scan planning response builder for specsById and deleteFiles#17638
amogh-jahagirdar merged 1 commit into
apache:mainfrom
dramaticlly:1.12deprecation-scan-planning-visibility

Conversation

@dramaticlly

Copy link
Copy Markdown
Contributor

Reduces visibility on the scan-planning response internals that were deprecated in 1.11.0 with visibility to be reduced in 1.12.0.

.palantir/revapi.yml: add 9 entries with

  • 4 noLongerDeprecated
  • 4 visibilityReduced
  • 1 method.removed.

1. Reduce visibility

The specs map and derived delete files are serialization internals, not part of the response payload:

  • specsById() on the response and on Builderprotected
  • Builder.withSpecsById(Map)protected
  • Builder.deleteFiles()protected
  • Builder.withDeleteFiles(List) removed — delete files are always derived from the tasks that reference them

Since withSpecsById is no longer publicly callable, servers need another way to supply the specs and response copiers need a way to carry them across:

  • builder(Map<Integer, PartitionSpec>) on PlanTableScanResponse, FetchPlanningResultResponse and FetchScanTasksResponse
  • toBuilder() on PlanTableScanResponse and FetchPlanningResultResponse

CatalogHandlers passes table.specs() to builder(...) at the three scan-planning call sites. RESTServerCatalogAdapter uses toBuilder() to inject storage credentials rather than rebuilding field by field.

2. Clear derived delete files when file scan tasks are cleared

BaseScanTaskResponse.Builder derives deleteFiles from fileScanTasks, but only refreshed the derived set when the incoming list was non-null:

this.fileScanTasks = tasks;
if (fileScanTasks != null) {
  this.deleteFiles = DeleteFileSet.of(...);
}

So withFileScanTasks(null) after a non-null call left the previously derived delete files behind, and the response then failed its own validate():

IllegalArgumentException: Invalid response: deleteFiles should only be returned
with fileScanTasks that reference them

even though the caller had explicitly cleared the tasks. This is latent on main but becomes load-bearing in commit 2, where toBuilder() makes builder reuse routine — TestRESTScanPlanning now relies on exactly this path to turn a COMPLETED response into a FAILED one.

Covered by two new tests at different entry points: TestFetchScanTasksResponseParser.clearingFileScanTasksAlsoClearsDerivedDeleteFiles (direct builder) and TestPlanTableScanResponseParser.toBuilderClearsDeleteFilesWhenClearingFileScanTasks (via toBuilder()). Both fail without the fix.

Callout

  1. I've added toBuilder() as a new public API for out-of-package org.apache.iceberg.rest caller to copy a response with the specs.

  2. Note withCredentials appends rather than replaces, so toBuilder().withCredentials(extra) preserves any existing credentials; the TestRESTScanPlanning simplification is behavior-preserving.

Split out of #16449 to reduce reviewer burden.

AI Disclosure

Model: Claude Opus 5 (1M context)
Platform/Tool: Claude Code
Human Oversight: reviewed
Prompt Summary: split #16449 into smaller self-contained PRs; verify each group compiles and tests green standalone

@dramaticlly

Copy link
Copy Markdown
Contributor Author

FYI @amogh-jahagirdar if you can help check against #14838
and also @singhpk234 for #14485

DeleteFileSet.of(
() -> tasks.stream().flatMap(task -> task.deletes().stream()).iterator());
}
this.deleteFiles =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is this changing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

thanks @RussellSpitzer, basically we need a way to clean the deleteFiles after the withDeleteFiles setter has been removed.

Given the delete files can only be derived from from fileScanTasks, use withFileScanTasks(null) would allow clear the internal state of this.deleteFiles.

@nssalian nssalian added this to the Iceberg 1.12.0 milestone Aug 15, 2026
@dramaticlly
dramaticlly force-pushed the 1.12deprecation-scan-planning-visibility branch 2 times, most recently from dbd8b81 to def9c77 Compare August 18, 2026 23:05
@dramaticlly
dramaticlly force-pushed the 1.12deprecation-scan-planning-visibility branch 2 times, most recently from 5fc7251 to 60b7ff2 Compare August 19, 2026 23:41
@amogh-jahagirdar

Copy link
Copy Markdown
Contributor

Note withCredentials appends rather than replaces, so toBuilder().withCredentials(extra) preserves any existing credentials; the TestRESTScanPlanning simplification is behavior-preserving.

This is actually quite surprising imo. I know it's an existing behavior but I feel like that's definitley not expected from a builder API; I forget if this was intentional or not, maybe @singhpk234 remembers. I guess it's not an issue hit in practice but I feel like we should have this replace credentials, and if we have a need for adding, we could do an addCredentials.

@amogh-jahagirdar amogh-jahagirdar 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.

I think independently this change makes sense though the withCredentials behavior (which I get is existing) is a bit surprising. that's something we can take separately though since it is existing behavior.

@dramaticlly

Copy link
Copy Markdown
Contributor Author

Note withCredentials appends rather than replaces, so toBuilder().withCredentials(extra) preserves any existing credentials; the TestRESTScanPlanning simplification is behavior-preserving.

This is actually quite surprising imo. I know it's an existing behavior but I feel like that's definitley not expected from a builder API; I forget if this was intentional or not, maybe @singhpk234 remembers. I guess it's not an issue hit in practice but I feel like we should have this replace credentials, and if we have a need for adding, we could do an addCredentials.

Thanks @amogh-jahagirdar , once we figured out the expected behaviour, happy to follow up and give a pass for all builder method deal with collections.

… constructors

The specs map and derived delete files on BaseScanTaskResponse are
serialization internals, not part of the response payload. They were
deprecated in 1.11.0 with visibility to be reduced in 1.12.0:

- specsById() on the response and on Builder -> protected
- Builder.withSpecsById(Map) -> protected
- Builder.deleteFiles() -> protected
- Builder.withDeleteFiles(List) removed; delete files are always derived
  from the file scan tasks that reference them

Generated-by: Claude Code
@dramaticlly
dramaticlly force-pushed the 1.12deprecation-scan-planning-visibility branch from 60b7ff2 to 87388ef Compare August 21, 2026 17:03
@amogh-jahagirdar

Copy link
Copy Markdown
Contributor

I'll go ahead and merge. Thanks @dramaticlly !

@amogh-jahagirdar

Copy link
Copy Markdown
Contributor

and thanks @uros-b @RussellSpitzer for reviewing!

@amogh-jahagirdar
amogh-jahagirdar merged commit 3bf74aa into apache:main Aug 26, 2026
39 checks passed
dramaticlly added a commit to dramaticlly/iceberg that referenced this pull request Aug 26, 2026
…data to 1.13.0

The removal of the ability to write position deletes with row data (PDWR) was
scheduled for 1.12.0 (see apache#17706). The 1.12.0 release candidate is being cut
now and the removal touches public API across Core, Data and Spark 3.5/4.0/4.1,
so it should not be rushed through review.

This moves the removal target of those deprecations from 1.12.0 to 1.13.0. The
deprecations themselves are unchanged: everything is still deprecated as of
1.11.0 and callers should still migrate off the row-carrying overloads. Only
the stated removal release moves out by one cycle. This mirrors apache#14392, which
retargeted a batch of removal versions in the other direction.

Markers moved to 1.13.0:

- PositionDelete.set(CharSequence, long, R) and row()
- PositionDeleteWriter constructor (appender type narrowing)
- RewriteTablePathUtil.PositionDeleteReaderWriter.writer(..., Schema)
- GenericFileWriterFactory positionDeleteRowSchema constructors, the builder
  setter, and the nine configureDataWrite/configureEqualityDelete/
  configurePositionDelete methods superseded by FormatModelRegistry, whose
  removal was bundled into the same change
- SparkFileWriterFactory positionDeleteRowSchema/positionDeleteSparkType
  builder setters, the row-schema constructor, and the runtime warning message
- SparkPositionDeltaWrite.Context.deleteSparkType()

Additionally, PositionDeltaWriter.delete(CharSequence, long, T, PartitionSpec,
StructLike) is now deprecated for removal in 1.13.0. It was previously not
deprecated at all, so without this its deprecation cycle would only start in
1.13.0 and the row parameter could not be dropped until 1.14.0. Deprecating it
here keeps that narrowing on schedule for 1.13.0 alongside the rest. The
annotation is additive: the overload stays abstract, so implementations are
unaffected, while callers can migrate to the four-argument overload today. All
three SparkPositionDeltaWrite call sites already use it; the only override is
BasePositionDeltaWriter, which already calls the deprecated
PositionDelete.set(CharSequence, long, R).

Flink is not deferred. Its only PDWR surface was FlinkAppenderFactory, whose
entire position delete path is built on row data. That class was deprecated as
of 1.11.0 in favor of FlinkFileWriterFactory for removal in 1.12.0, and nothing
in the repo references it outside its own test, so it is removed here on
schedule from all three Flink versions rather than carried into 1.13.0. That
also drops org.apache.iceberg.io.TestAppenderFactory from iceberg-data: the
three TestFlinkAppenderFactory classes were its only subclasses, so with those
gone the abstract base exercises nothing. iceberg-flink is not a revapi project
and TestAppenderFactory is a test class, so neither removal needs a revapi
baseline entry.

The REST scan-planning markers in BaseScanTaskResponse, PlanTableScanRequest
and PlanTableScanResponse are left alone; they are handled separately in apache#17638.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants