Skip to content

Commit 3c9f9ce

Browse files
authored
[Dataflow Streaming] [Multi Key] MultiKey failure handling + Integration (#38919)
1 parent f6c7a06 commit 3c9f9ce

38 files changed

Lines changed: 1673 additions & 394 deletions

runners/google-cloud-dataflow-java/worker/src/main/java/org/apache/beam/runners/dataflow/worker/DataflowExecutionContext.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ boolean isSinkFullHintSet() {
150150
// the state size might grow unbounded.
151151
}
152152

153+
protected final long getBytesSinked() {
154+
return bytesSinked;
155+
}
156+
153157
/**
154158
* Sets a flag to indicate that a sink has enough data written to it. This hint is read by
155159
* upstream producers to stop producing if they can. Mainly used in streaming.
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.beam.runners.dataflow.worker;
19+
20+
import com.google.auto.value.AutoValue;
21+
import java.util.concurrent.TimeUnit;
22+
import org.apache.beam.sdk.annotations.Internal;
23+
import org.apache.beam.sdk.options.ExperimentalOptions;
24+
import org.apache.beam.sdk.options.PipelineOptions;
25+
import org.checkerframework.checker.nullness.qual.Nullable;
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
29+
@AutoValue
30+
@Internal
31+
public abstract class MultiKeyBundleOptions {
32+
// TODO: consider moving this to be part of PipelineOptions after the feature is stable
33+
34+
private static final Logger LOG = LoggerFactory.getLogger(MultiKeyBundleOptions.class);
35+
36+
// Don't use. Experiment guarding multi key bundles. The feature is work in progress and
37+
// incomplete.
38+
public static final String UNSTABLE_ENABLE_MULTI_KEY_BUNDLE = "unstable_enable_multi_key_bundle";
39+
40+
private static final String WINDMILL_MAX_KEY_GROUP_BATCH_SIZE =
41+
"windmill_max_key_group_batch_size";
42+
private static final String WINDMILL_MAX_KEY_GROUP_BATCH_TIME_MS =
43+
"windmill_max_key_group_batch_time_ms";
44+
private static final String WINDMILL_MAX_KEY_GROUP_BATCH_SINK_BYTES =
45+
"windmill_max_key_group_batch_sink_bytes";
46+
47+
public abstract int maxKeyGroupBatchSize();
48+
49+
public abstract long maxKeyGroupBatchTimeNanos();
50+
51+
public abstract boolean multiKeyBundleEnabled();
52+
53+
public abstract long maxKeyGroupBatchSinkBytes();
54+
55+
public static Builder builder() {
56+
return new AutoValue_MultiKeyBundleOptions.Builder();
57+
}
58+
59+
public static MultiKeyBundleOptions fromOptions(PipelineOptions options) {
60+
int maxKeyGroupBatchSize =
61+
tryParseInt(
62+
ExperimentalOptions.getExperimentValue(options, WINDMILL_MAX_KEY_GROUP_BATCH_SIZE),
63+
100,
64+
WINDMILL_MAX_KEY_GROUP_BATCH_SIZE);
65+
66+
long batchTimeMs =
67+
tryParseLong(
68+
ExperimentalOptions.getExperimentValue(options, WINDMILL_MAX_KEY_GROUP_BATCH_TIME_MS),
69+
100,
70+
WINDMILL_MAX_KEY_GROUP_BATCH_TIME_MS);
71+
72+
boolean multiKeyBundleEnabled =
73+
ExperimentalOptions.hasExperiment(options, UNSTABLE_ENABLE_MULTI_KEY_BUNDLE);
74+
75+
long maxKeyGroupBatchSinkBytes =
76+
tryParseLong(
77+
ExperimentalOptions.getExperimentValue(
78+
options, WINDMILL_MAX_KEY_GROUP_BATCH_SINK_BYTES),
79+
StreamingDataflowWorker.MAX_SINK_BYTES,
80+
WINDMILL_MAX_KEY_GROUP_BATCH_SINK_BYTES);
81+
82+
return builder()
83+
.setMaxKeyGroupBatchSize(maxKeyGroupBatchSize)
84+
.setMaxKeyGroupBatchTimeNanos(TimeUnit.MILLISECONDS.toNanos(batchTimeMs))
85+
.setMultiKeyBundleEnabled(multiKeyBundleEnabled)
86+
.setMaxKeyGroupBatchSinkBytes(maxKeyGroupBatchSinkBytes)
87+
.build();
88+
}
89+
90+
private static int tryParseInt(@Nullable String value, int defaultValue, String experimentName) {
91+
if (value == null) {
92+
return defaultValue;
93+
}
94+
try {
95+
return Integer.parseInt(value);
96+
} catch (NumberFormatException e) {
97+
LOG.warn(
98+
"Failed to parse experiment {} value '{}' as integer, falling back to default: {}",
99+
experimentName,
100+
value,
101+
defaultValue,
102+
e);
103+
return defaultValue;
104+
}
105+
}
106+
107+
private static long tryParseLong(
108+
@Nullable String value, long defaultValue, String experimentName) {
109+
if (value == null) {
110+
return defaultValue;
111+
}
112+
try {
113+
return Long.parseLong(value);
114+
} catch (NumberFormatException e) {
115+
LOG.warn(
116+
"Failed to parse experiment {} value '{}' as long, falling back to default: {}",
117+
experimentName,
118+
value,
119+
defaultValue,
120+
e);
121+
return defaultValue;
122+
}
123+
}
124+
125+
@AutoValue.Builder
126+
public abstract static class Builder {
127+
128+
public abstract Builder setMaxKeyGroupBatchSize(int size);
129+
130+
public abstract Builder setMaxKeyGroupBatchTimeNanos(long nanos);
131+
132+
public abstract Builder setMultiKeyBundleEnabled(boolean enabled);
133+
134+
public abstract Builder setMaxKeyGroupBatchSinkBytes(long bytes);
135+
136+
public abstract MultiKeyBundleOptions build();
137+
}
138+
}

runners/google-cloud-dataflow-java/worker/src/main/java/org/apache/beam/runners/dataflow/worker/StreamingDataflowWorker.java

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,6 @@ public final class StreamingDataflowWorker {
179179
// Experiment make the monitor within BoundedQueueExecutor fair
180180
public static final String BOUNDED_QUEUE_EXECUTOR_USE_FAIR_MONITOR_EXPERIMENT =
181181
"windmill_bounded_queue_executor_use_fair_monitor";
182-
// Don't use. Experiment guarding multi key bundles. The feature is work in progress and
183-
// incomplete.
184-
private static final String UNSTABLE_ENABLE_MULTI_KEY_BUNDLE = "unstable_enable_multi_key_bundle";
185182

186183
private final WindmillStateCache stateCache;
187184
private AtomicReference<StreamingWorkerStatusPages> statusPages = new AtomicReference<>();
@@ -211,7 +208,7 @@ public final class StreamingDataflowWorker {
211208
private StreamingDataflowWorker(
212209
WindmillServerStub windmillServer,
213210
long clientId,
214-
ComputationConfig.Fetcher configFetcher,
211+
Fetcher configFetcher,
215212
ComputationStateCache computationStateCache,
216213
WindmillStateCache windmillStateCache,
217214
BoundedQueueExecutor workUnitExecutor,
@@ -228,7 +225,8 @@ private StreamingDataflowWorker(
228225
GrpcWindmillStreamFactory windmillStreamFactory,
229226
ScheduledExecutorService activeWorkRefreshExecutorFn,
230227
ConcurrentMap<String, StageInfo> stageInfoMap,
231-
@Nullable GrpcDispatcherClient dispatcherClient) {
228+
@Nullable GrpcDispatcherClient dispatcherClient,
229+
MultiKeyBundleOptions multiKeyBundleOptions) {
232230
// Register standard file systems.
233231
FileSystems.setDefaultPipelineOptions(options);
234232
this.configFetcher = configFetcher;
@@ -257,6 +255,7 @@ private StreamingDataflowWorker(
257255
this.streamingWorkScheduler =
258256
StreamingWorkScheduler.create(
259257
options,
258+
multiKeyBundleOptions,
260259
clock,
261260
readerCache,
262261
mapTaskExecutorFactory,
@@ -627,7 +626,8 @@ public static StreamingDataflowWorker fromOptions(DataflowWorkerHarnessOptions o
627626
ConcurrentMap<String, StageInfo> stageInfo = new ConcurrentHashMap<>();
628627
StreamingCounters streamingCounters = StreamingCounters.create();
629628
WorkUnitClient dataflowServiceClient = new DataflowWorkUnitClient(options, LOG);
630-
BoundedQueueExecutor workExecutor = createWorkUnitExecutor(options);
629+
MultiKeyBundleOptions multiKeyBundleOptions = MultiKeyBundleOptions.fromOptions(options);
630+
BoundedQueueExecutor workExecutor = createWorkUnitExecutor(options, multiKeyBundleOptions);
631631
ScheduledExecutorService commitFinalizerCleanupExecutor =
632632
Executors.newScheduledThreadPool(
633633
1,
@@ -726,7 +726,8 @@ public static StreamingDataflowWorker fromOptions(DataflowWorkerHarnessOptions o
726726
Executors.newSingleThreadScheduledExecutor(
727727
new ThreadFactoryBuilder().setNameFormat("RefreshWork").build()),
728728
stageInfo,
729-
configFetcherComputationStateCacheAndWindmillClient.windmillDispatcherClient());
729+
configFetcherComputationStateCacheAndWindmillClient.windmillDispatcherClient(),
730+
multiKeyBundleOptions);
730731
}
731732

732733
/**
@@ -876,7 +877,8 @@ static StreamingDataflowWorker forTesting(
876877
StreamingCounters streamingCounters,
877878
WindmillStubFactoryFactory stubFactory) {
878879
ConcurrentMap<String, StageInfo> stageInfo = new ConcurrentHashMap<>();
879-
BoundedQueueExecutor workExecutor = createWorkUnitExecutor(options);
880+
MultiKeyBundleOptions multiKeyBundleOptions = MultiKeyBundleOptions.fromOptions(options);
881+
BoundedQueueExecutor workExecutor = createWorkUnitExecutor(options, multiKeyBundleOptions);
880882
ScheduledExecutorService commitFinalizerCleanupExecutor =
881883
Executors.newScheduledThreadPool(
882884
1,
@@ -990,7 +992,8 @@ static StreamingDataflowWorker forTesting(
990992
: windmillStreamFactory.build(),
991993
executorSupplier.apply("RefreshWork"),
992994
stageInfo,
993-
grpcDispatcherClient);
995+
grpcDispatcherClient,
996+
multiKeyBundleOptions);
994997
}
995998

996999
private static GrpcWindmillStreamFactory.Builder createGrpcwindmillStreamFactoryBuilder(
@@ -1020,11 +1023,11 @@ private static JobHeader createJobHeader(DataflowWorkerHarnessOptions options, l
10201023
.build();
10211024
}
10221025

1023-
private static BoundedQueueExecutor createWorkUnitExecutor(DataflowWorkerHarnessOptions options) {
1026+
private static BoundedQueueExecutor createWorkUnitExecutor(
1027+
DataflowWorkerHarnessOptions options, MultiKeyBundleOptions multiKeyBundleOptions) {
10241028
boolean useFairMonitor =
10251029
DataflowRunner.hasExperiment(options, BOUNDED_QUEUE_EXECUTOR_USE_FAIR_MONITOR_EXPERIMENT);
1026-
boolean useKeyGroupWorkQueue =
1027-
DataflowRunner.hasExperiment(options, UNSTABLE_ENABLE_MULTI_KEY_BUNDLE);
1030+
boolean useKeyGroupWorkQueue = multiKeyBundleOptions.multiKeyBundleEnabled();
10281031
return new BoundedQueueExecutor(
10291032
chooseMaxThreads(options),
10301033
THREAD_EXPIRATION_TIME_SEC,
@@ -1206,9 +1209,14 @@ private void onCompleteCommit(CompleteCommit completeCommit) {
12061209
computationStateCache
12071210
.getIfPresent(completeCommit.computationId())
12081211
.ifPresent(
1209-
state ->
1212+
state -> {
1213+
if (completeCommit.retryableFailure()) {
1214+
state.reexecuteActiveWork(completeCommit.shardedKey(), completeCommit.workId());
1215+
} else {
12101216
state.completeWorkAndScheduleNextWorkForKey(
1211-
completeCommit.shardedKey(), completeCommit.workId()));
1217+
completeCommit.shardedKey(), completeCommit.workId());
1218+
}
1219+
});
12121220
}
12131221

12141222
@AutoValue

0 commit comments

Comments
 (0)