@@ -16,12 +16,7 @@ class Accumulator
1616 private Usage $ usage ;
1717
1818 /**
19- * In-memory buffer for metrics.
20- * Keyed by "{tenant}:{metric}:{type}:{tagsHash}" — events are summed, gauges
21- * use last-write-wins. Tenant is part of the key so metrics for different
22- * tenants never collapse into one entry.
23- *
24- * @var array<string, array{tenant: string, metric: string, value: int, type: string, tags: array<string, mixed>}>
19+ * @var array<string, array{tenant: string, metric: string, value: int, type: string, tags: array<string, mixed>, time?: \DateTime}>
2520 */
2621 private array $ buffer = [];
2722
@@ -40,18 +35,12 @@ public function __construct(Usage $usage)
4035 /**
4136 * Collect a metric into the in-memory buffer for deferred flushing.
4237 *
43- * For event type: multiple collect() calls for the same metric are summed.
44- * For gauge type: last-write-wins semantics.
45- * No period fan-out — raw timestamps are used.
38+ * Events fold additively; earliest non-null time wins on merge.
39+ * Gauges use last-write-wins.
4640 *
47- * @param string $tenant Tenant scope (shared-tables mode)
48- * @param string $metric Metric name
49- * @param int $value Value
50- * @param string $type Metric type: 'event' or 'gauge'
51- * @param array<string,mixed> $tags Optional tags
52- * @return self
41+ * @param array<string,mixed> $tags
5342 */
54- public function collect (string $ tenant , string $ metric , int $ value , string $ type , array $ tags = []): self
43+ public function collect (string $ tenant , string $ metric , int $ value , string $ type , array $ tags = [], ? \ DateTime $ time = null ): self
5544 {
5645 // Compare against '' rather than empty(): the string "0" is a valid
5746 // tenant/metric id but empty("0") is true in PHP.
@@ -77,17 +66,23 @@ public function collect(string $tenant, string $metric, int $value, string $type
7766 $ key = md5 (json_encode ([$ tenant , $ metric , $ type , $ canonicalTags ], JSON_THROW_ON_ERROR ));
7867
7968 if ($ type === Usage::TYPE_EVENT && isset ($ this ->buffer [$ key ])) {
80- // Additive: sum values for the same tenant + metric + tags combination
69+ // earliest time wins on merge
8170 $ this ->buffer [$ key ]['value ' ] += $ value ;
71+ if ($ time !== null && (!isset ($ this ->buffer [$ key ]['time ' ]) || $ time < $ this ->buffer [$ key ]['time ' ])) {
72+ $ this ->buffer [$ key ]['time ' ] = $ time ;
73+ }
8274 } else {
83- // New event entry, or gauge (last-write-wins)
84- $ this ->buffer [$ key ] = [
75+ $ entry = [
8576 'tenant ' => $ tenant ,
8677 'metric ' => $ metric ,
8778 'value ' => $ value ,
8879 'type ' => $ type ,
8980 'tags ' => $ tags ,
9081 ];
82+ if ($ time !== null ) {
83+ $ entry ['time ' ] = $ time ;
84+ }
85+ $ this ->buffer [$ key ] = $ entry ;
9186 }
9287
9388 return $ this ;
0 commit comments