Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
5154679
[Dataflow Streaming][Multikey] Support MultiKey commits in windmill c…
arunpandianp Jun 2, 2026
73faa68
[Dataflow Streaming] [Multi Key] StreamingModeExecutionContext refact…
arunpandianp Jun 4, 2026
53bc9a6
trigger postsubmit tests
arunpandianp Jun 4, 2026
7720cf8
fix tests
arunpandianp Jun 4, 2026
72740d2
fix tests
arunpandianp Jun 4, 2026
0f96e72
improve work synchronization
arunpandianp Jun 4, 2026
51b9257
cleanup logic
arunpandianp Jun 5, 2026
9a4e7be
cleanup logic
arunpandianp Jun 5, 2026
3f36afd
address comments
arunpandianp Jun 8, 2026
f3cc628
improve WindowingWindmillReader
arunpandianp Jun 8, 2026
58e0ef9
spotless fix
arunpandianp Jun 8, 2026
3dceab0
[Dataflow Streaming] Fix nullness supression in StreamingModeExecutio…
arunpandianp Jun 8, 2026
e199438
make windmillTagEncoding final
arunpandianp Jun 8, 2026
700dfbc
address comments
arunpandianp Jun 8, 2026
bc5bee2
Move SideInputStateFetcherFactory from start to constructor
arunpandianp Jun 8, 2026
6d7f28e
Merge branch 'contextnullness' into multikey_context_review
arunpandianp Jun 8, 2026
c4a52c1
Merge remote-tracking branch 'beam/master' into multikey_context_review
arunpandianp Jun 8, 2026
1107a60
Merge beam/master into multikey_context_review
arunpandianp Jun 8, 2026
47eb7d6
Address comment
arunpandianp Jun 8, 2026
24505dd
Address comment
arunpandianp Jun 8, 2026
4e0d174
Fix UnderInitialization
arunpandianp Jun 9, 2026
5f9fef5
Merge branch 'multikey_commit' into tmp
arunpandianp Jun 11, 2026
0e8157f
Merge branch 'multikey_context_review' into tmp
arunpandianp Jun 11, 2026
93de23a
Multikey commit failure handling and integration
arunpandianp Jun 11, 2026
114780e
Retry on commit size validation
arunpandianp Jun 11, 2026
9de3d84
Fix tests and add sink byte limit for batching
arunpandianp Jun 11, 2026
0df9f54
spotless
arunpandianp Jun 11, 2026
18ae844
Merge remote-tracking branch 'beam/master' into multikey_commit
arunpandianp Jun 29, 2026
ad177c0
Merge remote-tracking branch 'beam/master' into multikey_failure
arunpandianp Jun 30, 2026
557f95e
Merge branch 'multikey_commit' into multikey_failure
arunpandianp Jun 30, 2026
00e5fbf
fix merge
arunpandianp Jul 1, 2026
9ff2cad
Address comments
arunpandianp Jul 23, 2026
6c5481a
Merge branch 'multikey_commit' into multikey_failure
arunpandianp Jul 23, 2026
5ddc9d4
Merge remote-tracking branch 'beam/master' into multikey_failure
arunpandianp Jul 23, 2026
ab33927
fix merge
arunpandianp Jul 23, 2026
ef77b1f
fix merge
arunpandianp Jul 23, 2026
43c7f1a
Address comments
arunpandianp Jul 23, 2026
0f79170
Merge branch 'multikey_failure' into multikey_commit_validation
arunpandianp Jul 23, 2026
c38a831
fix merge
arunpandianp Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ boolean isSinkFullHintSet() {
// the state size might grow unbounded.
}

protected final long getBytesSinked() {
return bytesSinked;
}

/**
* Sets a flag to indicate that a sink has enough data written to it. This hint is read by
* upstream producers to stop producing if they can. Mainly used in streaming.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.beam.runners.dataflow.worker;

import com.google.auto.value.AutoValue;
import java.util.concurrent.TimeUnit;
import org.apache.beam.sdk.annotations.Internal;
import org.apache.beam.sdk.options.ExperimentalOptions;
import org.apache.beam.sdk.options.PipelineOptions;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@AutoValue
@Internal
public abstract class MultiKeyBundleOptions {
// TODO: consider moving this to be part of PipelineOptions after the feature is stable

private static final Logger LOG = LoggerFactory.getLogger(MultiKeyBundleOptions.class);

// Don't use. Experiment guarding multi key bundles. The feature is work in progress and
// incomplete.
public static final String UNSTABLE_ENABLE_MULTI_KEY_BUNDLE = "unstable_enable_multi_key_bundle";

private static final String WINDMILL_MAX_KEY_GROUP_BATCH_SIZE =
"windmill_max_key_group_batch_size";
private static final String WINDMILL_MAX_KEY_GROUP_BATCH_TIME_MS =
"windmill_max_key_group_batch_time_ms";
private static final String WINDMILL_MAX_KEY_GROUP_BATCH_SINK_BYTES =
"windmill_max_key_group_batch_sink_bytes";

public abstract int maxKeyGroupBatchSize();

public abstract long maxKeyGroupBatchTimeNanos();

public abstract boolean multiKeyBundleEnabled();

public abstract long maxKeyGroupBatchSinkBytes();

public static Builder builder() {
return new AutoValue_MultiKeyBundleOptions.Builder();
}

public static MultiKeyBundleOptions fromOptions(PipelineOptions options) {
int maxKeyGroupBatchSize =
tryParseInt(
ExperimentalOptions.getExperimentValue(options, WINDMILL_MAX_KEY_GROUP_BATCH_SIZE),
100,
WINDMILL_MAX_KEY_GROUP_BATCH_SIZE);

long batchTimeMs =
tryParseLong(
ExperimentalOptions.getExperimentValue(options, WINDMILL_MAX_KEY_GROUP_BATCH_TIME_MS),
100,
WINDMILL_MAX_KEY_GROUP_BATCH_TIME_MS);

boolean multiKeyBundleEnabled =
ExperimentalOptions.hasExperiment(options, UNSTABLE_ENABLE_MULTI_KEY_BUNDLE);

long maxKeyGroupBatchSinkBytes =
tryParseLong(
ExperimentalOptions.getExperimentValue(
options, WINDMILL_MAX_KEY_GROUP_BATCH_SINK_BYTES),
StreamingDataflowWorker.MAX_SINK_BYTES,
WINDMILL_MAX_KEY_GROUP_BATCH_SINK_BYTES);

return builder()
.setMaxKeyGroupBatchSize(maxKeyGroupBatchSize)
.setMaxKeyGroupBatchTimeNanos(TimeUnit.MILLISECONDS.toNanos(batchTimeMs))
.setMultiKeyBundleEnabled(multiKeyBundleEnabled)
.setMaxKeyGroupBatchSinkBytes(maxKeyGroupBatchSinkBytes)
.build();
}

private static int tryParseInt(@Nullable String value, int defaultValue, String experimentName) {
if (value == null) {
return defaultValue;
}
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
LOG.warn(
"Failed to parse experiment {} value '{}' as integer, falling back to default: {}",
experimentName,
value,
defaultValue,
e);
return defaultValue;
}
}

private static long tryParseLong(
@Nullable String value, long defaultValue, String experimentName) {
if (value == null) {
return defaultValue;
}
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
LOG.warn(
"Failed to parse experiment {} value '{}' as long, falling back to default: {}",
experimentName,
value,
defaultValue,
e);
return defaultValue;
}
}

@AutoValue.Builder
public abstract static class Builder {

public abstract Builder setMaxKeyGroupBatchSize(int size);

public abstract Builder setMaxKeyGroupBatchTimeNanos(long nanos);

public abstract Builder setMultiKeyBundleEnabled(boolean enabled);

public abstract Builder setMaxKeyGroupBatchSinkBytes(long bytes);

public abstract MultiKeyBundleOptions build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,6 @@ public final class StreamingDataflowWorker {
// Experiment make the monitor within BoundedQueueExecutor fair
public static final String BOUNDED_QUEUE_EXECUTOR_USE_FAIR_MONITOR_EXPERIMENT =
"windmill_bounded_queue_executor_use_fair_monitor";
// Don't use. Experiment guarding multi key bundles. The feature is work in progress and
// incomplete.
private static final String UNSTABLE_ENABLE_MULTI_KEY_BUNDLE = "unstable_enable_multi_key_bundle";

private final WindmillStateCache stateCache;
private AtomicReference<StreamingWorkerStatusPages> statusPages = new AtomicReference<>();
Expand Down Expand Up @@ -211,7 +208,7 @@ public final class StreamingDataflowWorker {
private StreamingDataflowWorker(
WindmillServerStub windmillServer,
long clientId,
ComputationConfig.Fetcher configFetcher,
Fetcher configFetcher,
ComputationStateCache computationStateCache,
WindmillStateCache windmillStateCache,
BoundedQueueExecutor workUnitExecutor,
Expand All @@ -228,7 +225,8 @@ private StreamingDataflowWorker(
GrpcWindmillStreamFactory windmillStreamFactory,
ScheduledExecutorService activeWorkRefreshExecutorFn,
ConcurrentMap<String, StageInfo> stageInfoMap,
@Nullable GrpcDispatcherClient dispatcherClient) {
@Nullable GrpcDispatcherClient dispatcherClient,
MultiKeyBundleOptions multiKeyBundleOptions) {
// Register standard file systems.
FileSystems.setDefaultPipelineOptions(options);
this.configFetcher = configFetcher;
Expand Down Expand Up @@ -257,6 +255,7 @@ private StreamingDataflowWorker(
this.streamingWorkScheduler =
StreamingWorkScheduler.create(
options,
multiKeyBundleOptions,
clock,
readerCache,
mapTaskExecutorFactory,
Expand Down Expand Up @@ -627,7 +626,8 @@ public static StreamingDataflowWorker fromOptions(DataflowWorkerHarnessOptions o
ConcurrentMap<String, StageInfo> stageInfo = new ConcurrentHashMap<>();
StreamingCounters streamingCounters = StreamingCounters.create();
WorkUnitClient dataflowServiceClient = new DataflowWorkUnitClient(options, LOG);
BoundedQueueExecutor workExecutor = createWorkUnitExecutor(options);
MultiKeyBundleOptions multiKeyBundleOptions = MultiKeyBundleOptions.fromOptions(options);
BoundedQueueExecutor workExecutor = createWorkUnitExecutor(options, multiKeyBundleOptions);
ScheduledExecutorService commitFinalizerCleanupExecutor =
Executors.newScheduledThreadPool(
1,
Expand Down Expand Up @@ -726,7 +726,8 @@ public static StreamingDataflowWorker fromOptions(DataflowWorkerHarnessOptions o
Executors.newSingleThreadScheduledExecutor(
new ThreadFactoryBuilder().setNameFormat("RefreshWork").build()),
stageInfo,
configFetcherComputationStateCacheAndWindmillClient.windmillDispatcherClient());
configFetcherComputationStateCacheAndWindmillClient.windmillDispatcherClient(),
multiKeyBundleOptions);
}

/**
Expand Down Expand Up @@ -876,7 +877,8 @@ static StreamingDataflowWorker forTesting(
StreamingCounters streamingCounters,
WindmillStubFactoryFactory stubFactory) {
ConcurrentMap<String, StageInfo> stageInfo = new ConcurrentHashMap<>();
BoundedQueueExecutor workExecutor = createWorkUnitExecutor(options);
MultiKeyBundleOptions multiKeyBundleOptions = MultiKeyBundleOptions.fromOptions(options);
BoundedQueueExecutor workExecutor = createWorkUnitExecutor(options, multiKeyBundleOptions);
ScheduledExecutorService commitFinalizerCleanupExecutor =
Executors.newScheduledThreadPool(
1,
Expand Down Expand Up @@ -990,7 +992,8 @@ static StreamingDataflowWorker forTesting(
: windmillStreamFactory.build(),
executorSupplier.apply("RefreshWork"),
stageInfo,
grpcDispatcherClient);
grpcDispatcherClient,
multiKeyBundleOptions);
}

private static GrpcWindmillStreamFactory.Builder createGrpcwindmillStreamFactoryBuilder(
Expand Down Expand Up @@ -1020,11 +1023,11 @@ private static JobHeader createJobHeader(DataflowWorkerHarnessOptions options, l
.build();
}

private static BoundedQueueExecutor createWorkUnitExecutor(DataflowWorkerHarnessOptions options) {
private static BoundedQueueExecutor createWorkUnitExecutor(
DataflowWorkerHarnessOptions options, MultiKeyBundleOptions multiKeyBundleOptions) {
boolean useFairMonitor =
DataflowRunner.hasExperiment(options, BOUNDED_QUEUE_EXECUTOR_USE_FAIR_MONITOR_EXPERIMENT);
boolean useKeyGroupWorkQueue =
DataflowRunner.hasExperiment(options, UNSTABLE_ENABLE_MULTI_KEY_BUNDLE);
boolean useKeyGroupWorkQueue = multiKeyBundleOptions.multiKeyBundleEnabled();
return new BoundedQueueExecutor(
chooseMaxThreads(options),
THREAD_EXPIRATION_TIME_SEC,
Expand Down Expand Up @@ -1206,9 +1209,14 @@ private void onCompleteCommit(CompleteCommit completeCommit) {
computationStateCache
.getIfPresent(completeCommit.computationId())
.ifPresent(
state ->
state -> {
if (completeCommit.retryableFailure()) {
state.reexecuteActiveWork(completeCommit.shardedKey(), completeCommit.workId());
} else {
state.completeWorkAndScheduleNextWorkForKey(
completeCommit.shardedKey(), completeCommit.workId()));
completeCommit.shardedKey(), completeCommit.workId());
}
});
}

@AutoValue
Expand Down
Loading
Loading