Skip to content

GH-46454: [C++][Dataset][Acero] Preserve order when writting with TeeNode - #46455

Merged
zanmato1984 merged 5 commits into
apache:mainfrom
gitmodimo:TeeNodeOrder
Aug 28, 2026
Merged

GH-46454: [C++][Dataset][Acero] Preserve order when writting with TeeNode#46455
zanmato1984 merged 5 commits into
apache:mainfrom
gitmodimo:TeeNodeOrder

Conversation

@gitmodimo

@gitmodimo gitmodimo commented May 15, 2025

Copy link
Copy Markdown
Contributor

Rationale for this change

TeeNode needs to sequence batches when implicit order within processed dataset.

What changes are included in this PR?

Conditionally sequence batches when preserve_order=true

Are these changes tested?

I tested it in my use case. No CI tests AFAIK.

Are there any user-facing changes?

Dataset will now be ordered as expected.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #46454 has been automatically assigned in GitHub to PR creator.

@gitmodimo

Copy link
Copy Markdown
Contributor Author

@pitrou Can I ping you when PR is ready?
This change is TeeNode equivalent to @EnricoMi WriteNode #44470

@pitrou

pitrou commented Aug 26, 2026

Copy link
Copy Markdown
Member

@gitmodimo Well, the expert is really @zanmato1984 on this.

@gitmodimo

Copy link
Copy Markdown
Contributor Author

@gitmodimo Well, the expert is really @zanmato1984 on this.

@pitrou is there a way I can request review from @zanmato1984? Github automatically assigned you for this.

@pitrou

pitrou commented Aug 26, 2026

Copy link
Copy Markdown
Member

Well, he receives these messages, so he will know about this PR :)

@zanmato1984 zanmato1984 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 for working on this. Having TeeNode honor preserve_order seems reasonable: TeeNode performs the dataset write itself, so sequencing further downstream cannot restore the order of data that TeeNode has already written.

The overall direction looks good. I found one correctness issue that should be addressed, plus two non-blocking comments about the test.

: MapNode(plan, std::move(inputs), std::move(output_schema)),
write_options_(std::move(write_options)) {}
write_options_(std::move(write_options)) {
if (write_options.preserve_order) {

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.

Could we add the same input-ordering validation used by SinkNode and ConsumingSinkNode?

With preserve_order=true, TeeNode creates a SerialSequencingQueue even when inputs_[0]->ordering() is unordered. In that case, batches may retain kUnsequencedIndex. SerialSequencingQueue::InsertBatch DCHECKs this in debug builds; in release builds, such batches never match the queue's initial next_index_ == 0, so they can remain queued and prevent the plan from finishing.

TeeNode should reject this configuration in Validate(), with a regression test for unordered input plus preserve_order=true.

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.

I added validation and test for it

Comment thread cpp/src/arrow/dataset/file_test.cc Outdated
ASSERT_OK_AND_ASSIGN(auto output_table,
acero::TableFromExecBatches(dataset->schema(), output_batches));
ASSERT_OK_AND_ASSIGN(auto output_out_of_order, HasOutOfOrderRows(*output_table));
ASSERT_FALSE(output_out_of_order);

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.

Non-blocking: Is this output-order check necessary for testing TeeNode?

The default SinkNode already sequences input with a meaningful ordering, so the collected output should be ordered even if TeeNode forwards batches out of order. As a result, this assertion does not directly exercise TeeNode's preserve_order behavior. The checks against the datasets written by the unordered and ordered TeeNodes below appear to provide the relevant coverage.

The sink is still needed to consume the plan, but the conversion to output_table and its ordering assertion may be unnecessary.

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.

removed

Comment thread cpp/src/arrow/dataset/file_test.cc Outdated

auto dataset = std::make_shared<MockDataset>(schema({field("f0", int32())}));

auto delay_func = std::make_shared<compute::ScalarFunction>(

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.

Non-blocking: Could this use the existing test JitterNode with a fixed seed instead of relying on the scalar function's wall-clock delay?

JitterNode is designed to resequence physical batch delivery while preserving batch indices, and is already used by the order-by tests for this purpose. Reusing it here would make the test intent clearer and avoid depending on thread timing and sleeps to produce out-of-order arrival.

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.

Yes this is cleaner. I also migrated write node to use jitter node stimulus.

@github-actions github-actions Bot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Aug 27, 2026
@gitmodimo
gitmodimo requested a review from zanmato1984 August 28, 2026 13:36
Comment thread cpp/src/arrow/dataset/file_test.cc Outdated
write_options.preserve_order = preserve_order;

ASSERT_OK(FileSystemDataset::Write(write_options, scanner));
ASSERT_OK(acero::DeclarationToStatus(acero::Declaration::Sequence(

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.

Could we keep the existing MultiThreadedWritePersistsOrder test using FileSystemDataset::Write(write_options, scanner)? My understanding is that it provides regression coverage for GH-26818, including scan sequencing and implicit ordering across fragments. Replacing it with table_source -> jitter -> write seems to test only the WriteNode path directly. Would it be better to retain the existing integration test and use JitterNode only for the new TeeNode test?

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.

Oh I see it also validates scanner gets the require_sequenced_output.

@gitmodimo
gitmodimo requested a review from zanmato1984 August 28, 2026 17:53

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

+1

Thanks for working on this.

@zanmato1984
zanmato1984 merged commit 43eca86 into apache:main Aug 28, 2026
59 of 61 checks passed
@zanmato1984 zanmato1984 removed the awaiting committer review Awaiting committer review label Aug 28, 2026
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.

3 participants