This is an automated email from the ASF dual-hosted git repository.
stankiewicz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push:
new 3e69022449d [#20502] Remove nullness suppressions from the direct-java
runner (#40295)
3e69022449d is described below
commit 3e69022449dbb8bd8fdab872c82d8ca4e6df1e06
Author: Nitin Ware <[email protected]>
AuthorDate: Mon Sep 28 06:46:15 2026 -0500
[#20502] Remove nullness suppressions from the direct-java runner (#40295)
[#20502] Remove nullness suppressions across direct-java
Replaced blanket @SuppressWarnings("nullness") annotations across
runners/direct-java with targeted root-cause fixes, keeping rawtypes (#20447)
and keyfor where required.
Type Contracts and Annotations:
- Marked legitimately nullable fields, getters, and parameters with
@Nullable across ImmutableListBundleFactory, StepTransformResult,
DirectExecutionContext, and CopyOnAccessInMemoryStateInternals.
- Explicitly typed nullable async results and atomics, including
BoundedReadEvaluatorFactory dynamic-split futures, SideInputContainer view
references, and TransformExecutorServices.currentlyEvaluating.
- Annotated PipelineExecutor.waitUntilFinish as @Nullable because it
returns null on timeout.
Invariants and Assertions:
- Replaced blanket suppressions with checkStateNotNull and
checkArgumentNotNull where non-null invariants are guaranteed by lifecycle
contracts, such as non-root transforms, guaranteed main inputs in
ParDoEvaluatorFactory, and output-bundle PCollection targets.
- Captured nullable returns from methods like peek, pollFirst, and
getExistingReader into local variables before checking and dereferencing.
Initialization and Thread Safety:
- Broke the EvaluationContext and SideInputContainer circular dependency by
initializing sideInputContainer in the create factory rather than leaking this
from the constructor.
- Replaced instance-method invocations on partially initialized receivers
with static helpers in GroupByKeyOnlyEvaluatorFactory and
ExecutorServiceParallelExecutor.
- Replaced hashCode in SplittableProcessElementsEvaluatorFactory with a
static AtomicInteger sequence for thread naming.
Exceptions and Documented Residuals:
- MultiStepCombine: Retained the class-level nullness suppression. AccumT
can legitimately be null (such as empty HllCount sketches), and enforcing
non-null broke zetasketch tests.
- UnboundedReadEvaluatorFactory: Added a targeted method-scoped suppression
on of() due to google/auto#1320 dropping @Nullable on type-variable constructor
parameters.
- DirectRunner.waitUntilFinish(Duration): Kept a narrow suppression to
avoid cascading signature changes across all runner implementations of
PipelineResult.
---
.../direct/BoundedReadEvaluatorFactory.java | 21 ++--
.../beam/runners/direct/CloningBundleFactory.java | 11 +-
.../beam/runners/direct/CommittedResult.java | 3 -
.../direct/CopyOnAccessInMemoryStateInternals.java | 13 +-
.../runners/direct/DirectExecutionContext.java | 16 +--
.../apache/beam/runners/direct/DirectGraph.java | 10 +-
.../beam/runners/direct/DirectGraphVisitor.java | 14 +--
.../apache/beam/runners/direct/DirectMetrics.java | 10 +-
.../apache/beam/runners/direct/DirectRunner.java | 13 +-
.../runners/direct/DirectWriteViewVisitor.java | 18 +--
.../beam/runners/direct/DisplayDataValidator.java | 11 +-
.../beam/runners/direct/EvaluationContext.java | 26 ++--
.../direct/ExecutorServiceParallelExecutor.java | 33 ++---
.../direct/GroupAlsoByWindowEvaluatorFactory.java | 17 +--
.../direct/GroupByKeyOnlyEvaluatorFactory.java | 7 +-
.../direct/ImmutabilityCheckingBundleFactory.java | 12 +-
.../direct/ImmutabilityEnforcementFactory.java | 9 +-
.../runners/direct/ImmutableListBundleFactory.java | 12 +-
.../runners/direct/KeyedPValueTrackingVisitor.java | 11 +-
.../beam/runners/direct/MultiStepCombine.java | 6 +-
.../apache/beam/runners/direct/ParDoEvaluator.java | 13 +-
.../beam/runners/direct/ParDoEvaluatorFactory.java | 7 +-
.../beam/runners/direct/PipelineExecutor.java | 3 +-
.../beam/runners/direct/QuiescenceDriver.java | 27 ++--
.../beam/runners/direct/RootProviderRegistry.java | 3 +-
.../beam/runners/direct/SideInputContainer.java | 30 +++--
.../SplittableProcessElementsEvaluatorFactory.java | 12 +-
.../direct/StatefulParDoEvaluatorFactory.java | 27 ++--
.../beam/runners/direct/StepTransformResult.java | 10 +-
.../runners/direct/TestStreamEvaluatorFactory.java | 3 +-
.../runners/direct/TransformEvaluatorRegistry.java | 6 +-
.../runners/direct/TransformExecutorServices.java | 6 +-
.../direct/UnboundedReadEvaluatorFactory.java | 9 +-
.../runners/direct/WatermarkCallbackExecutor.java | 24 ++--
.../beam/runners/direct/WatermarkManager.java | 140 +++++++++++++--------
.../runners/direct/WindowEvaluatorFactory.java | 2 +-
36 files changed, 328 insertions(+), 267 deletions(-)
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/BoundedReadEvaluatorFactory.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/BoundedReadEvaluatorFactory.java
index b31798e7e46..aeedc84eb3f 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/BoundedReadEvaluatorFactory.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/BoundedReadEvaluatorFactory.java
@@ -17,6 +17,8 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
+
import com.google.auto.value.AutoValue;
import java.io.IOException;
import java.util.Collection;
@@ -52,8 +54,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* for the {@link PrimitiveBoundedRead SplittableParDo.PrimitiveBoundedRead}
{@link PTransform}.
*/
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
final class BoundedReadEvaluatorFactory implements TransformEvaluatorFactory {
/**
@@ -149,7 +150,8 @@ final class BoundedReadEvaluatorFactory implements
TransformEvaluatorFactory {
BoundedSource<OutputT> source = element.getValue().getSource();
try (final BoundedReader<OutputT> reader = source.createReader(options))
{
boolean contentsRemaining = reader.start();
- Future<BoundedSource<OutputT>> residualFuture =
startDynamicSplitThread(source, reader);
+ Future<@Nullable BoundedSource<OutputT>> residualFuture =
+ startDynamicSplitThread(source, reader);
UncommittedBundle<OutputT> output =
evaluationContext.createBundle(outputPCollection);
while (contentsRemaining) {
output.add(
@@ -159,24 +161,24 @@ final class BoundedReadEvaluatorFactory implements
TransformEvaluatorFactory {
}
resultBuilder.addOutput(output);
try {
- BoundedSource<OutputT> residual = residualFuture.get();
+ @Nullable BoundedSource<OutputT> residual = residualFuture.get();
if (residual != null) {
resultBuilder.addUnprocessedElements(
element.withValue(BoundedSourceShard.of(residual)));
}
} catch (ExecutionException exex) {
// Un-and-rewrap the exception thrown by attempting to split
- throw UserCodeException.wrap(exex.getCause());
+ throw UserCodeException.wrap(checkStateNotNull(exex.getCause()));
}
}
}
- private Future<BoundedSource<OutputT>> startDynamicSplitThread(
+ private Future<@Nullable BoundedSource<OutputT>> startDynamicSplitThread(
BoundedSource<OutputT> source, BoundedReader<OutputT> reader) throws
Exception {
if (source.getEstimatedSizeBytes(options) > minimumDynamicSplitSize) {
return produceSplitExecutor.submit(new
GenerateSplitAtHalfwayPoint<>(reader));
} else {
- SettableFuture<BoundedSource<OutputT>> emptyFuture =
SettableFuture.create();
+ SettableFuture<@Nullable BoundedSource<OutputT>> emptyFuture =
SettableFuture.create();
emptyFuture.set(null);
return emptyFuture;
}
@@ -230,7 +232,8 @@ final class BoundedReadEvaluatorFactory implements
TransformEvaluatorFactory {
}
}
- private static class GenerateSplitAtHalfwayPoint<T> implements
Callable<BoundedSource<T>> {
+ private static class GenerateSplitAtHalfwayPoint<T>
+ implements Callable<@Nullable BoundedSource<T>> {
private final BoundedReader<T> reader;
private GenerateSplitAtHalfwayPoint(BoundedReader<T> reader) {
@@ -238,7 +241,7 @@ final class BoundedReadEvaluatorFactory implements
TransformEvaluatorFactory {
}
@Override
- public BoundedSource<T> call() throws Exception {
+ public @Nullable BoundedSource<T> call() throws Exception {
// Splits at halfway of the remaining work.
Double currentlyConsumed = reader.getFractionConsumed();
if (currentlyConsumed == null || currentlyConsumed == 1.0) {
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/CloningBundleFactory.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/CloningBundleFactory.java
index c3d30bfbf74..182200b021c 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/CloningBundleFactory.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/CloningBundleFactory.java
@@ -17,6 +17,8 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
+
import org.apache.beam.runners.local.StructuralKey;
import org.apache.beam.sdk.coders.Coder;
import org.apache.beam.sdk.coders.CoderException;
@@ -25,15 +27,13 @@ import org.apache.beam.sdk.util.UserCodeException;
import org.apache.beam.sdk.values.PCollection;
import org.apache.beam.sdk.values.WindowedValue;
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.MoreObjects;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.joda.time.Instant;
/**
* A {@link BundleFactory} where a created {@link UncommittedBundle} clones
all elements added to it
* using the coder of the {@link PCollection}.
*/
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
class CloningBundleFactory implements BundleFactory {
private static final CloningBundleFactory INSTANCE = new
CloningBundleFactory();
@@ -70,11 +70,12 @@ class CloningBundleFactory implements BundleFactory {
private CloningBundle(UncommittedBundle<T> underlying) {
this.underlying = underlying;
- this.coder = underlying.getPCollection().getCoder();
+ // CloningBundle only wraps non-root bundles, whose PCollection is
always present.
+ this.coder = checkStateNotNull(underlying.getPCollection()).getCoder();
}
@Override
- public PCollection<T> getPCollection() {
+ public @Nullable PCollection<T> getPCollection() {
return underlying.getPCollection();
}
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/CommittedResult.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/CommittedResult.java
index af0c92d09c5..0a58fdf9e71 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/CommittedResult.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/CommittedResult.java
@@ -24,9 +24,6 @@ import org.apache.beam.sdk.runners.AppliedPTransform;
/** A {@link TransformResult} that has been committed. */
@AutoValue
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
abstract class CommittedResult<ExecutableT> {
/** Returns the {@link AppliedPTransform} that produced this result. */
public abstract ExecutableT getExecutable();
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/CopyOnAccessInMemoryStateInternals.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/CopyOnAccessInMemoryStateInternals.java
index 720ce77c388..2d11cdb7404 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/CopyOnAccessInMemoryStateInternals.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/CopyOnAccessInMemoryStateInternals.java
@@ -56,6 +56,7 @@ import org.apache.beam.sdk.transforms.windowing.BoundedWindow;
import org.apache.beam.sdk.transforms.windowing.TimestampCombiner;
import org.apache.beam.sdk.util.CombineFnUtil;
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Iterables;
+import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.joda.time.Instant;
@@ -65,10 +66,9 @@ import org.joda.time.Instant;
* accessed, an independent copy will be created within this table.
*/
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
-class CopyOnAccessInMemoryStateInternals<K> implements StateInternals {
+class CopyOnAccessInMemoryStateInternals<K extends @NonNull Object> implements
StateInternals {
private final CopyOnAccessInMemoryStateTable table;
private K key;
@@ -77,12 +77,13 @@ class CopyOnAccessInMemoryStateInternals<K> implements
StateInternals {
* Creates a new {@link CopyOnAccessInMemoryStateInternals} with the
underlying (possibly null)
* StateInternals.
*/
- public static <K> CopyOnAccessInMemoryStateInternals withUnderlying(
+ public static <K extends @NonNull Object> CopyOnAccessInMemoryStateInternals
withUnderlying(
K key, @Nullable CopyOnAccessInMemoryStateInternals underlying) {
return new CopyOnAccessInMemoryStateInternals<>(key, underlying);
}
- private CopyOnAccessInMemoryStateInternals(K key,
CopyOnAccessInMemoryStateInternals underlying) {
+ private CopyOnAccessInMemoryStateInternals(
+ K key, @Nullable CopyOnAccessInMemoryStateInternals underlying) {
this.key = key;
table = new CopyOnAccessInMemoryStateTable(underlying == null ? null :
underlying.table);
}
@@ -169,7 +170,7 @@ class CopyOnAccessInMemoryStateInternals<K> implements
StateInternals {
/** The earliest watermark hold in this table. */
private Optional<Instant> earliestWatermarkHold;
- public CopyOnAccessInMemoryStateTable(StateTable underlying) {
+ public CopyOnAccessInMemoryStateTable(@Nullable StateTable underlying) {
this.underlying = Optional.ofNullable(underlying);
binderFactory = new CopyOnBindBinderFactory(this.underlying);
earliestWatermarkHold = Optional.empty();
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectExecutionContext.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectExecutionContext.java
index 249934c4308..63c86ef79df 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectExecutionContext.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectExecutionContext.java
@@ -28,6 +28,7 @@ import
org.apache.beam.runners.direct.WatermarkManager.TimerUpdate;
import org.apache.beam.runners.direct.WatermarkManager.TransformWatermarks;
import org.apache.beam.runners.local.StructuralKey;
import org.apache.beam.sdk.transforms.DoFn.BundleFinalizer;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Execution Context for the {@link DirectRunner}.
@@ -36,21 +37,20 @@ import org.apache.beam.sdk.transforms.DoFn.BundleFinalizer;
* for each thread that requires it.
*/
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
class DirectExecutionContext {
private final Clock clock;
private final StructuralKey<?> key;
- private final CopyOnAccessInMemoryStateInternals existingState;
+ private final @Nullable CopyOnAccessInMemoryStateInternals existingState;
private final TransformWatermarks watermarks;
private Map<String, DirectStepContext> cachedStepContexts = new
LinkedHashMap<>();
public DirectExecutionContext(
Clock clock,
StructuralKey<?> key,
- CopyOnAccessInMemoryStateInternals existingState,
+ @Nullable CopyOnAccessInMemoryStateInternals existingState,
TransformWatermarks watermarks) {
this.clock = clock;
this.key = key;
@@ -69,9 +69,9 @@ class DirectExecutionContext {
/** Step Context for the {@link DirectRunner}. */
public class DirectStepContext implements StepContext {
- private CopyOnAccessInMemoryStateInternals<?> stateInternals;
- private DirectTimerInternals timerInternals;
- private InMemoryBundleFinalizer bundleFinalizer;
+ private @Nullable CopyOnAccessInMemoryStateInternals<?> stateInternals;
+ private @Nullable DirectTimerInternals timerInternals;
+ private @Nullable InMemoryBundleFinalizer bundleFinalizer;
public DirectStepContext() {}
@@ -111,7 +111,7 @@ class DirectExecutionContext {
* Commits the state of this step, and returns the committed state. If the
step has not accessed
* any state, return null.
*/
- public CopyOnAccessInMemoryStateInternals commitState() {
+ public @Nullable CopyOnAccessInMemoryStateInternals commitState() {
if (stateInternals != null) {
return stateInternals.commit();
}
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectGraph.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectGraph.java
index 00f24608949..3c3782e724a 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectGraph.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectGraph.java
@@ -17,6 +17,8 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
+
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -34,7 +36,7 @@ import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ListMu
* Methods for interacting with the underlying structure of a {@link Pipeline}
that is being
* executed with the {@link DirectRunner}.
*/
-@SuppressWarnings({"keyfor", "nullness"}) //
TODO(https://github.com/apache/beam/issues/20497)
+@SuppressWarnings({"keyfor"}) //
TODO(https://github.com/apache/beam/issues/20497)
class DirectGraph implements ExecutableGraph<AppliedPTransform<?, ?, ?>,
PValue> {
private final Map<PCollection<?>, AppliedPTransform<?, ?, ?>> producers;
private final Map<PCollectionView<?>, AppliedPTransform<?, ?, ?>>
viewWriters;
@@ -68,7 +70,7 @@ class DirectGraph implements
ExecutableGraph<AppliedPTransform<?, ?, ?>, PValue>
@Override
public AppliedPTransform<?, ?, ?> getProducer(PValue produced) {
if (produced instanceof PCollection) {
- return producers.get(produced);
+ return checkStateNotNull(producers.get(produced), "No producer for %s",
produced);
} else if (produced instanceof PCollectionView) {
return getWriter((PCollectionView<?>) produced);
}
@@ -96,7 +98,7 @@ class DirectGraph implements
ExecutableGraph<AppliedPTransform<?, ?, ?>, PValue>
}
private AppliedPTransform<?, ?, ?> getWriter(PCollectionView<?> view) {
- return viewWriters.get(view);
+ return checkStateNotNull(viewWriters.get(view), "No writer for %s", view);
}
@Override
@@ -119,6 +121,6 @@ class DirectGraph implements
ExecutableGraph<AppliedPTransform<?, ?, ?>, PValue>
}
String getStepName(AppliedPTransform<?, ?, ?> step) {
- return stepNames.get(step);
+ return checkStateNotNull(stepNames.get(step), "No step name for %s", step);
}
}
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectGraphVisitor.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectGraphVisitor.java
index adaae0b5494..436e013bbf2 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectGraphVisitor.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectGraphVisitor.java
@@ -47,9 +47,6 @@ import org.slf4j.LoggerFactory;
* {@link Pipeline}. This is used to schedule consuming {@link PTransform
PTransforms} to consume
* input after the upstream transform has produced and committed output.
*/
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
class DirectGraphVisitor extends PipelineVisitor.Defaults {
private static final Logger LOG =
LoggerFactory.getLogger(DirectGraphVisitor.class);
@@ -119,12 +116,11 @@ class DirectGraphVisitor extends PipelineVisitor.Defaults
{
allConsumers.put(value, appliedTransform);
}
}
- if (node.getTransform() instanceof ParDo.MultiOutput) {
- consumedViews.addAll(
- ((ParDo.MultiOutput<?, ?>)
node.getTransform()).getSideInputs().values());
- } else if (node.getTransform() instanceof WriteView) {
- viewWriters.put(
- ((WriteView) node.getTransform()).getView(),
node.toAppliedPTransform(getPipeline()));
+ PTransform<?, ?> transform = node.getTransform();
+ if (transform instanceof ParDo.MultiOutput) {
+ consumedViews.addAll(((ParDo.MultiOutput<?, ?>)
transform).getSideInputs().values());
+ } else if (transform instanceof WriteView) {
+ viewWriters.put(((WriteView) transform).getView(),
node.toAppliedPTransform(getPipeline()));
}
}
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectMetrics.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectMetrics.java
index b7cf0357447..dbbf6d925ff 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectMetrics.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectMetrics.java
@@ -47,12 +47,10 @@ import org.apache.beam.sdk.metrics.MetricResults;
import org.apache.beam.sdk.metrics.MetricsFilter;
import org.apache.beam.sdk.metrics.StringSetResult;
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ImmutableList;
+import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
/** Implementation of {@link MetricResults} for the Direct Runner. */
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
class DirectMetrics extends MetricResults {
private interface MetricAggregation<UpdateT, ResultT> {
@@ -69,7 +67,7 @@ class DirectMetrics extends MetricResults {
* @param <UpdateT> The type of raw data received and aggregated across
updates.
* @param <ResultT> The type of result extracted from the data.
*/
- private static class DirectMetric<UpdateT, ResultT> {
+ private static class DirectMetric<UpdateT extends @NonNull Object, ResultT> {
private final MetricAggregation<UpdateT, ResultT> aggregation;
private final Executor executor;
@@ -326,10 +324,10 @@ class DirectMetrics extends MetricResults {
}
private <ResultT> void maybeExtractResult(
- MetricsFilter filter,
+ @Nullable MetricsFilter filter,
ImmutableList.Builder<MetricResult<ResultT>> resultsBuilder,
Map.Entry<MetricKey, ? extends DirectMetric<?, ResultT>> entry) {
- if (MetricFiltering.matches(filter, entry.getKey())) {
+ if (filter == null || MetricFiltering.matches(filter, entry.getKey())) {
resultsBuilder.add(
MetricResult.create(
entry.getKey(),
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectRunner.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectRunner.java
index 6997b590e01..9cc94ceaea9 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectRunner.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectRunner.java
@@ -17,6 +17,8 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
+
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.Collection;
@@ -66,9 +68,6 @@ import org.joda.time.Duration;
* contained within a {@link Pipeline} does not break assumptions within the
Beam model, to improve
* the ability to execute a {@link Pipeline} at scale on a distributed backend.
*/
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
public class DirectRunner extends PipelineRunner<DirectPipelineResult> {
enum Enforcement {
@@ -222,7 +221,7 @@ public class DirectRunner extends
PipelineRunner<DirectPipelineResult> {
try {
result.waitUntilFinish();
} catch (UserCodeException userException) {
- throw new PipelineExecutionException(userException.getCause());
+ throw new
PipelineExecutionException(checkStateNotNull(userException.getCause()));
} catch (RuntimeException | OutOfMemoryError e) {
throw e;
} catch (Throwable t) {
@@ -368,6 +367,10 @@ public class DirectRunner extends
PipelineRunner<DirectPipelineResult> {
* org.apache.beam.sdk.PipelineResult.State#FAILED}.
*/
@Override
+ // PipelineResult.waitUntilFinish(Duration) is documented to return null
on timeout, but its
+ // interface return type is not annotated @Nullable; annotating it would
ripple across every
+ // runner's override, so the timeout null is suppressed here rather than
in the sdk-core API.
+ @SuppressWarnings("nullness")
public State waitUntilFinish(Duration duration) {
if (this.state.isTerminal()) {
return this.state;
@@ -379,7 +382,7 @@ public class DirectRunner extends
PipelineRunner<DirectPipelineResult> {
// Emulates the behavior of Pipeline#run(), where a stack trace caused
by a
// UserCodeException is truncated and replaced with the stack starting
at the call to
// waitToFinish
- throw new Pipeline.PipelineExecutionException(uce.getCause());
+ throw new
Pipeline.PipelineExecutionException(checkStateNotNull(uce.getCause()));
} catch (Exception e) {
if (e instanceof InterruptedException) {
Thread.currentThread().interrupt();
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectWriteViewVisitor.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectWriteViewVisitor.java
index 8bb16809785..e7edeaae7ca 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectWriteViewVisitor.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DirectWriteViewVisitor.java
@@ -17,6 +17,8 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
+
import java.util.HashSet;
import java.util.Set;
import org.apache.beam.sdk.Pipeline;
@@ -41,15 +43,12 @@ import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Lists;
* Adds a {@link DirectRunner}-specific {@link WriteView} step for each {@link
PCollectionView} for
* scheduling materialization of side inputs.
*/
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
class DirectWriteViewVisitor extends PipelineVisitor.Defaults {
/** Private URN for identifying {@link DirectRunner}-specific view writing
transform. */
static final String DIRECT_WRITE_VIEW_URN =
"beam:directrunner:transforms:write_view:v1";
- private Set<PCollectionView<?>> viewsToWrite;
+ private Set<PCollectionView<?>> viewsToWrite = new HashSet<>();
@Override
public void enterPipeline(Pipeline p) {
@@ -66,14 +65,16 @@ class DirectWriteViewVisitor extends
PipelineVisitor.Defaults {
@Override
public void visitPrimitiveTransform(TransformHierarchy.Node node) {
- if (node.getTransform() instanceof ParDo.MultiOutput) {
- ParDo.MultiOutput<?, ?> parDo = (ParDo.MultiOutput<?, ?>)
node.getTransform();
+ PTransform<?, ?> transform = node.getTransform();
+ if (transform instanceof ParDo.MultiOutput) {
+ ParDo.MultiOutput<?, ?> parDo = (ParDo.MultiOutput<?, ?>) transform;
viewsToWrite.addAll(parDo.getSideInputs().values());
}
}
private <ElemT, ViewT> void visitView(PCollectionView<ViewT> view) {
- PCollection<ElemT> collectionToMaterialize = (PCollection<ElemT>)
view.getPCollection();
+ PCollection<ElemT> collectionToMaterialize =
+ (PCollection<ElemT>) checkStateNotNull(view.getPCollection());
collectionToMaterialize.apply("GroupAndWriteView", new
GroupAndWriteView<>(view));
}
@@ -95,7 +96,8 @@ class DirectWriteViewVisitor extends PipelineVisitor.Defaults
{
iterable =
input
.apply(
-
MapElements.into(TypeDescriptors.iterables(input.getTypeDescriptor()))
+ MapElements.into(
+
TypeDescriptors.iterables(checkStateNotNull(input.getTypeDescriptor())))
.via(Lists::newArrayList))
.setCoder(IterableCoder.of(input.getCoder()));
} else {
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DisplayDataValidator.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DisplayDataValidator.java
index 69bb98bf8ba..b7a94dc666a 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DisplayDataValidator.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/DisplayDataValidator.java
@@ -22,14 +22,12 @@ import org.apache.beam.sdk.options.PipelineOptions;
import org.apache.beam.sdk.runners.TransformHierarchy;
import org.apache.beam.sdk.transforms.display.DisplayData;
import org.apache.beam.sdk.transforms.display.HasDisplayData;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Validate correct implementation of {@link DisplayData} by evaluating {@link
* HasDisplayData#populateDisplayData(DisplayData.Builder)} during pipeline
construction.
*/
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
class DisplayDataValidator {
// Do not instantiate
private DisplayDataValidator() {}
@@ -46,8 +44,11 @@ class DisplayDataValidator {
pipeline.traverseTopologically(Visitor.INSTANCE);
}
- private static void evaluateDisplayData(HasDisplayData component) {
- DisplayData.from(component);
+ private static void evaluateDisplayData(@Nullable HasDisplayData component) {
+ // A node's transform is null only for the root node, which has no display
data to validate.
+ if (component != null) {
+ DisplayData.from(component);
+ }
}
private static class Visitor extends Pipeline.PipelineVisitor.Defaults {
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/EvaluationContext.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/EvaluationContext.java
index 0fca686bced..5920225d50f 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/EvaluationContext.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/EvaluationContext.java
@@ -17,6 +17,7 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
import static
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkNotNull;
import java.util.Collection;
@@ -51,6 +52,7 @@ import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.Vi
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ImmutableList;
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Iterables;
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.util.concurrent.MoreExecutors;
+import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.joda.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -73,9 +75,8 @@ import org.slf4j.LoggerFactory;
*/
@SuppressWarnings({
"rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "keyfor",
- "nullness"
-}) // TODO(https://github.com/apache/beam/issues/20497)
+ "keyfor"
+})
class EvaluationContext {
private static final Logger LOG =
LoggerFactory.getLogger(EvaluationContext.class);
@@ -97,7 +98,9 @@ class EvaluationContext {
private final ConcurrentMap<StepAndKey, CopyOnAccessInMemoryStateInternals>
applicationStateInternals;
- private final SideInputContainer sideInputContainer;
+ // Set once in create() after construction completes, because
SideInputContainer.create needs a
+ // fully-initialized EvaluationContext (it holds the context to schedule
callbacks later).
+ private @MonotonicNonNull SideInputContainer sideInputContainer;
private final DirectMetrics metrics;
@@ -109,7 +112,11 @@ class EvaluationContext {
DirectGraph graph,
Set<PValue> keyedPValues,
ExecutorService executorService) {
- return new EvaluationContext(clock, bundleFactory, graph, keyedPValues,
executorService);
+ EvaluationContext evaluationContext =
+ new EvaluationContext(clock, bundleFactory, graph, keyedPValues,
executorService);
+ evaluationContext.sideInputContainer =
+ SideInputContainer.create(evaluationContext, graph.getViews());
+ return evaluationContext;
}
private EvaluationContext(
@@ -124,7 +131,6 @@ class EvaluationContext {
this.keyedPValues = keyedPValues;
this.watermarkManager = WatermarkManager.create(clock, graph,
AppliedPTransform::getFullName);
- this.sideInputContainer = SideInputContainer.create(this,
graph.getViews());
this.applicationStateInternals = new ConcurrentHashMap<>();
this.metrics = new DirectMetrics(executorService);
@@ -222,7 +228,9 @@ class EvaluationContext {
Iterable<? extends UncommittedBundle<?>> bundles) {
ImmutableList.Builder<CommittedBundle<?>> completed =
ImmutableList.builder();
for (UncommittedBundle<?> inProgress : bundles) {
- AppliedPTransform<?, ?, ?> producing =
graph.getProducer(inProgress.getPCollection());
+ // Bundles committed here are transform outputs, which always target a
PCollection.
+ AppliedPTransform<?, ?, ?> producing =
+ graph.getProducer(checkStateNotNull(inProgress.getPCollection()));
TransformWatermarks watermarks =
watermarkManager.getWatermarks(producing);
CommittedBundle<?> committed =
inProgress.commit(watermarks.getSynchronizedProcessingOutputTime());
@@ -283,7 +291,7 @@ class EvaluationContext {
*/
public <ElemT, ViewT> PCollectionViewWriter<ElemT, ViewT>
createPCollectionViewWriter(
PCollection<Iterable<ElemT>> input, final PCollectionView<ViewT> output)
{
- return values -> sideInputContainer.write(output, values);
+ return values -> checkStateNotNull(sideInputContainer).write(output,
values);
}
/**
@@ -371,7 +379,7 @@ class EvaluationContext {
*/
public ReadyCheckingSideInputReader createSideInputReader(
final List<PCollectionView<?>> sideInputs) {
- return sideInputContainer.createReaderForViews(sideInputs);
+ return
checkStateNotNull(sideInputContainer).createReaderForViews(sideInputs);
}
/** Returns the metrics container for this pipeline. */
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ExecutorServiceParallelExecutor.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ExecutorServiceParallelExecutor.java
index 603832843bb..dd4df5df353 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ExecutorServiceParallelExecutor.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ExecutorServiceParallelExecutor.java
@@ -60,9 +60,6 @@ import org.slf4j.LoggerFactory;
* An {@link PipelineExecutor} that uses an underlying {@link ExecutorService}
and {@link
* EvaluationContext} to execute a {@link Pipeline}.
*/
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
final class ExecutorServiceParallelExecutor
implements PipelineExecutor,
BundleProcessor<PCollection<?>, CommittedBundle<?>,
AppliedPTransform<?, ?, ?>> {
@@ -122,7 +119,7 @@ final class ExecutorServiceParallelExecutor
CacheBuilder.newBuilder()
.weakValues()
.removalListener(shutdownExecutorServiceListener())
- .build(serialTransformExecutorServiceCacheLoader());
+ .build(serialTransformExecutorServiceCacheLoader(executorService));
this.visibleUpdates = new QueueMessageReceiver();
@@ -130,8 +127,8 @@ final class ExecutorServiceParallelExecutor
executorFactory = new DirectTransformExecutor.Factory(context, registry,
transformEnforcements);
}
- private CacheLoader<StepAndKey, TransformExecutorService>
- serialTransformExecutorServiceCacheLoader() {
+ private static CacheLoader<StepAndKey, TransformExecutorService>
+ serialTransformExecutorServiceCacheLoader(ExecutorService
executorService) {
return new CacheLoader<StepAndKey, TransformExecutorService>() {
@Override
public TransformExecutorService load(StepAndKey stepAndKey) throws
Exception {
@@ -140,7 +137,8 @@ final class ExecutorServiceParallelExecutor
};
}
- private RemovalListener<StepAndKey, TransformExecutorService>
shutdownExecutorServiceListener() {
+ private static RemovalListener<StepAndKey, TransformExecutorService>
+ shutdownExecutorServiceListener() {
return notification -> {
TransformExecutorService service = notification.getValue();
if (service != null) {
@@ -168,10 +166,11 @@ final class ExecutorServiceParallelExecutor
throw UserCodeException.wrap(e);
} finally {
// Metrics emitted initial split are reported along with the first
bundle
- if (pending.peek() != null) {
+ CommittedBundle<?> firstPending = pending.peek();
+ if (firstPending != null) {
evaluationContext
.getMetrics()
- .commitPhysical(pending.peek(),
metricsContainer.getCumulative());
+ .commitPhysical(firstPending, metricsContainer.getCumulative());
}
}
pendingRootBundles.put(root, pending);
@@ -224,7 +223,8 @@ final class ExecutorServiceParallelExecutor
final CompletionCallback onComplete) {
TransformExecutorService transformExecutor;
- if (isKeyed(bundle.getPCollection())) {
+ @Nullable PCollection<T> pcollection = bundle.getPCollection();
+ if (pcollection != null && isKeyed(pcollection)) {
final StepAndKey stepAndKey = StepAndKey.of(transform, bundle.getKey());
// This executor will remain reachable until it has executed all
scheduled transforms.
// The TransformExecutors keep a strong reference to the Executor, the
ExecutorService keeps
@@ -248,7 +248,7 @@ final class ExecutorServiceParallelExecutor
}
@Override
- public State waitUntilFinish(Duration duration) throws Exception {
+ public @Nullable State waitUntilFinish(Duration duration) throws Exception {
Instant completionTime;
if (duration.equals(Duration.ZERO)) {
completionTime = new Instant(Long.MAX_VALUE);
@@ -259,7 +259,7 @@ final class ExecutorServiceParallelExecutor
while (Instant.now().isBefore(completionTime)) {
// Get an update; don't block forever if another thread has handled it.
The call to poll will
// wait the entire timeout; this call primarily exists to relinquish any
core.
- VisibleExecutorUpdate update =
visibleUpdates.tryNext(Duration.millis(25L));
+ @Nullable VisibleExecutorUpdate update =
visibleUpdates.tryNext(Duration.millis(25L));
if (update == null && pipelineState.get().isTerminal()) {
// state and updates have seperate locks so it is possible for an
update
@@ -300,7 +300,8 @@ final class ExecutorServiceParallelExecutor
}
private boolean isTerminalStateUpdate(VisibleExecutorUpdate update) {
- return update.getNewState() != null && update.getNewState().isTerminal();
+ State newState = update.getNewState();
+ return newState != null && newState.isTerminal();
}
@Override
@@ -348,7 +349,7 @@ final class ExecutorServiceParallelExecutor
} catch (final Exception e) {
errors.add(e);
}
- IllegalStateException exception = null;
+ @Nullable IllegalStateException exception = null;
try {
if (!errors.isEmpty()) {
exception =
@@ -393,12 +394,12 @@ final class ExecutorServiceParallelExecutor
return new VisibleExecutorUpdate(State.CANCELLED, null);
}
- private VisibleExecutorUpdate(State newState, @Nullable Throwable
exception) {
+ private VisibleExecutorUpdate(@Nullable State newState, @Nullable
Throwable exception) {
this.thrown = Optional.ofNullable(exception);
this.newState = newState;
}
- State getNewState() {
+ @Nullable State getNewState() {
return newState;
}
}
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/GroupAlsoByWindowEvaluatorFactory.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/GroupAlsoByWindowEvaluatorFactory.java
index c6726fb3463..003fc3d090d 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/GroupAlsoByWindowEvaluatorFactory.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/GroupAlsoByWindowEvaluatorFactory.java
@@ -17,6 +17,8 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;
@@ -57,9 +59,8 @@ import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Iterab
*/
@SuppressWarnings({
"rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "keyfor",
- "nullness"
-}) // TODO(https://github.com/apache/beam/issues/20497)
+ "keyfor"
+})
class GroupAlsoByWindowEvaluatorFactory implements TransformEvaluatorFactory {
private final EvaluationContext evaluationContext;
private final PipelineOptions options;
@@ -145,7 +146,9 @@ class GroupAlsoByWindowEvaluatorFactory implements
TransformEvaluatorFactory {
unprocessedElements = ImmutableList.builder();
Coder<V> valueCoder =
-
application.getTransform().getValueCoder(inputBundle.getPCollection().getCoder());
+ application
+ .getTransform()
+
.getValueCoder(checkStateNotNull(inputBundle.getPCollection()).getCoder());
reduceFn = SystemReduceFn.buffering(valueCoder);
droppedDueToLateness =
Metrics.counter(
@@ -190,8 +193,8 @@ class GroupAlsoByWindowEvaluatorFactory implements
TransformEvaluatorFactory {
@Override
public TransformResult<KeyedWorkItem<K, V>> finishBundle() throws
Exception {
- // State is initialized within the constructor. It can never be null.
- CopyOnAccessInMemoryStateInternals state = stepContext.commitState();
+ // processElement always accesses state internals, so commitState is
non-null here.
+ CopyOnAccessInMemoryStateInternals state =
checkStateNotNull(stepContext.commitState());
return StepTransformResult.<KeyedWorkItem<K, V>>withHold(
application, state.getEarliestWatermarkHold())
.withState(state)
@@ -226,7 +229,7 @@ class GroupAlsoByWindowEvaluatorFactory implements
TransformEvaluatorFactory {
+ "window: {} since it is too far behind
inputWatermark: {}",
DirectGroupAlsoByWindow.class.getSimpleName(),
input.getTimestamp(),
- key,
+ String.valueOf(key),
window,
timerInternals.currentInputWatermarkTime());
}
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/GroupByKeyOnlyEvaluatorFactory.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/GroupByKeyOnlyEvaluatorFactory.java
index 121c6f8bd5d..e73828904cf 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/GroupByKeyOnlyEvaluatorFactory.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/GroupByKeyOnlyEvaluatorFactory.java
@@ -44,8 +44,7 @@ import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Iterab
* PTransform}.
*/
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
class GroupByKeyOnlyEvaluatorFactory implements TransformEvaluatorFactory {
private final EvaluationContext evaluationContext;
@@ -101,13 +100,13 @@ class GroupByKeyOnlyEvaluatorFactory implements
TransformEvaluatorFactory {
this.groupingMap = new HashMap<>();
}
- private Coder<K> getKeyCoder(Coder<KV<K, V>> coder) {
+ private static <K, V> Coder<K> getKeyCoder(Coder<KV<K, V>> coder) {
checkState(
coder instanceof KvCoder,
"%s requires a coder of class %s."
+ " This is an internal error; this is checked during pipeline
construction"
+ " but became corrupted.",
- getClass().getSimpleName(),
+ GroupByKeyOnlyEvaluator.class.getSimpleName(),
KvCoder.class.getSimpleName());
@SuppressWarnings("unchecked")
Coder<K> keyCoder = ((KvCoder<K, V>) coder).getKeyCoder();
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ImmutabilityCheckingBundleFactory.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ImmutabilityCheckingBundleFactory.java
index 370829451d3..1f157975ece 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ImmutabilityCheckingBundleFactory.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ImmutabilityCheckingBundleFactory.java
@@ -17,6 +17,7 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
import static
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkNotNull;
import org.apache.beam.runners.direct.DirectRunner.Enforcement;
@@ -31,6 +32,7 @@ import org.apache.beam.sdk.values.PCollection;
import org.apache.beam.sdk.values.WindowedValue;
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.HashMultimap;
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.SetMultimap;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.joda.time.Instant;
/**
@@ -43,9 +45,6 @@ import org.joda.time.Instant;
* <p>This catches errors during the execution of a {@link DoFn} caused by
modifying an element
* after it is added to an output {@link PCollection}.
*/
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
class ImmutabilityCheckingBundleFactory implements BundleFactory {
/**
* Create a new {@link ImmutabilityCheckingBundleFactory} that uses the
underlying {@link
@@ -100,11 +99,12 @@ class ImmutabilityCheckingBundleFactory implements
BundleFactory {
public ImmutabilityEnforcingBundle(UncommittedBundle<T> underlying) {
this.underlying = underlying;
mutationDetectors = HashMultimap.create();
- coder = getPCollection().getCoder();
+ // Immutability enforcement only wraps non-root bundles, whose
PCollection is always present.
+ coder = checkStateNotNull(underlying.getPCollection()).getCoder();
}
@Override
- public PCollection<T> getPCollection() {
+ public @Nullable PCollection<T> getPCollection() {
return underlying.getPCollection();
}
@@ -130,7 +130,7 @@ class ImmutabilityCheckingBundleFactory implements
BundleFactory {
String.format(
"PTransform %s mutated value %s after it was output (new
value was %s)."
+ " Values must not be mutated in any way after being
output.",
- graph.getProducer(underlying.getPCollection()).getFullName(),
+
graph.getProducer(checkStateNotNull(underlying.getPCollection())).getFullName(),
exn.getSavedValue(),
exn.getNewValue()),
exn.getSavedValue(),
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ImmutabilityEnforcementFactory.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ImmutabilityEnforcementFactory.java
index 84509bded75..ca19051f611 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ImmutabilityEnforcementFactory.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ImmutabilityEnforcementFactory.java
@@ -17,6 +17,8 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
+
import java.util.IdentityHashMap;
import org.apache.beam.sdk.Pipeline.PipelineVisitor;
import org.apache.beam.sdk.coders.Coder;
@@ -36,8 +38,7 @@ import org.apache.beam.sdk.values.WindowedValue;
* element.
*/
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
class ImmutabilityEnforcementFactory implements ModelEnforcementFactory {
public static ModelEnforcementFactory create() {
@@ -112,7 +113,7 @@ class ImmutabilityEnforcementFactory implements
ModelEnforcementFactory {
private ImmutabilityCheckingEnforcement(
CommittedBundle<T> input, AppliedPTransform<?, ?, ?> transform) {
this.transform = transform;
- coder = input.getPCollection().getCoder();
+ coder = checkStateNotNull(input.getPCollection()).getCoder();
mutationElements = new IdentityHashMap<>();
}
@@ -128,7 +129,7 @@ class ImmutabilityEnforcementFactory implements
ModelEnforcementFactory {
@Override
public void afterElement(WindowedValue<T> element) {
- verifyUnmodified(mutationElements.get(element));
+ verifyUnmodified(checkStateNotNull(mutationElements.get(element)));
}
@Override
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ImmutableListBundleFactory.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ImmutableListBundleFactory.java
index 5cfb6fa5937..d9f330ea6f4 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ImmutableListBundleFactory.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ImmutableListBundleFactory.java
@@ -33,9 +33,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.joda.time.Instant;
/** A factory that produces bundles that perform no additional validation. */
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
class ImmutableListBundleFactory implements BundleFactory {
private static final ImmutableListBundleFactory FACTORY = new
ImmutableListBundleFactory();
@@ -63,7 +60,7 @@ class ImmutableListBundleFactory implements BundleFactory {
/** A {@link UncommittedBundle} that buffers elements in memory. */
private static final class UncommittedImmutableListBundle<T> implements
UncommittedBundle<T> {
- private final PCollection<T> pcollection;
+ private final @Nullable PCollection<T> pcollection;
private final StructuralKey<?> key;
private boolean committed = false;
private ImmutableList.Builder<WindowedValue<T>> elements;
@@ -73,18 +70,19 @@ class ImmutableListBundleFactory implements BundleFactory {
* Create a new {@link UncommittedImmutableListBundle} for the specified
{@link PCollection}.
*/
public static <T> UncommittedImmutableListBundle<T> create(
- PCollection<T> pcollection, StructuralKey<?> key) {
+ @Nullable PCollection<T> pcollection, StructuralKey<?> key) {
return new UncommittedImmutableListBundle<>(pcollection, key);
}
- private UncommittedImmutableListBundle(PCollection<T> pcollection,
StructuralKey<?> key) {
+ private UncommittedImmutableListBundle(
+ @Nullable PCollection<T> pcollection, StructuralKey<?> key) {
this.pcollection = pcollection;
this.key = key;
this.elements = ImmutableList.builder();
}
@Override
- public PCollection<T> getPCollection() {
+ public @Nullable PCollection<T> getPCollection() {
return pcollection;
}
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/KeyedPValueTrackingVisitor.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/KeyedPValueTrackingVisitor.java
index 38938b3ff69..353eb29858a 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/KeyedPValueTrackingVisitor.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/KeyedPValueTrackingVisitor.java
@@ -17,6 +17,7 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
import static
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkState;
import java.util.HashSet;
@@ -46,8 +47,7 @@ import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Immuta
// TODO: Handle Key-preserving transforms when appropriate and more
aggressively make PTransforms
// unkeyed
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
class KeyedPValueTrackingVisitor extends PipelineVisitor.Defaults {
@@ -87,7 +87,7 @@ class KeyedPValueTrackingVisitor extends
PipelineVisitor.Defaults {
node);
if (node.isRootNode()) {
finalized = true;
- } else if
(PRODUCES_KEYED_OUTPUTS.contains(node.getTransform().getClass())) {
+ } else if
(PRODUCES_KEYED_OUTPUTS.contains(checkStateNotNull(node.getTransform()).getClass()))
{
Map<TupleTag<?>, PCollection<?>> outputs = node.getOutputs();
for (PValue output : outputs.values()) {
keyedValues.add(output);
@@ -101,8 +101,9 @@ class KeyedPValueTrackingVisitor extends
PipelineVisitor.Defaults {
for (PValue input : producer.getInputs().values()) {
inputsAreKeyed = inputsAreKeyed && keyedValues.contains(input);
}
- if (PRODUCES_KEYED_OUTPUTS.contains(producer.getTransform().getClass())
- || (isKeyPreserving(producer.getTransform()) && inputsAreKeyed)) {
+ PTransform<?, ?> transform = checkStateNotNull(producer.getTransform());
+ if (PRODUCES_KEYED_OUTPUTS.contains(transform.getClass())
+ || (isKeyPreserving(transform) && inputsAreKeyed)) {
keyedValues.add(value);
}
}
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/MultiStepCombine.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/MultiStepCombine.java
index bd8613da611..5ec3ebd9e12 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/MultiStepCombine.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/MultiStepCombine.java
@@ -65,7 +65,11 @@ import org.joda.time.Instant;
/** A {@link Combine} that performs the combine in multiple steps. */
@SuppressWarnings({
"rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ // The "nullness" suppression is retained deliberately: this class is
generic over a nullable
+ // AccumT (e.g. an empty HllCount sketch has a null accumulator), so
accumulator values, keys and
+ // combine inputs are legitimately nullable in normal operation. The checker
cannot express that
+ // invariant here, and forcing @NonNull bounds converts valid nulls into
runtime failures.
+ "nullness"
})
class MultiStepCombine<
K extends @Nullable Object,
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ParDoEvaluator.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ParDoEvaluator.java
index 6124f4a5413..8fb582ab04e 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ParDoEvaluator.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/ParDoEvaluator.java
@@ -17,7 +17,7 @@
*/
package org.apache.beam.runners.direct;
-import static
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
+import static org.apache.beam.sdk.util.Preconditions.checkArgumentNotNull;
import java.util.HashMap;
import java.util.List;
@@ -48,10 +48,10 @@ import org.apache.beam.sdk.values.TupleTag;
import org.apache.beam.sdk.values.WindowedValue;
import org.apache.beam.sdk.values.WindowingStrategy;
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ImmutableList;
+import org.checkerframework.checker.nullness.qual.Nullable;
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
class ParDoEvaluator<InputT> implements TransformEvaluator<InputT> {
@@ -272,7 +272,7 @@ class ParDoEvaluator<InputT> implements
TransformEvaluator<InputT> {
throw UserCodeException.wrap(e);
}
StepTransformResult.Builder<InputT> resultBuilder;
- CopyOnAccessInMemoryStateInternals state = stepContext.commitState();
+ @Nullable CopyOnAccessInMemoryStateInternals state =
stepContext.commitState();
if (state != null) {
resultBuilder =
StepTransformResult.<InputT>withHold(transform,
state.getEarliestWatermarkHold())
@@ -301,8 +301,9 @@ class ParDoEvaluator<InputT> implements
TransformEvaluator<InputT> {
@Override
public <OutputT> void output(TupleTag<OutputT> tag, WindowedValue<OutputT>
output) {
- checkArgument(bundles.containsKey(tag), "Unknown output tag %s", tag);
- bundles.get(tag).add((WindowedValue) output);
+ UncommittedBundle<?> bundle =
+ checkArgumentNotNull(bundles.get(tag), "Unknown output tag %s", tag);
+ bundle.add((WindowedValue) output);
}
}
}
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 22729d91720..c545deb3883 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
@@ -17,6 +17,8 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
+
import java.util.List;
import java.util.Map;
import org.apache.beam.runners.direct.DirectExecutionContext.DirectStepContext;
@@ -39,9 +41,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** A {@link TransformEvaluatorFactory} for {@link ParDo.MultiOutput}. */
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
final class ParDoEvaluatorFactory<InputT, OutputT> implements
TransformEvaluatorFactory {
private static final Logger LOG =
LoggerFactory.getLogger(ParDoEvaluatorFactory.class);
@@ -80,7 +79,7 @@ final class ParDoEvaluatorFactory<InputT, OutputT> implements
TransformEvaluator
(TransformEvaluator<T>)
createEvaluator(
(AppliedPTransform) application,
- (PCollection<InputT>) inputBundle.getPCollection(),
+ (PCollection<InputT>)
checkStateNotNull(inputBundle.getPCollection()),
inputBundle.getKey(),
ParDoTranslation.getSideInputs(application),
(TupleTag<OutputT>)
ParDoTranslation.getMainOutputTag(application),
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/PipelineExecutor.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/PipelineExecutor.java
index e851541b44b..e52385c3057 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/PipelineExecutor.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/PipelineExecutor.java
@@ -21,6 +21,7 @@ import org.apache.beam.sdk.Pipeline;
import org.apache.beam.sdk.PipelineResult.State;
import org.apache.beam.sdk.runners.AppliedPTransform;
import org.apache.beam.sdk.transforms.PTransform;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.joda.time.Duration;
/**
@@ -46,7 +47,7 @@ interface PipelineExecutor {
* @throws Exception whenever an executor thread throws anything, transfers
to the waiting thread
* and rethrows it
*/
- State waitUntilFinish(Duration duration) throws Exception;
+ @Nullable State waitUntilFinish(Duration duration) throws Exception;
/** Gets the current state of the {@link Pipeline}. */
State getPipelineState();
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/QuiescenceDriver.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/QuiescenceDriver.java
index 8915c25bef8..78d5d318081 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/QuiescenceDriver.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/QuiescenceDriver.java
@@ -17,6 +17,8 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
+
import com.google.auto.value.AutoValue;
import java.util.ArrayList;
import java.util.Collection;
@@ -48,9 +50,8 @@ import org.slf4j.LoggerFactory;
*/
@SuppressWarnings({
"rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "keyfor",
- "nullness"
-}) // TODO(https://github.com/apache/beam/issues/20497)
+ "keyfor"
+})
class QuiescenceDriver implements ExecutionDriver {
private static final Logger LOG =
LoggerFactory.getLogger(QuiescenceDriver.class);
@@ -228,7 +229,7 @@ class QuiescenceDriver implements ExecutionDriver {
// Pull all available work off of the queue, then schedule it all,
so this loop
// terminates
while (!pendingRootEntry.getValue().isEmpty()) {
- CommittedBundle<?> bundle = pendingRootEntry.getValue().poll();
+ CommittedBundle<?> bundle =
checkStateNotNull(pendingRootEntry.getValue().poll());
bundles.add(bundle);
}
for (CommittedBundle<?> bundle : bundles) {
@@ -299,7 +300,8 @@ class QuiescenceDriver implements ExecutionDriver {
for (CommittedBundle<?> outputBundle : committedResult.getOutputs()) {
pendingWork.offer(
WorkUpdate.fromBundle(
- outputBundle,
graph.getPerElementConsumers(outputBundle.getPCollection())));
+ outputBundle,
+
graph.getPerElementConsumers(checkStateNotNull(outputBundle.getPCollection()))));
}
Optional<? extends CommittedBundle<?>> unprocessedInputs =
committedResult.getUnprocessedInputs();
@@ -307,7 +309,8 @@ class QuiescenceDriver implements ExecutionDriver {
if (inputBundle.getPCollection() == null) {
// TODO: Split this logic out of an if statement
synchronized (pendingRootBundles) {
-
pendingRootBundles.get(result.getTransform()).offer(unprocessedInputs.get());
+ checkStateNotNull(pendingRootBundles.get(result.getTransform()))
+ .offer(unprocessedInputs.get());
}
} else {
pendingWork.offer(
@@ -319,12 +322,12 @@ class QuiescenceDriver implements ExecutionDriver {
state.set(ExecutorState.ACTIVE);
}
synchronized (inflightBundles) {
- inflightBundles.compute(
- result.getTransform(),
- (k, v) -> {
- v.remove(inputBundle);
- return v.isEmpty() ? null : v;
- });
+ Collection<CommittedBundle<?>> remaining =
+ checkStateNotNull(inflightBundles.get(result.getTransform()));
+ remaining.remove(inputBundle);
+ if (remaining.isEmpty()) {
+ inflightBundles.remove(result.getTransform());
+ }
}
outstandingWork.decrementAndGet();
return committedResult;
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/RootProviderRegistry.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/RootProviderRegistry.java
index 7ac36e2fa8b..527d92a514e 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/RootProviderRegistry.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/RootProviderRegistry.java
@@ -36,8 +36,7 @@ import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Immuta
* based on the type of {@link PTransform} of the application.
*/
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
class RootProviderRegistry {
/** Returns a {@link RootProviderRegistry} that supports the Java SDK root
transforms. */
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/SideInputContainer.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/SideInputContainer.java
index acbce34e0f9..276aa0781af 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/SideInputContainer.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/SideInputContainer.java
@@ -17,6 +17,7 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
import static
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
import java.util.ArrayList;
@@ -57,8 +58,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* and writing to a {@link PCollectionView}.
*/
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
class SideInputContainer {
private static final Set<String> SUPPORTED_MATERIALIZATIONS =
@@ -68,7 +68,7 @@ class SideInputContainer {
private final Collection<PCollectionView<?>> containedViews;
private final LoadingCache<
- PCollectionViewWindow<?>, AtomicReference<Iterable<? extends
WindowedValue<?>>>>
+ PCollectionViewWindow<?>, AtomicReference<@Nullable Iterable<?
extends WindowedValue<?>>>>
viewByWindows;
/** Create a new {@link SideInputContainer} with the provided views and the
provided context. */
@@ -84,14 +84,18 @@ class SideInputContainer {
pCollectionView.getViewFn().getMaterialization().getUrn(),
pCollectionView.getTagInternal().getId());
}
- LoadingCache<PCollectionViewWindow<?>, AtomicReference<Iterable<? extends
WindowedValue<?>>>>
+ LoadingCache<
+ PCollectionViewWindow<?>,
+ AtomicReference<@Nullable Iterable<? extends WindowedValue<?>>>>
viewByWindows = CacheBuilder.newBuilder().build(new
CallbackSchedulingLoader(context));
return new SideInputContainer(containedViews, viewByWindows);
}
private SideInputContainer(
Collection<PCollectionView<?>> containedViews,
- LoadingCache<PCollectionViewWindow<?>, AtomicReference<Iterable<?
extends WindowedValue<?>>>>
+ LoadingCache<
+ PCollectionViewWindow<?>,
+ AtomicReference<@Nullable Iterable<? extends WindowedValue<?>>>>
viewByWindows) {
this.containedViews = ImmutableSet.copyOf(containedViews);
this.viewByWindows = viewByWindows;
@@ -153,7 +157,7 @@ class SideInputContainer {
private void updatePCollectionViewWindowValues(
PCollectionView<?> view, BoundedWindow window,
Collection<WindowedValue<?>> windowValues) {
PCollectionViewWindow<?> windowedView = PCollectionViewWindow.of(view,
window);
- AtomicReference<Iterable<? extends WindowedValue<?>>> contents =
+ AtomicReference<@Nullable Iterable<? extends WindowedValue<?>>> contents =
viewByWindows.getUnchecked(windowedView);
if (contents.compareAndSet(null, windowValues)) {
// the value had never been set, so we set it and are done.
@@ -164,7 +168,7 @@ class SideInputContainer {
Iterable<? extends WindowedValue<?>> existingValues;
long existingPane;
do {
- existingValues = contents.get();
+ existingValues = checkStateNotNull(contents.get());
existingPane =
Iterables.isEmpty(existingValues)
? -1L
@@ -175,7 +179,8 @@ class SideInputContainer {
private static class CallbackSchedulingLoader
extends CacheLoader<
- PCollectionViewWindow<?>, AtomicReference<Iterable<? extends
WindowedValue<?>>>> {
+ PCollectionViewWindow<?>,
+ AtomicReference<@Nullable Iterable<? extends WindowedValue<?>>>> {
private final EvaluationContext context;
public CallbackSchedulingLoader(EvaluationContext context) {
@@ -183,10 +188,11 @@ class SideInputContainer {
}
@Override
- public AtomicReference<Iterable<? extends WindowedValue<?>>> load(
+ public AtomicReference<@Nullable Iterable<? extends WindowedValue<?>>>
load(
PCollectionViewWindow<?> view) {
- AtomicReference<Iterable<? extends WindowedValue<?>>> contents = new
AtomicReference<>();
+ AtomicReference<@Nullable Iterable<? extends WindowedValue<?>>> contents
=
+ new AtomicReference<>();
WindowingStrategy<?, ?> windowingStrategy =
view.getView().getWindowingStrategyInternal();
context.scheduleAfterOutputWouldBeProduced(
@@ -201,12 +207,12 @@ class SideInputContainer {
private static class WriteEmptyViewContents implements Runnable {
private final PCollectionView<?> view;
private final BoundedWindow window;
- private final AtomicReference<Iterable<? extends WindowedValue<?>>>
contents;
+ private final AtomicReference<@Nullable Iterable<? extends
WindowedValue<?>>> contents;
private WriteEmptyViewContents(
PCollectionView<?> view,
BoundedWindow window,
- AtomicReference<Iterable<? extends WindowedValue<?>>> contents) {
+ AtomicReference<@Nullable Iterable<? extends WindowedValue<?>>>
contents) {
this.contents = contents;
this.view = view;
this.window = window;
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/SplittableProcessElementsEvaluatorFactory.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/SplittableProcessElementsEvaluatorFactory.java
index b134e872b65..afd12a4a6cf 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/SplittableProcessElementsEvaluatorFactory.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/SplittableProcessElementsEvaluatorFactory.java
@@ -17,10 +17,12 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
import static
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.atomic.AtomicInteger;
import org.apache.beam.runners.core.DoFnRunners;
import org.apache.beam.runners.core.KeyedWorkItem;
import
org.apache.beam.runners.core.OutputAndTimeBoundedSplittableProcessElementInvoker;
@@ -39,12 +41,11 @@ import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.util.concurren
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.joda.time.Duration;
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
class SplittableProcessElementsEvaluatorFactory<
InputT, OutputT, RestrictionT, PositionT, WatermarkEstimatorStateT>
implements TransformEvaluatorFactory {
+ private static final AtomicInteger FACTORY_INSTANCE_IDS = new
AtomicInteger();
+
private final ParDoEvaluatorFactory<KeyedWorkItem<byte[], KV<InputT,
RestrictionT>>, OutputT>
delegateFactory;
private final ScheduledExecutorService ses;
@@ -82,7 +83,8 @@ class SplittableProcessElementsEvaluatorFactory<
new ThreadFactoryBuilder()
.setThreadFactory(MoreExecutors.platformThreadFactory())
.setNameFormat(
- "direct-splittable-process-element-checkpoint-executor_" +
hashCode())
+ "direct-splittable-process-element-checkpoint-executor_"
+ + FACTORY_INSTANCE_IDS.getAndIncrement())
.build());
}
@@ -120,7 +122,7 @@ class SplittableProcessElementsEvaluatorFactory<
delegateFactory.createEvaluator(
(AppliedPTransform) application,
(PCollection<KeyedWorkItem<byte[], KV<InputT, RestrictionT>>>)
- inputBundle.getPCollection(),
+ checkStateNotNull(inputBundle.getPCollection()),
inputBundle.getKey(),
application.getTransform().getSideInputs(),
application.getTransform().getMainOutputTag(),
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/StatefulParDoEvaluatorFactory.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/StatefulParDoEvaluatorFactory.java
index 8edbd45f2b5..1e8b6ccc411 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/StatefulParDoEvaluatorFactory.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/StatefulParDoEvaluatorFactory.java
@@ -17,6 +17,8 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
+
import com.google.auto.value.AutoValue;
import java.util.Collections;
import java.util.NavigableSet;
@@ -47,8 +49,7 @@ import org.joda.time.Instant;
/** A {@link TransformEvaluatorFactory} for stateful {@link ParDo}. */
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
final class StatefulParDoEvaluatorFactory<K, InputT, OutputT> implements
TransformEvaluatorFactory {
@@ -104,7 +105,7 @@ final class StatefulParDoEvaluatorFactory<K, InputT,
OutputT> implements Transfo
DoFnLifecycleManagerRemovingTransformEvaluator<KV<K, InputT>>
delegateEvaluator =
delegateFactory.createEvaluator(
(AppliedPTransform) application,
- (PCollection) inputBundle.getPCollection(),
+ (PCollection) checkStateNotNull(inputBundle.getPCollection()),
inputBundle.getKey(),
application.getTransform().getSideInputs(),
application.getTransform().getMainOutputTag(),
@@ -176,7 +177,7 @@ final class StatefulParDoEvaluatorFactory<K, InputT,
OutputT> implements Transfo
NavigableSet<TimerData> earlierTimers =
timerInternals.getModifiedTimersOrdered(timerData.getDomain()).headSet(timerData,
true);
while (!earlierTimers.isEmpty()) {
- TimerData insertedTimer = earlierTimers.pollFirst();
+ TimerData insertedTimer =
checkStateNotNull(earlierTimers.pollFirst());
if (timerModified(insertedTimer)) {
continue;
}
@@ -237,17 +238,21 @@ final class StatefulParDoEvaluatorFactory<K, InputT,
OutputT> implements Transfo
clearWatermarkHold(timerData);
}
- CopyOnAccessInMemoryStateInternals state;
+ @Nullable CopyOnAccessInMemoryStateInternals delegateState =
delegateResult.getState();
+ @Nullable CopyOnAccessInMemoryStateInternals state;
Instant watermarkHold;
- if (isTimerDeclared && delegateResult.getState() != null) { // For both
State and Timer Holds
- state = delegateResult.getState();
- watermarkHold = stepContext.commitState().getEarliestWatermarkHold();
+ if (isTimerDeclared && delegateState != null) { // For both State and
Timer Holds
+ state = delegateState;
+ watermarkHold =
checkStateNotNull(stepContext.commitState()).getEarliestWatermarkHold();
} else if (isTimerDeclared) { // For only Timer holds
- state = stepContext.commitState();
- watermarkHold = state.getEarliestWatermarkHold();
+ // A declared timer accessed state via setWatermarkHold, so
commitState is non-null.
+ CopyOnAccessInMemoryStateInternals committedState =
+ checkStateNotNull(stepContext.commitState());
+ state = committedState;
+ watermarkHold = committedState.getEarliestWatermarkHold();
} else { // For only State ( non Timer ) holds
- state = delegateResult.getState();
+ state = delegateState;
watermarkHold = delegateResult.getWatermarkHold();
}
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/StepTransformResult.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/StepTransformResult.java
index 8cfe07bdbb7..c9ec6ec6464 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/StepTransformResult.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/StepTransformResult.java
@@ -33,14 +33,14 @@ import org.apache.beam.sdk.runners.AppliedPTransform;
import org.apache.beam.sdk.transforms.windowing.BoundedWindow;
import org.apache.beam.sdk.values.WindowedValue;
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ImmutableList;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.joda.time.Instant;
/** An immutable {@link TransformResult}. */
@AutoValue
@AutoValue.CopyAnnotations
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
abstract class StepTransformResult<InputT> implements TransformResult<InputT> {
@@ -73,7 +73,7 @@ abstract class StepTransformResult<InputT> implements
TransformResult<InputT> {
private final ImmutableList.Builder<UncommittedBundle<?>> bundlesBuilder;
private final ImmutableList.Builder<WindowedValue<InputT>>
unprocessedElementsBuilder;
private MetricUpdates metricUpdates;
- private CopyOnAccessInMemoryStateInternals state;
+ private @Nullable CopyOnAccessInMemoryStateInternals state;
private TimerUpdate timerUpdate;
private List<Finalization> finalizations;
private final Set<OutputType> producedOutputs;
@@ -85,7 +85,7 @@ abstract class StepTransformResult<InputT> implements
TransformResult<InputT> {
this.bundlesBuilder = ImmutableList.builder();
this.producedOutputs = EnumSet.noneOf(OutputType.class);
this.unprocessedElementsBuilder = ImmutableList.builder();
- this.timerUpdate = TimerUpdate.builder(null).build();
+ this.timerUpdate = TimerUpdate.empty();
this.metricUpdates = MetricUpdates.EMPTY;
this.finalizations = Collections.EMPTY_LIST;
}
@@ -108,7 +108,7 @@ abstract class StepTransformResult<InputT> implements
TransformResult<InputT> {
return this;
}
- public Builder<InputT> withState(CopyOnAccessInMemoryStateInternals state)
{
+ public Builder<InputT> withState(@Nullable
CopyOnAccessInMemoryStateInternals state) {
this.state = state;
return this;
}
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/TestStreamEvaluatorFactory.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/TestStreamEvaluatorFactory.java
index 8ca2a909e36..c1bc06f069c 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/TestStreamEvaluatorFactory.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/TestStreamEvaluatorFactory.java
@@ -52,8 +52,7 @@ import org.joda.time.Instant;
/** The {@link TransformEvaluatorFactory} for the {@link TestStream}
primitive. */
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
class TestStreamEvaluatorFactory implements TransformEvaluatorFactory {
private final EvaluationContext evaluationContext;
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/TransformEvaluatorRegistry.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/TransformEvaluatorRegistry.java
index 544222c8253..e416f85f0d7 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/TransformEvaluatorRegistry.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/TransformEvaluatorRegistry.java
@@ -23,6 +23,7 @@ import static
org.apache.beam.runners.direct.DirectWriteViewVisitor.DIRECT_WRITE
import static
org.apache.beam.runners.direct.MultiStepCombine.DIRECT_MERGE_ACCUMULATORS_EXTRACT_OUTPUT_URN;
import static
org.apache.beam.runners.direct.ParDoMultiOverrideFactory.DIRECT_STATEFUL_PAR_DO_URN;
import static
org.apache.beam.runners.direct.TestStreamEvaluatorFactory.DirectTestStreamFactory.DIRECT_TEST_STREAM_URN;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
import static
org.apache.beam.sdk.util.construction.PTransformTranslation.ASSIGN_WINDOWS_TRANSFORM_URN;
import static
org.apache.beam.sdk.util.construction.PTransformTranslation.CREATE_VIEW_TRANSFORM_URN;
import static
org.apache.beam.sdk.util.construction.PTransformTranslation.FLATTEN_TRANSFORM_URN;
@@ -56,8 +57,7 @@ import org.slf4j.LoggerFactory;
* implementations based on the type of {@link PTransform} of the application.
*/
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
class TransformEvaluatorRegistry {
private static final Logger LOG =
LoggerFactory.getLogger(TransformEvaluatorRegistry.class);
@@ -181,7 +181,7 @@ class TransformEvaluatorRegistry {
toThrow.addSuppressed(e);
}
}
- throw toThrow;
+ throw checkStateNotNull(toThrow);
}
}
}
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/TransformExecutorServices.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/TransformExecutorServices.java
index 1ec5b1c3f17..eab5e753465 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/TransformExecutorServices.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/TransformExecutorServices.java
@@ -24,13 +24,11 @@ import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.MoreObjects;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Static factory methods for constructing instances of {@link
TransformExecutorService}. */
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
final class TransformExecutorServices {
private TransformExecutorServices() {
// Do not instantiate
@@ -115,7 +113,7 @@ final class TransformExecutorServices {
private static class SerialTransformExecutor implements
TransformExecutorService {
private final ExecutorService executor;
- private AtomicReference<TransformExecutor> currentlyEvaluating;
+ private AtomicReference<@Nullable TransformExecutor> currentlyEvaluating;
private final Queue<TransformExecutor> workQueue;
private boolean active = true;
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/UnboundedReadEvaluatorFactory.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/UnboundedReadEvaluatorFactory.java
index c4a4fe86bf9..29b612e0539 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/UnboundedReadEvaluatorFactory.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/UnboundedReadEvaluatorFactory.java
@@ -52,8 +52,7 @@ import org.joda.time.Instant;
* PTransform}.
*/
@SuppressWarnings({
- "rawtypes", // TODO(https://github.com/apache/beam/issues/20447)
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
+ "rawtypes" // TODO(https://github.com/apache/beam/issues/20447)
})
class UnboundedReadEvaluatorFactory implements TransformEvaluatorFactory {
// Occasionally close an existing reader and resume from checkpoint, to
exercise close-and-resume
@@ -239,10 +238,11 @@ class UnboundedReadEvaluatorFactory implements
TransformEvaluatorFactory {
private boolean startReader(
UnboundedReader<OutputT> reader, UnboundedSourceShard<OutputT,
CheckpointMarkT> shard)
throws IOException {
- if (shard.getExistingReader() == null) {
+ UnboundedReader<OutputT> existingReader = shard.getExistingReader();
+ if (existingReader == null) {
return reader.start();
} else {
- return shard.getExistingReader().advance();
+ return existingReader.advance();
}
}
@@ -298,6 +298,7 @@ class UnboundedReadEvaluatorFactory implements
TransformEvaluatorFactory {
return of(source, deduplicator, null, null);
}
+ @SuppressWarnings("nullness") // https://github.com/google/auto/issues/1320
static <T, CheckpointT extends CheckpointMark> UnboundedSourceShard<T,
CheckpointT> of(
UnboundedSource<T, CheckpointT> source,
UnboundedReadDeduplicator deduplicator,
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/WatermarkCallbackExecutor.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/WatermarkCallbackExecutor.java
index 9bfbb67fad2..a66683f3dee 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/WatermarkCallbackExecutor.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/WatermarkCallbackExecutor.java
@@ -17,6 +17,8 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.Serializable;
import java.util.ArrayList;
@@ -51,9 +53,6 @@ import org.joda.time.Instant;
* followed by a call to {@link #fireForWatermark(AppliedPTransform, Instant)}
for the same
* transform with the current value of the watermark.
*/
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
class WatermarkCallbackExecutor {
/** Create a new {@link WatermarkCallbackExecutor}. */
public static WatermarkCallbackExecutor create(Executor executor) {
@@ -84,10 +83,9 @@ class WatermarkCallbackExecutor {
PriorityQueue<WatermarkCallback> callbackQueue = callbacks.get(step);
if (callbackQueue == null) {
- callbackQueue = new PriorityQueue<>(11, new CallbackOrdering());
- if (callbacks.putIfAbsent(step, callbackQueue) != null) {
- callbackQueue = callbacks.get(step);
- }
+ PriorityQueue<WatermarkCallback> newQueue = new PriorityQueue<>(11, new
CallbackOrdering());
+ PriorityQueue<WatermarkCallback> existing = callbacks.putIfAbsent(step,
newQueue);
+ callbackQueue = existing != null ? existing : newQueue;
}
synchronized (callbackQueue) {
@@ -109,10 +107,9 @@ class WatermarkCallbackExecutor {
PriorityQueue<WatermarkCallback> callbackQueue = callbacks.get(step);
if (callbackQueue == null) {
- callbackQueue = new PriorityQueue<>(11, new CallbackOrdering());
- if (callbacks.putIfAbsent(step, callbackQueue) != null) {
- callbackQueue = callbacks.get(step);
- }
+ PriorityQueue<WatermarkCallback> newQueue = new PriorityQueue<>(11, new
CallbackOrdering());
+ PriorityQueue<WatermarkCallback> existing = callbacks.putIfAbsent(step,
newQueue);
+ callbackQueue = existing != null ? existing : newQueue;
}
synchronized (callbackQueue) {
@@ -132,8 +129,9 @@ class WatermarkCallbackExecutor {
}
synchronized (callbackQueue) {
List<Runnable> toFire = new ArrayList<>();
- while (!callbackQueue.isEmpty() &&
callbackQueue.peek().shouldFire(watermark)) {
- toFire.add(callbackQueue.poll().getCallback());
+ while (!callbackQueue.isEmpty()
+ && checkStateNotNull(callbackQueue.peek()).shouldFire(watermark)) {
+ toFire.add(checkStateNotNull(callbackQueue.poll()).getCallback());
}
if (!toFire.isEmpty()) {
CountDownLatch latch = new CountDownLatch(toFire.size());
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/WatermarkManager.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/WatermarkManager.java
index b048b4e82c1..47530da5f6c 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/WatermarkManager.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/WatermarkManager.java
@@ -17,6 +17,7 @@
*/
package org.apache.beam.runners.direct;
+import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
import static
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
import static
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkNotNull;
@@ -66,6 +67,7 @@ import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Queues
import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Sets;
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.SortedMultiset;
import
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.TreeMultiset;
+import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.joda.time.Instant;
@@ -129,10 +131,7 @@ import org.joda.time.Instant;
* Watermark_PCollection = Watermark_Out_ProducingPTransform
* </pre>
*/
-@SuppressWarnings({
- "nullness" // TODO(https://github.com/apache/beam/issues/20497)
-})
-class WatermarkManager<ExecutableT, CollectionT> {
+class WatermarkManager<ExecutableT extends @NonNull Object, CollectionT
extends @NonNull Object> {
// The number of updates to apply in #tryApplyPendingUpdates
private static final int MAX_INCREMENTAL_UPDATES = 10;
@@ -233,11 +232,11 @@ class WatermarkManager<ExecutableT, CollectionT> {
private final SortedMultiset<TimerData> pendingTimers;
// Entries in this table represent the authoritative timestamp for which
- // a per-key-and-StateNamespace timer is set.
- private final Map<StructuralKey<?>, Map<String, TimerData>> existingTimers;
+ // a per-key-and-StateNamespace timer is set. The key is null for a
keyless (empty) TimerUpdate.
+ private final Map<@Nullable StructuralKey<?>, Map<String, TimerData>>
existingTimers;
// This per-key sorted set allows quick retrieval of timers that should
fire for a key
- private final Map<StructuralKey<?>, NavigableSet<TimerData>> objectTimers;
+ private final Map<@Nullable StructuralKey<?>, NavigableSet<TimerData>>
objectTimers;
private final AtomicReference<Instant> currentWatermark;
@@ -299,9 +298,11 @@ class WatermarkManager<ExecutableT, CollectionT> {
minInputWatermark = INSTANT_ORDERING.min(minInputWatermark,
inputWatermark.get());
}
if (!pendingElements.isEmpty()) {
+ // Guarded by the isEmpty() check above, so firstEntry() is always
present.
minInputWatermark =
INSTANT_ORDERING.min(
- minInputWatermark,
pendingElements.firstEntry().getElement().getMinimumTimestamp());
+ minInputWatermark,
+
checkStateNotNull(pendingElements.firstEntry()).getElement().getMinimumTimestamp());
}
Instant newWatermark = INSTANT_ORDERING.max(oldWatermark,
minInputWatermark);
currentWatermark.set(newWatermark);
@@ -326,7 +327,9 @@ class WatermarkManager<ExecutableT, CollectionT> {
}
private Instant getMinimumOutputTimestamp(SortedMultiset<TimerData>
timers) {
- Instant minimumOutputTimestamp =
timers.firstEntry().getElement().getOutputTimestamp();
+ // Only called with a non-empty multiset, so firstEntry() is always
present.
+ Instant minimumOutputTimestamp =
+
checkStateNotNull(timers.firstEntry()).getElement().getOutputTimestamp();
for (TimerData timerData : timers) {
minimumOutputTimestamp =
INSTANT_ORDERING.min(timerData.getOutputTimestamp(),
minimumOutputTimestamp);
@@ -430,7 +433,7 @@ class WatermarkManager<ExecutableT, CollectionT> {
currentWatermark = new
AtomicReference<>(BoundedWindow.TIMESTAMP_MIN_VALUE);
}
- public synchronized void updateHold(Object key, Instant newHold) {
+ public synchronized void updateHold(@Nullable Object key, Instant newHold)
{
if (newHold == null) {
holds.removeHold(key);
} else {
@@ -509,7 +512,8 @@ class WatermarkManager<ExecutableT, CollectionT> {
private final Collection<Bundle<?, ?>> pendingBundles;
private final Map<StructuralKey<?>, NavigableSet<TimerData>>
processingTimers;
private final Map<StructuralKey<?>, NavigableSet<TimerData>>
synchronizedProcessingTimers;
- private final Map<StructuralKey<?>, Map<String, TimerData>> existingTimers;
+ // The key is null for a keyless (empty) TimerUpdate; keyed timers always
carry a non-null key.
+ private final Map<@Nullable StructuralKey<?>, Map<String, TimerData>>
existingTimers;
private final NavigableSet<TimerData> pendingTimers;
@@ -622,10 +626,12 @@ class WatermarkManager<ExecutableT, CollectionT> {
Map<String, TimerData> existingTimersForKey =
existingTimers.computeIfAbsent(update.key, k -> Maps.newHashMap());
+ // A TimerUpdate carrying set or deleted timers was built via
builder(key), so its key is
+ // non-null; only the keyless empty() update has a null key and it
carries no such timers.
HashSet<String> newSetTimers = Sets.newHashSet();
for (TimerData addedTimer : update.setTimers.values()) {
NavigableSet<TimerData> timerQueue =
- processQueueForDomain(update.key, addedTimer.getDomain());
+ processQueueForDomain(checkStateNotNull(update.key),
addedTimer.getDomain());
if (timerQueue == null) {
continue;
}
@@ -643,7 +649,7 @@ class WatermarkManager<ExecutableT, CollectionT> {
for (TimerData deletedTimer : update.deletedTimers.values()) {
NavigableSet<TimerData> timerQueue =
- processQueueForDomain(update.key, deletedTimer.getDomain());
+ processQueueForDomain(checkStateNotNull(update.key),
deletedTimer.getDomain());
if (timerQueue == null) {
continue;
}
@@ -749,7 +755,7 @@ class WatermarkManager<ExecutableT, CollectionT> {
this.latestRefresh = new
AtomicReference<>(BoundedWindow.TIMESTAMP_MIN_VALUE);
}
- public synchronized void updateHold(Object key, Instant newHold) {
+ public synchronized void updateHold(@Nullable Object key, Instant newHold)
{
if (newHold == null) {
holds.removeHold(key);
} else {
@@ -838,15 +844,17 @@ class WatermarkManager<ExecutableT, CollectionT> {
* <p>The result collection retains ordering of timers (from earliest to
latest).
*/
private static synchronized Map<StructuralKey<?>, List<TimerData>>
extractFiredTimers(
- Instant latestTime, Map<StructuralKey<?>, NavigableSet<TimerData>>
objectTimers) {
+ Instant latestTime,
+ Map<? extends @Nullable StructuralKey<?>, NavigableSet<TimerData>>
objectTimers) {
Map<StructuralKey<?>, List<TimerData>> result = new HashMap<>();
- Set<StructuralKey<?>> emptyKeys = new HashSet<>();
- for (Map.Entry<StructuralKey<?>, NavigableSet<TimerData>> pendingTimers :
+ Set<@Nullable StructuralKey<?>> emptyKeys = new HashSet<>();
+ for (Map.Entry<? extends @Nullable StructuralKey<?>,
NavigableSet<TimerData>> pendingTimers :
objectTimers.entrySet()) {
NavigableSet<TimerData> timers = pendingTimers.getValue();
if (!timers.isEmpty() &&
timers.first().getTimestamp().isBefore(latestTime)) {
ArrayList<TimerData> keyFiredTimers = new ArrayList<>();
- result.put(pendingTimers.getKey(), keyFiredTimers);
+ // A non-empty timer set is only ever stored under a non-null key.
+ result.put(checkStateNotNull(pendingTimers.getKey()), keyFiredTimers);
while (!timers.isEmpty() &&
timers.first().getTimestamp().isBefore(latestTime)) {
keyFiredTimers.add(timers.first());
timers.remove(timers.first());
@@ -909,12 +917,17 @@ class WatermarkManager<ExecutableT, CollectionT> {
* @param getName a function for producing a short identifier for the
executable in watermark
* tracing log messages.
*/
- public static <ExecutableT, CollectionT>
- WatermarkManager<ExecutableT, ? super CollectionT> create(
+ public static <ExecutableT extends @NonNull Object, CollectionT extends
@NonNull Object>
+ WatermarkManager<ExecutableT, CollectionT> create(
Clock clock,
- ExecutableGraph<ExecutableT, ? super CollectionT> graph,
+ ExecutableGraph<ExecutableT, CollectionT> graph,
Function<ExecutableT, String> getName) {
- return new WatermarkManager<>(clock, graph, getName);
+ WatermarkManager<ExecutableT, CollectionT> watermarkManager =
+ new WatermarkManager<>(clock, graph, getName);
+ // Populate the per-transform watermarks after construction completes,
because
+ // getTransformWatermark reads instance state and must run on a
fully-initialized receiver.
+ watermarkManager.initializeTransformWatermarks();
+ return watermarkManager;
}
private WatermarkManager(
@@ -931,7 +944,9 @@ class WatermarkManager<ExecutableT, CollectionT> {
this.pendingRefreshes = new HashSet<>();
transformToWatermarks = new HashMap<>();
+ }
+ private void initializeTransformWatermarks() {
for (ExecutableT rootTransform : graph.getRootTransforms()) {
getTransformWatermark(rootTransform);
}
@@ -979,23 +994,19 @@ class WatermarkManager<ExecutableT, CollectionT> {
return wms;
}
- private static <ExecutableT> Consumer<TimerData> timerUpdateConsumer(
+ private static <ExecutableT extends @NonNull Object> Consumer<TimerData>
timerUpdateConsumer(
Map<ExecutableT, Set<String>> transformsWithAlreadyExtractedTimers,
ExecutableT executable) {
return update -> {
String timerIdWithNs = update.stringKey();
synchronized (transformsWithAlreadyExtractedTimers) {
- transformsWithAlreadyExtractedTimers.compute(
- executable,
- (k, v) -> {
- if (v != null) {
- v.remove(timerIdWithNs);
- if (v.isEmpty()) {
- v = null;
- }
- }
- return v;
- });
+ Set<String> extracted =
transformsWithAlreadyExtractedTimers.get(executable);
+ if (extracted != null) {
+ extracted.remove(timerIdWithNs);
+ if (extracted.isEmpty()) {
+ transformsWithAlreadyExtractedTimers.remove(executable);
+ }
+ }
}
};
}
@@ -1037,7 +1048,8 @@ class WatermarkManager<ExecutableT, CollectionT> {
* @return a snapshot of the input watermark and output watermark for the
provided executable
*/
public TransformWatermarks getWatermarks(ExecutableT executable) {
- return transformToWatermarks.get(executable);
+ // Every executable in the graph is registered during construction, so a
watermark is present.
+ return checkStateNotNull(transformToWatermarks.get(executable));
}
public void initialize(
@@ -1046,7 +1058,8 @@ class WatermarkManager<ExecutableT, CollectionT> {
try {
for (Map.Entry<ExecutableT, ? extends Iterable<Bundle<?, CollectionT>>>
rootEntry :
initialBundles.entrySet()) {
- TransformWatermarks rootWms =
transformToWatermarks.get(rootEntry.getKey());
+ TransformWatermarks rootWms =
+ checkStateNotNull(transformToWatermarks.get(rootEntry.getKey()));
for (Bundle<?, ? extends CollectionT> initialBundle :
rootEntry.getValue()) {
rootWms.addPending(initialBundle);
}
@@ -1129,7 +1142,9 @@ class WatermarkManager<ExecutableT, CollectionT> {
private void applyNUpdates(int numUpdates) {
synchronized (pendingUpdates) {
for (int i = 0; !pendingUpdates.isEmpty() && ((i < numUpdates) ||
(numUpdates <= 0)); i++) {
- PendingWatermarkUpdate<ExecutableT, CollectionT> pending =
pendingUpdates.poll();
+ // Guarded by the isEmpty() check in the loop condition, so poll()
returns non-null.
+ PendingWatermarkUpdate<ExecutableT, CollectionT> pending =
+ checkStateNotNull(pendingUpdates.poll());
applyPendingUpdate(pending);
pendingRefreshes.add(pending.getExecutable());
}
@@ -1148,7 +1163,7 @@ class WatermarkManager<ExecutableT, CollectionT> {
pending.getUnprocessedInputs(),
pending.getOutputs());
- TransformWatermarks transformWms = transformToWatermarks.get(executable);
+ TransformWatermarks transformWms =
checkStateNotNull(transformToWatermarks.get(executable));
transformWms.setEventTimeHold(
inputBundle == null ? null : inputBundle.getKey(),
pending.getEarliestHold());
@@ -1172,7 +1187,7 @@ class WatermarkManager<ExecutableT, CollectionT> {
* information about the parameters of this method.
*/
private void updatePending(
- Bundle<?, ? extends CollectionT> input,
+ @Nullable Bundle<?, ? extends CollectionT> input,
TimerUpdate timerUpdate,
ExecutableT executable,
@Nullable Bundle<?, ? extends CollectionT> unprocessedInputs,
@@ -1181,13 +1196,15 @@ class WatermarkManager<ExecutableT, CollectionT> {
// do not share a Mutex within this call and thus can be interleaved with
external calls to
// refresh.
for (Bundle<?, ? extends CollectionT> bundle : outputs) {
- for (ExecutableT consumer :
graph.getPerElementConsumers(bundle.getPCollection())) {
- TransformWatermarks watermarks = transformToWatermarks.get(consumer);
+ for (ExecutableT consumer :
+
graph.getPerElementConsumers(checkStateNotNull(bundle.getPCollection()))) {
+ TransformWatermarks watermarks =
checkStateNotNull(transformToWatermarks.get(consumer));
watermarks.addPending(bundle);
}
}
- TransformWatermarks completedTransform =
transformToWatermarks.get(executable);
+ TransformWatermarks completedTransform =
+ checkStateNotNull(transformToWatermarks.get(executable));
if (unprocessedInputs != null) {
// Add the unprocessed inputs
completedTransform.addPending(unprocessedInputs);
@@ -1225,7 +1242,7 @@ class WatermarkManager<ExecutableT, CollectionT> {
}
private Set<ExecutableT> refreshWatermarks(final ExecutableT toRefresh) {
- TransformWatermarks myWatermarks = transformToWatermarks.get(toRefresh);
+ TransformWatermarks myWatermarks =
checkStateNotNull(transformToWatermarks.get(toRefresh));
WatermarkUpdate updateResult = myWatermarks.refresh();
if (updateResult.isAdvanced()) {
Set<ExecutableT> additionalRefreshes = new HashSet<>();
@@ -1296,17 +1313,17 @@ class WatermarkManager<ExecutableT, CollectionT> {
* as the key is arbitrarily ordered via identity, rather than object
equality.
*/
private static final class KeyedHold implements Comparable<KeyedHold> {
- private static final Ordering<Object> KEY_ORDERING =
Ordering.arbitrary().nullsLast();
+ private static final Ordering<@Nullable Object> KEY_ORDERING =
Ordering.arbitrary().nullsLast();
- private final Object key;
+ private final @Nullable Object key;
private final Instant timestamp;
/** Create a new KeyedHold with the specified key and timestamp. */
- public static KeyedHold of(Object key, Instant timestamp) {
+ public static KeyedHold of(@Nullable Object key, Instant timestamp) {
return new KeyedHold(key, MoreObjects.firstNonNull(timestamp,
THE_END_OF_TIME.get()));
}
- private KeyedHold(Object key, Instant timestamp) {
+ private KeyedHold(@Nullable Object key, Instant timestamp) {
this.key = key;
this.timestamp = timestamp;
}
@@ -1349,6 +1366,9 @@ class WatermarkManager<ExecutableT, CollectionT> {
private static class PerKeyHolds {
private final Map<Object, KeyedHold> keyedHolds;
+ // The single hold for a keyless (null-key) update, tracked separately so
keyedHolds never needs
+ // a null map key.
+ private @Nullable KeyedHold keylessHold;
private final NavigableSet<KeyedHold> allHolds;
private PerKeyHolds() {
@@ -1371,13 +1391,23 @@ class WatermarkManager<ExecutableT, CollectionT> {
public void updateHold(@Nullable Object key, Instant newHold) {
removeHold(key);
KeyedHold newKeyedHold = KeyedHold.of(key, newHold);
- keyedHolds.put(key, newKeyedHold);
+ if (key == null) {
+ keylessHold = newKeyedHold;
+ } else {
+ keyedHolds.put(key, newKeyedHold);
+ }
allHolds.add(newKeyedHold);
}
/** Removes the hold of the provided key. */
- public void removeHold(Object key) {
- KeyedHold oldHold = keyedHolds.remove(key);
+ public void removeHold(@Nullable Object key) {
+ KeyedHold oldHold;
+ if (key == null) {
+ oldHold = keylessHold;
+ keylessHold = null;
+ } else {
+ oldHold = keyedHolds.remove(key);
+ }
if (oldHold != null) {
allHolds.remove(oldHold);
}
@@ -1459,11 +1489,11 @@ class WatermarkManager<ExecutableT, CollectionT> {
return eventOutputUpdate.union(syncOutputUpdate);
}
- private void setEventTimeHold(Object key, Instant newHold) {
+ private void setEventTimeHold(@Nullable Object key, Instant newHold) {
outputWatermark.updateHold(key, newHold);
}
- private void setSynchronizedProcessingTimeHold(Object key, Instant
newHold) {
+ private void setSynchronizedProcessingTimeHold(@Nullable Object key,
Instant newHold) {
synchronizedProcessingOutputWatermark.updateHold(key, newHold);
}
@@ -1556,7 +1586,7 @@ class WatermarkManager<ExecutableT, CollectionT> {
* the input to the executed step.
*/
public static class TimerUpdate {
- private final StructuralKey<?> key;
+ private final @Nullable StructuralKey<?> key;
private final Iterable<? extends TimerData> completedTimers;
private final Map<TimerKey, ? extends TimerData> setTimers;
private final Map<TimerKey, ? extends TimerData> deletedTimers;
@@ -1640,7 +1670,7 @@ class WatermarkManager<ExecutableT, CollectionT> {
}
private TimerUpdate(
- StructuralKey<?> key,
+ @Nullable StructuralKey<?> key,
Iterable<? extends TimerData> completedTimers,
Map<TimerKey, ? extends TimerData> setTimers,
Map<TimerKey, ? extends TimerData> deletedTimers) {
@@ -1651,7 +1681,7 @@ class WatermarkManager<ExecutableT, CollectionT> {
}
@VisibleForTesting
- StructuralKey<?> getKey() {
+ @Nullable StructuralKey<?> getKey() {
return key;
}
diff --git
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/WindowEvaluatorFactory.java
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/WindowEvaluatorFactory.java
index 2724312c99a..b032fe5bbf4 100644
---
a/runners/direct-java/src/main/java/org/apache/beam/runners/direct/WindowEvaluatorFactory.java
+++
b/runners/direct-java/src/main/java/org/apache/beam/runners/direct/WindowEvaluatorFactory.java
@@ -35,7 +35,7 @@ import org.joda.time.Instant;
* The {@link DirectRunner} {@link TransformEvaluatorFactory} for the {@link
Window.Assign}
* primitive {@link PTransform}.
*/
-@SuppressWarnings({"keyfor", "nullness"}) //
TODO(https://github.com/apache/beam/issues/20497)
+@SuppressWarnings("keyfor")
class WindowEvaluatorFactory implements TransformEvaluatorFactory {
private final EvaluationContext evaluationContext;