foundationdb/fdbclient/include/fdbclient/TenantEntryCache.actor.h

591 lines
24 KiB
C++

/*
* TenantEntryCache.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* 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.
*/
#if defined(NO_INTELLISENSE) && !defined(FDBCLIENT_TENANTENTRYCACHE_ACTOR_G_H)
#define FDBCLIENT_TENANTENTRYCACHE_ACTOR_G_H
#include "fdbclient/TenantEntryCache.actor.g.h"
#elif !defined(FDBCLIENT_TENANTENTRYCACHE_ACTOR_H)
#define FDBCLIENT_TENANTENTRYCACHE_ACTOR_H
#pragma once
#include "fdbclient/DatabaseContext.h"
#include "fdbclient/FDBOptions.g.h"
#include "fdbclient/FDBTypes.h"
#include "fdbclient/RunRYWTransaction.actor.h"
#include "fdbclient/Tenant.h"
#include "fdbclient/TenantManagement.actor.h"
#include "fdbclient/Knobs.h"
#include "fdbrpc/TenantName.h"
#include "flow/IndexedSet.h"
#include <functional>
#include <unordered_map>
#include "flow/actorcompiler.h" // has to be last include
using TenantNameEntryPair = std::pair<TenantName, TenantMapEntry>;
using TenantNameEntryPairVec = std::vector<TenantNameEntryPair>;
enum class TenantEntryCacheRefreshReason {
INIT = 1,
PERIODIC_TASK = 2,
CACHE_MISS = 3,
REMOVE_ENTRY = 4,
WATCH_TRIGGER = 5
};
enum class TenantEntryCacheRefreshMode { PERIODIC_TASK = 1, WATCH = 2, NONE = 3 };
template <class T>
struct TenantEntryCachePayload {
TenantMapEntry entry;
// Custom client payload
T payload;
};
template <class T>
using TenantEntryCachePayloadFunc = std::function<TenantEntryCachePayload<T>(const TenantMapEntry&)>;
// In-memory cache for TenantEntryMap objects. It supports three indices:
// 1. Lookup by 'TenantId'
// 2. Lookup by 'TenantPrefix'
// 3. Lookup by 'TenantName'
// TODO: Currently this cache performs poorly if there are tenant access happening to unknown tenants which happens most
// frequently in optional tenant mode but can also happen in required mode if there are alot of tenants created. Further
// as a consequence of the design we cannot be sure that the state of a given tenant is accurate even if its present in
// the cache.
template <class T>
class TenantEntryCache : public ReferenceCounted<TenantEntryCache<T>>, NonCopyable {
private:
UID uid;
Database db;
TenantEntryCachePayloadFunc<T> createPayloadFunc;
TenantEntryCacheRefreshMode refreshMode;
Future<Void> refresher;
Future<Void> watchRefresher;
Future<Void> lastTenantIdRefresher;
Promise<Void> setInitialWatch;
Optional<int64_t> lastTenantId;
Map<int64_t, TenantEntryCachePayload<T>> mapByTenantId;
Map<TenantName, TenantEntryCachePayload<T>> mapByTenantName;
CounterCollection metrics;
Counter hits;
Counter misses;
Counter refreshByCacheInit;
Counter refreshByCacheMiss;
Counter numRefreshes;
Counter refreshByWatchTrigger;
ACTOR static Future<std::vector<std::pair<int64_t, TenantMapEntry>>> getTenantList(
Reference<ReadYourWritesTransaction> tr) {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE);
KeyBackedRangeResult<std::pair<int64_t, TenantMapEntry>> tenantList =
wait(TenantMetadata::tenantMap().getRange(tr, {}, {}, CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1));
ASSERT(tenantList.results.size() <= CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER && !tenantList.more);
TraceEvent(SevDebug, "TenantEntryCacheGetTenantList").detail("Count", tenantList.results.size());
return tenantList.results;
}
ACTOR static Future<Void> refreshCacheById(int64_t tenantId,
TenantEntryCache<T>* cache,
TenantEntryCacheRefreshReason reason) {
TraceEvent(SevDebug, "TenantEntryCacheIDRefreshStart", cache->id()).detail("Reason", static_cast<int>(reason));
state Reference<ReadYourWritesTransaction> tr = cache->getDatabase()->createTransaction();
loop {
try {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE);
state Optional<TenantMapEntry> entry = wait(TenantMetadata::tenantMap().get(tr, tenantId));
if (entry.present()) {
cache->put(entry.get());
updateCacheRefreshMetrics(cache, reason);
}
break;
} catch (Error& e) {
wait(tr->onError(e));
}
}
TraceEvent(SevDebug, "TenantEntryCacheIDRefreshEnd", cache->id()).detail("Reason", static_cast<int>(reason));
return Void();
}
ACTOR static Future<Void> refreshCacheByName(TenantName name,
TenantEntryCache<T>* cache,
TenantEntryCacheRefreshReason reason) {
TraceEvent(SevDebug, "TenantEntryCacheNameRefreshStart", cache->id())
.detail("Reason", static_cast<int>(reason));
state Reference<ReadYourWritesTransaction> tr = cache->getDatabase()->createTransaction();
loop {
try {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE);
state Optional<int64_t> tenantId = wait(TenantMetadata::tenantNameIndex().get(tr, name));
if (tenantId.present()) {
Optional<TenantMapEntry> entry = wait(TenantMetadata::tenantMap().get(tr, tenantId.get()));
if (entry.present()) {
cache->put(entry.get());
updateCacheRefreshMetrics(cache, reason);
}
}
break;
} catch (Error& e) {
wait(tr->onError(e));
}
}
TraceEvent(SevDebug, "TenantEntryCacheNameRefreshEnd", cache->id()).detail("Reason", static_cast<int>(reason));
return Void();
}
static void updateCacheRefreshMetrics(TenantEntryCache<T>* cache, TenantEntryCacheRefreshReason reason) {
if (reason == TenantEntryCacheRefreshReason::INIT) {
cache->refreshByCacheInit += 1;
} else if (reason == TenantEntryCacheRefreshReason::CACHE_MISS) {
cache->refreshByCacheMiss += 1;
} else if (reason == TenantEntryCacheRefreshReason::WATCH_TRIGGER) {
cache->refreshByWatchTrigger += 1;
}
cache->numRefreshes += 1;
}
ACTOR static Future<Void> refreshCacheUsingWatch(TenantEntryCache<T>* cache, TenantEntryCacheRefreshReason reason) {
TraceEvent(SevDebug, "TenantEntryCacheRefreshUsingWatchStart", cache->id())
.detail("Reason", static_cast<int>(reason));
state Reference<ReadYourWritesTransaction> tr = cache->getDatabase()->createTransaction();
loop {
try {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
state Future<Void> tenantModifiedWatch = TenantMetadata::lastTenantModification().watch(tr);
wait(tr->commit());
TraceEvent(SevDebug, "TenantEntryCacheRefreshWatchSet", cache->id());
// setInitialWatch is set to indicate that an inital watch has been set for the lastTenantModification
// key. Currently this is only used in simulation to avoid a race condition where a tenant is created
// before the inital watch is set. However, it can be enabled by passing waitForInitalWatch = true to
// the init() method.
if (cache->setInitialWatch.canBeSet()) {
cache->setInitialWatch.send(Void());
}
wait(tenantModifiedWatch);
// If watch triggered then refresh the cache as tenant metadata was updated
TraceEvent(SevDebug, "TenantEntryCacheRefreshUsingWatchTriggered", cache->id())
.detail("Reason", static_cast<int>(reason));
wait(refreshImpl(cache, reason));
tr->reset();
} catch (Error& e) {
if (e.code() != error_code_actor_cancelled) {
TraceEvent("TenantEntryCacheRefreshUsingWatchError", cache->id())
.errorUnsuppressed(e)
.suppressFor(1.0);
}
wait(tr->onError(e));
// In case the watch threw an error then refresh the cache just in case it was updated
wait(refreshImpl(cache, reason));
}
}
}
static bool tenantsEnabled(TenantEntryCache<T>* cache) {
// Avoid using the cache if the tenant mode is disabled. However since we use clientInfo, sometimes it may not
// be fully up to date (i.e it may indicate the tenantMode is disabled when in fact it is required). Thus if
// there is at least one tenant that has been created on the cluster then use the cache to avoid an incorrect
// miss.
if (cache->getDatabase()->clientInfo->get().tenantMode == TenantMode::DISABLED) {
if (!cache->lastTenantId.present()) {
return false;
}
return cache->lastTenantId.get() > 0;
}
return true;
}
ACTOR static Future<Void> setLastTenantId(TenantEntryCache<T>* cache) {
state Reference<ReadYourWritesTransaction> tr = cache->getDatabase()->createTransaction();
loop {
try {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE);
tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
Optional<int64_t> lastTenantId = wait(TenantMetadata::lastTenantId().get(tr));
cache->lastTenantId = lastTenantId;
return Void();
} catch (Error& e) {
wait(tr->onError(e));
}
}
}
ACTOR static Future<Void> lastTenantIdWatch(TenantEntryCache<T>* cache) {
TraceEvent(SevDebug, "TenantEntryCacheLastTenantIdWatchStart", cache->id());
// monitor for any changes on the last tenant id and update it as necessary
state Reference<ReadYourWritesTransaction> tr = cache->getDatabase()->createTransaction();
loop {
try {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
state Future<Void> lastTenantIdWatch = tr->watch(TenantMetadata::lastTenantId().key);
wait(tr->commit());
wait(lastTenantIdWatch);
wait(setLastTenantId(cache));
tr->reset();
} catch (Error& e) {
state Error err(e);
if (err.code() != error_code_actor_cancelled) {
TraceEvent("TenantEntryCacheLastTenantIdWatchError", cache->id())
.errorUnsuppressed(err)
.suppressFor(1.0);
// In case watch errors out refresh the lastTenantId in case it has changed or we would have missed
// an update
wait(setLastTenantId(cache));
}
wait(tr->onError(err));
}
}
}
ACTOR static Future<Void> refreshImpl(TenantEntryCache<T>* cache, TenantEntryCacheRefreshReason reason) {
TraceEvent(SevDebug, "TenantEntryCacheRefreshStart", cache->id()).detail("Reason", static_cast<int>(reason));
state Reference<ReadYourWritesTransaction> tr = cache->getDatabase()->createTransaction();
loop {
try {
state std::vector<std::pair<int64_t, TenantMapEntry>> tenantList = wait(getTenantList(tr));
// Refresh cache entries reflecting the latest database state
cache->clear();
for (auto& tenant : tenantList) {
cache->put(tenant.second);
}
updateCacheRefreshMetrics(cache, reason);
break;
} catch (Error& e) {
if (e.code() != error_code_actor_cancelled) {
TraceEvent("TenantEntryCacheRefreshError", cache->id()).errorUnsuppressed(e).suppressFor(1.0);
}
wait(tr->onError(e));
}
}
TraceEvent(SevDebug, "TenantEntryCacheRefreshEnd", cache->id()).detail("Reason", static_cast<int>(reason));
return Void();
}
ACTOR static Future<Optional<TenantEntryCachePayload<T>>> getByIdImpl(TenantEntryCache<T>* cache,
int64_t tenantId) {
Optional<TenantEntryCachePayload<T>> ret = cache->lookupById(tenantId);
if (ret.present()) {
cache->hits += 1;
return ret;
}
if (!tenantsEnabled(cache)) {
// If tenants are disabled on the cluster avoid using the cache
return Optional<TenantEntryCachePayload<T>>();
}
TraceEvent("TenantEntryCacheGetByIdRefresh").detail("TenantId", tenantId);
if (cache->refreshMode == TenantEntryCacheRefreshMode::WATCH) {
// Entry not found. Do a point refresh
// TODO: Don't initiate refresh if tenantId < maxTenantId (stored as a system key currently) as we know that
// such a tenant does not exist (it has either never existed or has been deleted)
wait(refreshCacheById(tenantId, cache, TenantEntryCacheRefreshReason::CACHE_MISS));
} else {
// Entry not found. Refresh cacheEntries by scanning underlying KeyRange.
wait(refreshImpl(cache, TenantEntryCacheRefreshReason::CACHE_MISS));
}
cache->misses += 1;
return cache->lookupById(tenantId);
}
ACTOR static Future<Optional<TenantEntryCachePayload<T>>> getByNameImpl(TenantEntryCache<T>* cache,
TenantName name) {
Optional<TenantEntryCachePayload<T>> ret = cache->lookupByName(name);
if (ret.present()) {
cache->hits += 1;
return ret;
}
if (!tenantsEnabled(cache)) {
// If tenants are disabled on the cluster avoid using the cache
return Optional<TenantEntryCachePayload<T>>();
}
TraceEvent("TenantEntryCacheGetByNameRefresh").detail("TenantName", name);
if (cache->refreshMode == TenantEntryCacheRefreshMode::WATCH) {
// Entry not found. Do a point refresh
wait(refreshCacheByName(name, cache, TenantEntryCacheRefreshReason::CACHE_MISS));
} else {
// Entry not found. Refresh cacheEntries by scanning underlying KeyRange.
wait(refreshImpl(cache, TenantEntryCacheRefreshReason::CACHE_MISS));
}
cache->misses += 1;
return cache->lookupByName(name);
}
Optional<TenantEntryCachePayload<T>> lookupById(int64_t tenantId) {
Optional<TenantEntryCachePayload<T>> ret;
auto itr = mapByTenantId.find(tenantId);
if (itr == mapByTenantId.end()) {
return ret;
}
return itr->value;
}
Optional<TenantEntryCachePayload<T>> lookupByName(TenantName name) {
Optional<TenantEntryCachePayload<T>> ret;
auto itr = mapByTenantName.find(name);
if (itr == mapByTenantName.end()) {
return ret;
}
return itr->value;
}
Future<Void> refresh(TenantEntryCacheRefreshReason reason) { return refreshImpl(this, reason); }
static TenantEntryCachePayload<Void> defaultCreatePayload(const TenantMapEntry& entry) {
TenantEntryCachePayload<Void> payload;
payload.entry = entry;
return payload;
}
Future<Void> removeEntryInt(Optional<int64_t> tenantId,
Optional<KeyRef> tenantPrefix,
Optional<TenantName> tenantName,
bool refreshCache) {
typename Map<int64_t, TenantEntryCachePayload<T>>::iterator itrId;
typename Map<TenantName, TenantEntryCachePayload<T>>::iterator itrName;
if (tenantId.present() || tenantPrefix.present()) {
// Ensure either tenantId OR tenantPrefix is valid (but not both)
ASSERT(tenantId.present() != tenantPrefix.present());
ASSERT(!tenantName.present());
int64_t tId = tenantId.present() ? tenantId.get() : TenantAPI::prefixToId(tenantPrefix.get());
TraceEvent("TenantEntryCacheRemoveEntry").detail("Id", tId);
itrId = mapByTenantId.find(tId);
if (itrId == mapByTenantId.end()) {
return Void();
}
// Ensure byId and byName cache are in-sync
itrName = mapByTenantName.find(itrId->value.entry.tenantName);
ASSERT(itrName != mapByTenantName.end());
} else if (tenantName.present()) {
ASSERT(!tenantId.present() && !tenantPrefix.present());
TraceEvent("TenantEntryCacheRemoveEntry").detail("Name", tenantName.get());
itrName = mapByTenantName.find(tenantName.get());
if (itrName == mapByTenantName.end()) {
return Void();
}
// Ensure byId and byName cache are in-sync
itrId = mapByTenantId.find(itrName->value.entry.id);
ASSERT(itrId != mapByTenantId.end());
} else {
// Invalid input, one of: tenantId, tenantPrefix or tenantName needs to be valid.
throw operation_failed();
}
ASSERT(itrId != mapByTenantId.end() && itrName != mapByTenantName.end());
TraceEvent("TenantEntryCacheRemoveEntry")
.detail("Id", itrId->key)
.detail("Prefix", itrId->value.entry.prefix)
.detail("Name", itrName->key);
mapByTenantId.erase(itrId);
mapByTenantName.erase(itrName);
if (refreshCache) {
return refreshImpl(this, TenantEntryCacheRefreshReason::REMOVE_ENTRY);
}
return Void();
}
public:
TenantEntryCache(Database db)
: uid(deterministicRandom()->randomUniqueID()), db(db), createPayloadFunc(defaultCreatePayload),
refreshMode(TenantEntryCacheRefreshMode::PERIODIC_TASK), metrics("TenantEntryCacheMetrics", uid.toString()),
hits("TenantEntryCacheHits", metrics), misses("TenantEntryCacheMisses", metrics),
refreshByCacheInit("TenantEntryCacheRefreshInit", metrics),
refreshByCacheMiss("TenantEntryCacheRefreshMiss", metrics),
numRefreshes("TenantEntryCacheNumRefreshes", metrics),
refreshByWatchTrigger("TenantEntryCacheRefreshWatchTrigger", metrics) {
TraceEvent("TenantEntryCacheCreatedDefaultFunc", uid);
}
TenantEntryCache(Database db, TenantEntryCacheRefreshMode mode)
: uid(deterministicRandom()->randomUniqueID()), db(db), createPayloadFunc(defaultCreatePayload),
refreshMode(mode), metrics("TenantEntryCacheMetrics", uid.toString()), hits("TenantEntryCacheHits", metrics),
misses("TenantEntryCacheMisses", metrics), refreshByCacheInit("TenantEntryCacheRefreshInit", metrics),
refreshByCacheMiss("TenantEntryCacheRefreshMiss", metrics),
numRefreshes("TenantEntryCacheNumRefreshes", metrics),
refreshByWatchTrigger("TenantEntryCacheRefreshWatchTrigger", metrics) {
TraceEvent("TenantEntryCacheCreatedDefaultFunc", uid);
}
TenantEntryCache(Database db, TenantEntryCachePayloadFunc<T> fn)
: uid(deterministicRandom()->randomUniqueID()), db(db), createPayloadFunc(fn),
refreshMode(TenantEntryCacheRefreshMode::PERIODIC_TASK), metrics("TenantEntryCacheMetrics", uid.toString()),
hits("TenantEntryCacheHits", metrics), misses("TenantEntryCacheMisses", metrics),
refreshByCacheInit("TenantEntryCacheRefreshInit", metrics),
refreshByCacheMiss("TenantEntryCacheRefreshMiss", metrics),
numRefreshes("TenantEntryCacheNumRefreshes", metrics),
refreshByWatchTrigger("TenantEntryCacheRefreshWatchTrigger", metrics) {
TraceEvent("TenantEntryCacheCreated", uid);
}
TenantEntryCache(Database db, UID id, TenantEntryCachePayloadFunc<T> fn)
: uid(id), db(db), createPayloadFunc(fn), refreshMode(TenantEntryCacheRefreshMode::PERIODIC_TASK),
metrics("TenantEntryCacheMetrics", uid.toString()), hits("TenantEntryCacheHits", metrics),
misses("TenantEntryCacheMisses", metrics), refreshByCacheInit("TenantEntryCacheRefreshInit", metrics),
refreshByCacheMiss("TenantEntryCacheRefreshMiss", metrics),
numRefreshes("TenantEntryCacheNumRefreshes", metrics),
refreshByWatchTrigger("TenantEntryCacheRefreshWatchTrigger", metrics) {
TraceEvent("TenantEntryCacheCreated", uid);
}
TenantEntryCache(Database db, UID id, TenantEntryCachePayloadFunc<T> fn, TenantEntryCacheRefreshMode mode)
: uid(id), db(db), createPayloadFunc(fn), refreshMode(mode), metrics("TenantEntryCacheMetrics", uid.toString()),
hits("TenantEntryCacheHits", metrics), misses("TenantEntryCacheMisses", metrics),
refreshByCacheInit("TenantEntryCacheRefreshInit", metrics),
refreshByCacheMiss("TenantEntryCacheRefreshMiss", metrics),
numRefreshes("TenantEntryCacheNumRefreshes", metrics),
refreshByWatchTrigger("TenantEntryCacheRefreshWatchTrigger", metrics) {
TraceEvent("TenantEntryCacheCreated", uid);
}
Future<Void> init(bool waitForInitalWatch = false) {
TraceEvent("TenantEntryCacheInit", uid);
Future<Void> f = refreshImpl(this, TenantEntryCacheRefreshReason::INIT);
// Launch reaper task to periodically refresh cache by scanning database KeyRange
TenantEntryCacheRefreshReason reason = TenantEntryCacheRefreshReason::PERIODIC_TASK;
Future<Void> initalWatchFuture = Void();
lastTenantIdRefresher = lastTenantIdWatch(this);
if (refreshMode == TenantEntryCacheRefreshMode::PERIODIC_TASK) {
refresher = recurringAsync([&, reason]() { return refresh(reason); },
CLIENT_KNOBS->TENANT_ENTRY_CACHE_LIST_REFRESH_INTERVAL, /* interval */
true, /* absoluteIntervalDelay */
CLIENT_KNOBS->TENANT_ENTRY_CACHE_LIST_REFRESH_INTERVAL, /* intialDelay */
TaskPriority::Worker);
} else if (refreshMode == TenantEntryCacheRefreshMode::WATCH) {
if (waitForInitalWatch) {
initalWatchFuture = setInitialWatch.getFuture();
}
watchRefresher = refreshCacheUsingWatch(this, TenantEntryCacheRefreshReason::WATCH_TRIGGER);
}
Future<Void> setLastTenant = setLastTenantId(this);
return f && initalWatchFuture && setLastTenant;
}
Database getDatabase() const { return db; }
UID id() const { return uid; }
void clear() {
mapByTenantId.clear();
mapByTenantName.clear();
}
Future<Void> removeEntryById(int64_t tenantId, bool refreshCache = false) {
return removeEntryInt(tenantId, Optional<KeyRef>(), Optional<TenantName>(), refreshCache);
}
Future<Void> removeEntryByPrefix(KeyRef tenantPrefix, bool refreshCache = false) {
return removeEntryInt(Optional<int64_t>(), tenantPrefix, Optional<TenantName>(), refreshCache);
}
Future<Void> removeEntryByName(TenantName tenantName, bool refreshCache = false) {
return removeEntryInt(Optional<int64_t>(), Optional<KeyRef>(), tenantName, refreshCache);
}
void put(const TenantMapEntry& entry) {
TenantEntryCachePayload<T> payload = createPayloadFunc(entry);
auto idItr = mapByTenantId.find(entry.id);
auto nameItr = mapByTenantName.find(entry.tenantName);
Optional<TenantName> existingName;
Optional<int64_t> existingId;
if (nameItr != mapByTenantName.end()) {
existingId = nameItr->value.entry.id;
}
if (idItr != mapByTenantId.end()) {
existingName = idItr->value.entry.tenantName;
}
if (existingId.present()) {
mapByTenantId.erase(existingId.get());
}
if (existingName.present()) {
mapByTenantName.erase(existingName.get());
}
mapByTenantId[entry.id] = payload;
mapByTenantName[entry.tenantName] = payload;
TraceEvent("TenantEntryCachePut")
.detail("TenantName", entry.tenantName)
.detail("TenantNameExisting", existingName)
.detail("TenantID", entry.id)
.detail("TenantIDExisting", existingId)
.detail("TenantPrefix", entry.prefix);
CODE_PROBE(idItr == mapByTenantId.end() && nameItr == mapByTenantName.end(), "TenantCache new entry");
CODE_PROBE(idItr != mapByTenantId.end() && nameItr == mapByTenantName.end(), "TenantCache entry name updated");
CODE_PROBE(idItr == mapByTenantId.end() && nameItr != mapByTenantName.end(), "TenantCache entry id updated");
CODE_PROBE(idItr != mapByTenantId.end() && nameItr != mapByTenantName.end(),
"TenantCache entry id and name updated");
}
Future<Optional<TenantEntryCachePayload<T>>> getById(int64_t tenantId) { return getByIdImpl(this, tenantId); }
Future<Optional<TenantEntryCachePayload<T>>> getByPrefix(KeyRef prefix) {
int64_t id = TenantAPI::prefixToId(prefix);
return getByIdImpl(this, id);
}
Future<Optional<TenantEntryCachePayload<T>>> getByName(TenantName name) { return getByNameImpl(this, name); }
// Counter access APIs
Counter::Value numCacheRefreshes() const { return numRefreshes.getValue(); }
Counter::Value numRefreshByMisses() const { return refreshByCacheMiss.getValue(); }
Counter::Value numRefreshByInit() const { return refreshByCacheInit.getValue(); }
Counter::Value numWatchRefreshes() const { return refreshByWatchTrigger.getValue(); }
};
#include "flow/unactorcompiler.h"
#endif // FDBCLIENT_TENANTENTRYCACHE_ACTOR_H