private boolean eventuallyGetMaxKeyInDB(final FDate key, final boolean force) { // not updating highest allowed key, since this already happened during key adjustment final FDate newMaxKeyInDB = getAdjustKeyProvider().getHighestAllowedKey(); if (newMaxKeyInDB != null) { if (newMaxKeyInDB.isAfter(maxKeyInDB)) { maxKeyInDB = newMaxKeyInDB; return true; } else { return false; } } // fallback to normal procedure if curHighWaterMark is not provided by provider if (maxKeyInDB == null || force) { final V maxValue = readNewestValueFromDB(maxKey()); if (maxValue != null) { final FDate maxValueKey = extractKey(key, maxValue); if (maxKeyInDB == null || maxValueKey.compareTo(maxKeyInDB) <= -1) { maxKeyInDB = maxValueKey; getValuesMap().put(maxValueKey, maxValue); return true; } } } return false; }
private V eventuallyGetMinValue(final FDate key, final boolean newMinKey) { // if key < minKey; use value for minKey if (minKeyInDB != null) { final boolean afterMinKey = !newMinKey && key.compareTo(minKey) >= 0; if (afterMinKey && key.compareTo(minKeyInDB) <= 0 && containsKey(minKey)) { // via readNewestValueTo return query().withFuture().getValue(minKey); } if (key.compareTo(minKeyInDB) <= 0 && containsKey(minKeyInDB)) { // via searchInFurtherValues return query().withFuture().getValue(minKeyInDB); } } return (V) null; }
private boolean eventuallyGetMinKeyInDB(final FDate key, final boolean force) { if (minKeyInDB == null || force) { final V minValue = readNewestValueFromDB(minKey()); if (minValue != null) { final FDate minValueKey = extractKey(key, minValue); // min key must be kept intact if all values have been loaded from a later key if (minKeyInDB == null || minValueKey.compareTo(minKeyInDB) <= -1) { minKeyInDB = minValueKey; getValuesMap().put(minValueKey, minValue); return true; } } } return false; }
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 updateMinKey(final FDate key) { if (minKey == null || key.compareTo(minKey) <= -1) { minKey = key; return true; } else { return false; } }
private boolean updateMaxKey(final FDate key) { if (maxKey == null || key.compareTo(maxKey) >= 1) { maxKey = key; return true; } else { 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 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 void assertFurtherValuesSorting(final FDate key) { final FDate firstKey = extractKey(key, furtherValues.getHead()); if (firstKey.compareTo(key) <= -1) { /* * readAllValuesAscendingFrom loads all data, thus we set the min key very deep so that later queries are * skipped if they are before minKey */ minKey = minKey(); } if (minKeyInDB == null || firstKey.compareTo(minKey) <= -1) { minKeyInDB = firstKey; } minKeyInDBFromLoadFurtherValues = FDates.min(minKeyInDBFromLoadFurtherValues, firstKey); final FDate lastKey = extractKey(key, furtherValues.getTail()); if (maxKeyInDB == null || lastKey.compareTo(maxKeyInDB) <= -1) { maxKeyInDB = FDates.max(maxKeyInDB, lastKey); } maxKeyInDBFromLoadFurtherValues = FDates.max(maxKeyInDBFromLoadFurtherValues, lastKey); if (furtherValues.size() > 1) { Assertions.checkState( firstKey.compareTo(lastKey) <= 0, "Not ascending sorted! At firstKey [%s] and lastKey [%s]", firstKey, lastKey); } }
private V loadFromCacheBeforeLoadFurtherValues( final FDate key, final boolean newMaxKey, final boolean newMinKey) { final V value = eventuallyGetMinValue(key, newMinKey); if (value != null) { return value; } // maybe use max value if (maxKeyInDB != null && key.compareTo(maxKeyInDB) >= 0 && containsKey(maxKeyInDB)) { return query().withFuture().getValue(maxKeyInDB); } return (V) null; }
/** * These checks may only be called after furtherValues were searched and eventuelly the list has * been reloaded. */ private V tryLoadFromCacheAfterLoadFurtherValues( final FDate key, final boolean newMaxKey, final FDate previousMaxKey) { // maybe minKey in db did not change even though the minKey in the cache changed // after reloading of furtherValues it is ok to search this again instead of doing another query // for the newest value if (furtherValuesLoaded) { final V value = eventuallyGetMinValue(key, false); if (value != null) { return value; } } // with maxKey if (newMaxKey && previousMaxKey != null && containsKey(previousMaxKey) && key.isAfterOrEqualTo(maxKeyInDB)) { // use the last maxKey // because this one is behind it and not a new one // thus working if the db does not have further values return query().withFuture().getValue(previousMaxKey); } return (V) null; }
/** * when this does not match, then getLatestValue will be used automatically anyway to go further * back in time */ private FDate determineEaliestStartOfLoadFurtherValues(final FDate key) { // 1 day is fine for most cases return key.addMilliseconds(-cacheMissCounter.getOptimalReadBackStepMillis()); }
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; }