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;
}