32 $this->store = $store;
33 $this->metricSpecs = [];
34 foreach ( $specs as $name => $spec ) {
35 $this->metricSpecs[$name] =
new MetricSpec( $spec );
37 $this->prefixComponents = is_array( $prefix ) ? $prefix : [ $prefix ];
38 if ( !count( $this->prefixComponents ) ) {
40 ': there must be at least one prefix component' );
77 $metricSpec = $this->metricSpecs[$metricName] ??
null;
78 if ( $metricSpec ===
null ) {
79 throw new WRStatsError(
"Unrecognised metric \"$metricName\"" );
84 foreach ( $metricSpec->sequences as $seqSpec ) {
85 $seqStart = $now - $seqSpec->softExpiry;
86 if ( $seqStart <= $range->start ) {
94 throw new WRStatsError(
'There should have been at least one sequence' );
97 $timeStep = $seqSpec->timeStep;
98 $firstBucket = (int)( $range->start / $timeStep );
99 $lastBucket = (int)ceil( $range->end / $timeStep );
100 for ( $bucket = $firstBucket; $bucket <= $lastBucket; $bucket++ ) {
101 $key = $this->store->makeKey(
102 $this->prefixComponents,
103 [ $metricName, $seqSpec->name, $bucket ],
106 if ( !isset( $this->cachedValues[$key] ) ) {
107 $this->queuedKeys[$key] =
true;
110 return new RatePromise( $this, $metricName, $entity, $metricSpec, $seqSpec, $range );
183 $timeStep = $seqSpec->timeStep;
184 $firstBucket = (int)( $range->start / $timeStep );
185 $lastBucket = (int)( $range->end / $timeStep );
188 for ( $bucket = $firstBucket; $bucket <= $lastBucket; $bucket++ ) {
189 $key = $this->store->makeKey(
190 $this->prefixComponents,
191 [ $metricName, $seqSpec->name, $bucket ],
194 $value = $this->cachedValues[$key] ?? 0;
197 } elseif ( $bucket === $firstBucket ) {
198 if ( $bucket === $lastBucket ) {
200 $bucketStartTime = $bucket * $timeStep;
201 $rateInterpolationEndTime = min( $bucketStartTime + $timeStep, $now );
202 $interpolationDuration = $rateInterpolationEndTime - $bucketStartTime;
203 if ( $interpolationDuration > 0 ) {
204 $total += $value * $range->
getDuration() / $interpolationDuration;
207 $overlapDuration = max( ( $bucket + 1 ) * $timeStep - $range->start, 0 );
208 $total += $value * $overlapDuration / $timeStep;
210 } elseif ( $bucket === $lastBucket ) {
212 $bucketStartTime = $bucket * $timeStep;
213 $rateInterpolationEndTime = min( $bucketStartTime + $timeStep, $now );
214 $overlapDuration = max( $range->end - $bucketStartTime, 0 );
215 $interpolationDuration = $rateInterpolationEndTime - $bucketStartTime;
216 if ( $overlapDuration === $interpolationDuration ) {
219 } elseif ( $interpolationDuration > 0 ) {
220 $total += $value * $overlapDuration / $interpolationDuration;
227 $rounded = round( $total ) * $metricSpec->resolution;
229 if ( is_int( $metricSpec->resolution ) ) {
230 $rounded = (int)$rounded;