in cachelib/allocator/nvmcache/NvmCache-inl.h [444:535]
void NvmCache<C>::put(ItemHandle& hdl, PutToken token) {
util::LatencyTracker tracker(stats().nvmInsertLatency_);
XDCHECK(hdl);
auto& item = *hdl;
// for regular items that can only write to nvmcache upon eviction, we
// should not be recording a write for an nvmclean item unless it is marked
// as evicted from nvmcache.
if (item.isNvmClean() && !item.isNvmEvicted()) {
throw std::runtime_error(folly::sformat(
"Item is not nvm evicted and nvm clean {}", item.toString()));
}
if (item.isChainedItem()) {
throw std::invalid_argument(
folly::sformat("Invalid item {}", item.toString()));
}
// we skip writing if we know that the item is expired or has chained items
if (!isEnabled() || item.isExpired()) {
return;
}
stats().numNvmPuts.inc();
if (hasTombStone(item.getKey())) {
stats().numNvmAbortedPutOnTombstone.inc();
return;
}
auto nvmItem = makeNvmItem(hdl);
if (!nvmItem) {
stats().numNvmPutEncodeFailure.inc();
return;
}
if (item.isNvmClean() && item.isNvmEvicted()) {
stats().numNvmPutFromClean.inc();
}
auto iobuf = toIOBuf(std::move(nvmItem));
const auto valSize = iobuf.length();
auto val = folly::ByteRange{iobuf.data(), iobuf.length()};
auto shard = getShardForKey(item.getKey());
auto& putContexts = putContexts_[shard];
auto& ctx = putContexts.createContext(item.getKey(), std::move(iobuf),
std::move(tracker));
// capture array reference for putContext. it is stable
auto putCleanup = [&putContexts, &ctx]() { putContexts.destroyContext(ctx); };
auto guard = folly::makeGuard([putCleanup]() { putCleanup(); });
// On a concurrent get, we remove the key from inflight evictions and hence
// key not being present means a concurrent get happened with an inflight
// eviction, and we should abandon this write to navy since we already
// reported the key doesn't exist in the cache.
const bool executed = token.executeIfValid([&]() {
auto status = navyCache_->insertAsync(
makeBufferView(ctx.key()), makeBufferView(val),
[this, putCleanup, valSize, val](navy::Status st,
navy::BufferView key) {
if (st == navy::Status::Ok) {
stats().nvmPutSize_.trackValue(valSize);
} else if (st == navy::Status::BadState) {
// we set disable navy since we got a BadState from navy
disableNavy("Delete Failure. BadState");
} else {
// put failed, DRAM eviction happened and destructor was not
// executed. we unconditionally trigger destructor here for cleanup.
evictCB(key, makeBufferView(val), navy::DestructorEvent::PutFailed);
}
putCleanup();
});
if (status == navy::Status::Ok) {
guard.dismiss();
// mark it as NvmClean and unNvmEvicted if we put it into the queue
// so handle destruction awares that there's a NVM copy (at least in the
// queue)
item.markNvmClean();
item.unmarkNvmEvicted();
} else {
stats().numNvmPutErrs.inc();
}
});
// if insertAsync is not executed or put into scheduler queue successfully,
// NvmClean is not marked for the item, destructor of the item will be invoked
// upon handle release.
if (!executed) {
stats().numNvmAbortedPutOnInflightGet.inc();
}
}