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

org.apache.geode.internal.cache.RemoteInvalidateMessage Maven / Gradle / Ivy

Go to download

Apache Geode provides a database-like consistency model, reliable transaction processing and a shared-nothing architecture to maintain very low latency performance with high concurrency processing

There is a newer version: 1.15.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.internal.cache;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;

import org.apache.logging.log4j.Logger;

import org.apache.geode.CancelException;
import org.apache.geode.cache.CacheException;
import org.apache.geode.cache.EntryExistsException;
import org.apache.geode.cache.EntryNotFoundException;
import org.apache.geode.cache.TransactionDataNotColocatedException;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.internal.DM;
import org.apache.geode.distributed.internal.DirectReplyProcessor;
import org.apache.geode.distributed.internal.DistributionManager;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.distributed.internal.ReplyException;
import org.apache.geode.distributed.internal.ReplyMessage;
import org.apache.geode.distributed.internal.ReplyProcessor21;
import org.apache.geode.distributed.internal.ReplySender;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
import org.apache.geode.internal.Assert;
import org.apache.geode.internal.InternalDataSerializer;
import org.apache.geode.internal.NanoTimer;
import org.apache.geode.internal.cache.versions.DiskVersionTag;
import org.apache.geode.internal.cache.versions.VersionTag;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.logging.log4j.LocalizedMessage;
import org.apache.geode.internal.logging.log4j.LogMarker;
import org.apache.geode.internal.offheap.annotations.Released;

import static org.apache.geode.internal.cache.DistributedCacheOperation.VALUE_IS_BYTES;
import static org.apache.geode.internal.cache.DistributedCacheOperation.VALUE_IS_SERIALIZED_OBJECT;

public final class RemoteInvalidateMessage extends RemoteDestroyMessage {

  private static final Logger logger = LogService.getLogger();

  /**
   * Empty constructor to satisfy {@link org.apache.geode.DataSerializer} requirements
   */
  public RemoteInvalidateMessage() {}

  private RemoteInvalidateMessage(Set recipients, String regionPath, DirectReplyProcessor processor,
      EntryEventImpl event, boolean useOriginRemote, boolean possibleDuplicate) {
    super(recipients, regionPath, processor, event, null,
        DistributionManager.PARTITIONED_REGION_EXECUTOR, useOriginRemote, possibleDuplicate);
  }

  public static boolean distribute(EntryEventImpl event, boolean onlyPersistent) {
    boolean successful = false;
    DistributedRegion r = (DistributedRegion) event.getRegion();
    Collection replicates = onlyPersistent
        ? r.getCacheDistributionAdvisor().adviseInitializedPersistentMembers().keySet()
        : r.getCacheDistributionAdvisor().adviseInitializedReplicates();
    if (replicates.isEmpty()) {
      return false;
    }
    if (replicates.size() > 1) {
      ArrayList l = new ArrayList(replicates);
      Collections.shuffle(l);
      replicates = l;
    }
    int attempts = 0;
    for (Iterator it = replicates.iterator(); it.hasNext();) {
      InternalDistributedMember replicate = it.next();
      try {
        attempts++;
        final boolean posDup = (attempts > 1);
        InvalidateResponse processor = send(replicate, event.getRegion(), event,
            DistributionManager.SERIAL_EXECUTOR, false, posDup);
        processor.waitForCacheException();
        VersionTag versionTag = processor.getVersionTag();
        if (versionTag != null) {
          event.setVersionTag(versionTag);
          if (event.getRegion().getVersionVector() != null) {
            event.getRegion().getVersionVector().recordVersion(versionTag.getMemberID(),
                versionTag);
          }
        }
        event.setInhibitDistribution(true);
        return true;

      } catch (TransactionDataNotColocatedException enfe) {
        throw enfe;

      } catch (CancelException e) {
        event.getRegion().getCancelCriterion().checkCancelInProgress(e);

      } catch (EntryNotFoundException e) {
        throw new EntryNotFoundException("" + event.getKey());

      } catch (CacheException e) {
        if (logger.isDebugEnabled()) {
          logger.debug("RemoteDestroyMessage caught CacheException during distribution", e);
        }
        successful = true; // not a cancel-exception, so don't complain any more about it

      } catch (RemoteOperationException e) {
        if (logger.isTraceEnabled(LogMarker.DM)) {
          logger.trace(LogMarker.DM,
              "RemoteDestroyMessage caught an unexpected exception during distribution", e);
        }
      }
    }
    return successful;
  }

  /**
   * Sends a transactional RemoteInvalidateMessage
   * {@link org.apache.geode.cache.Region#invalidate(Object)}message to the recipient
   * 
   * @param recipient the recipient of the message
   * @param r the ReplicateRegion for which the invalidate was performed
   * @param event the event causing this message
   * @param processorType the type of executor to use
   * @param useOriginRemote whether the receiver should use originRemote=true in its event
   * @return the InvalidateResponse processor used to await the potential
   *         {@link org.apache.geode.cache.CacheException}
   */
  public static InvalidateResponse send(DistributedMember recipient, LocalRegion r,
      EntryEventImpl event, int processorType, boolean useOriginRemote, boolean possibleDuplicate)
      throws RemoteOperationException {
    // Assert.assertTrue(recipient != null, "RemoteInvalidateMessage NULL recipient"); recipient may
    // be null for remote notifications
    Set recipients = Collections.singleton(recipient);
    InvalidateResponse p = new InvalidateResponse(r.getSystem(), recipients, event.getKey());
    RemoteInvalidateMessage m = new RemoteInvalidateMessage(recipients, r.getFullPath(), p, event,
        useOriginRemote, possibleDuplicate);
    m.setTransactionDistributed(r.getCache().getTxManager().isDistributed());
    Set failures = r.getDistributionManager().putOutgoing(m);
    if (failures != null && failures.size() > 0) {
      throw new RemoteOperationException(
          LocalizedStrings.InvalidateMessage_FAILED_SENDING_0.toLocalizedString(m));
    }
    return p;
  }

  /**
   * This method is called upon receipt and make the desired changes to the PartitionedRegion Note:
   * It is very important that this message does NOT cause any deadlocks as the sender will wait
   * indefinitely for the acknowledgement
   * 
   * @throws EntryExistsException
   */
  @Override
  protected boolean operateOnRegion(DistributionManager dm, LocalRegion r, long startTime)
      throws EntryExistsException, RemoteOperationException {

    InternalDistributedMember eventSender = originalSender;
    if (eventSender == null) {
      eventSender = getSender();
    }
    final Object key = getKey();
    @Released
    final EntryEventImpl event = EntryEventImpl.create(r, getOperation(), key, null, /* newValue */
        getCallbackArg(),
        this.useOriginRemote/* originRemote - false to force distribution in buckets */,
        eventSender, true/* generateCallbacks */, false/* initializeId */);
    try {
      if (this.bridgeContext != null) {
        event.setContext(this.bridgeContext);
      }

      event.setCausedByMessage(this);

      if (this.versionTag != null) {
        this.versionTag.replaceNullIDs(getSender());
        event.setVersionTag(this.versionTag);
      }
      Assert.assertTrue(eventId != null);
      event.setEventId(eventId);

      event.setPossibleDuplicate(this.possibleDuplicate);

      // for cqs, which needs old value based on old value being sent on wire.
      boolean eventShouldHaveOldValue = getHasOldValue();
      if (eventShouldHaveOldValue) {
        if (getOldValueIsSerialized()) {
          event.setSerializedOldValue(getOldValueBytes());
        } else {
          event.setOldValue(getOldValueBytes());
        }
      }
      boolean sendReply = true;
      try {
        r.checkReadiness();
        r.checkForLimitedOrNoAccess();
        r.basicInvalidate(event);
        if (logger.isTraceEnabled(LogMarker.DM)) {
          logger.trace(LogMarker.DM, "remoteInvalidated key: {}", key);
        }
        sendReply(getSender(), this.processorId, dm, /* ex */null, event.getRegion(),
            event.getVersionTag(), startTime);
        sendReply = false;
      } catch (EntryNotFoundException eee) {
        // failed = true;
        if (logger.isDebugEnabled()) {
          logger.debug("operateOnRegion caught EntryNotFoundException");
        }
        sendReply(getSender(), getProcessorId(), dm, new ReplyException(eee), r, null, startTime);
        sendReply = false; // this prevents us from acking later
      } catch (PrimaryBucketException pbe) {
        sendReply(getSender(), getProcessorId(), dm, new ReplyException(pbe), r, startTime);
        return false;
      }

      return sendReply;
    } finally {
      event.release();
    }
  }


  // override reply processor type from PartitionMessage
  RemoteOperationResponse createReplyProcessor(PartitionedRegion r, Set recipients, Object key) {
    return new InvalidateResponse(r.getSystem(), recipients, key);
  }

  // override reply message type from PartitionMessage
  @Override
  protected void sendReply(InternalDistributedMember member, int procId, DM dm, ReplyException ex,
      LocalRegion r, long startTime) {
    sendReply(member, procId, dm, ex, r, null, startTime);
  }

  protected void sendReply(InternalDistributedMember member, int procId, DM dm, ReplyException ex,
      LocalRegion r, VersionTag versionTag, long startTime) {
    /*
     * if (pr != null && startTime > 0) { pr.getPrStats().endPartitionMessagesProcessing(startTime);
     * }
     */
    InvalidateReplyMessage.send(member, procId, getReplySender(dm), versionTag, ex);
  }

  @Override
  public int getDSFID() {
    return R_INVALIDATE_MESSAGE;
  }


  public static final class InvalidateReplyMessage extends ReplyMessage {
    private VersionTag versionTag;

    private static final byte HAS_VERSION = 0x01;
    private static final byte PERSISTENT = 0x02;

    /**
     * DSFIDFactory constructor
     */
    public InvalidateReplyMessage() {}

    private InvalidateReplyMessage(int processorId, VersionTag versionTag, ReplyException ex) {
      super();
      setProcessorId(processorId);
      this.versionTag = versionTag;
      setException(ex);
    }

    /** Send an ack */
    public static void send(InternalDistributedMember recipient, int processorId,
        ReplySender replySender, VersionTag versionTag, ReplyException ex) {
      Assert.assertTrue(recipient != null, "InvalidateReplyMessage NULL reply message");
      InvalidateReplyMessage m = new InvalidateReplyMessage(processorId, versionTag, ex);
      m.setRecipient(recipient);
      replySender.putOutgoing(m);
    }

    /**
     * Processes this message. This method is invoked by the receiver of the message.
     * 
     * @param dm the distribution manager that is processing the message.
     */
    @Override
    public void process(final DM dm, final ReplyProcessor21 rp) {
      final long startTime = getTimestamp();
      if (logger.isTraceEnabled(LogMarker.DM)) {
        logger.trace(LogMarker.DM,
            "InvalidateReplyMessage process invoking reply processor with processorId:{}",
            this.processorId);
      }

      if (rp == null) {
        if (logger.isTraceEnabled(LogMarker.DM)) {
          logger.trace(LogMarker.DM, "InvalidateReplyMessage processor not found");
        }
        return;
      }
      if (this.versionTag != null) {
        this.versionTag.replaceNullIDs(getSender());
      }
      if (rp instanceof InvalidateResponse) {
        InvalidateResponse processor = (InvalidateResponse) rp;
        processor.setResponse(this.versionTag);
      }
      rp.process(this);

      if (logger.isTraceEnabled(LogMarker.DM)) {
        logger.trace(LogMarker.DM, "{} processed {}", rp, this);
      }

      dm.getStats().incReplyMessageTime(NanoTimer.getTime() - startTime);
    }

    @Override
    public int getDSFID() {
      return R_INVALIDATE_REPLY_MESSAGE;
    }

    @Override
    public void toData(DataOutput out) throws IOException {
      super.toData(out);
      byte b = 0;
      if (this.versionTag != null) {
        b |= HAS_VERSION;
      }
      if (this.versionTag instanceof DiskVersionTag) {
        b |= PERSISTENT;
      }
      out.writeByte(b);
      if (this.versionTag != null) {
        InternalDataSerializer.invokeToData(this.versionTag, out);
      }
    }

    @Override
    public void fromData(DataInput in) throws IOException, ClassNotFoundException {
      super.fromData(in);
      byte b = in.readByte();
      boolean hasTag = (b & HAS_VERSION) != 0;
      boolean persistentTag = (b & PERSISTENT) != 0;
      if (hasTag) {
        this.versionTag = VersionTag.create(persistentTag, in);
      }
    }

    @Override
    public String toString() {
      StringBuffer sb = new StringBuffer();
      sb.append("InvalidateReplyMessage ").append("processorid=").append(this.processorId)
          .append(" exception=").append(getException());
      if (this.versionTag != null) {
        sb.append("version=").append(this.versionTag);
      }
      return sb.toString();
    }

  }
  /**
   * A processor to capture the value returned by {@link RemoteInvalidateMessage}
   * 
   * @since GemFire 6.5
   */
  public static class InvalidateResponse extends RemoteOperationResponse {
    private volatile boolean returnValueReceived;
    final Object key;
    VersionTag versionTag;

    public InvalidateResponse(InternalDistributedSystem ds, Set recipients, Object key) {
      super(ds, recipients, true);
      this.key = key;
    }

    public void setResponse(VersionTag versionTag) {
      this.returnValueReceived = true;
      this.versionTag = versionTag;
    }

    /**
     * @throws CacheException if the peer generates an error
     */
    public void waitForResult() throws CacheException, RemoteOperationException {
      try {
        waitForCacheException();
      } catch (RemoteOperationException e) {
        e.checkKey(key);
        throw e;
      }
      if (!this.returnValueReceived) {
        throw new RemoteOperationException(
            LocalizedStrings.InvalidateMessage_NO_RESPONSE_CODE_RECEIVED.toLocalizedString());
      }
      return;
    }

    public VersionTag getVersionTag() {
      return this.versionTag;
    }
  }



}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy