/**
   * @param key Key.
   * @param part Partition.
   * @param locVals Local values.
   * @return {@code True} if there is no need to further search value.
   */
  private boolean localGet(KeyCacheObject key, int part, Map<K, V> locVals) {
    assert cctx.affinityNode() : this;

    GridDhtCacheAdapter<K, V> cache = cache();

    while (true) {
      GridCacheEntryEx entry;

      try {
        entry = cache.context().isSwapOrOffheapEnabled() ? cache.entryEx(key) : cache.peekEx(key);

        // If our DHT cache do has value, then we peek it.
        if (entry != null) {
          boolean isNew = entry.isNewLocked();

          CacheObject v = null;
          GridCacheVersion ver = null;

          if (needVer) {
            T2<CacheObject, GridCacheVersion> res =
                entry.innerGetVersioned(
                    null,
                    null,
                    /*swap*/ true,
                    /*unmarshal*/ true,
                    /** update-metrics */
                    false,
                    /*event*/ !skipVals,
                    subjId,
                    null,
                    taskName,
                    expiryPlc,
                    !deserializeBinary);

            if (res != null) {
              v = res.get1();
              ver = res.get2();
            }
          } else {
            v =
                entry.innerGet(
                    null,
                    null,
                    /*swap*/ true,
                    /*read-through*/ false,
                    /** update-metrics */
                    false,
                    /*event*/ !skipVals,
                    /*temporary*/ false,
                    subjId,
                    null,
                    taskName,
                    expiryPlc,
                    !deserializeBinary);
          }

          cache.context().evicts().touch(entry, topVer);

          // Entry was not in memory or in swap, so we remove it from cache.
          if (v == null) {
            if (isNew && entry.markObsoleteIfEmpty(ver)) cache.removeEntry(entry);
          } else {
            cctx.addResult(
                locVals, key, v, skipVals, keepCacheObjects, deserializeBinary, true, ver);

            return true;
          }
        }

        boolean topStable = cctx.isReplicated() || topVer.equals(cctx.topology().topologyVersion());

        // Entry not found, do not continue search if topology did not change and there is no store.
        if (!cctx.readThroughConfigured() && (topStable || partitionOwned(part))) {
          if (!skipVals && cctx.config().isStatisticsEnabled()) cache.metrics0().onRead(false);

          return true;
        }

        return false;
      } catch (GridCacheEntryRemovedException ignored) {
        // No-op, will retry.
      } catch (GridDhtInvalidPartitionException ignored) {
        return false;
      } catch (IgniteCheckedException e) {
        onDone(e);

        return true;
      }
    }
  }