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

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

There is a newer version: 8.7.0-alpha2
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.atomix.cluster.MemberId;
import io.camunda.zeebe.transport.stream.api.RemoteStreamErrorHandler;
import io.camunda.zeebe.transport.stream.api.RemoteStreamMetrics;
import io.camunda.zeebe.transport.stream.api.StreamResponseException;
import io.camunda.zeebe.transport.stream.impl.AggregatedRemoteStream.StreamId;
import io.camunda.zeebe.transport.stream.impl.messages.ErrorResponse;
import io.camunda.zeebe.transport.stream.impl.messages.PushStreamRequest;
import io.camunda.zeebe.transport.stream.impl.messages.PushStreamResponse;
import io.camunda.zeebe.transport.stream.impl.messages.StreamResponseDecoder;
import io.camunda.zeebe.util.buffer.BufferWriter;
import io.camunda.zeebe.util.logging.ThrottledLogger;
import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A naive implementation to push jobs out, which performs no retries of any kind, but reports
 * errors on failure.
 *
 * @param 

the payload type to be pushed out */ final class RemoteStreamPusher

{ private static final Logger LOG = LoggerFactory.getLogger(RemoteStreamPusher.class); private final StreamResponseDecoder responseDecoder = new StreamResponseDecoder(); private final ThrottledLogger pushErrorLogger = new ThrottledLogger(LOG, Duration.ofSeconds(5)); private final ThrottledLogger pushWarnLogger = new ThrottledLogger(LOG, Duration.ofSeconds(5)); private final RemoteStreamMetrics metrics; private final Transport transport; private final Executor executor; RemoteStreamPusher( final Transport transport, final Executor executor, final RemoteStreamMetrics metrics) { this.metrics = Objects.requireNonNull(metrics, "must specify remote stream metrics"); this.transport = Objects.requireNonNull(transport, "must provide a network transport"); this.executor = Objects.requireNonNull(executor, "must provide an asynchronous executor"); } public void pushAsync( final P payload, final RemoteStreamErrorHandler

errorHandler, final StreamId streamId) { Objects.requireNonNull(errorHandler, "must specify a error handler"); try { Objects.requireNonNull(payload, "must specify a payload"); executor.execute( () -> push(payload, instrumentingErrorHandler(errorHandler, streamId), streamId)); } catch (final Exception e) { errorHandler.handleError(e, payload); } } private RemoteStreamErrorHandler

instrumentingErrorHandler( final RemoteStreamErrorHandler

errorHandler, final StreamId streamId) { return (error, payload) -> { if (error == null) { return; } if (error instanceof final StreamResponseException e) { logResponseError(streamId, payload, e); e.details().forEach(d -> metrics.pushTryFailed(d.code())); } else { pushWarnLogger.warn( "Failed to push (size = {}) to stream {}", payload.getLength(), streamId, error); } metrics.pushFailed(); errorHandler.handleError(error, payload); }; } private void logResponseError( final StreamId streamId, final P payload, final StreamResponseException e) { switch (e.code()) { case INVALID, MALFORMED -> pushErrorLogger.error( "Failed to push (size = {}) to stream {}, request could not be parsed", payload.getLength(), streamId, e); case EXHAUSTED -> LOG.trace( "Failed to push (size = {}) to stream {} after trying all clients", payload.getLength(), streamId, e); default -> pushWarnLogger.warn( "Failed to push (size = {}) to stream {}", payload.getLength(), streamId, e); } } private void push( final P payload, final RemoteStreamErrorHandler

errorHandler, final StreamId streamId) { final var request = new PushStreamRequest().streamId(streamId.streamId()).payload(payload); try { transport .send(request, streamId.receiver()) .whenCompleteAsync( (response, error) -> onPush(payload, errorHandler, response, error), executor); LOG.trace("Pushed {} to stream {}", payload, streamId); } catch (final Exception e) { errorHandler.handleError(e, payload); } } private void onPush( final P payload, final RemoteStreamErrorHandler

errorHandler, final byte[] responseBuffer, final Throwable error) { if (error != null) { errorHandler.handleError(error, payload); return; } responseDecoder .decode(responseBuffer, new PushStreamResponse()) .mapLeft(ErrorResponse::asException) .ifRightOrLeft( ok -> metrics.pushSucceeded(), failure -> errorHandler.handleError(failure, payload)); } /** * A small abstraction over the network transport. This allows for better testability, and also * removes the need for this class to know how communication occurs (e.g. which topic the message * is sent over) */ interface Transport { /** * Sends the given request out to the given receiver. May throw errors, e.g. serialization * errors. * * @param request the request to send * @param receiver the expected target * @return a future which is completed when the request has been acknowledged by the receiver, * or an error occurred * @throws Exception if an error occurs before the request is sent out, i.e. serialization error */ CompletableFuture send(final PushStreamRequest request, final MemberId receiver) throws Exception; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy