All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.camunda.zeebe.transport.stream.impl.RemoteStreamImpl Maven / Gradle / Ivy

There is a newer version: 8.7.0-alpha1
Show newest version
/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
 * one or more contributor license agreements. See the NOTICE file distributed
 * with this work for additional information regarding copyright ownership.
 * Licensed under the Camunda License 1.0. You may not use this file
 * except in compliance with the Camunda License 1.0.
 */
package io.camunda.zeebe.transport.stream.impl;

import io.camunda.zeebe.transport.stream.api.RemoteStream;
import io.camunda.zeebe.transport.stream.api.RemoteStreamErrorHandler;
import io.camunda.zeebe.transport.stream.api.StreamExhaustedException;
import io.camunda.zeebe.transport.stream.impl.AggregatedRemoteStream.StreamConsumer;
import io.camunda.zeebe.util.buffer.BufferWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.ThreadLocalRandom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class RemoteStreamImpl implements RemoteStream {

  private static final Logger LOGGER = LoggerFactory.getLogger(RemoteStreamImpl.class);
  private final AggregatedRemoteStream stream;
  private final RemoteStreamPusher

streamer; private final RemoteStreamErrorHandler

errorHandler; public RemoteStreamImpl( final AggregatedRemoteStream stream, final RemoteStreamPusher

streamer, final RemoteStreamErrorHandler

errorHandler) { this.stream = stream; this.streamer = streamer; this.errorHandler = errorHandler; } @Override public M metadata() { return stream.logicalId().metadata(); } @Override public void push(final P payload) { final var initialConsumer = pickInitialConsumer(); if (initialConsumer == null) { errorHandler.handleError( new StreamExhaustedException( "Failed to push to stream %s, all consumers were removed since it was picked" .formatted(stream.logicalId())), payload); return; } final var retryHandler = new RetryHandler(errorHandler, initialConsumer); streamer.pushAsync(payload, retryHandler, initialConsumer.id()); } private StreamConsumer pickInitialConsumer() { final var consumers = stream.streamConsumers(); var size = consumers.size(); // since we can get concurrent modifications of the stream consumers list, we have to handle the // case where the size changes while we're picking a consumer, so we loop as long as we fail to // pick a consumer or the list is empty while (size > 0) { final var index = ThreadLocalRandom.current().nextInt(size); try { return consumers.get(index); } catch (final IndexOutOfBoundsException e) { LOGGER.trace( "Stream consumer list concurrently modified while picking consumer; retrying", e); size = consumers.size(); } } return null; } private final class RetryHandler implements RemoteStreamErrorHandler

{ private final RemoteStreamErrorHandler

errorHandler; private final StreamConsumer initialConsumer; private RetryHandler( final RemoteStreamErrorHandler

errorHandler, final StreamConsumer initialConsumer) { this.errorHandler = errorHandler; this.initialConsumer = initialConsumer; } /** Called the first time a push is retried */ @Override public void handleError(final Throwable error, final P data) { final var consumers = new ArrayList<>(stream.streamConsumers()); if (consumers.isEmpty()) { onConsumersExhausted(error, data); return; } consumers.remove(initialConsumer); Collections.shuffle(consumers); final var iterator = consumers.iterator(); retry(error, data, iterator); } /** Called during future retries */ private void retry( final Throwable throwable, final P payload, final Iterator> iterator) { if (!iterator.hasNext()) { onConsumersExhausted(throwable, payload); return; } final var client = iterator.next(); LOGGER.trace( "Failed to push payload (size = {}), retrying with next stream", payload.getLength()); streamer.pushAsync(payload, (error, data) -> retry(error, data, iterator), client.id()); } private void onConsumersExhausted(final Throwable throwable, final P payload) { LOGGER.trace( "Failed to push payload (size = {}), no more streams to retry", payload.getLength()); errorHandler.handleError(throwable, payload); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy