Skip to content

branch-4.1:[feature](iceberg) Add compute_partition_stats iceberg procedure. - #67810

Draft
hubgeter wants to merge 3 commits into
apache:branch-4.1from
hubgeter:docs/iceberg-maintenance-branch-4.1
Draft

hubgeter wants to merge 3 commits into
apache:branch-4.1from
hubgeter:docs/iceberg-maintenance-branch-4.1

Conversation

@hubgeter

@hubgeter hubgeter commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: N/A

Related PR: N/A

Problem Summary:

Doris does not expose a maintenance command to compute and register Iceberg
partition statistics. Add ALTER TABLE ... EXECUTE compute_partition_stats,
with an optional snapshot_id that defaults to the current snapshot. Use the
Iceberg 1.11.0 SDK to compute or reuse a partition statistics file, register it
in table metadata, invalidate the local cache after commit, and return its path.

Extend BaseExecuteAction to support zero or multiple result rows so this
procedure can return zero rows with its column metadata intact when no statistics
file is produced. Adapt the nine existing Iceberg actions to the new internal
return type while preserving their existing single-row schemas and values.

Include the Parquet runtime reader/writer required by the SDK. Move Doris-generated
Parquet Thrift Java classes into a separate namespace and remove legacy generated
sources/classes during source generation to prevent conflicts with the official
Parquet classes. The Thrift wire definitions and generated C++ content are unchanged.

Release note

Add an Iceberg compute_partition_stats procedure to compute and register
partition statistics for the current or a specified snapshot.

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason
  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

@hubgeter
hubgeter requested a review from yiguolei as a code owner September 10, 2026 11:37
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@hubgeter
hubgeter marked this pull request as draft September 10, 2026 16:30
@hubgeter

Copy link
Copy Markdown
Contributor Author

run buildall

@hubgeter

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot 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.

Reviewed the authoritative 24-file patch on exact head 7709352fe84ee4ed04929b8ae0b2d60cc9fb6132. I am requesting changes for six distinct issues: ORC-default tables cannot produce fresh partition-statistics files; the authenticated table view performs a synchronized authentication transition per manifest entry; successful MySQL follower result sets are reported as errors and lose row accounting; an unknown commit outcome can leave every FE cache stale after a durable commit; Arrow Flight action results cannot cross a follower boundary; and zero-placeholder prepared execution loses binary row encoding when forwarded.

Critical checkpoint conclusions:

  • Scope and existing context: the authoritative diff paths exactly match pr_changed_files.txt; the live base/head still match the bundle; no existing review or inline thread duplicates any accepted issue.
  • Iceberg correctness and lifecycle: snapshot ancestry, catalog-generation retry fencing, single-shot commit behavior, worker I/O authentication, stream cleanup, and borrowed FileIO ownership were traced. Definite-failure orphan handling and same-snapshot descriptor reuse match Iceberg's upstream action contract; the durable-but-unknown refresh gap remains blocking.
  • Authorization and result compatibility: ALTER authorization precedes external loading/mutation. Direct MySQL and direct Arrow result delivery are sound, but the three independent follower/protocol failures below remain. All nine migrated action adapters preserve their prior one-row result shape.
  • Dependency and generated artifacts: the Parquet runtime dependency, shaded Thrift ABI, Java/C++ namespace split, stale generated-class cleanup, and clean/incremental packaging paths are consistent; no additional artifact defect survived review.
  • Validation: all final-round normal and risk-focused reviewers returned NO_NEW_VALUABLE_FINDINGS on this exact six-comment set. Per the review-task constraint, no local build or tests were run. git diff --check only reports the generated fixture's conventional extra trailing newline. Live COMPILE, Cloud UT, and completed static checks pass; the current FE UT failures are unrelated Lance class-redefinition/JNI glibc environment failures with no changed-path overlap, while several jobs remain pending.
  • User focus: review_focus.txt states that there is no additional user-provided focus, so the complete PR was reviewed without a narrower focus override.

// Keep the selected snapshot even if the table head changes during computation.
long snapshotId = snapshot.snapshotId();
Table statisticsTable = new IcebergPartitionStatsTable(icebergTable, authenticator);
PartitionStatisticsFile file = PartitionStatsHandler.computeAndWriteStatsFile(statisticsTable, snapshotId);

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.

[P1] Support ORC tables before exposing this action

Iceberg 1.11 chooses the statistics-file format from the supplied table's write.format.default, but its internal writer has providers only for Avro and Parquet. The new integration test demonstrates the consequence: reuse of an older file works, then the first new snapshot on an ORC-default table fails with unregistered internal data format: ORC. Doris already supports ORC Iceberg tables and the factory exposes this procedure without a format restriction, so please decouple the statistics-file format (for example, present Parquet through the scoped table view) or gate the action consistently, and replace the expected failure with successful ORC compute/readback coverage.

}

@Override
public Snapshot snapshot(long snapshotId) {

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.

[P1] Avoid one authentication transition per manifest entry

Iceberg 1.11 calls table.snapshot(entry.snapshotId()) inside its loop over every manifest entry. This override turns that in-memory metadata lookup into authenticator.execute -> Hadoop doAs/getUGI; the Kerberos UGI lookup is synchronized. Large tables can therefore perform millions of needless authentication transitions and serialize worker progress even though BaseTable.snapshot only reads captured TableMetadata. Keep authentication around the actual FileIO and lazy stream operations, but serve snapshot metadata without re-entering the authenticator, and add an invocation-count regression test.

byte[] bytes = new byte[definition.remaining()];
definition.get(bytes);
Assertions.assertTrue(new String(bytes, StandardCharsets.UTF_8).contains("partition_statistics_file"));
Assertions.assertEquals(QueryState.MysqlStateType.EOF, state.getStateType());

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.

[P1] Exercise and fix the complete follower path

This assertion captures the state that breaks forwarding: sendResultSet leaves a successful result at EOF, while ConnectProcessor.proxyExecute assigns status 0 and affectedRows only for OK. A follower still replays these packet buffers, so this test passes, but it receives ERR_UNKNOWN_ERROR, loses returned-row accounting, records an audit error, and stops any remaining multi-statements. Please cover the complete master-to-follower path for 0/1/multiple rows, treat a successful result-set EOF as proxy success, and carry the real returned-row count.

icebergTable.updatePartitionStatistics().setPartitionStatistics(file).commit();
Env.getCurrentEnv().getExtMetaCacheMgr().invalidateTableCache(dorisTable);
return Collections.singletonList(Collections.singletonList(file.path()));
} catch (Exception e) {

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.

[P1] Refresh metadata after an unknown commit outcome

A CommitStateUnknownException can be raised after the metadata update is durable; the new integration test simulates exactly that and observes the registered statistics entry. This catch path leaves the local invalidation unexecuted and also prevents ExecuteActionCommand from publishing its follower refresh log, so every FE can keep serving pre-commit metadata after a real commit. Do not retry the mutation or delete the file, but conservatively invalidate and propagate the refresh before rethrowing, and make the command-level test require those cache/edit-log interactions.

for (int rowCount : new int[] {0, 1, 2}) {
ConnectContext ctx = Mockito.mock(ConnectContext.class);
ProxyMysqlChannel channel = new ProxyMysqlChannel();
Mockito.when(ctx.getConnectType()).thenReturn(ConnectType.MYSQL);

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.

[P1] Support Arrow Flight clients on follower FEs

ExecuteActionCommand is ForwardWithSync, but this new result path is tested only with a MySQL proxy channel. On a non-master FE, an Arrow Flight connection fails while building the forward request because buildStmtForwardParams unconditionally calls ctx.getMysqlChannel().clientDeprecatedEOF(), and FlightSqlConnectContext.getMysqlChannel() throws. Merely guarding that read is not enough: the request carries no result protocol and the master returns MySQL packet buffers rather than a Flight ResultSet. The action therefore works or fails according to which FE receives the same Flight request. Please make forwarding/result transport protocol-aware (or explicitly route/reject this command) and cover empty, one-row, and multi-row results through a follower Flight connection.

}
ResultSet result = new CommonResultSet(new CommonResultSetMetaData(Collections.singletonList(
new Column("partition_statistics_file", PrimitiveType.STRING, true))), rows);
new StmtExecutor(ctx, new OriginStatement("", 0), true).sendResultSet(result);

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.

[P1] Preserve binary mode for zero-parameter prepared executes

The common compute_partition_stats() call has no placeholders. For a server-prepared execution, handleExecute saves prepareExecuteBuffer only when paramCount > 0, so the follower omits it from TMasterOpRequest; the master then takes queryRetry() through the text-protocol proxy constructor and serializes result rows with sendTextResultRow(). The client is waiting for binary rows, whose first byte/null bitmap differ. This test uses that same text-only constructor and checks only packet count/metadata, so it cannot catch the corruption. Forward an explicit COM_STMT_EXECUTE/binary-result flag independently of parameter bytes, and assert binary row framing for a zero-argument action on a follower.

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 75.00% (102/136) 🎉
Increment coverage report
Complete coverage report

@hubgeter

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

Cloud UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.17% (1819/2486)
Line Coverage 60.97% (32555/53395)
Region Coverage 48.50% (30770/63438)
Branch Coverage 51.41% (9542/18560)

@hubgeter hubgeter changed the title [feature](iceberg) Add compute_partition_stats iceberg procedure. branch-4.1:[feature](iceberg) Add compute_partition_stats iceberg procedure. Sep 11, 2026
@hubgeter
hubgeter marked this pull request as ready for review September 14, 2026 03:48
Allow BaseExecuteAction to return a list of rows, including zero rows with
column metadata. Adapt all existing Iceberg actions to keep their original
single-row results.

Cover empty and multi-row results, row validation, and forwarding through
ProxyMysqlChannel.
Add ALTER TABLE ... EXECUTE compute_partition_stats with optional snapshot_id.
Delegate statistics computation and registration to the Iceberg SDK, return
the statistics file path or an empty result set, and invalidate the local
table cache after commit.

Provide the Parquet runtime module and isolate generated Java Thrift classes
from official Parquet classes. Add action, SDK, command, and SQL regression
coverage.
@hubgeter
hubgeter force-pushed the docs/iceberg-maintenance-branch-4.1 branch from bb17822 to 7027db0 Compare September 14, 2026 03:48
@hubgeter

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

Cloud UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.21% (1823/2490)
Line Coverage 61.07% (32648/53463)
Region Coverage 48.51% (30804/63503)
Branch Coverage 51.55% (9586/18596)

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 76.06% (108/142) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100% (0/0) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 74.55% (31486/42234)
Line Coverage 58.83% (351217/597044)
Region Coverage 55.51% (292922/527713)
Branch Coverage 56.37% (132174/234493)

@hubgeter
hubgeter marked this pull request as draft September 16, 2026 09:58
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