Skip to content
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

EZP-29659: As a v2 Cluster Admin & Developer I want support for InMemory SPI cache #2553

Merged
merged 17 commits into from Mar 18, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -31,7 +31,7 @@ public function __construct(PersistenceLogger $logger)
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$this->data = [
'count' => $this->logger->getCount(),
'stats' => $this->logger->getStats(),
'calls_logging_enabled' => $this->logger->isCallsLoggingEnabled(),
'calls' => $this->logger->getCalls(),
'handlers' => $this->logger->getLoadedUnCachedHandlers(),
Expand All @@ -46,11 +46,25 @@ public function getName()
/**
* Returns call count.
*
* @deprecaterd since 7.5, use getStats().
*
* @return int
*/
public function getCount()
{
return $this->data['count'];
return $this->data['stats']['call'] + $this->data['stats']['miss'];
}

/**
* Returns stats on Persistance cache usage.
*
* @since 7.5
*
* @return int[<string>]
*/
public function getStats()
{
return $this->data['stats'];
}

/**
Expand All @@ -66,28 +80,35 @@ public function getCallsLoggingEnabled()
}

/**
* Returns calls.
* Returns all calls.
*
* @return array
*/
public function getCalls()
{
$calls = [];
foreach ($this->data['calls'] as $call) {
if (empty($this->data['calls'])) {
return [];
}

$calls = $count = [];
foreach ($this->data['calls'] as $hash => $call) {
list($class, $method) = explode('::', $call['method']);
andrerom marked this conversation as resolved.
Show resolved Hide resolved
$namespace = explode('\\', $class);
$class = array_pop($namespace);
$calls[] = array(
$calls[$hash] = [
'namespace' => $namespace,
'class' => $class,
'method' => $method,
'arguments' => empty($call['arguments']) ?
'' :
preg_replace(array('/^array\s\(\s/', '/,\s\)$/'), '', var_export($call['arguments'], true)),
'trace' => implode(', ', $call['trace']),
);
'arguments' => $call['arguments'],
'traces' => $call['traces'],
'stats' => $call['stats'],
];
// Leave out in-memory lookups from sorting
$count[$hash] = $call['stats']['uncached'] + $call['stats']['miss'] + $call['stats']['hit'];
}

array_multisort($count, SORT_DESC, $calls);

return $calls;
}

Expand Down
@@ -1,8 +1,6 @@
{% extends 'WebProfilerBundle:Profiler:layout.html.twig' %}

{% block toolbar %}
{# {% set status = (collector.hits/collector.calls) < 0.33 ? 'red' : (collector.hits/collector.calls) < 0.66 ? 'yellow' : '' %} #}

{% set icon %}
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1200 1200" enable-background="new 0 0 1200 1200" xml:space="preserve">
<g>
Expand All @@ -25,7 +23,12 @@

{% endset %}

{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status|default('') }) }}
{# Set to red if over 100 uncached, and to yellow if either over 15 uncached or over 100 cache hits lookups #}
{% set stats = collector.getCollector('ezpublish.debug.persistence').stats %}
{% set total_uncached = stats.uncached + stats.miss %}
{% set status_logo = total_uncached > 100 ? 'red' : (total_uncached > 15 ? 'yellow' : (stats.hit > 100 ? 'yellow' : '')) %}

{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_logo|default('') }) }}
{% endblock %}

{% block menu %}
Expand Down
@@ -1,37 +1,89 @@
<h3 title="Calls made to SPI\Persistence, persistance layer beneath Repository, which where not cached in SPI\Persistence\Cache">
Uncached Persistence\Cache calls
<h3 title="Calls made to SPI\Persistence, persistance layer beneath Repository">
Persistence\Cache calls
</h3>

<p class="text-small text-muted">TIP: This can represent both cold cache, and calls to methods which has not been made to be cached <em>(yet)</em>. Make sure to reload a few times to warmup cache in order to only see the latter.</p>
{% set stats = collector.stats %}

<table>
<tr>
<th>Total Uncached calls:</th>
<td>{{ collector.count }}</td>
<th>Uncached method calls:</th>
<td>{{ stats.uncached }}</td>
</tr>
<tr>
<th>Cached method calls:</th>
<td>From memory: {{ stats.memory }}, hits: {{ stats.hit }}, misses: {{ stats.miss }}</td>
</tr>
{% if collector.handlerscount %}
<tr>
<th>Uncached SPI handlers(times loaded):</th>
<th>Uncached SPI handlers:</th>
<td>{{ collector.handlers|join(', ') }}</td>
</tr>
{% endif %}
</table>

{% if collector.callsLoggingEnabled %}

<h4 title="Calls made to SPI\Persistence">
Logged calls to Persistence\Cache
</h4>

<p class="text-small text-muted">
TIP: Calls are ordered by # of backend lookups. As misses <em>can</em> represent cold cache, make sure to reload corresponding page to warmup cache a few times to see info on cached paged.
</p>

<table>
<tr>
<th>Class</th>
<th>Method</th>
<th>Class::method</th>
<th>Arguments</th>
<th title="Simplified trace">Trace</th>
<th title="Simplified traces of the calls being done">Traces</th>
</tr>

{% for call in collector.calls %}
<tr>
<td>{{ call.class }}</td>
<td>{{ call.method }}</td>
<td>{{ call.arguments }}</td>
<td class="text-small text-muted">{{ call.trace }}</td>
<td>
{{ call.class }}<wbr>::{{ call.method }}<br/>
<p class="text-small text-muted">
{% if call.stats.uncached %}<br/>Uncached calls: {{ call.stats.uncached }}{% endif %}
{% if call.stats.miss %}<br/>Cache misses: {{ call.stats.miss }}{% endif %}
{% if call.stats.hit %}<br/>Cache hits: {{ call.stats.hit }}{% endif %}
{% if call.stats.memory %}<br/>Cache hits from memory: {{ call.stats.memory }}{% endif %}
</p>
</td>
<td>
{% for key, argument in call.arguments %}
{{ key }}:
{% if argument is iterable %}
{{ argument|join(', ') }}
{% else %}
{{ argument }}
{% endif %}
<br>
{% endfor %}
</td>
<td>
<table style="margin: 0; box-shadow: none;">
<tr>
<th>#</th>
<th title="Simplified trace">Trace</th>
</tr>
{% for traceInfo in call.traces %}
<tr>
<td class="text-small text-muted">
{{ traceInfo.count }}
</td>
<td class="text-small text-muted">
{% for calltrace in traceInfo.trace %}
{{ calltrace }}<br>
{% endfor %}
</td>
</tr>
{% endfor %}
</table>
</td>
</tr>
{% endfor %}
</table>

{% else %}
<p class="text-small text-muted">NOTE: Call logging is by default only enabled in debug mode as <code>ezpublish.spi.persistence.cache.persistenceLogger.enableCallLogging: "%kernel.debug%"</code>, enable debug or change the setting in order to see calls made and trace for where the calls comes from.</p>
{% endif %}
@@ -1,9 +1,16 @@
{% set stats = collector.stats %}
{# Set cache status to green if misses is below 1 and total hits is below 20 (with remote Redis 20 cache lookups can take 50-100ms) #}
{% set status_cache = stats.miss > 100 ? 'red' : (stats.hit > 100 ? 'yellow' : (stats.miss < 1 and stats.hit < 20 ? 'green' : '')) %}

<div class="sf-toolbar-info-piece">
<b>SPI (persistence)</b>
<b>SPI Persistence/Cache</b>
</div>
<div class="sf-toolbar-info-piece">
<b>calls</b> <span class="sf-toolbar-status sf-toolbar-status-green">{{ collector.count }}</span>
<b>uncached calls</b> <span class="sf-toolbar-status sf-toolbar-status-{{ stats.uncached > 10 ? 'yellow' : (stats.uncached < 2 ? 'green' : '') }}">{{ stats.uncached }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>handlers</b> <span class="sf-toolbar-status sf-toolbar-status-green">{{ collector.handlerscount }}</span>
<b>misses / hits / memory</b> <span class="sf-toolbar-status sf-toolbar-status-{{ status_cache }}">{{ stats.miss }} / {{ stats.hit }} / {{ stats.memory }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>uncached handlers</b> <span class="sf-toolbar-status sf-toolbar-status-{{ collector.handlerscount > 1 ? '' : 'green' }}">{{ collector.handlerscount }}</span>
</div>
2 changes: 1 addition & 1 deletion eZ/Publish/API/Repository/Tests/LanguageServiceTest.php
Expand Up @@ -393,7 +393,7 @@ public function testLoadLanguageThrowsNotFoundException()

$languageService = $repository->getContentLanguageService();

$languages = $languageService->loadLanguageListById(['fre-FR']);
$languages = $languageService->loadLanguageListByCode(['fre-FR']);

$this->assertInternalType('iterable', $languages);
$this->assertCount(0, $languages);
Expand Down