GfErrType LocalRegion::getNoThrow()

in cppcache/src/LocalRegion.cpp [851:1026]


GfErrType LocalRegion::getNoThrow(
    const std::shared_ptr<CacheableKey>& keyPtr,
    std::shared_ptr<Cacheable>& value,
    const std::shared_ptr<Serializable>& aCallbackArgument) {
  CHECK_DESTROY_PENDING_NOTHROW(shared_lock);
  GfErrType err = GF_NOERR;

  if (keyPtr == nullptr) {
    return GF_CACHE_ILLEGAL_ARGUMENT_EXCEPTION;
  }
  TXState* txState = getTXState();
  if (txState != nullptr) {
    if (isLocalOp()) {
      return GF_NOTSUP;
    }
    std::shared_ptr<VersionTag> versionTag;
    err = getNoThrow_remote(keyPtr, value, aCallbackArgument, versionTag);
    if (err == GF_NOERR) {
      txState->setDirty();
    }
    if (CacheableToken::isInvalid(value) ||
        CacheableToken::isTombstone(value)) {
      value = nullptr;
    }
    return err;
  }

  m_regionStats->incGets();
  auto& cachePerfStats = m_cacheImpl->getCachePerfStats();
  cachePerfStats.incGets();

  // TODO:  CacheableToken::isInvalid should be completely hidden
  // inside MapSegment; this should be done both for the value obtained
  // from local cache as well as oldValue in every instance
  std::shared_ptr<MapEntryImpl> me;
  int updateCount = -1;
  bool isLoaderInvoked = false;
  bool isLocal = false;
  bool cachingEnabled = m_regionAttributes.getCachingEnabled();
  std::shared_ptr<Cacheable> localValue = nullptr;
  if (cachingEnabled) {
    isLocal = m_entries->get(keyPtr, value, me);
    if (isLocal && (value != nullptr && !CacheableToken::isInvalid(value))) {
      m_regionStats->incHits();
      cachePerfStats.incHits();
      updateAccessAndModifiedTimeForEntry(me, false);
      updateAccessAndModifiedTime(false);
      return err;  // found it in local cache...
    }
    localValue = value;
    value = nullptr;
    // start tracking the entry
    if (!m_regionAttributes.getConcurrencyChecksEnabled()) {
      updateCount =
          m_entries->addTrackerForEntry(keyPtr, value, true, false, false);
      LOGDEBUG(
          "Region::get: added tracking with update counter [%d] for key "
          "[%s] with value [%s]",
          updateCount, Utils::nullSafeToString(keyPtr).c_str(),
          Utils::nullSafeToString(value).c_str());
    }
  }

  // remove tracking for the entry before exiting the function
  struct RemoveTracking {
   private:
    const std::shared_ptr<CacheableKey>& m_key;
    const int& m_updateCount;
    LocalRegion& m_region;

   public:
    RemoveTracking(const std::shared_ptr<CacheableKey>& key,
                   const int& updateCount, LocalRegion& region)
        : m_key(key), m_updateCount(updateCount), m_region(region) {}
    ~RemoveTracking() {
      if (m_updateCount >= 0 &&
          !m_region.getAttributes().getConcurrencyChecksEnabled()) {
        m_region.m_entries->removeTrackerForEntry(m_key);
      }
    }
  } _removeTracking(keyPtr, updateCount, *this);

  // The control will come here only when caching is disabled or/and
  // the entry was not found. In this case atleast update the region
  // access times.
  updateAccessAndModifiedTime(false);
  m_regionStats->incMisses();

  cachePerfStats.incMisses();
  std::shared_ptr<VersionTag> versionTag;
  // Get from some remote source (e.g. external java server) if required.
  err = getNoThrow_remote(keyPtr, value, aCallbackArgument, versionTag);

  // Its a cache missor it is invalid token then Check if we have a local
  // loader.
  if ((value == nullptr || CacheableToken::isInvalid(value) ||
       CacheableToken::isTombstone(value)) &&
      m_loader != nullptr) {
    try {
      isLoaderInvoked = true;
      /*Update the statistics*/
      int64_t sampleStartNanos = startStatOpTime();
      value = m_loader->load(*this, keyPtr, aCallbackArgument);
      updateStatOpTime(m_regionStats->getStat(),
                       m_regionStats->getLoaderCallTimeId(), sampleStartNanos);
      m_regionStats->incLoaderCallsCompleted();
    } catch (const Exception& ex) {
      LOGERROR("Error in CacheLoader::load: %s: %s", ex.getName().c_str(),
               ex.what());
      err = GF_CACHE_LOADER_EXCEPTION;
    } catch (...) {
      LOGERROR("Error in CacheLoader::load, unknown");
      err = GF_CACHE_LOADER_EXCEPTION;
    }
    if (err != GF_NOERR) {
      return err;
    }
  }

  std::shared_ptr<Cacheable> oldValue;
  // Found it somehow, so store it.
  if (value != nullptr /*&& value != CacheableToken::invalid( )*/ &&
      cachingEnabled &&
      !(CacheableToken::isTombstone(value) &&
        (localValue == nullptr || CacheableToken::isInvalid(localValue)))) {
    //  try to create the entry and if that returns an existing value
    // (e.g. from another thread or notification) then return that
    LOGDEBUG(
        "Region::get: creating entry with tracking update counter [%d] for "
        "key "
        "[%s]",
        updateCount, Utils::nullSafeToString(keyPtr).c_str());
    if ((err = putLocal("Region::get", false, keyPtr, value, oldValue,
                        cachingEnabled, updateCount, 0, versionTag)) !=
        GF_NOERR) {
      if (err == GF_CACHE_CONCURRENT_MODIFICATION_EXCEPTION) {
        LOGDEBUG(
            "Region::get: putLocal for key [%s] failed because the cache already contains \
          an entry with higher version.",
            Utils::nullSafeToString(keyPtr).c_str());
        if (CacheableToken::isInvalid(value) ||
            CacheableToken::isTombstone(value)) {
          value = nullptr;
        }
        // don't do anything and  exit
        return GF_NOERR;
      }

      LOGDEBUG("Region::get: putLocal for key [%s] failed with error %d",
               Utils::nullSafeToString(keyPtr).c_str(), err);
      err = GF_NOERR;
      if (oldValue != nullptr && !CacheableToken::isInvalid(oldValue)) {
        LOGDEBUG("Region::get: returning updated value [%s] for key [%s]",
                 Utils::nullSafeToString(oldValue).c_str(),
                 Utils::nullSafeToString(keyPtr).c_str());
        value = oldValue;
      }
    }
  }

  if (CacheableToken::isInvalid(value) || CacheableToken::isTombstone(value)) {
    value = nullptr;
  }

  // invokeCacheListenerForEntryEvent method has the check that if oldValue
  // is a CacheableToken then it sets it to nullptr; also determines if it
  // should be AFTER_UPDATE or AFTER_CREATE depending on oldValue, so don't
  // check here.
  if (isLoaderInvoked == false && err == GF_NOERR && value != nullptr) {
    err = invokeCacheListenerForEntryEvent(
        keyPtr, oldValue, value, aCallbackArgument, CacheEventFlags::NORMAL,
        AFTER_UPDATE, isLocal);
  }

  return err;
}