def __init__(self, _time, milliseconds, bucket_numbers, bucket_data_length, enabled): self.time = _time self.milliseconds = milliseconds self.buckets = BucketCircular(bucket_numbers) self.bucket_numbers = bucket_numbers self.bucket_data_length = bucket_data_length self.enabled = enabled self.snapshot = PercentileSnapshot(0) self._new_bucket_lock = RLock()
class RollingPercentile(object): def __init__(self, _time, milliseconds, bucket_numbers, bucket_data_length, enabled): self.time = _time self.milliseconds = milliseconds self.buckets = BucketCircular(bucket_numbers) self.bucket_numbers = bucket_numbers self.bucket_data_length = bucket_data_length self.enabled = enabled self.snapshot = PercentileSnapshot(0) self._new_bucket_lock = RLock() def buckets_size_in_milliseconds(self): return self.milliseconds / self.bucket_numbers def current_bucket(self): current_time = self.time.current_time_in_millis() current_bucket = self.buckets.peek_last() if current_bucket is not None and current_time < (current_bucket.window_start + self.buckets_size_in_milliseconds()): return current_bucket with self._new_bucket_lock: # If we didn't find the current bucket above, then we have to # create one. if self.buckets.peek_last() is None: new_bucket = Bucket(current_time, self.bucket_data_length) self.buckets.add_last(new_bucket) return new_bucket else: for i in range(self.bucket_numbers): last_bucket = self.buckets.peek_last() if current_time < (last_bucket.window_start + self.buckets_size_in_milliseconds()): return last_bucket elif current_time - (last_bucket.window_start + self.buckets_size_in_milliseconds()) > self.milliseconds: self.reset() return self.current_bucket() else: all_buckets = [b for b in self.buckets] self.buckets.add_last(Bucket(last_bucket.window_start + self.buckets_size_in_milliseconds(), self.bucket_data_length)) self.snapshot = PercentileSnapshot(*all_buckets) return self.buckets.peek_last() # we didn't get the lock so just return the latest bucket while # another thread creates the next one current_bucket = self.buckets.peek_last() if current_bucket is not None: return current_bucket else: # The rare scenario where multiple threads raced to create the # very first bucket wait slightly and then use recursion while # the other thread finishes creating a bucket time.sleep(5) self.current_bucket() def add_value(self, *values): ''' Add value (or values) to current bucket. ''' if not self.enabled: return for value in values: self.current_bucket().data.add_value(value) def percentile(self, percentile): if not self.enabled: return -1 # Force logic to move buckets forward in case other requests aren't # making it happen self.current_bucket() # Fetch the current snapshot return self.current_percentile_snapshot().percentile(percentile) def current_percentile_snapshot(self): return self.snapshot def mean(self): if not self.enabled: return -1 # Force logic to move buckets forward in case other requests aren't # making it happen self.current_bucket() # Fetch the current snapshot return self.current_percentile_snapshot().mean()