(self, metric_query: str, time_start: int, time_end: int)
Look up an range of items in the locally-stored metrics cache

        The cache is stored as a mapping of dictionary -> timeseries lists (i.e., a sorted list of tuples [(time1,
        value1), (time2, value2), ...]), indexed by a metrics query.

        Note that we assume that writes to the metric store happen sequentially.  In other words, we aren't going
        back to fill in values "after the fact", because this could result in a cache with incomplete entries.  There
        *could* be cases where writes to the datastore haven't completed when we read the cache, but this will only
        result in incomplete information if write 1 finishes after write 2, but write 1 has an earlier timestamp.  To
        protect against this case, we don't store data that's newer than 5 minutes (see self._cache_store, below).

        :param metric_query: the name of the metric
        :param time_start: int, unix timestamp of first requested time to look up
        :param time_end: int, unix timestamp of last requested time to look up
        :returns: (cached_data, new_start, new_end) tuple -- [new_start, new_end] is the range of timestamps we need
            to look up in the datastore
Look up an range of items in the locally-stored metrics cache
Look up an range of items in the locally - stored metrics cache
def _cache_lookup(self, metric_query: str, time_start: int, time_end: int) -> Tuple[MetricsValuesDict, int, int]:
        """ Look up an range of items in the locally-stored metrics cache

        The cache is stored as a mapping of dictionary -> timeseries lists (i.e., a sorted list of tuples [(time1,
        value1), (time2, value2), ...]), indexed by a metrics query.

        Note that we assume that writes to the metric store happen sequentially.  In other words, we aren't going
        back to fill in values "after the fact", because this could result in a cache with incomplete entries.  There
        *could* be cases where writes to the datastore haven't completed when we read the cache, but this will only
        result in incomplete information if write 1 finishes after write 2, but write 1 has an earlier timestamp.  To
        protect against this case, we don't store data that's newer than 5 minutes (see self._cache_store, below).

        :param metric_query: the name of the metric
        :param time_start: int, unix timestamp of first requested time to look up
        :param time_end: int, unix timestamp of last requested time to look up
        :returns: (cached_data, new_start, new_end) tuple -- [new_start, new_end] is the range of timestamps we need
            to look up in the datastore
        """
        centry = self._cache[metric_query]
        cache_size = len(centry.data)

        if time_start >= time_end:
            raise ValueError("time_start must be earlier than time_end")
        elif cache_size == 0:
            return defaultdict(list), time_start, time_end
        elif time_start < centry.time_start:
            # If the start time is before the beginning of the cache, we return nothing, since otherwise we'd
            # need more complicated logic to know what to fetch from the datastore.  The cache will then get
            # invalidated when we try to store the data back in.
            #
            # In normal circumstances this should never happen because we assume the requests are monotonically
            # increasing in time_start
            logger.warning(
                "time_start was before the first cached value; this should not happen ({ts} < {c})".format(
                    ts=time_start, c=centry.time_start,
                )
            )
            return defaultdict(list), time_start, time_end
        else:
            cached_metrics: MetricsValuesDict = defaultdict(list)
            min_time, max_time = float("inf"), 0
            for metric_name, ts in centry.data.items():
                # using -inf and inf here ensures that cache_start and cache_end are inclusive
                cache_start = bisect(ts, (time_start, float("-inf")))
                cache_end = bisect(ts, (time_end, float("inf")))
                if len(ts) > 0 and cache_start < len(ts):
                    cached_metrics[metric_name] = ts[cache_start:cache_end]
                    min_time = min(ts[cache_start][0], min_time)
                    max_time = max(ts[cache_end - 1][0], max_time)

                # Prune off everything before the original time_start request to keep the cache small-ish
                num_cache_entries_to_prune = len(centry.data[metric_name][:cache_start])
                if num_cache_entries_to_prune > 0:
                    logger.info(
                        "Removing {num} cache entries before {t} for {key}".format(
                            num=num_cache_entries_to_prune, t=time_start, key=metric_name,
                        )
                    )
                    del centry.data[metric_name][:cache_start]

            centry.data = defaultdict(list, {metric_name: ts for metric_name, ts in centry.data.items() if ts})

            if cached_metrics:
                logger.info("Using cached metrics from {start} to {end}".format(start=min_time, end=max_time,))

                # The first time we need to look up in the datastore is 1 second after the last thing
                # we retreived from the cache
                next_uncached_time = max_time + 1
            else:
                logger.info(
                    "No cached metrics found in range {start} to {end} for {query}".format(
                        start=time_start, end=time_end, query=metric_query,
                    )
                )
                next_uncached_time = time_start

            centry.time_start = time_start
            return cached_metrics, next_uncached_time, time_end
def _cache_lookup ( self , metric_query : str , time_start : int , time_end : int ) -> Tuple [ MetricsValuesDict , int , int ] : centry = self . _cache [ metric_query ] cache_size = len ( centry . data ) if time_start >= time_end : raise ValueError ( "time_start must be earlier than time_end" ) elif cache_size == 0 : return defaultdict ( list ) , time_start , time_end elif time_start < centry . time_start : # If the start time is before the beginning of the cache, we return nothing, since otherwise we'd # need more complicated logic to know what to fetch from the datastore.  The cache will then get # invalidated when we try to store the data back in. # # In normal circumstances this should never happen because we assume the requests are monotonically # increasing in time_start logger . warning ( "time_start was before the first cached value; this should not happen ({ts} < {c})" . format ( ts = time_start , c = centry . time_start , ) ) return defaultdict ( list ) , time_start , time_end else : cached_metrics : MetricsValuesDict = defaultdict ( list ) min_time , max_time = float ( "inf" ) , 0 for metric_name , ts in centry . data . items ( ) : # using -inf and inf here ensures that cache_start and cache_end are inclusive cache_start = bisect ( ts , ( time_start , float ( "-inf" ) ) ) cache_end = bisect ( ts , ( time_end , float ( "inf" ) ) ) if len ( ts ) > 0 and cache_start < len ( ts ) : cached_metrics [ metric_name ] = ts [ cache_start : cache_end ] min_time = min ( ts [ cache_start ] [ 0 ] , min_time ) max_time = max ( ts [ cache_end - 1 ] [ 0 ] , max_time ) # Prune off everything before the original time_start request to keep the cache small-ish num_cache_entries_to_prune = len ( centry . data [ metric_name ] [ : cache_start ] ) if num_cache_entries_to_prune > 0 : logger . info ( "Removing {num} cache entries before {t} for {key}" . format ( num = num_cache_entries_to_prune , t = time_start , key = metric_name , ) ) del centry . data [ metric_name ] [ : cache_start ] centry . data = defaultdict ( list , { metric_name : ts for metric_name , ts in centry . data . items ( ) if ts } ) if cached_metrics : logger . info ( "Using cached metrics from {start} to {end}" . format ( start = min_time , end = max_time , ) ) # The first time we need to look up in the datastore is 1 second after the last thing # we retreived from the cache next_uncached_time = max_time + 1 else : logger . info ( "No cached metrics found in range {start} to {end} for {query}" . format ( start = time_start , end = time_end , query = metric_query , ) ) next_uncached_time = time_start centry . time_start = time_start return cached_metrics , next_uncached_time , time_end