private boolean eventuallyLoadFurtherValues(
      final String source,
      final FDate key,
      final FDate adjustedKey,
      final boolean newMinKey,
      final boolean forced) {
    if (forced || shouldLoadFurtherValues(key, newMinKey)) {
      final FDate keyForReadAllValues;
      if (newMinKey
          && minKeyInDBFromLoadFurtherValues != null
          && key.isBefore(minKeyInDBFromLoadFurtherValues)) {
        // performance optimization for first load
        keyForReadAllValues = FDates.min(minKeyInDB, FDates.max(minKeyInDB, adjustedKey));
      } else {
        keyForReadAllValues = FDates.max(minKeyInDB, adjustedKey);
      }
      furtherValues.clear();
      lastValuesFromFurtherValues.clear();
      furtherValues.addAll(readAllValuesAscendingFrom(keyForReadAllValues));

      if (!furtherValues.isEmpty()) {
        assertFurtherValuesSorting(key);
      }
      return true;
    }
    return false;
  }
  private boolean shouldLoadFurtherValues(final FDate key, final boolean newMinKey) {
    if (furtherValues.isEmpty()) {
      final V tail = lastValuesFromFurtherValues.getTail();
      if (tail == null) {
        return true;
      }
      final V head = lastValuesFromFurtherValues.getHead();
      final FDate tailKey = extractKey(key, tail);
      final FDate headKey = extractKey(key, head);
      final boolean isEndReachedAnyway =
          tailKey.equals(maxKeyInDB)
              && key.isBeforeOrEqualTo(maxKeyInDB)
              && headKey.isBeforeOrEqualTo(key);
      return !isEndReachedAnyway;
    }
    final boolean keyIsBeforeMinKeyFromLoadFurtherValues =
        newMinKey && key.isBefore(minKeyInDBFromLoadFurtherValues);
    if (keyIsBeforeMinKeyFromLoadFurtherValues) {
      return true;
    }
    final boolean newMinKeyFromDBMayFindNewValues =
        isMinKeyInDBFromLoadFurtherValues() && key.compareTo(minKeyInDB) <= -1 && newMinKey;
    if (newMinKeyFromDBMayFindNewValues) {
      return true;
    }

    return false;
  }
  private V readNewestValueFromDB(final FDate key) {
    // we give up and use the newest value from db
    V value = readLatestValueFor(key);

    // try to use first value of furthervalues
    if (value == null && furtherValuesLoaded && !furtherValues.isEmpty()) {
      value = furtherValues.getHead();
    }

    if (value != null) {
      // we remember the db key of the value so that it can be found again later
      // to use the parameter key would make the result incorrect
      final FDate valueKey = extractKey(key, value);
      getValuesMap().put(valueKey, value);
      return value;
    } else {
      return (V) null;
    }
  }
  private boolean isPotentiallyAlreadyEvicted(final FDate key, final V value) {
    final boolean isEvictedBeforeCurrentFurtherValues =
        (value == null || extractKey(key, value).isAfter(key))
            && (key.isAfter(minKeyInDB) || key.isAfter(minKeyInDBFromLoadFurtherValues));
    if (isEvictedBeforeCurrentFurtherValues) {
      return true;
    }
    final boolean mightBeEvictedAfterFurtherValues = value != null && furtherValues.isEmpty();
    if (mightBeEvictedAfterFurtherValues) {
      final FDate valueKey = extractKey(key, value);
      final boolean isEvictedAfterCurrentFurtherValues =
          valueKey.isBefore(key) && valueKey.isBeforeOrEqualTo(maxKeyInDB);
      if (isEvictedAfterCurrentFurtherValues) {
        return true;
      }
    }

    return false;
  }
  private V searchInFurtherValues(final FDate key) {
    // Take the first matching value from the sorted list
    // Search for the newest value
    V prevValue = (V) null;
    FDate prevKey = null;
    if (!lastValuesFromFurtherValues.isEmpty()) {
      // though maybe use the last one for smaller increments than the data itself is loaded
      for (final V lastValueFromFurtherValues : lastValuesFromFurtherValues) {
        final FDate keyLastValueFromFurtherValues = extractKey(key, lastValueFromFurtherValues);
        if (keyLastValueFromFurtherValues.isBeforeOrEqualTo(key)) {
          prevValue = lastValueFromFurtherValues;
          prevKey = keyLastValueFromFurtherValues;
        } else {
          // only go to further values if it might be possible that those are useable
          return prevValue;
        }
      }
    }

    final FDate earliestStartOfLoadFurtherValues = determineEaliestStartOfLoadFurtherValues(key);
    while (furtherValues.size() > 0) {
      final V newValue = furtherValues.getHead();
      final FDate newValueKey = extractKey(key, newValue);
      final int compare = key.compareTo(newValueKey);
      if (compare < 0) {
        // key < newValueKey
        // run over the key we wanted
        break;
      } else if (compare == 0) {
        // key == newValueKey
        // This is the value we searched for! It will later be added with the db key to the cache.
        pushLastValueFromFurtherValues();
        return newValue;
      } else {
        // key > newValueKey
        // put this value into the cache; gaps do not get filled here, so that the max size of the
        // cache does not get reached prematurely
        put(newValueKey, newValue, prevKey, prevValue);
        pushLastValueFromFurtherValues();
        // continue with the next one
        prevValue = newValue;
        prevKey = newValueKey;

        if (furtherValues.isEmpty()
            && newValueKey.isBefore(maxKeyInDB)
            && key.isBefore(maxKeyInDB)
            && maxKeyInDBFromLoadFurtherValues.isBefore(maxKeyInDB)) {
          final FDate timeForLoadFurtherValues =
              FDates.max(newValueKey, earliestStartOfLoadFurtherValues);
          Assertions.checkState(
              eventuallyLoadFurtherValues(
                  "searchInFurtherValues", newValueKey, timeForLoadFurtherValues, false, true));
          if (!furtherValues.isEmpty()) {
            pushLastValueFromFurtherValues();
            if (!timeForLoadFurtherValues.equals(newValue)) {
              // do not distort prev/next lookup when using earlisetStartOfLoadFurtherValues, thus
              // reset those
              prevValue = null;
              prevKey = null;
            }
          }
        }
      }
    }
    return prevValue;
  }