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

io.camunda.zeebe.gateway.impl.broker.RequestRetryHandler 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.gateway.impl.broker;

import io.camunda.zeebe.broker.client.api.BrokerClient;
import io.camunda.zeebe.broker.client.api.BrokerErrorException;
import io.camunda.zeebe.broker.client.api.BrokerResponseConsumer;
import io.camunda.zeebe.broker.client.api.BrokerTopologyManager;
import io.camunda.zeebe.broker.client.api.NoTopologyAvailableException;
import io.camunda.zeebe.broker.client.api.RequestDispatchStrategy;
import io.camunda.zeebe.broker.client.api.RequestRetriesExhaustedException;
import io.camunda.zeebe.broker.client.api.dto.BrokerRequest;
import io.camunda.zeebe.broker.client.api.dto.BrokerResponse;
import io.camunda.zeebe.broker.client.impl.PartitionIdIterator;
import io.camunda.zeebe.protocol.record.ErrorCode;
import java.net.ConnectException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * When a requests to a partition fails, request will be retried with a different partition until
 * all partitions are tried. The request is retried only for specific errors such as connection
 * errors or resource exhausted errors. The request is not retried for time outs.
 *
 * 

Use carefully! Only certain requests can be retried on other partitions, and this class will * overwrite the specific partition previously assigned to a request! */ public final class RequestRetryHandler { private static final Logger LOGGER = LoggerFactory.getLogger(RequestRetryHandler.class); private final RequestDispatchStrategy roundRobinDispatchStrategy = RequestDispatchStrategy.roundRobin(); private final BrokerClient brokerClient; private final BrokerTopologyManager topologyManager; public RequestRetryHandler( final BrokerClient brokerClient, final BrokerTopologyManager topologyManager) { this.brokerClient = brokerClient; this.topologyManager = topologyManager; } public void sendRequest( final BrokerRequest request, final BrokerResponseConsumer responseConsumer, final Consumer throwableConsumer) { final Function< BrokerRequest, CompletableFuture>> requestSender = brokerClient::sendRequest; sendRequestInternal(request, requestSender, responseConsumer, throwableConsumer); } public void sendRequest( final BrokerRequest request, final BrokerResponseConsumer responseConsumer, final Consumer throwableConsumer, final Duration requestTimeout) { final Function< BrokerRequest, CompletableFuture>> requestSender = r -> brokerClient.sendRequest(r, requestTimeout); sendRequestInternal(request, requestSender, responseConsumer, throwableConsumer); } private void sendRequestInternal( final BrokerRequest request, final Function< BrokerRequest, CompletableFuture>> requestSender, final BrokerResponseConsumer responseConsumer, final Consumer throwableConsumer) { final var topology = topologyManager.getTopology(); if (topology == null || topology.getPartitionsCount() == 0) { throwableConsumer.accept(new NoTopologyAvailableException()); return; } sendRequestWithRetry( request, requestSender, partitionIdIteratorForType(topology.getPartitionsCount()), responseConsumer, throwableConsumer, new ArrayList<>()); } private void sendRequestWithRetry( final BrokerRequest request, final Function< BrokerRequest, CompletableFuture>> requestSender, final PartitionIdIterator partitionIdIterator, final BrokerResponseConsumer responseConsumer, final Consumer throwableConsumer, final Collection errors) { if (partitionIdIterator.hasNext()) { final int partitionId = partitionIdIterator.next(); // partitions to check request.setPartitionId(partitionId); requestSender .apply(request) .whenComplete( (response, error) -> { if (error == null) { responseConsumer.accept(response.getKey(), response.getResponse()); } else if (shouldRetryWithNextPartition(error)) { LOGGER.trace( "Failed to create process on partition {}", partitionIdIterator.getCurrentPartitionId(), error); errors.add(error); sendRequestWithRetry( request, requestSender, partitionIdIterator, responseConsumer, throwableConsumer, errors); } else { throwableConsumer.accept(error); } }); } else { // no partition left to check final var exception = new RequestRetriesExhaustedException(); errors.forEach(exception::addSuppressed); throwableConsumer.accept(exception); } } private boolean shouldRetryWithNextPartition(final Throwable error) { if (error instanceof ConnectException) { return true; } else if (error instanceof BrokerErrorException) { final ErrorCode code = ((BrokerErrorException) error).getError().getCode(); return code == ErrorCode.PARTITION_LEADER_MISMATCH || code == ErrorCode.RESOURCE_EXHAUSTED; } return false; } private PartitionIdIterator partitionIdIteratorForType(final int partitionsCount) { final int nextPartitionId = roundRobinDispatchStrategy.determinePartition(topologyManager); return new PartitionIdIterator(nextPartitionId, partitionsCount, topologyManager); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy