33 $this->store = $store;
34 $this->metricSpecs = [];
35 foreach ( $specs as $name => $spec ) {
36 $this->metricSpecs[$name] =
new MetricSpec( $spec );
38 $this->prefixComponents = is_array( $prefix ) ? $prefix : [ $prefix ];
39 if ( !count( $this->prefixComponents ) ) {
41 ': there must be at least one prefix component' );
78 $metricSpec = $this->metricSpecs[$metricName] ??
null;
79 if ( $metricSpec ===
null ) {
80 throw new WRStatsError( __METHOD__ .
": Unrecognised metric \"$metricName\"" );
85 foreach ( $metricSpec->sequences as $seqSpec ) {
86 $seqStart = $now - $seqSpec->softExpiry;
87 if ( $seqStart <= $range->start ) {
95 throw new WRStatsError(
'There should have been at least one sequence' );
98 $timeStep = $seqSpec->timeStep;
99 $firstBucket = (int)( $range->start / $timeStep );
100 $lastBucket = (int)ceil( $range->end / $timeStep );
101 for ( $bucket = $firstBucket; $bucket <= $lastBucket; $bucket++ ) {
102 $key = $this->store->makeKey(
103 $this->prefixComponents,
104 [ $metricName, $seqSpec->name, $bucket ],
107 if ( !isset( $this->cachedValues[$key] ) ) {
108 $this->queuedKeys[$key] =
true;
111 return new RatePromise( $this, $metricName, $entity, $metricSpec, $seqSpec, $range );
187 $timeStep = $seqSpec->timeStep;
188 $firstBucket = (int)( $range->start / $timeStep );
189 $lastBucket = (int)( $range->end / $timeStep );
192 for ( $bucket = $firstBucket; $bucket <= $lastBucket; $bucket++ ) {
193 $key = $this->store->makeKey(
194 $this->prefixComponents,
195 [ $metricName, $seqSpec->name, $bucket ],
198 $value = $this->cachedValues[$key] ?? 0;
201 } elseif ( $bucket === $firstBucket ) {
202 if ( $bucket === $lastBucket ) {
204 $bucketStartTime = $bucket * $timeStep;
205 $rateInterpolationEndTime = min( $bucketStartTime + $timeStep, $now );
206 $interpolationDuration = $rateInterpolationEndTime - $bucketStartTime;
207 if ( $interpolationDuration > 0 ) {
208 $total += $value * $range->
getDuration() / $interpolationDuration;
211 $overlapDuration = max( ( $bucket + 1 ) * $timeStep - $range->start, 0 );
212 $total += $value * $overlapDuration / $timeStep;
214 } elseif ( $bucket === $lastBucket ) {
216 $bucketStartTime = $bucket * $timeStep;
217 $rateInterpolationEndTime = min( $bucketStartTime + $timeStep, $now );
218 $overlapDuration = max( $range->end - $bucketStartTime, 0 );
219 $interpolationDuration = $rateInterpolationEndTime - $bucketStartTime;
220 if ( $overlapDuration === $interpolationDuration ) {
223 } elseif ( $interpolationDuration > 0 ) {
224 $total += $value * $overlapDuration / $interpolationDuration;
231 $rounded = round( $total ) * $metricSpec->resolution;
233 if ( is_int( $metricSpec->resolution ) ) {
234 $rounded = (int)$rounded;