Skip to content

Commit aae1743

Browse files
authored
Merge pull request #7793 from ProcessMaker/feature/FOUR-20915
Cache Performance Monitoring
2 parents e1e6b4e + 955b0ac commit aae1743

15 files changed

Lines changed: 1746 additions & 19 deletions
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<?php
2+
3+
namespace ProcessMaker\Cache\Monitoring;
4+
5+
use ProcessMaker\Cache\CacheInterface;
6+
use ProcessMaker\Cache\Monitoring\CacheMetricsInterface;
7+
use ProcessMaker\Cache\Screens\ScreenCacheInterface;
8+
9+
/**
10+
* Decorator class that adds metrics tracking about cache operations
11+
* including hits, misses, write sizes, and timing information.
12+
*/
13+
class CacheMetricsDecorator implements CacheInterface, ScreenCacheInterface
14+
{
15+
protected CacheInterface|ScreenCacheInterface $cache;
16+
17+
protected CacheMetricsInterface $metrics;
18+
19+
/**
20+
* Create a new cache metrics decorator instance
21+
*
22+
* @param CacheInterface|ScreenCacheInterface $cache The cache implementation to decorate
23+
* @param CacheMetricsInterface $metrics The metrics implementation to use
24+
*/
25+
public function __construct(CacheInterface|ScreenCacheInterface $cache, CacheMetricsInterface $metrics)
26+
{
27+
$this->cache = $cache;
28+
$this->metrics = $metrics;
29+
}
30+
31+
/**
32+
* Create a cache key for screen data
33+
*
34+
* @param int $processId Process ID
35+
* @param int $processVersionId Process version ID
36+
* @param string $language Language code
37+
* @param int $screenId Screen ID
38+
* @param int $screenVersionId Screen version ID
39+
* @return string Generated cache key
40+
* @throws \RuntimeException If underlying cache doesn't support createKey
41+
*/
42+
public function createKey(int $processId, int $processVersionId, string $language, int $screenId, int $screenVersionId): string
43+
{
44+
if ($this->cache instanceof ScreenCacheInterface) {
45+
return $this->cache->createKey($processId, $processVersionId, $language, $screenId, $screenVersionId);
46+
}
47+
48+
throw new \RuntimeException('Underlying cache implementation does not support createKey method');
49+
}
50+
51+
/**
52+
* Get a value from the cache
53+
*
54+
* Records timing and hit/miss metrics for the get operation
55+
*
56+
* @param string $key Cache key
57+
* @param mixed $default Default value if key not found
58+
* @return mixed Cached value or default
59+
*/
60+
public function get(string $key, mixed $default = null): mixed
61+
{
62+
$startTime = microtime(true);
63+
$value = $this->cache->get($key, $default);
64+
$endTime = microtime(true);
65+
$duration = $endTime - $startTime;
66+
67+
if ($value === $default) {
68+
$this->metrics->recordMiss($key, $duration);
69+
} else {
70+
$this->metrics->recordHit($key, $duration);
71+
}
72+
73+
return $value;
74+
}
75+
76+
/**
77+
* Store a value in the cache
78+
*
79+
* Records metrics about the size of stored data
80+
*
81+
* @param string $key Cache key
82+
* @param mixed $value Value to cache
83+
* @param null|int|\DateInterval $ttl Optional TTL
84+
* @return bool True if successful
85+
*/
86+
public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool
87+
{
88+
$result = $this->cache->set($key, $value, $ttl);
89+
90+
if ($result) {
91+
// Calculate approximate size in bytes
92+
$size = $this->calculateSize($value);
93+
$this->metrics->recordWrite($key, $size);
94+
}
95+
96+
return $result;
97+
}
98+
99+
/**
100+
* Delete a value from the cache
101+
*
102+
* @param string $key Cache key
103+
* @return bool True if successful
104+
*/
105+
public function delete(string $key): bool
106+
{
107+
return $this->cache->delete($key);
108+
}
109+
110+
/**
111+
* Clear all values from the cache
112+
*
113+
* @return bool True if successful
114+
*/
115+
public function clear(): bool
116+
{
117+
return $this->cache->clear();
118+
}
119+
120+
/**
121+
* Check if a key exists in the cache
122+
*
123+
* @param string $key Cache key
124+
* @return bool True if key exists
125+
*/
126+
public function has(string $key): bool
127+
{
128+
return $this->cache->has($key);
129+
}
130+
131+
/**
132+
* Check if a key is missing from the cache
133+
*
134+
* @param string $key Cache key
135+
* @return bool True if key is missing
136+
*/
137+
public function missing(string $key): bool
138+
{
139+
return $this->cache->missing($key);
140+
}
141+
142+
/**
143+
* Calculate the approximate size in bytes of a value
144+
*
145+
* @param mixed $value Value to calculate size for
146+
* @return int Size in bytes
147+
*/
148+
protected function calculateSize(mixed $value): int
149+
{
150+
if (is_string($value)) {
151+
return strlen($value);
152+
}
153+
154+
if (is_array($value) || is_object($value)) {
155+
return strlen(serialize($value));
156+
}
157+
158+
if (is_int($value)) {
159+
return PHP_INT_SIZE;
160+
}
161+
162+
if (is_float($value)) {
163+
return 8; // typical double size
164+
}
165+
166+
if (is_bool($value)) {
167+
return 1;
168+
}
169+
170+
return 0; // for null or other types
171+
}
172+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
namespace ProcessMaker\Cache\Monitoring;
4+
5+
/**
6+
* Interface for monitoring cache metrics and performance
7+
*/
8+
interface CacheMetricsInterface
9+
{
10+
/**
11+
* Record a cache hit event
12+
*
13+
* @param string $key Cache key that was accessed
14+
* @param float $microtime Time taken for the operation in microseconds
15+
*/
16+
public function recordHit(string $key, $microtime): void;
17+
18+
/**
19+
* Record a cache miss event
20+
*
21+
* @param string $key Cache key that was accessed
22+
* @param float $microtime Time taken for the operation in microseconds
23+
*/
24+
public function recordMiss(string $key, $microtime): void;
25+
26+
/**
27+
* Record a cache write operation
28+
*
29+
* @param string $key Cache key that was written
30+
* @param int $size Size of the cached data in bytes
31+
*/
32+
public function recordWrite(string $key, int $size): void;
33+
34+
/**
35+
* Get the hit rate for a specific cache key
36+
*
37+
* @param string $key Cache key to check
38+
* @return float Hit rate as a percentage between 0 and 1
39+
*/
40+
public function getHitRate(string $key): float;
41+
42+
/**
43+
* Get the miss rate for a specific cache key
44+
*
45+
* @param string $key Cache key to check
46+
* @return float Miss rate as a percentage between 0 and 1
47+
*/
48+
public function getMissRate(string $key): float;
49+
50+
/**
51+
* Get the average time taken for cache hits
52+
*
53+
* @param string $key Cache key to analyze
54+
* @return float Average time in microseconds
55+
*/
56+
public function getHitAvgTime(string $key): float;
57+
58+
/**
59+
* Get the average time taken for cache misses
60+
*
61+
* @param string $key Cache key to analyze
62+
* @return float Average time in microseconds
63+
*/
64+
public function getMissAvgTime(string $key): float;
65+
66+
/**
67+
* Get the most frequently accessed cache keys
68+
*
69+
* @param int $count Number of top keys to return
70+
* @return array Array of top keys with their access counts
71+
*/
72+
public function getTopKeys(int $count = 5): array;
73+
74+
/**
75+
* Get memory usage statistics for a cache key
76+
*
77+
* @param string $key Cache key to analyze
78+
* @return array Array containing memory usage details
79+
*/
80+
public function getMemoryUsage(string $key): array;
81+
82+
/**
83+
* Reset all metrics data
84+
*/
85+
public function resetMetrics(): void;
86+
87+
/**
88+
* Get a summary of all cache metrics
89+
*
90+
* @return array Array containing overall cache statistics
91+
*/
92+
public function getSummary(): array;
93+
}

0 commit comments

Comments
 (0)