@Override
  public boolean invalidate(EntryEventImpl event, boolean invokeCallbacks, boolean forceNewEntry,
      boolean forceCallbacks) throws EntryNotFoundException {
    final boolean isDebugEnabled = logger.isDebugEnabled();

    final LocalRegion owner = _getOwner();
    if (owner == null) {
      // "fix" for bug 32440
      Assert.assertTrue(false, "The owner for RegionMap " + this + " is null for event " + event);

    }
    logger.debug("ARM.invalidate invoked for key {}", event.getKey());
    boolean didInvalidate = false;
    RegionEntry invalidatedRe = null;
    boolean clearOccured = false;
    DiskRegion dr = owner.getDiskRegion();
    boolean ownerIsInitialized = owner.isInitialized();

    // Fix for Bug #44431. We do NOT want to update the region and wait
    // later for index INIT as region.clear() can cause inconsistency if
    // happened in parallel as it also does index INIT.
    IndexManager oqlIndexManager = owner.getIndexManager();
    if (oqlIndexManager != null) {
      oqlIndexManager.waitForIndexInit();
    }
    lockForCacheModification(owner, event);
    final boolean locked = owner.lockWhenRegionIsInitializing();
    try {
      try {
        try {
          if (forceNewEntry || forceCallbacks) {
            boolean opCompleted = false;
            RegionEntry newRe =
                getEntryFactory().createEntry(owner, event.getKey(), Token.REMOVED_PHASE1);
            synchronized (newRe) {
              try {
                RegionEntry oldRe = putEntryIfAbsent(event.getKey(), newRe);

                while (!opCompleted && oldRe != null) {
                  synchronized (oldRe) {
                    // if the RE is in phase 2 of removal, it will really be removed
                    // from the map. Otherwise, we can use it here and the thread
                    // that is destroying the RE will see the invalidation and not
                    // proceed to phase 2 of removal.
                    if (oldRe.isRemovedPhase2()) {
                      owner.getCachePerfStats().incRetries();
                      getEntryMap().remove(event.getKey(), oldRe);
                      oldRe = putEntryIfAbsent(event.getKey(), newRe);
                    } else {
                      opCompleted = true;
                      event.setRegionEntry(oldRe);
                      if (oldRe.isDestroyed()) {
                        if (isDebugEnabled) {
                          logger.debug(
                              "mapInvalidate: Found DESTROYED token, not invalidated; key={}",
                              event.getKey());
                        }
                      } else if (oldRe.isInvalid()) {

                        // was already invalid, do not invoke listeners or increment stat
                        handleAlreadyInvalidEntry(event, owner, oldRe);
                        try {
                          oldRe.setValue(owner, oldRe.getValueInVM(owner)); // OFFHEAP noop setting
                                                                            // an already invalid to
                                                                            // invalid; No need to
                                                                            // call
                                                                            // prepareValueForCache
                                                                            // since it is an
                                                                            // invalid token.
                        } catch (RegionClearedException e) {
                          // that's okay - when writing an invalid into a disk, the
                          // region has been cleared (including this token)
                        }
                      } else {
                        owner.serverInvalidate(event);
                        if (owner.getConcurrencyChecksEnabled()
                            && event.noVersionReceivedFromServer()) {
                          // server did not perform the invalidation, so don't leave an invalid
                          // entry here
                          return false;
                        }
                        final int oldSize = owner.calculateRegionEntryValueSize(oldRe);
                        // added for cq which needs old value. rdubey
                        FilterProfile fp = owner.getFilterProfile();
                        if (!oldRe.isRemoved() && (fp != null && fp.getCqCount() > 0)) {

                          Object oldValue = oldRe.getValueInVM(owner); // OFFHEAP EntryEventImpl
                                                                       // oldValue

                          // this will not fault in the value.
                          if (oldValue == Token.NOT_AVAILABLE) {
                            event.setOldValue(oldRe.getValueOnDiskOrBuffer(owner));
                          } else {
                            event.setOldValue(oldValue);
                          }
                        }
                        boolean isCreate = false;
                        try {
                          if (oldRe.isRemoved()) {
                            processVersionTag(oldRe, event);
                            event.putNewEntry(owner, oldRe);
                            EntryLogger.logInvalidate(event);
                            owner.recordEvent(event);
                            if (!oldRe.isTombstone()) {
                              owner.updateSizeOnPut(event.getKey(), oldSize,
                                  event.getNewValueBucketSize());
                            } else {
                              owner.updateSizeOnCreate(event.getKey(),
                                  event.getNewValueBucketSize());
                              isCreate = true;
                            }
                          } else {
                            processVersionTag(oldRe, event);
                            event.putExistingEntry(owner, oldRe);
                            EntryLogger.logInvalidate(event);
                            owner.recordEvent(event);
                            owner.updateSizeOnPut(event.getKey(), oldSize,
                                event.getNewValueBucketSize());
                          }
                        } catch (RegionClearedException e) {
                          // generate versionTag for the event
                          EntryLogger.logInvalidate(event);
                          owner.recordEvent(event);
                          clearOccured = true;
                        }
                        owner.basicInvalidatePart2(oldRe, event,
                            clearOccured /* conflict with clear */, invokeCallbacks);
                        if (!clearOccured) {
                          if (isCreate) {
                            lruEntryCreate(oldRe);
                          } else {
                            lruEntryUpdate(oldRe);
                          }
                        }
                        didInvalidate = true;
                        invalidatedRe = oldRe;
                      }
                    }
                  } // synchronized oldRe
                } // while oldRe exists

                if (!opCompleted) {
                  if (forceNewEntry && event.isFromServer()) {
                    // don't invoke listeners - we didn't force new entries for
                    // CCU invalidations before 7.0, and listeners don't care
                    if (!FORCE_INVALIDATE_EVENT) {
                      event.inhibitCacheListenerNotification(true);
                    }
                  }
                  event.setRegionEntry(newRe);
                  owner.serverInvalidate(event);
                  if (!forceNewEntry && event.noVersionReceivedFromServer()) {
                    // server did not perform the invalidation, so don't leave an invalid
                    // entry here
                    return false;
                  }
                  try {
                    ownerIsInitialized = owner.isInitialized();
                    if (!ownerIsInitialized && owner.getDataPolicy().withReplication()) {
                      final int oldSize = owner.calculateRegionEntryValueSize(newRe);
                      invalidateEntry(event, newRe, oldSize);
                    } else {
                      invalidateNewEntry(event, owner, newRe);
                    }
                  } catch (RegionClearedException e) {
                    // TODO: deltaGII: do we even need RegionClearedException?
                    // generate versionTag for the event
                    owner.recordEvent(event);
                    clearOccured = true;
                  }
                  owner.basicInvalidatePart2(newRe, event, clearOccured /* conflict with clear */,
                      invokeCallbacks);
                  if (!clearOccured) {
                    lruEntryCreate(newRe);
                    incEntryCount(1);
                  }
                  opCompleted = true;
                  didInvalidate = true;
                  invalidatedRe = newRe;
                  // Don't leave an entry in the cache, if we
                  // just wanted to force the distribution and events
                  // for this invalidate
                  if (!forceNewEntry) {
                    removeEntry(event.getKey(), newRe, false);
                  }
                } // !opCompleted
              } catch (ConcurrentCacheModificationException ccme) {
                VersionTag tag = event.getVersionTag();
                if (tag != null && tag.isTimeStampUpdated()) {
                  // Notify gateways of new time-stamp.
                  owner.notifyTimestampsToGateways(event);
                }
                throw ccme;
              } finally {
                if (!opCompleted) {
                  removeEntry(event.getKey(), newRe, false);
                }
              }
            } // synchronized newRe
          } // forceNewEntry
          else { // !forceNewEntry
            boolean retry = true;
            // RegionEntry retryEntry = null;
            // int retries = -1;

            while (retry) {
              retry = false;
              boolean entryExisted = false;
              RegionEntry re = getEntry(event.getKey());
              RegionEntry tombstone = null;
              boolean haveTombstone = false;
              if (re != null && re.isTombstone()) {
                tombstone = re;
                haveTombstone = true;
                re = null;
              }
              if (re == null) {
                ownerIsInitialized = owner.isInitialized();
                if (!ownerIsInitialized) {
                  // when GII message arrived or processed later than invalidate
                  // message, the entry should be created as placeholder
                  RegionEntry newRe = haveTombstone ? tombstone
                      : getEntryFactory().createEntry(owner, event.getKey(), Token.INVALID);
                  synchronized (newRe) {
                    if (haveTombstone && !tombstone.isTombstone()) {
                      // state of the tombstone has changed so we need to retry
                      retry = true;
                      // retryEntry = tombstone; // leave this in place for debugging
                      continue;
                    }
                    re = putEntryIfAbsent(event.getKey(), newRe);
                    if (re == tombstone) {
                      re = null; // pretend we don't have an entry
                    }
                  }
                } else if (owner.getServerProxy() != null) {
                  Object sync = haveTombstone ? tombstone : new Object();
                  synchronized (sync) {
                    if (haveTombstone && !tombstone.isTombstone()) {
                      // bug 45295: state of the tombstone has changed so we need to retry
                      retry = true;
                      // retryEntry = tombstone; // leave this in place for debugging
                      continue;
                    }

                    // bug #43287 - send event to server even if it's not in the client (LRU may
                    // have evicted it)
                    owner.serverInvalidate(event);
                    if (owner.getConcurrencyChecksEnabled()) {
                      if (event.getVersionTag() == null) {
                        // server did not perform the invalidation, so don't leave an invalid
                        // entry here
                        return false;
                      } else if (tombstone != null) {
                        processVersionTag(tombstone, event);
                        try {
                          tombstone.setValue(owner, Token.TOMBSTONE);
                        } catch (RegionClearedException e) {
                          // that's okay - when writing a tombstone into a disk, the
                          // region has been cleared (including this tombstone)
                        } catch (ConcurrentCacheModificationException ccme) {
                          VersionTag tag = event.getVersionTag();
                          if (tag != null && tag.isTimeStampUpdated()) {
                            // Notify gateways of new time-stamp.
                            owner.notifyTimestampsToGateways(event);
                          }
                          throw ccme;
                        }
                        // update the tombstone's version to prevent an older CCU/putAll from
                        // overwriting it
                        owner.rescheduleTombstone(tombstone, event.getVersionTag());
                      }
                    }
                  }
                  entryExisted = true;
                }
              }
              if (re != null) {
                // Gester: Race condition in GII
                // when adding the placeholder for invalidate entry during GII,
                // if the GII got processed earlier for this entry, then do
                // normal invalidate operation
                synchronized (re) {
                  if (!event.isOriginRemote() && event.getOperation().isExpiration()) {
                    // If this expiration started locally then only do it if the RE is not being
                    // used by a tx.
                    if (re.isInUseByTransaction()) {
                      return false;
                    }
                  }
                  if (re.isTombstone() || (!re.isRemoved() && !re.isDestroyed())) {
                    entryExisted = true;
                    if (re.isInvalid()) {
                      // was already invalid, do not invoke listeners or increment
                      // stat
                      handleAlreadyInvalidEntry(event, owner, re);
                    } else { // previous value not invalid
                      event.setRegionEntry(re);
                      owner.serverInvalidate(event);
                      if (owner.getConcurrencyChecksEnabled()
                          && event.noVersionReceivedFromServer()) {
                        // server did not perform the invalidation, so don't leave an invalid
                        // entry here
                        if (isDebugEnabled) {
                          logger.debug(
                              "returning early because server did not generate a version stamp for this event:{}",
                              event);
                        }
                        return false;
                      }
                      // in case of overflow to disk we need the old value for cqs.
                      if (owner.getFilterProfile().getCqCount() > 0) {
                        // use to be getValue and can cause dead lock rdubey.
                        if (re.isValueNull()) {
                          event.setOldValue(re.getValueOnDiskOrBuffer(owner));
                        } else {
                          Object v = re.getValueInVM(owner);
                          event.setOldValue(v); // OFFHEAP escapes to EntryEventImpl oldValue
                        }
                      }
                      final boolean oldWasTombstone = re.isTombstone();
                      final int oldSize = _getOwner().calculateRegionEntryValueSize(re);
                      try {
                        invalidateEntry(event, re, oldSize);
                      } catch (RegionClearedException rce) {
                        // generate versionTag for the event
                        EntryLogger.logInvalidate(event);
                        _getOwner().recordEvent(event);
                        clearOccured = true;
                      } catch (ConcurrentCacheModificationException ccme) {
                        VersionTag tag = event.getVersionTag();
                        if (tag != null && tag.isTimeStampUpdated()) {
                          // Notify gateways of new time-stamp.
                          owner.notifyTimestampsToGateways(event);
                        }
                        throw ccme;
                      }
                      owner.basicInvalidatePart2(re, event, clearOccured /* conflict with clear */,
                          invokeCallbacks);
                      if (!clearOccured) {
                        if (oldWasTombstone) {
                          lruEntryCreate(re);
                        } else {
                          lruEntryUpdate(re);
                        }
                      }
                      didInvalidate = true;
                      invalidatedRe = re;
                    } // previous value not invalid
                  }
                } // synchronized re
              } // re != null
              else {
                // At this point, either it's not in GII mode, or the placeholder
                // is in region, do nothing
              }
              if (!entryExisted) {
                owner.checkEntryNotFound(event.getKey());
              }
            } // while(retry)
          } // !forceNewEntry
        } catch (DiskAccessException dae) {
          invalidatedRe = null;
          didInvalidate = false;
          _getOwner().handleDiskAccessException(dae);
          throw dae;
        } finally {
          if (oqlIndexManager != null) {
            oqlIndexManager.countDownIndexUpdaters();
          }
          if (invalidatedRe != null) {
            owner.basicInvalidatePart3(invalidatedRe, event, invokeCallbacks);
          }
          if (didInvalidate && !clearOccured) {
            try {
              lruUpdateCallback();
            } catch (DiskAccessException dae) {
              _getOwner().handleDiskAccessException(dae);
              throw dae;
            }
          } else if (!didInvalidate) {
            resetThreadLocals();
          }
        }
        return didInvalidate;
      } finally {
        if (ownerIsInitialized) {
          forceInvalidateEvent(event, owner);
        }
      }
    } finally {
      if (locked) {
        owner.unlockWhenRegionIsInitializing();
      }
      releaseCacheModificationLock(owner, event);
    }

  }
Process an incoming version tag for concurrent operation detection. This must be done before
modifying the region entry.

@param regionEntry the entry that is to be modified
@param event the modification to the entry
@throws InvalidDeltaException if the event contains a delta that cannot be applied
@throws ConcurrentCacheModificationException if the event is in conflict with a previously
        applied change
geode-core/src/main/java/org/apache/geode/internal/cache/AbstractRegionMap.java