Java Code Examples for org.apache.beam.sdk.util.WindowedValue#getValue()

The following examples show how to use org.apache.beam.sdk.util.WindowedValue#getValue() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: PushBackDoFnTransform.java    From incubator-nemo with Apache License 2.0 6 votes vote down vote up
@Override
public void onData(final WindowedValue data) {
  // Need to distinguish side/main inputs and push-back main inputs.
  if (data.getValue() instanceof SideInputElement) {
    // This element is a Side Input
    // TODO #287: Consider Explicit Multi-Input IR Transform
    final WindowedValue<SideInputElement> sideInputElement = (WindowedValue<SideInputElement>) data;
    final PCollectionView view = getSideInputs().get(sideInputElement.getValue().getSideInputIndex());
    getSideInputReader().addSideInputElement(view, data);

    handlePushBacks();

    // See if we can emit a new watermark, as we may have processed some pushed-back elements
    onWatermark(new Watermark(curInputWatermark));
  } else {
    // This element is the Main Input
    checkAndInvokeBundle();
    final Iterable<WindowedValue<InputT>> pushedBack =
      getPushBackRunner().processElementInReadyWindows(data);
    for (final WindowedValue wv : pushedBack) {
      curPushedBackWatermark = Math.min(curPushedBackWatermark, wv.getTimestamp().getMillis());
      curPushedBacks.add(wv);
    }
    checkAndFinishBundle();
  }
}
 
Example 2
Source File: ImmutabilityEnforcementFactoryTest.java    From beam with Apache License 2.0 6 votes vote down vote up
@Test
public void mutatedDuringProcessElementThrows() {
  WindowedValue<byte[]> element = WindowedValue.valueInGlobalWindow("bar".getBytes(UTF_8));
  CommittedBundle<byte[]> elements =
      bundleFactory.createBundle(pcollection).add(element).commit(Instant.now());

  ModelEnforcement<byte[]> enforcement = factory.forBundle(elements, consumer);
  enforcement.beforeElement(element);
  element.getValue()[0] = 'f';
  thrown.expect(IllegalMutationException.class);
  thrown.expectMessage(consumer.getFullName());
  thrown.expectMessage("illegaly mutated");
  thrown.expectMessage("Input values must not be mutated");
  enforcement.afterElement(element);
  enforcement.afterFinish(
      elements,
      StepTransformResult.<byte[]>withoutHold(consumer).build(),
      Collections.emptyList());
}
 
Example 3
Source File: GroupByKeyAndWindowDoFnTransform.java    From incubator-nemo with Apache License 2.0 6 votes vote down vote up
/**
 * It collects data for each key.
 * The collected data are emitted at {@link GroupByKeyAndWindowDoFnTransform#onWatermark(Watermark)}
 *
 * @param element data element
 */
@Override
public void onData(final WindowedValue<KV<K, InputT>> element) {
  checkAndInvokeBundle();
  dataReceived = true;

  // We can call Beam's DoFnRunner#processElement here,
  // but it may generate some overheads if we call the method for each data.
  // The `processElement` requires a `Iterator` of data, so we emit the buffered data every watermark.
  // TODO #250: But, this approach can delay the event processing in streaming,
  // TODO #250: if the watermark is not triggered for a long time.
  final KV<K, InputT> kv = element.getValue();
  keyToValues.putIfAbsent(kv.getKey(), new ArrayList<>());
  keyToValues.get(kv.getKey()).add(element.withValue(kv.getValue()));

  checkAndFinishBundle();
}
 
Example 4
Source File: AssignWindowsRunner.java    From beam with Apache License 2.0 6 votes vote down vote up
WindowedValue<T> assignWindows(WindowedValue<T> input) throws Exception {
  // TODO: BEAM-4272 consider allocating only once and updating the current value per call.
  WindowFn<T, W>.AssignContext ctxt =
      windowFn.new AssignContext() {
        @Override
        public T element() {
          return input.getValue();
        }

        @Override
        public Instant timestamp() {
          return input.getTimestamp();
        }

        @Override
        public BoundedWindow window() {
          return Iterables.getOnlyElement(input.getWindows());
        }
      };
  Collection<W> windows = windowFn.assignWindows(ctxt);
  return WindowedValue.of(input.getValue(), input.getTimestamp(), windows, input.getPane());
}
 
Example 5
Source File: FnApiWindowMappingFnTest.java    From beam with Apache License 2.0 6 votes vote down vote up
@Override
public <T> CloseableFnDataReceiver<T> send(LogicalEndpoint outputLocation, Coder<T> coder) {
  return new CloseableFnDataReceiver<T>() {
    @Override
    public void accept(T value) throws Exception {
      WindowedValue<KV<Object, Object>> windowedValue =
          (WindowedValue<KV<Object, Object>>) value;
      inputValues.add(windowedValue);
      KV<Object, Object> kv = windowedValue.getValue();
      inboundReceiver.accept(windowedValue.withValue(KV.of(kv.getKey(), outputValue)));
      inboundDataClient.complete();
    }

    @Override
    public void flush() throws Exception {}

    @Override
    public void close() throws Exception {}
  };
}
 
Example 6
Source File: ImmutabilityEnforcementFactoryTest.java    From beam with Apache License 2.0 6 votes vote down vote up
@Test
public void mutatedAfterProcessElementFails() {

  WindowedValue<byte[]> element = WindowedValue.valueInGlobalWindow("bar".getBytes(UTF_8));
  CommittedBundle<byte[]> elements =
      bundleFactory.createBundle(pcollection).add(element).commit(Instant.now());

  ModelEnforcement<byte[]> enforcement = factory.forBundle(elements, consumer);
  enforcement.beforeElement(element);
  enforcement.afterElement(element);

  element.getValue()[0] = 'f';
  thrown.expect(IllegalMutationException.class);
  thrown.expectMessage(consumer.getFullName());
  thrown.expectMessage("illegaly mutated");
  thrown.expectMessage("Input values must not be mutated");
  enforcement.afterFinish(
      elements,
      StepTransformResult.<byte[]>withoutHold(consumer).build(),
      Collections.emptyList());
}
 
Example 7
Source File: BatchModeUngroupingParDoFn.java    From beam with Apache License 2.0 6 votes vote down vote up
@Override
@SuppressWarnings("unchecked")
public void processElement(Object untypedElem) throws Exception {
  WindowedValue<?> windowedValue = (WindowedValue<?>) untypedElem;

  KV<K, Iterable<KV<Instant, WindowedValue<V>>>> gbkElem =
      (KV<K, Iterable<KV<Instant, WindowedValue<V>>>>) windowedValue.getValue();

  // Each GBK output is the beginning of a key
  stepContext.setKey(gbkElem.getKey());

  for (KV<Instant, WindowedValue<V>> timestampedElem : gbkElem.getValue()) {
    underlyingParDoFn.processElement(timestampedElem.getValue());
  }

  // Process all the timers for the key, since the watermark is moved to infinity
  underlyingParDoFn.processTimers();
}
 
Example 8
Source File: GroupByKeyOnlyEvaluatorFactory.java    From beam with Apache License 2.0 5 votes vote down vote up
@Override
public void processElement(WindowedValue<KV<K, V>> element) {
  KV<K, V> kv = element.getValue();
  K key = kv.getKey();
  StructuralKey<K> groupingKey = StructuralKey.of(key, keyCoder);
  List<WindowedValue<V>> values =
      groupingMap.computeIfAbsent(groupingKey, k -> new ArrayList<>());
  values.add(element.withValue(kv.getValue()));
}
 
Example 9
Source File: TestStreamEvaluatorFactory.java    From beam with Apache License 2.0 5 votes vote down vote up
@Override
public void processElement(WindowedValue<TestStreamIndex<T>> element) throws Exception {
  TestStreamIndex<T> streamIndex = element.getValue();
  List<Event<T>> events = streamIndex.getTestStream().getEvents();
  int index = streamIndex.getIndex();
  Instant watermark = element.getTimestamp();
  Event<T> event = events.get(index);

  if (event.getType().equals(EventType.ELEMENT)) {
    UncommittedBundle<T> bundle =
        context.createBundle(
            (PCollection<T>) Iterables.getOnlyElement(application.getOutputs().values()));
    for (TimestampedValue<T> elem : ((ElementEvent<T>) event).getElements()) {
      bundle.add(
          WindowedValue.timestampedValueInGlobalWindow(elem.getValue(), elem.getTimestamp()));
    }
    resultBuilder.addOutput(bundle);
  }

  if (event.getType().equals(EventType.WATERMARK)) {
    watermark = ((WatermarkEvent<T>) event).getWatermark();
  }

  if (event.getType().equals(EventType.PROCESSING_TIME)) {
    ((TestClock) context.getClock())
        .advance(((ProcessingTimeEvent<T>) event).getProcessingTimeAdvance());
  }

  TestStreamIndex<T> next = streamIndex.next();
  if (next.getIndex() < events.size()) {
    resultBuilder.addUnprocessedElements(
        Collections.singleton(WindowedValue.timestampedValueInGlobalWindow(next, watermark)));
  }
}
 
Example 10
Source File: BatchViewOverrides.java    From beam with Apache License 2.0 5 votes vote down vote up
@SuppressFBWarnings(
    value = "NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION",
    justification = "https://github.com/google/guava/issues/920")
@Override
public V apply(@Nonnull WindowedValue<V> input) {
  return input.getValue();
}
 
Example 11
Source File: UnboundedReadEvaluatorFactoryTest.java    From beam with Apache License 2.0 5 votes vote down vote up
@Test
public void unboundedSourceInMemoryTransformEvaluatorProducesElements() throws Exception {
  when(context.createRootBundle()).thenReturn(bundleFactory.createRootBundle());

  Collection<CommittedBundle<?>> initialInputs =
      new UnboundedReadEvaluatorFactory.InputProvider(context, options)
          .getInitialInputs(graph.getProducer(longs), 1);

  CommittedBundle<?> inputShards = Iterables.getOnlyElement(initialInputs);
  UnboundedSourceShard<Long, ?> inputShard =
      (UnboundedSourceShard<Long, ?>)
          Iterables.getOnlyElement(inputShards.getElements()).getValue();
  TransformEvaluator<? super UnboundedSourceShard<Long, ?>> evaluator =
      factory.forApplication(graph.getProducer(longs), inputShards);

  evaluator.processElement((WindowedValue) Iterables.getOnlyElement(inputShards.getElements()));
  TransformResult<? super UnboundedSourceShard<Long, ?>> result = evaluator.finishBundle();

  WindowedValue<? super UnboundedSourceShard<Long, ?>> residual =
      Iterables.getOnlyElement(result.getUnprocessedElements());
  assertThat(residual.getTimestamp(), Matchers.lessThan(DateTime.now().toInstant()));
  UnboundedSourceShard<Long, ?> residualShard =
      (UnboundedSourceShard<Long, ?>) residual.getValue();
  assertThat(residualShard.getSource(), equalTo(inputShard.getSource()));
  assertThat(residualShard.getCheckpoint(), not(nullValue()));
  assertThat(
      output.commit(Instant.now()).getElements(),
      containsInAnyOrder(
          tgw(1L), tgw(2L), tgw(4L), tgw(8L), tgw(9L), tgw(7L), tgw(6L), tgw(5L), tgw(3L),
          tgw(0L)));
}
 
Example 12
Source File: KvToKeyedWorkItemOp.java    From beam with Apache License 2.0 5 votes vote down vote up
@Override
public void processElement(
    WindowedValue<KV<K, V>> inputElement, OpEmitter<KeyedWorkItem<K, V>> emitter) {
  final KV<K, V> kv = inputElement.getValue();
  for (WindowedValue<KV<K, V>> windowedValue : inputElement.explodeWindows()) {
    final KeyedWorkItem<K, V> workItem =
        new SingletonKeyedWorkItem<>(kv.getKey(), windowedValue.withValue(kv.getValue()));
    emitter.emitElement(windowedValue.withValue(workItem));
  }
}
 
Example 13
Source File: SparkCombineFn.java    From beam with Apache License 2.0 5 votes vote down vote up
private WindowFn<Object, BoundedWindow>.MergeContext asMergeContext(
    WindowFn<Object, BoundedWindow> windowFn,
    BiFunction<AccumT, AccumT, AccumT> mergeFn,
    BiConsumer<Collection<BoundedWindow>, KV<BoundedWindow, AccumT>> afterMerge,
    Map<BoundedWindow, WindowedValue<AccumT>> map) {

  return windowFn.new MergeContext() {

    @Override
    public Collection<BoundedWindow> windows() {
      return map.keySet();
    }

    @Override
    public void merge(Collection<BoundedWindow> toBeMerged, BoundedWindow mergeResult) {
      AccumT accumulator = null;
      for (BoundedWindow w : toBeMerged) {
        WindowedValue<AccumT> windowAccumulator = Objects.requireNonNull(map.get(w));
        if (accumulator == null) {
          accumulator = windowAccumulator.getValue();
        } else {
          accumulator = mergeFn.apply(accumulator, windowAccumulator.getValue());
        }
      }
      afterMerge.accept(toBeMerged, KV.of(mergeResult, accumulator));
    }
  };
}
 
Example 14
Source File: SparkCombineFn.java    From beam with Apache License 2.0 5 votes vote down vote up
SingleWindowWindowedAccumulator(
    Function<InputT, ValueT> toValue, WindowedValue<AccumT> accumulator) {
  this.toValue = toValue;
  this.windowAccumulator = accumulator.getValue();
  this.accTimestamp =
      accumulator.getTimestamp().equals(BoundedWindow.TIMESTAMP_MIN_VALUE)
          ? null
          : accumulator.getTimestamp();
  this.accWindow = getWindow(accumulator);
}
 
Example 15
Source File: SparkAssignWindowFn.java    From beam with Apache License 2.0 5 votes vote down vote up
@Override
@SuppressWarnings("unchecked")
public WindowedValue<T> call(WindowedValue<T> windowedValue) throws Exception {
  final BoundedWindow boundedWindow = Iterables.getOnlyElement(windowedValue.getWindows());
  final T element = windowedValue.getValue();
  final Instant timestamp = windowedValue.getTimestamp();
  Collection<W> windows =
      ((WindowFn<T, W>) fn)
          .assignWindows(
              ((WindowFn<T, W>) fn).new AssignContext() {
                @Override
                public T element() {
                  return element;
                }

                @Override
                public Instant timestamp() {
                  return timestamp;
                }

                @Override
                public BoundedWindow window() {
                  return boundedWindow;
                }
              });
  return WindowedValue.of(element, timestamp, windows, PaneInfo.NO_FIRING);
}
 
Example 16
Source File: StatefulParDoP.java    From beam with Apache License 2.0 5 votes vote down vote up
@Override
protected void processElementWithRunner(
    DoFnRunner<KV<?, ?>, OutputT> runner, WindowedValue<KV<?, ?>> windowedValue) {
  KV<?, ?> kv = windowedValue.getValue();
  Object key = kv.getKey();
  keyedStepContext.setKey(key);

  super.processElementWithRunner(runner, windowedValue);
}
 
Example 17
Source File: WindowFnTransform.java    From incubator-nemo with Apache License 2.0 5 votes vote down vote up
@Override
public void onData(final WindowedValue<T> windowedValue) {
  final BoundedWindow boundedWindow = Iterables.getOnlyElement(windowedValue.getWindows());
  final T element = windowedValue.getValue();
  final Instant timestamp = windowedValue.getTimestamp();

  try {
    final Collection<W> windows =
      ((WindowFn<T, W>) windowFn)
        .assignWindows(
          ((WindowFn<T, W>) windowFn).new AssignContext() {
            @Override
            public T element() {
              return element;
            }

            @Override
            public Instant timestamp() {
              return timestamp;
            }

            @Override
            public BoundedWindow window() {
              return boundedWindow;
            }
          });

    // Emit compressed windows for efficiency
    outputCollector.emit(WindowedValue.of(element, timestamp, windows, PaneInfo.NO_FIRING));
  } catch (final Exception e) {
    throw new RuntimeException(e);
  }
}
 
Example 18
Source File: ShuffleSink.java    From beam with Apache License 2.0 4 votes vote down vote up
@Override
public long add(WindowedValue<T> windowedElem) throws IOException {
  T elem = windowedElem.getValue();
  long bytes = 0;
  if (shardByKey) {
    if (!(elem instanceof KV)) {
      throw new AssertionError(
          "expecting the values written to a key-grouping shuffle " + "to be KVs");
    }
    KV<?, ?> kv = (KV) elem;
    Object key = kv.getKey();
    Object value = kv.getValue();

    bytes += encodeToChunk(keyCoder, key);

    if (sortValues) {
      if (!(value instanceof KV)) {
        throw new AssertionError(
            "expecting the value parts of the KVs written to "
                + "a value-sorting shuffle to also be KVs");
      }
      KV<?, ?> kvValue = (KV) value;
      Object sortKey = kvValue.getKey();
      Object sortValue = kvValue.getValue();

      // Sort values by key and then timestamp so that any GroupAlsoByWindows
      // can run more efficiently. We produce an order preserving encoding of this composite
      // key by concatenating the nested encoding of sortKey and outer encoding of the
      // timestamp. An alternative implementation would be to use OrderedCode but it
      // is unnecessary here because the nested encoding of sortKey achieves the same effect,
      // due to the nested encoding being a https://en.wikipedia.org/wiki/Prefix_code.
      // Move forward enough bytes so we can prefix the size on after performing the write
      int initialChunkSize = chunk.size();
      chunk.resetTo(initialChunkSize + Ints.BYTES);
      sortKeyCoder.encode(sortKey, chunk.asOutputStream());

      if (!windowedElem.getTimestamp().equals(BoundedWindow.TIMESTAMP_MIN_VALUE)) {
        // Empty timestamp suffixes sort before all other sort value keys with
        // the same prefix. So We can omit this suffix for this common value here
        // for efficiency and only encode when its not the minimum timestamp.
        InstantCoder.of()
            .encode(windowedElem.getTimestamp(), chunk.asOutputStream(), Context.OUTER);
      }

      int elementSize = chunk.size() - initialChunkSize - Ints.BYTES;
      byte[] internalBytes = chunk.array();
      internalBytes[initialChunkSize] = (byte) ((elementSize >>> 24) & 0xFF);
      internalBytes[initialChunkSize + 1] = (byte) ((elementSize >>> 16) & 0xFF);
      internalBytes[initialChunkSize + 2] = (byte) ((elementSize >>> 8) & 0xFF);
      internalBytes[initialChunkSize + 3] = (byte) ((elementSize >>> 0) & 0xFF);
      bytes += elementSize;

      bytes += encodeToChunk(sortValueCoder, sortValue);
    } else if (groupValues) {
      // Sort values by timestamp so that GroupAlsoByWindows can run efficiently.
      if (windowedElem.getTimestamp().equals(BoundedWindow.TIMESTAMP_MIN_VALUE)) {
        // Empty secondary keys sort before all other secondary keys, so we
        // can omit this common value here for efficiency.
        chunk.asOutputStream().write(NULL_VALUE_WITH_SIZE_PREFIX);
      } else {
        bytes += encodeToChunk(InstantCoder.of(), windowedElem.getTimestamp());
      }
      bytes += encodeToChunk(valueCoder, value);
    } else {
      chunk.asOutputStream().write(NULL_VALUE_WITH_SIZE_PREFIX);
      bytes += encodeToChunk(windowedValueCoder, windowedElem.withValue(value));
    }

  } else {
    // Not partitioning or grouping by key, just resharding values.
    // <key> is ignored, except by the shuffle splitter.  Use a seq#
    // as the key, so we can split records anywhere.  This also works
    // for writing a single-sharded ordered PCollection through a
    // shuffle, since the order of elements in the input will be
    // preserved in the output.
    bytes += encodeToChunk(BigEndianLongCoder.of(), seqNum++);
    chunk.asOutputStream().write(NULL_VALUE_WITH_SIZE_PREFIX);
    bytes += encodeToChunk(windowedElemCoder, windowedElem);
  }

  if (chunk.size() > MIN_OUTPUT_CHUNK_SIZE) {
    try (Closeable trackedWriteState = tracker.enterState(writeState)) {
      outputChunk();
    }
  }

  perDatasetBytesCounter.addValue(bytes);
  return bytes;
}
 
Example 19
Source File: IsmSink.java    From beam with Apache License 2.0 4 votes vote down vote up
@Override
public long add(WindowedValue<IsmRecord<V>> windowedRecord) throws IOException {
  // The windowed portion of the value is ignored.
  IsmRecord<V> record = windowedRecord.getValue();

  checkArgument(coder.getKeyComponentCoders().size() == record.getKeyComponents().size());

  List<Integer> keyOffsetPositions = new ArrayList<>();
  final int currentShard =
      coder.encodeAndHash(record.getKeyComponents(), currentKeyBytes, keyOffsetPositions);
  // Put each component of the key into the Bloom filter so that we can use the Bloom
  // filter for key prefix checks.
  for (Integer offsetPosition : keyOffsetPositions) {
    bloomFilterBuilder.put(currentKeyBytes.array(), 0, offsetPosition);
  }

  // If we are moving to another shard, finish outputting the last shard.
  if (previousShard.isPresent() && currentShard != previousShard.get()) {
    // We reset last shard to be empty.
    finishShard();
  }

  long positionOfCurrentKey = out.getCount();

  // If we are outputting our first key for this shard, then we can assume 0 bytes are saved
  // from the previous key.
  int sharedKeySize;
  if (!previousShard.isPresent()) {
    sharedKeySize = 0;
    // Create a new shard record for the current value being output validating
    // that we have never seen this shard before.
    IsmShard ismShard = IsmShard.of(currentShard, positionOfCurrentKey);
    checkState(
        shardKeyToShardMap.put(currentShard, ismShard) == null,
        "Unexpected insertion of keys %s for shard which already exists %s. "
            + "Ism files expect that all shards are written contiguously.",
        record.getKeyComponents(),
        ismShard);
  } else {
    sharedKeySize = commonPrefixLengthWithOrderCheck(previousKeyBytes, currentKeyBytes);
  }

  // Put key-value mapping record into block buffer
  int unsharedKeySize = currentKeyBytes.size() - sharedKeySize;
  KeyPrefix keyPrefix = KeyPrefix.of(sharedKeySize, unsharedKeySize);
  KeyPrefixCoder.of().encode(keyPrefix, out);
  currentKeyBytes.writeTo(out, sharedKeySize, unsharedKeySize);
  if (IsmFormat.isMetadataKey(record.getKeyComponents())) {
    ByteArrayCoder.of().encode(record.getMetadata(), out);
  } else {
    coder.getValueCoder().encode(record.getValue(), out);
  }

  // If the start of the current elements position is more than block size away from our
  // last indexed position
  if (positionOfCurrentKey > lastIndexedPosition + getBlockSize()) {
    // Note that the first key of each shard will never be in the index allowing us to ignore
    // the fact that there is an empty key and use the lastIndexKeyBytes to be the 0 byte array
    // at the start of each shard.
    int sharedIndexKeySize =
        commonPrefixLengthWithOrderCheck(lastIndexKeyBytes, currentKeyBytes);
    int unsharedIndexKeySize = currentKeyBytes.size() - sharedIndexKeySize;
    KeyPrefix indexKeyPrefix = KeyPrefix.of(sharedIndexKeySize, unsharedIndexKeySize);
    KeyPrefixCoder.of().encode(indexKeyPrefix, indexOut.asOutputStream());
    currentKeyBytes.writeTo(
        indexOut.asOutputStream(), sharedIndexKeySize, unsharedIndexKeySize);
    VarInt.encode(positionOfCurrentKey, indexOut.asOutputStream());
    lastIndexKeyBytes.resetTo(0);
    currentKeyBytes.writeTo(lastIndexKeyBytes.asOutputStream(), 0, currentKeyBytes.size());
    lastIndexedPosition = out.getCount();
  }

  // Remember the shard for the current key.
  previousShard = Optional.of(currentShard);

  // Swap the current key and the previous key, resetting the previous key to be re-used.
  RandomAccessData temp = previousKeyBytes;
  previousKeyBytes = currentKeyBytes;
  currentKeyBytes = temp;
  currentKeyBytes.resetTo(0);

  numberOfKeysWritten += 1;
  return out.getCount() - positionOfCurrentKey;
}
 
Example 20
Source File: AggregatorCombiner.java    From beam with Apache License 2.0 4 votes vote down vote up
@Override
public Iterable<WindowedValue<AccumT>> merge(
    Iterable<WindowedValue<AccumT>> accumulators1,
    Iterable<WindowedValue<AccumT>> accumulators2) {

  // merge the windows of all the accumulators
  Iterable<WindowedValue<AccumT>> accumulators = Iterables.concat(accumulators1, accumulators2);
  Set<W> accumulatorsWindows = collectAccumulatorsWindows(accumulators);
  Map<W, W> windowToMergeResult;
  try {
    windowToMergeResult = mergeWindows(windowingStrategy, accumulatorsWindows);
  } catch (Exception e) {
    throw new RuntimeException("Unable to merge accumulators windows", e);
  }

  // group accumulators by their merged window
  Map<W, List<Tuple2<AccumT, Instant>>> mergedWindowToAccumulators = new HashMap<>();
  for (WindowedValue<AccumT> accumulatorWv : accumulators) {
    for (BoundedWindow accumulatorWindow : accumulatorWv.getWindows()) {
      W mergedWindowForAccumulator = windowToMergeResult.get(accumulatorWindow);
      mergedWindowForAccumulator =
          (mergedWindowForAccumulator == null)
              ? (W) accumulatorWindow
              : mergedWindowForAccumulator;

      // we need only the timestamp and the AccumT, we create a tuple
      Tuple2<AccumT, Instant> accumAndInstant =
          new Tuple2<>(
              accumulatorWv.getValue(),
              timestampCombiner.assign(
                  mergedWindowForAccumulator,
                  windowingStrategy
                      .getWindowFn()
                      .getOutputTime(accumulatorWv.getTimestamp(), mergedWindowForAccumulator)));
      if (mergedWindowToAccumulators.get(mergedWindowForAccumulator) == null) {
        mergedWindowToAccumulators.put(
            mergedWindowForAccumulator, Lists.newArrayList(accumAndInstant));
      } else {
        mergedWindowToAccumulators.get(mergedWindowForAccumulator).add(accumAndInstant);
      }
    }
  }
  // merge the accumulators for each mergedWindow
  List<WindowedValue<AccumT>> result = new ArrayList<>();
  for (Map.Entry<W, List<Tuple2<AccumT, Instant>>> entry :
      mergedWindowToAccumulators.entrySet()) {
    W mergedWindow = entry.getKey();
    List<Tuple2<AccumT, Instant>> accumsAndInstantsForMergedWindow = entry.getValue();

    // we need to create the first accumulator because combineFn.mergerAccumulators can modify the
    // first accumulator
    AccumT first = combineFn.createAccumulator();
    Iterable<AccumT> accumulatorsToMerge =
        Iterables.concat(
            Collections.singleton(first),
            accumsAndInstantsForMergedWindow.stream()
                .map(x -> x._1())
                .collect(Collectors.toList()));
    result.add(
        WindowedValue.of(
            combineFn.mergeAccumulators(accumulatorsToMerge),
            timestampCombiner.combine(
                accumsAndInstantsForMergedWindow.stream()
                    .map(x -> x._2())
                    .collect(Collectors.toList())),
            mergedWindow,
            PaneInfo.NO_FIRING));
  }
  return result;
}