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

io.zeebe.gossip.dissemination.SyncRequestEventHandler Maven / Gradle / Ivy

There is a newer version: 0.16.4
Show newest version
/*
 * Copyright © 2017 camunda services GmbH ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.zeebe.gossip.dissemination;

import static io.zeebe.util.buffer.BufferUtil.bufferAsString;

import io.zeebe.gossip.GossipContext;
import io.zeebe.gossip.GossipSyncRequestHandler;
import io.zeebe.gossip.Loggers;
import io.zeebe.gossip.membership.GossipTerm;
import io.zeebe.gossip.membership.Member;
import io.zeebe.gossip.membership.MembershipList;
import io.zeebe.gossip.protocol.GossipEvent;
import io.zeebe.gossip.protocol.GossipEventConsumer;
import io.zeebe.gossip.protocol.GossipEventSender;
import io.zeebe.util.buffer.BufferUtil;
import io.zeebe.util.collection.Reusable;
import io.zeebe.util.collection.ReusableObjectList;
import io.zeebe.util.collection.Tuple;
import io.zeebe.util.sched.ActorControl;
import io.zeebe.util.sched.future.ActorFuture;
import java.util.ArrayList;
import java.util.List;
import org.agrona.DirectBuffer;
import org.slf4j.Logger;

public class SyncRequestEventHandler implements GossipEventConsumer {
  private static final Logger LOG = Loggers.GOSSIP_LOGGER;

  private final ActorControl actor;
  private final MembershipList membershipList;
  private final CustomEventSyncResponseSupplier customEventSyncRequestSupplier;
  private final GossipEventSender gossipEventSender;

  private final List> handlers = new ArrayList<>();
  private final ReusableObjectList syncRequests =
      new ReusableObjectList<>(GossipSyncRequest::new);

  private final ReusableObjectList receivedRequests =
      new ReusableObjectList<>(ReceivedRequest::new);

  public SyncRequestEventHandler(
      GossipContext context,
      CustomEventSyncResponseSupplier customEventSyncRequestSupplier,
      ActorControl actor) {
    this.membershipList = context.getMembershipList();
    this.customEventSyncRequestSupplier = customEventSyncRequestSupplier;
    this.actor = actor;
    this.gossipEventSender = context.getGossipEventSender();
  }

  @Override
  public void accept(GossipEvent event, long requestId, int streamId) {
    receivedRequests.add().wrap(requestId, streamId);

    if (receivedRequests.size() == 1) {
      if (!handlers.isEmpty()) {
        final List> syncHandlerFutures = new ArrayList<>();

        for (Tuple tuple : handlers) {
          final GossipSyncRequest request = syncRequests.add();
          request.wrap(tuple.getLeft());

          LOG.trace(
              "Request SYNC data for custom event type '{}'", bufferAsString(tuple.getLeft()));

          final GossipSyncRequestHandler handler = tuple.getRight();
          final ActorFuture future = handler.onSyncRequest(request);
          syncHandlerFutures.add(future);
        }

        actor.runOnCompletion(
            syncHandlerFutures,
            (failure) -> {
              if (failure == null) {
                actor.submit(this::sendSyncResponse);
              } else {
                LOG.warn("Can't produce sync response.", failure);
              }
            });
      } else {
        actor.submit(this::sendSyncResponse);
      }
    } else {
      // don't request the data again if already requested
      // - instead, response the data from the ongoing request
      customEventSyncRequestSupplier.increaseSpreadLimit();
    }
  }

  private void sendSyncResponse() {
    for (GossipSyncRequest request : syncRequests) {
      for (GossipSyncResponsePart response : request.getResponse()) {
        final int nodeId = response.getNodeId();

        final Member member = membershipList.getMemberOrSelf(response.getNodeId());
        if (member != null) {
          final GossipTerm term = member.getTermForEventType(request.getType());
          if (term != null) {
            customEventSyncRequestSupplier
                .add()
                .type(request.getType())
                .senderId(member.getId())
                .senderGossipTerm(term)
                .payload(response.getPayload());
          } else {
            LOG.debug(
                "Ignore sync response with type '{}' and sender id '{}'. Event type is unknown. ",
                bufferAsString(request.getType()),
                nodeId);
          }
        } else {
          LOG.debug(
              "Ignore sync response with type '{}' and sender id '{}'. Sender is unknown. ",
              bufferAsString(request.getType()),
              nodeId);
        }
      }
    }

    for (ReceivedRequest request : receivedRequests) {
      final long requestId = request.getRequestId();
      final int streamId = request.getStreamId();

      LOG.trace("Send SYNC response");
      gossipEventSender.responseSync(requestId, streamId);
    }

    syncRequests.clear();
    receivedRequests.clear();
    customEventSyncRequestSupplier.reset();
  }

  public void registerSyncRequestHandler(DirectBuffer eventType, GossipSyncRequestHandler handler) {
    final Tuple tuple =
        new Tuple<>(BufferUtil.cloneBuffer(eventType), handler);
    handlers.add(tuple);
  }

  private class ReceivedRequest implements Reusable {
    private long requestId;
    private int streamId;

    public void wrap(long requestId, int streamId) {
      this.requestId = requestId;
      this.streamId = streamId;
    }

    public long getRequestId() {
      return requestId;
    }

    public int getStreamId() {
      return streamId;
    }

    @Override
    public void reset() {
      requestId = -1L;
      streamId = -1;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy