Skip to content

Cache Performance Monitoring #7793

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions ProcessMaker/Cache/Monitoring/CacheMetricsDecorator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<?php

namespace ProcessMaker\Cache\Monitoring;

use ProcessMaker\Cache\CacheInterface;
use ProcessMaker\Cache\Monitoring\CacheMetricsInterface;
use ProcessMaker\Cache\Screens\ScreenCacheInterface;

/**
* Decorator class that adds metrics tracking about cache operations
* including hits, misses, write sizes, and timing information.
*/
class CacheMetricsDecorator implements CacheInterface, ScreenCacheInterface
{
protected CacheInterface|ScreenCacheInterface $cache;

protected CacheMetricsInterface $metrics;

/**
* Create a new cache metrics decorator instance
*
* @param CacheInterface|ScreenCacheInterface $cache The cache implementation to decorate
* @param CacheMetricsInterface $metrics The metrics implementation to use
*/
public function __construct(CacheInterface|ScreenCacheInterface $cache, CacheMetricsInterface $metrics)
{
$this->cache = $cache;
$this->metrics = $metrics;
}

/**
* Create a cache key for screen data
*
* @param int $processId Process ID
* @param int $processVersionId Process version ID
* @param string $language Language code
* @param int $screenId Screen ID
* @param int $screenVersionId Screen version ID
* @return string Generated cache key
* @throws \RuntimeException If underlying cache doesn't support createKey
*/
public function createKey(int $processId, int $processVersionId, string $language, int $screenId, int $screenVersionId): string
{
if ($this->cache instanceof ScreenCacheInterface) {
return $this->cache->createKey($processId, $processVersionId, $language, $screenId, $screenVersionId);
}

throw new \RuntimeException('Underlying cache implementation does not support createKey method');
}

/**
* Get a value from the cache
*
* Records timing and hit/miss metrics for the get operation
*
* @param string $key Cache key
* @param mixed $default Default value if key not found
* @return mixed Cached value or default
*/
public function get(string $key, mixed $default = null): mixed
{
$startTime = microtime(true);
$value = $this->cache->get($key, $default);
$endTime = microtime(true);
$duration = $endTime - $startTime;

if ($value === $default) {
$this->metrics->recordMiss($key, $duration);
} else {
$this->metrics->recordHit($key, $duration);
}

return $value;
}

/**
* Store a value in the cache
*
* Records metrics about the size of stored data
*
* @param string $key Cache key
* @param mixed $value Value to cache
* @param null|int|\DateInterval $ttl Optional TTL
* @return bool True if successful
*/
public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool
{
$result = $this->cache->set($key, $value, $ttl);

if ($result) {
// Calculate approximate size in bytes
$size = $this->calculateSize($value);
$this->metrics->recordWrite($key, $size);
}

return $result;
}

/**
* Delete a value from the cache
*
* @param string $key Cache key
* @return bool True if successful
*/
public function delete(string $key): bool
{
return $this->cache->delete($key);
}

/**
* Clear all values from the cache
*
* @return bool True if successful
*/
public function clear(): bool
{
return $this->cache->clear();
}

/**
* Check if a key exists in the cache
*
* @param string $key Cache key
* @return bool True if key exists
*/
public function has(string $key): bool
{
return $this->cache->has($key);
}

/**
* Check if a key is missing from the cache
*
* @param string $key Cache key
* @return bool True if key is missing
*/
public function missing(string $key): bool
{
return $this->cache->missing($key);
}

/**
* Calculate the approximate size in bytes of a value
*
* @param mixed $value Value to calculate size for
* @return int Size in bytes
*/
protected function calculateSize(mixed $value): int
{
if (is_string($value)) {
return strlen($value);
}

if (is_array($value) || is_object($value)) {
return strlen(serialize($value));
}

if (is_int($value)) {
return PHP_INT_SIZE;
}

if (is_float($value)) {
return 8; // typical double size
}

if (is_bool($value)) {
return 1;
}

return 0; // for null or other types
}
}
93 changes: 93 additions & 0 deletions ProcessMaker/Cache/Monitoring/CacheMetricsInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

namespace ProcessMaker\Cache\Monitoring;

/**
* Interface for monitoring cache metrics and performance
*/
interface CacheMetricsInterface
{
/**
* Record a cache hit event
*
* @param string $key Cache key that was accessed
* @param float $microtime Time taken for the operation in microseconds
*/
public function recordHit(string $key, $microtime): void;

/**
* Record a cache miss event
*
* @param string $key Cache key that was accessed
* @param float $microtime Time taken for the operation in microseconds
*/
public function recordMiss(string $key, $microtime): void;

/**
* Record a cache write operation
*
* @param string $key Cache key that was written
* @param int $size Size of the cached data in bytes
*/
public function recordWrite(string $key, int $size): void;

/**
* Get the hit rate for a specific cache key
*
* @param string $key Cache key to check
* @return float Hit rate as a percentage between 0 and 1
*/
public function getHitRate(string $key): float;

/**
* Get the miss rate for a specific cache key
*
* @param string $key Cache key to check
* @return float Miss rate as a percentage between 0 and 1
*/
public function getMissRate(string $key): float;

/**
* Get the average time taken for cache hits
*
* @param string $key Cache key to analyze
* @return float Average time in microseconds
*/
public function getHitAvgTime(string $key): float;

/**
* Get the average time taken for cache misses
*
* @param string $key Cache key to analyze
* @return float Average time in microseconds
*/
public function getMissAvgTime(string $key): float;

/**
* Get the most frequently accessed cache keys
*
* @param int $count Number of top keys to return
* @return array Array of top keys with their access counts
*/
public function getTopKeys(int $count = 5): array;

/**
* Get memory usage statistics for a cache key
*
* @param string $key Cache key to analyze
* @return array Array containing memory usage details
*/
public function getMemoryUsage(string $key): array;

/**
* Reset all metrics data
*/
public function resetMetrics(): void;

/**
* Get a summary of all cache metrics
*
* @return array Array containing overall cache statistics
*/
public function getSummary(): array;
}
Loading
Loading