From 8702e0af8d51ed5cbe14426c824fde16deee5566 Mon Sep 17 00:00:00 2001 From: bvolpato Date: Wed, 28 Oct 2026 01:27:43 -0400 Subject: [PATCH] [DirectRunner] Reuse ParDo metadata between bundles --- .../runners/direct/ParDoEvaluatorFactory.java | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ParDoEvaluatorFactory.java b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ParDoEvaluatorFactory.java index c545deb38832..a97d493f00a7 100644 --- a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ParDoEvaluatorFactory.java +++ b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ParDoEvaluatorFactory.java @@ -19,8 +19,11 @@ import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull; +import java.io.Serializable; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.apache.beam.runners.direct.DirectExecutionContext.DirectStepContext; import org.apache.beam.runners.direct.ParDoEvaluator.DoFnRunnerFactory; import org.apache.beam.runners.local.StructuralKey; @@ -29,6 +32,7 @@ import org.apache.beam.sdk.transforms.DoFn; import org.apache.beam.sdk.transforms.DoFnSchemaInformation; import org.apache.beam.sdk.transforms.ParDo; +import org.apache.beam.sdk.util.SerializableUtils; import org.apache.beam.sdk.util.construction.ParDoTranslation; import org.apache.beam.sdk.values.PCollection; import org.apache.beam.sdk.values.PCollectionTuple; @@ -45,6 +49,8 @@ final class ParDoEvaluatorFactory implements TransformEvaluator private static final Logger LOG = LoggerFactory.getLogger(ParDoEvaluatorFactory.class); private final LoadingCache, DoFnLifecycleManager> fnClones; + private final ConcurrentMap, byte[]> serializedExecutionMetadata = + new ConcurrentHashMap<>(); private final EvaluationContext evaluationContext; private final PipelineOptions options; private final ParDoEvaluator.DoFnRunnerFactory runnerFactory; @@ -74,6 +80,14 @@ public DoFnLifecycleManager load(AppliedPTransform application) throws public TransformEvaluator forApplication( AppliedPTransform application, CommittedBundle inputBundle) throws Exception { + // Schema conversion functions have mutable state, so each evaluator needs its own copy. + ExecutionMetadata metadata = + (ExecutionMetadata) + SerializableUtils.deserializeFromByteArray( + serializedExecutionMetadata.computeIfAbsent( + application, ParDoEvaluatorFactory::serializeExecutionMetadata), + "ParDo execution metadata"); + @SuppressWarnings({"unchecked", "rawtypes"}) TransformEvaluator evaluator = (TransformEvaluator) @@ -84,14 +98,36 @@ public TransformEvaluator forApplication( ParDoTranslation.getSideInputs(application), (TupleTag) ParDoTranslation.getMainOutputTag(application), ParDoTranslation.getAdditionalOutputTags(application).getAll(), - ParDoTranslation.getSchemaInformation(application), - ParDoTranslation.getSideInputMapping(application)); + metadata.schemaInformation, + metadata.sideInputMapping); return evaluator; } @Override public void cleanup() throws Exception { - DoFnLifecycleManagers.removeAllFromManagers(fnClones.asMap().values()); + try { + DoFnLifecycleManagers.removeAllFromManagers(fnClones.asMap().values()); + } finally { + serializedExecutionMetadata.clear(); + } + } + + private static byte[] serializeExecutionMetadata(AppliedPTransform application) { + return SerializableUtils.serializeToByteArray( + new ExecutionMetadata( + ParDoTranslation.getSchemaInformation(application), + ParDoTranslation.getSideInputMapping(application))); + } + + private static class ExecutionMetadata implements Serializable { + private final DoFnSchemaInformation schemaInformation; + private final Map> sideInputMapping; + + private ExecutionMetadata( + DoFnSchemaInformation schemaInformation, Map> sideInputMapping) { + this.schemaInformation = schemaInformation; + this.sideInputMapping = sideInputMapping; + } } /**