void NvmCache::put()

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