Skip to content

Commit 81a370e

Browse files
committed
feat: add query monitor panel for object cache
1 parent a3f32d1 commit 81a370e

14 files changed

+506
-6
lines changed

phpmd.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<rule ref="rulesets/naming.xml">
2929
<exclude name="LongVariable"/>
3030
<exclude name="ShortVariable"/>
31+
<exclude name="ShortMethodName" />
3132
</rule>
3233

3334
<rule ref="rulesets/codesize.xml/CyclomaticComplexity"
@@ -88,4 +89,15 @@
8889
<property name="exceptions" value="id,x,y" />
8990
</properties>
9091
</rule>
92+
<rule name="ShortMethodName"
93+
since="0.2"
94+
message="Avoid using short method names like {0}::{1}(). The configured minimum method name length is {2}."
95+
class="PHPMD\Rule\Naming\ShortMethodName"
96+
externalInfoUrl="https://phpmd.org/rules/naming.html#shortmethodname">
97+
<priority>3</priority>
98+
<properties>
99+
<property name="minimum" description="Minimum length for a method or function name" value="3"/>
100+
<property name="exceptions" description="Comma-separated list of exceptions" value="id"/>
101+
</properties>
102+
</rule>
91103
</ruleset>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?= $this->before_non_tabular_output(); ?>
2+
<section>
3+
<h3>Hit Ratio</h3>
4+
<p class="qm-ltr"><code><?= esc_html($data['ratio']); ?>%</code></p>
5+
</section>
6+
7+
<section>
8+
<h3>Hits</h3>
9+
<p class="qm-ltr"><code><?= esc_html($data['hits']); ?></code></p>
10+
</section>
11+
12+
<section>
13+
<h3>Misses</h3>
14+
<p class="qm-ltr"><code><?= esc_html($data['misses']); ?></code></p>
15+
</section>
16+
17+
<?php if (!empty($data['requests'])): ?>
18+
<section>
19+
<h3>Requests to <?= esc_html($data['type']); ?></h3>
20+
<p class="qm-ltr"><code><?= esc_html($data['requests']); ?></code></p>
21+
</section>
22+
<?php endif; ?>
23+
24+
<?php if (!empty($data['request_time'])): ?>
25+
<section>
26+
<h3>Total time (ms)</h3>
27+
<p class="qm-ltr"><code><?= esc_html($data['request_time']); ?></code></p>
28+
</section>
29+
<?php endif; ?>
30+
<?= $this->after_non_tabular_output(); ?>

src/Configuration/EventManagementConfiguration.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public function modify(Container $container)
4646
new Subscriber\AssetsSubscriber($container['site_url'], $container['assets_url'], $container['ymir_project_type']),
4747
new Subscriber\ImageEditorSubscriber($container['console_client'], $container['file_manager']),
4848
new Subscriber\PluploadSubscriber($container['plugin_relative_path'], $container['rest_namespace'], $container['assets_url'], $container['plupload_error_messages']),
49+
new Subscriber\QueryMonitorSubscriber($container['query_monitor_collectors'], $container['query_monitor_panels'], $container['plugin_dir_path'].'/assets/view/query-monitor'),
4950
new Subscriber\RedirectSubscriber($container['ymir_primary_domain_name'], $container['is_multisite']),
5051
new Subscriber\RestApiSubscriber($container['rest_namespace'], $container['rest_endpoints']),
5152
new Subscriber\SecurityHeadersSubscriber(),
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of Ymir WordPress plugin.
7+
*
8+
* (c) Carl Alexander <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Ymir\Plugin\Configuration;
15+
16+
use Ymir\Plugin\DependencyInjection\Container;
17+
use Ymir\Plugin\DependencyInjection\ContainerConfigurationInterface;
18+
use Ymir\Plugin\QueryMonitor\ObjectCacheCollector;
19+
use Ymir\Plugin\QueryMonitor\ObjectCachePanel;
20+
21+
/**
22+
* Configures the dependency injection container with Query Monitor parameters and services.
23+
*/
24+
class QueryMonitorConfiguration implements ContainerConfigurationInterface
25+
{
26+
/**
27+
* {@inheritdoc}
28+
*/
29+
public function modify(Container $container)
30+
{
31+
$container['query_monitor_collectors'] = $container->service(function (Container $container) {
32+
return [
33+
$container['query_monitor_object_cache_collector'],
34+
];
35+
});
36+
$container['query_monitor_object_cache_collector'] = $container->service(function (Container $container) {
37+
return new ObjectCacheCollector($container['wp_object_cache']);
38+
});
39+
$container['query_monitor_panels'] = $container->service(function () {
40+
return [
41+
ObjectCachePanel::class,
42+
];
43+
});
44+
}
45+
}

src/Configuration/WordPressConfiguration.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,10 @@ public function modify(Container $container)
111111
$container['uploads_subdir'] = $container->service(function () {
112112
return wp_upload_dir()['subdir'] ?? '';
113113
});
114+
$container['wp_object_cache'] = $container->service(function () {
115+
global $wp_object_cache;
116+
117+
return $wp_object_cache;
118+
});
114119
}
115120
}

src/ObjectCache/AbstractPersistentObjectCache.php

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,20 @@ abstract class AbstractPersistentObjectCache implements PersistentObjectCacheInt
3434
*/
3535
protected const MODE_REPLACE = 2;
3636

37+
/**
38+
* The amount of requests made to the persistent cache.
39+
*
40+
* @var int
41+
*/
42+
protected $requests;
43+
44+
/**
45+
* The total time spent making requests to the persistent cache.
46+
*
47+
* @var int
48+
*/
49+
protected $requestTime;
50+
3751
/**
3852
* The current blog ID when multisite is active.
3953
*
@@ -53,7 +67,14 @@ abstract class AbstractPersistentObjectCache implements PersistentObjectCacheInt
5367
*
5468
* @var array
5569
*/
56-
private $globalGroups = [];
70+
private $globalGroups;
71+
72+
/**
73+
* The amount of times the cache data was already stored in the cache.
74+
*
75+
* @var int
76+
*/
77+
private $hits;
5778

5879
/**
5980
* Flag whether this is a multisite installation or not.
@@ -62,12 +83,19 @@ abstract class AbstractPersistentObjectCache implements PersistentObjectCacheInt
6283
*/
6384
private $isMultisite;
6485

86+
/**
87+
* Amount of times the cache didn't have the request in cache.
88+
*
89+
* @var int
90+
*/
91+
private $misses;
92+
6593
/**
6694
* List of non-persistent groups.
6795
*
6896
* @var array
6997
*/
70-
private $nonPersistentGroups = [];
98+
private $nonPersistentGroups;
7199

72100
/**
73101
* Prefix used for all cache keys.
@@ -89,9 +117,15 @@ abstract class AbstractPersistentObjectCache implements PersistentObjectCacheInt
89117
public function __construct(bool $isMultisite, string $prefix = '')
90118
{
91119
$this->cache = [];
120+
$this->globalGroups = [];
121+
$this->hits = 0;
92122
$this->isMultisite = $isMultisite;
123+
$this->misses = 0;
124+
$this->nonPersistentGroups = [];
93125
$this->prefix = trim($prefix);
94126
$this->requestedKeys = [];
127+
$this->requests = 0;
128+
$this->requestTime = 0;
95129

96130
if (!empty($this->prefix)) {
97131
$this->prefix = $this->sanitizeCacheKeyPart($this->prefix);
@@ -207,10 +241,14 @@ public function get(string $group, string $key, bool $force = false, &$found = n
207241
if ($this->isNonPersistentGroup($group) && !$this->hasInMemory($cacheKey)) {
208242
$found = false;
209243

244+
++$this->misses;
245+
210246
return false;
211247
} elseif ((!$force || $this->isNonPersistentGroup($group)) && $this->hasInMemory($cacheKey)) {
212248
$found = true;
213249

250+
++$this->hits;
251+
214252
return $this->getFromMemory($cacheKey);
215253
}
216254

@@ -222,6 +260,8 @@ public function get(string $group, string $key, bool $force = false, &$found = n
222260

223261
$found = false !== $value;
224262

263+
$found ? $this->hits++ : $this->misses++;
264+
225265
if (false !== $value) {
226266
$this->requestedKeys[$cacheKey] = true;
227267
$this->storeInMemory($cacheKey, $value);
@@ -230,6 +270,21 @@ public function get(string $group, string $key, bool $force = false, &$found = n
230270
return $value;
231271
}
232272

273+
/**
274+
* {@inheritdoc}
275+
*/
276+
public function getInfo(): array
277+
{
278+
return [
279+
'hits' => $this->hits,
280+
'misses' => $this->misses,
281+
'ratio' => round(($this->hits / ($this->hits + $this->misses)) * 100, 1),
282+
'requests' => $this->requests,
283+
'request_time' => $this->requestTime,
284+
'type' => str_replace('ObjectCache', '', (new \ReflectionClass($this))->getShortName()),
285+
];
286+
}
287+
233288
/**
234289
* {@inheritdoc}
235290
*/
@@ -251,7 +306,11 @@ public function getMultiple(string $group, array $keys, bool $force = false): ar
251306
$value = false;
252307

253308
if ((!$force || $this->isNonPersistentGroup($group)) && $this->hasInMemory($cacheKey)) {
309+
++$this->hits;
310+
254311
$value = $this->getFromMemory($cacheKey);
312+
} elseif ($this->isNonPersistentGroup($group) && !$this->hasInMemory($cacheKey)) {
313+
++$this->misses;
255314
}
256315

257316
return [$key => $value];
@@ -269,10 +328,6 @@ public function getMultiple(string $group, array $keys, bool $force = false): ar
269328
return $cacheKeys[$key] ?? '';
270329
})->filter()->all()));
271330

272-
$valuesFromPersistentCache->each(function ($value, string $key) {
273-
$this->storeInMemory($key, $value);
274-
});
275-
276331
$keysWithMissingValues->each(function (string $key) use ($cacheKeys, $values, $valuesFromPersistentCache) {
277332
$cacheKey = $cacheKeys[$key] ?? '';
278333

@@ -281,6 +336,16 @@ public function getMultiple(string $group, array $keys, bool $force = false): ar
281336
}
282337

283338
$values[$key] = $valuesFromPersistentCache[$cacheKey] ?? false;
339+
340+
if (false === $values[$key]) {
341+
++$this->misses;
342+
343+
return;
344+
}
345+
346+
++$this->hits;
347+
348+
$this->storeInMemory($cacheKey, $values[$key]);
284349
});
285350

286351
$order = $keys->flip()->all();

src/ObjectCache/DynamoDbObjectCache.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ protected function storeValueInPersistentCache(string $key, $value, int $expire
135135
*/
136136
private function getValue(string $key)
137137
{
138+
$start = round(microtime(true) * 1000);
139+
138140
$response = $this->dynamoDbClient->getItem([
139141
'TableName' => $this->table,
140142
'ConsistentRead' => false,
@@ -143,6 +145,9 @@ private function getValue(string $key)
143145
],
144146
]);
145147

148+
++$this->requests;
149+
$this->requestTime += (round(microtime(true) * 1000) - $start);
150+
146151
if (!isset($response['Item']['value']['S']) || $this->isExpired($response['Item'])) {
147152
return false;
148153
}
@@ -156,6 +161,8 @@ private function getValue(string $key)
156161
private function getValues(array $keys): array
157162
{
158163
return (new Collection($keys))->chunk(100)->map(function (Collection $chunkedKeys) {
164+
$start = round(microtime(true) * 1000);
165+
159166
$response = $this->dynamoDbClient->batchGetItem([
160167
'RequestItems' => [
161168
$this->table => [
@@ -167,6 +174,9 @@ private function getValues(array $keys): array
167174
],
168175
]);
169176

177+
++$this->requests;
178+
$this->requestTime += (round(microtime(true) * 1000) - $start);
179+
170180
$current = time();
171181

172182
return isset($response['Responses'][$this->table]) ? (new Collection($response['Responses'][$this->table]))->filter(function (array $item) use ($current) {

src/ObjectCache/ObjectCacheInterface.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ public function flush(): bool;
7070
*/
7171
public function get(string $group, string $key, bool $force = false, &$found = null);
7272

73+
/**
74+
* Get information on the object cache.
75+
*/
76+
public function getInfo(): array;
77+
7378
/**
7479
* Retrieves multiple values from the cache in one call.
7580
*

src/ObjectCache/WordPressObjectCache.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,19 @@ public function get(string $group, string $key, bool $force = false, &$found = n
9797
return $this->cache->get($key, $group, $force, $found);
9898
}
9999

100+
/**
101+
* {@inheritdoc}
102+
*/
103+
public function getInfo(): array
104+
{
105+
return [
106+
'hits' => $this->cache->cache_hits,
107+
'misses' => $this->cache->cache_misses,
108+
'ratio' => round(($this->cache->cache_hits / ($this->cache->cache_hits + $this->cache->cache_misses)) * 100, 1),
109+
'type' => str_replace('ObjectCache', '', (new \ReflectionClass($this))->getShortName()),
110+
];
111+
}
112+
100113
/**
101114
* {@inheritdoc}
102115
*/

src/Plugin.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public function __construct(string $filePath)
7474
Configuration\EventManagementConfiguration::class,
7575
Configuration\ObjectCacheConfiguration::class,
7676
Configuration\PhpConfiguration::class,
77+
Configuration\QueryMonitorConfiguration::class,
7778
Configuration\RestApiConfiguration::class,
7879
Configuration\UploadsConfiguration::class,
7980
Configuration\WordPressConfiguration::class,

0 commit comments

Comments
 (0)