Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
c26947d
Track exceptions thrown by fused downstream consumers
knockknock10 Nov 13, 2026
a51f5ae
Apply fused DoFn exception policy at bundle completion
knockknock10 Nov 13, 2026
680b404
Define fused DoFn exception handling experiment
knockknock10 Nov 13, 2026
4e9046f
Test fused DoFn exception handling policies
knockknock10 Nov 13, 2026
0b9b7bd
Add fused two-DoFn bundle regression tests
knockknock10 Nov 13, 2026
71bd2bc
Fix imports in fused DoFn regression test
knockknock10 Nov 13, 2026
1239f9b
Correct fused regression source data receiver typing
knockknock10 Nov 13, 2026
54fabd1
Format fused test source receiver
knockknock10 Nov 13, 2026
ad327c3
Fix Java test imports
knockknock10 Nov 13, 2026
57a45ed
Fix Java test imports
knockknock10 Nov 13, 2026
84ff929
Use WindowedValues helper in fused test
knockknock10 Nov 13, 2026
6714eb0
Use typed PCollection consumer in fused test
knockknock10 Nov 13, 2026
253ad2b
Match fused DoFn output mapping to its main output tag
knockknock10 Nov 13, 2026
741fdb3
Document fused DoFn exception handling
knockknock10 Nov 13, 2026
fbcb6c8
Document fused DoFn exception handling
knockknock10 Nov 13, 2026
7668378
Document fused DoFn exception handling
knockknock10 Nov 13, 2026
4751c88
Exercise fail-fast behavior across multiple fused inputs
knockknock10 Nov 13, 2026
8a302c3
Separate fused failure and warning completion actions
knockknock10 Nov 13, 2026
922457a
Log fused warnings only after successful bundle work
knockknock10 Nov 13, 2026
7a83495
Update registry tests for completion policies
knockknock10 Nov 13, 2026
b803259
Clarify failure tracker reset lifecycle
knockknock10 Nov 13, 2026
2c0f969
Fix Error Prone MissingFail warnings in registry tests
knockknock10 Nov 13, 2026
b356d64
Fix fused exception test input stream
knockknock10 Nov 13, 2026
b1c33ce
Limit fused fail-fast to the downstream transform that failed
knockknock10 Nov 13, 2026
1bef43e
Avoid relogging previously recorded fused failures
knockknock10 Nov 13, 2026
f304405
Always shut down handlers in fused exception tests
knockknock10 Nov 13, 2026
761f862
Cover successful bundle completion in warning mode
knockknock10 Nov 13, 2026
73d5bd0
Handle Error subclasses and isolate failed fused consumers
knockknock10 Nov 13, 2026
21549c9
Fail fused bundles before finishing failed DoFns
knockknock10 Nov 13, 2026
97fcdf1
Test fused consumer isolation and Error propagation
knockknock10 Nov 13, 2026
82221e2
Test finish-bundle suppression after fused failures
knockknock10 Nov 13, 2026
7c3e489
Preserve cancellation semantics in fused fail mode
knockknock10 Nov 13, 2026
cf50026
Reset fused exception test counters before each test
knockknock10 Nov 13, 2026
7ac28dc
Clean up bundle processors when swallowed errors fail bundles
knockknock10 Nov 13, 2026
d16dffe
Keep metrics consistent when all fused consumers have failed
knockknock10 Nov 13, 2026
e648eea
Preserve metrics for PCollections without consumers
knockknock10 Nov 13, 2026
928e9f5
Reset fused DoFn test counters between tests
knockknock10 Nov 13, 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 @@ -35,6 +35,9 @@ public interface ExperimentalOptions extends PipelineOptions {

String STATE_SAMPLING_PERIOD_MILLIS = "state_sampling_period_millis";

/** Experiment controlling exception handling at fused Java DoFn boundaries. */
String FUSED_DOFN_EXCEPTION_HANDLING = "fused_dofn_exception_handling";

@Description(
"Apache Beam provides a number of experimental features that can "
+ "be enabled with this flag. If executing against a managed service, please contact the "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
import org.apache.beam.sdk.metrics.MetricsContainer;
import org.apache.beam.sdk.metrics.MetricsEnvironment;
import org.apache.beam.sdk.metrics.MetricsEnvironment.MetricsEnvironmentState;
import org.apache.beam.sdk.options.ExperimentalOptions;
import org.apache.beam.sdk.options.PipelineOptions;
import org.apache.beam.sdk.options.SdkHarnessOptions;
import org.apache.beam.sdk.transforms.DoFn.BundleFinalizer;
Expand Down Expand Up @@ -560,13 +561,24 @@ public BeamFnApi.InstructionResponse.Builder processBundle(InstructionRequest re
bundleProcessor.getInboundEndpointApiServiceDescriptors());
}

// Need to reverse this since we want to call finish in topological order.
// Do not run @FinishBundle after a downstream failure was swallowed during element
// processing. Finish functions can flush state or write to external sinks.
bundleProcessor.getpCollectionConsumerRegistry().failBundleIfDownstreamFailed();

// Need to reverse this since we want to call finish in topological order. Check after
// each function as well, so a swallowed failure during @FinishBundle prevents later
// finish functions from running.
for (ThrowingRunnable finishFunction :
Lists.reverse(finishFunctionRegistry.getFunctions())) {
LOG.debug("Finishing function {}", finishFunction);
finishFunction.run();
bundleProcessor.getpCollectionConsumerRegistry().failBundleIfDownstreamFailed();
}

// Surface a failure raised by the final @FinishBundle function before outbound data is
// finalized.
bundleProcessor.getpCollectionConsumerRegistry().failBundleIfDownstreamFailed();
Comment thread
knockknock10 marked this conversation as resolved.
Resolved

// If bundleProcessor has not flushed any elements, embed them in response.
embedOutboundElementsIfApplicable(response, bundleProcessor);

Expand Down Expand Up @@ -598,6 +610,9 @@ public BeamFnApi.InstructionResponse.Builder processBundle(InstructionRequest re
}
}

// A warning is appropriate only once all bundle work has succeeded.
bundleProcessor.getpCollectionConsumerRegistry().logWarningIfDownstreamFailed();

// Mark the bundle processor as re-usable.
bundleProcessorCache.release(
request.getProcessBundle().getProcessBundleDescriptorId(), bundleProcessor);
Expand All @@ -615,7 +630,40 @@ public BeamFnApi.InstructionResponse.Builder processBundle(InstructionRequest re
// Ensure that if more data arrives for the instruction it is discarded.
beamFnDataClient.poisonInstructionId(instructionId);
throw e;
} catch (Error e) {
LOG.debug(
"Error processing bundle {} with bundleProcessor for {} after error",
instructionId,
request.getProcessBundle().getProcessBundleDescriptorId(),
e);
if (bundleProcessor != null) {
// Make sure we clean up from the active set of bundle processors.
bundleProcessorCache.discard(bundleProcessor);
}
// Ensure that if more data arrives for the instruction it is discarded.
beamFnDataClient.poisonInstructionId(instructionId);
throw e;
}
}

private static PCollectionConsumerRegistry.FusedDoFnExceptionHandling
getFusedDoFnExceptionHandling(PipelineOptions options) {
String value =
ExperimentalOptions.getExperimentValue(
options, ExperimentalOptions.FUSED_DOFN_EXCEPTION_HANDLING);
if (value == null || value.equalsIgnoreCase("legacy")) {
return PCollectionConsumerRegistry.FusedDoFnExceptionHandling.LEGACY;
}
if (value.equalsIgnoreCase("warn")) {
return PCollectionConsumerRegistry.FusedDoFnExceptionHandling.WARN;
}
if (value.equalsIgnoreCase("fail")) {
return PCollectionConsumerRegistry.FusedDoFnExceptionHandling.FAIL;
}
throw new IllegalArgumentException(
String.format(
"Unsupported value '%s' for experiment %s. Expected legacy, warn, or fail.",
value, ExperimentalOptions.FUSED_DOFN_EXCEPTION_HANDLING));
}

private void embedOutboundElementsIfApplicable(
Expand Down Expand Up @@ -803,20 +851,24 @@ private BundleProcessor createBundleProcessor(
new MetricsEnvironmentStateForBundle();
ExecutionStateTracker stateTracker = executionStateSampler.create();
bundleProgressReporterAndRegistrar.register(stateTracker);
PCollectionConsumerRegistry.FusedDoFnExceptionHandling fusedDoFnExceptionHandling =
getFusedDoFnExceptionHandling(options);
PCollectionConsumerRegistry pCollectionConsumerRegistry =
new PCollectionConsumerRegistry(
stateTracker,
shortIds,
bundleProgressReporterAndRegistrar,
bundleDescriptor,
dataSampler);
dataSampler,
fusedDoFnExceptionHandling);
HashSet<String> processedPTransformIds = new HashSet<>();

PTransformFunctionRegistry startFunctionRegistry =
new PTransformFunctionRegistry(shortIds, stateTracker, Urns.START_BUNDLE_MSECS);
PTransformFunctionRegistry finishFunctionRegistry =
new PTransformFunctionRegistry(shortIds, stateTracker, Urns.FINISH_BUNDLE_MSECS);
List<ThrowingRunnable> resetFunctions = new ArrayList<>();
resetFunctions.add(pCollectionConsumerRegistry::reset);
List<ThrowingRunnable> tearDownFunctions = new ArrayList<>();

// Build a multimap of PCollection ids to PTransform ids which consume said PCollections
Expand Down
Loading
Loading