Skip to content

Added cache data collector and profiler page #21065

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

Closed
wants to merge 9 commits into from

Conversation

Nyholm
Copy link
Member

@Nyholm Nyholm commented Dec 27, 2016

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #19297
License MIT
Doc PR n/a

Adding a first version of cache profiler page. Most things are taken from PHP-cache.
FYI: @aequasi

What is included?

A collector, recording adapter and a profiler page.

screen shot 2016-12-27 at 16 07 35
screen shot 2016-12-27 at 16 07 45

What is not included?

  • A good logo
  • Nice design on the profiler page

This PR aims to pass as the minimum requirement for a cache page. Im happy to do fancy extra features but those should be a different PR.

$loaders = array_map(function ($loader) { return new Reference($loader); }, $config['loaders']);
$loaders = array_map(function ($loader) {
return new Reference($loader);
}, $config['loaders']);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was from fabbot.io

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should revert them, fabbot is broken these days

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okey, thank you

@@ -674,7 +677,9 @@ private function registerTemplatingConfiguration(array $config, ContainerBuilder
));

$container->setParameter('templating.engines', $config['engines']);
$engines = array_map(function ($engine) { return new Reference('templating.engine.'.$engine); }, $config['engines']);
$engines = array_map(function ($engine) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was from fabbot.io

@javiereguiluz
Copy link
Member

@Nyholm thanks for this PR. Don't worry about the logo because the SensioLabs designer will take care of that. And same thing about the design, don't worry because we can iterate it later. Let's focus on the contents and the information provided. Thanks!

@Nyholm
Copy link
Member Author

Nyholm commented Dec 27, 2016

That is great. Then Im done with this PR =)

("Done" = "Waiting for community feedback")

@Koc
Copy link
Contributor

Koc commented Dec 27, 2016

It would be very useful collect this information also from production env and analyze it later. Can anybody idea how to make it possible without enabling whole profiler?

@Nyholm
Copy link
Member Author

Nyholm commented Dec 27, 2016

It would be very useful collect this information also from production env and analyze it later. Can anybody idea how to make it possible without enabling whole profiler?

With the current implementation you should only load cache_debug.xml and then make sure to store the data from the collector somewhere on kernel.terminate

*
* @return string
*/
private function getValueRepresentation($value)
Copy link

@Renrhaf Renrhaf Dec 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nyholm I'm not sure, but can't we use the VarDumper component here to print the value ? (BTW thanks a lot for this first collector, it's really nice!)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. That is a good idea. I'll see what I can do. Thanks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the representation should be handled by the data collector, using the cloneVar method of the base DataCollector

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot use the DataCollector::cloneVar in this context. The RecordingAdapter is not a data collector. However, I use the cloneVar once the data is moved to the CacheDataCollector

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Renrhaf I've updated the PR using your suggestion. Thank you!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nyholm seems good to me ;) thank you !

*
* @return object
*/
private function timeCall($name, array $arguments = array())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about using a stopwatch instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!

*
* @param $call
*/
private function addCall($call)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function wrapper for a oneliner could be removed

/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface RecordingAdapterInterface
Copy link
Member

@nicolas-grekas nicolas-grekas Dec 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd remove this interface entirely: we don't want a contract here, we need only a behavior (an implem)

foreach ($calls as $call) {
$statistics[$name]['calls'] += 1;
$statistics[$name]['time'] += $call->time;
if ($call->name === 'getItem') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about getItems?

Copy link
Member Author

@Nyholm Nyholm Dec 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. If one do getItems(['a', 'b', 'c']). I will count that as 1 call and 3 reads.

$result = $call->result;
$hits = 0;
$items = array();
foreach ($result as $item) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of a generator, I see two issues here:

  • this is consuming the generator. Since these are not rewindable, return $result; returns a non usable generator.
  • this is couting hits, but maybe the app didn't consume the generator on its side.

This means I think we should have two code paths here: one for array (doing the same as the current code),
and one for Traversable, which should wrap this in a generator-closure, and do things lazily.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've wrapped the result in a generator closure as you suggested. I fail to understand we need a code path for arrays. I know that getItems will return something that you can do foreach with. Why can't I just always wrap it in a generator-closure?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no strong reason, good also via a generator

* @author Aaron Scherer <aequasi@gmail.com>
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class RecordingAdapter implements AdapterInterface
Copy link
Member

@nicolas-grekas nicolas-grekas Dec 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I guess this should be named TraceableAdapter for consistency with the code base (eg TraceableUrlMatcher, etc.)

*
* @return string
*/
private function getValueRepresentation($value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need this logic, let's just use $value directly.
The clone may fail (implem can throw in __clone), and is fragile (not recursive).
and for other types of data, we don't care (resources cannot come up from a cache pool)
removing also will make the layer slimmer, which is also a nice target

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do. Thank you.

namespace Symfony\Component\Cache\Adapter;

use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not used anymore (mybad)

<?php

/*
* This file is part of php-cache\cache-bundle package.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is wrong

/*
* This file is part of php-cache\cache-bundle package.
*
* (c) 2015-2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symfony code must use the Symfony copyright

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, Copy/paste issue.. An my IDE does not show file headers. I will fix these

{% block head %}
{{ parent() }}
<style>
.color-red {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be better to include necessary styles in the CSS, to be usable for all collectors

{{ include('@WebProfiler/Icon/cache.svg') }}
</span>
<strong>Cache</strong>
<span class="count">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should have the time here anymore, for consistency with other panels

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want me to remove it?


{% block panel %}
<h2>Cache</h2>
{% for name, calls in collector.calls %}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to have the aggregated stats first as big numbers (as done in other panels), before having the detail for each pool

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no it is not done

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I misunderstood you. I put the aggregated stats for each adapter as big numbers.
screen shot 2017-01-02 at 18 46 11

$container->register($id.'.recorder', TraceableAdapter::class)
->setDecoratedService($id)
->addArgument(new Reference($id.'.recorder.inner'))
->addArgument(new Reference('debug.stopwatch'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation does not use Stopwatch

@Nyholm
Copy link
Member Author

Nyholm commented Dec 28, 2016

screen shot 2016-12-28 at 20 39 35
screen shot 2016-12-28 at 20 39 25

@xabbuh
Copy link
Member

xabbuh commented Dec 28, 2016

Just by looking at the screenshot I was not able to easily recognise what "ratio" refers to. Does it mean hits/reads?

@Nyholm
Copy link
Member Author

Nyholm commented Dec 28, 2016

Sorry about that. Yes, the ratio is the hits/reads ratio. See new screenshot:

screen shot 2016-12-28 at 20 59 03


FYI: I created a separate PR with the adapters. I added some tests and fixed some bugs in it. I will rebase this PR when/if #21082 gets merged.

@xabbuh
Copy link
Member

xabbuh commented Dec 28, 2016

I think I would change the label from "ratio" to "hits/reads" then.

@xabbuh
Copy link
Member

xabbuh commented Dec 28, 2016

And what about providing aggregated data across all pools?

@Nyholm
Copy link
Member Author

Nyholm commented Dec 28, 2016

I will.

And what about providing aggregated data across all pools?

That is only in the toolbar atm.

@ro0NL
Copy link
Contributor

ro0NL commented Dec 29, 2016

Nice :)

What about a tab per pool? That should organize things right?

The table with calls looks rather technical, so maybe we can improve that UX-wise.

  • Try to keep simple key | value | time headers (only save calls seem to have extra metadata)
  • Differ with colors between gets, deletes or saves (an in-row label or so)

👍

nicolas-grekas added a commit that referenced this pull request Dec 29, 2016
This PR was squashed before being merged into the 3.3-dev branch (closes #21082).

Discussion
----------

[Cache] TraceableAdapter

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | Related to #21065
| License       | MIT
| Doc PR        | n/a

I moved the TraceableAdapter to a separate PR and added some tests. I found a small bug which was fixed.

Commits
-------

5cc4baf [Cache] TraceableAdapter
@Nyholm Nyholm force-pushed the cache-data-collector branch from 3e5b858 to 7341375 Compare December 29, 2016 11:09
@Nyholm
Copy link
Member Author

Nyholm commented Dec 29, 2016

Thank you @ro0NL. I agree with your comments that this could be improved and I really like your suggestions. I think this is "good enough" and I will let other PRs improve this. Does it sound reasonable?

I've rebased this PR on master.

$statistics[$name]['misses'] += $count - $call->misses;
} elseif ($call->name === 'hasItem') {
$statistics[$name]['reads'] += 1;
if ($call->result->getRawData()[0][0] === false) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a bit ugly.

$call->result is a Symfony\Component\VarDumper\Cloner\Data. I know that hasItem will always return a boolean thanks to PSR-6. So I believe this is safe.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No better way for now, I may work on something better soon but this is the way to go for now.

@stof
Copy link
Member

stof commented Jan 2, 2017

And what about providing aggregated data across all pools?

That is only in the toolbar atm.

That's unfortunate, as it means that the main info displayed in the toolbar is not available anymore when looking at details.
Btw, I made the same comment earlier about that (and you said it was done while it is not

@Nyholm
Copy link
Member Author

Nyholm commented Jan 3, 2017

Ping @stof

screen shot 2017-01-03 at 10 46 53

@xabbuh
Copy link
Member

xabbuh commented Jan 3, 2017

@Nyholm Any reason not to aggregate missed and deletes? I would also change the order to be consistent with how it is for the cache pools.

@Nyholm
Copy link
Member Author

Nyholm commented Jan 3, 2017

I updated the order and added deletes. Thank you for the quick feedback.
screen shot 2017-01-03 at 10 52 16

@rvanlaak
Copy link
Contributor

rvanlaak commented Jan 3, 2017

We chain a apcu pool with a filesystem pool, which means we have three pools in the profiler overview. It would be nice (for a future PR) to think about some chain-specific aggregates, or maybe allow to disable profiler data collection per pool?

From DX perspective it also would be great to have some kind of (javascript?) filtering in the panel, as that would allow you to instantly see the behavior path of a specific cache item.

@Nyholm
Copy link
Member Author

Nyholm commented Jan 12, 2017

Is there anything else I can do to help getting this PR merged?

@fabpot
Copy link
Member

fabpot commented Jan 12, 2017

@Nyholm If you can rebase on current master, I can merge it :)

@nicolas-grekas
Copy link
Member

👍 from me
we should open an issue with the remaining ideas to not forget about them
for this next issue : about the compiler pass, ideally, we could keep track of removed private pools so that the panel show them eg grayed.

@Nyholm Nyholm force-pushed the cache-data-collector branch from b9114b8 to d09bcf3 Compare January 14, 2017 09:03
@Nyholm
Copy link
Member Author

Nyholm commented Jan 14, 2017

Thank you. The PR is rebased and tests are all green.

@fabpot
Copy link
Member

fabpot commented Jan 18, 2017

Thank you @Nyholm.

@fabpot fabpot closed this Jan 18, 2017
fabpot added a commit that referenced this pull request Jan 18, 2017
This PR was squashed before being merged into the 3.3-dev branch (closes #21065).

Discussion
----------

Added cache data collector and profiler page

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #19297
| License       | MIT
| Doc PR        | n/a

Adding a first version of cache profiler page. Most things are taken from PHP-cache.
FYI: @aequasi

### What is included?

A collector, recording adapter and a profiler page.

![screen shot 2016-12-27 at 16 07 35](https://cloud.githubusercontent.com/assets/1275206/21502325/4bee2ed4-cc4f-11e6-89fc-37ed16aca864.png)
![screen shot 2016-12-27 at 16 07 45](https://cloud.githubusercontent.com/assets/1275206/21502326/4bee9450-cc4f-11e6-904d-527b7b0ce85b.png)

### What is not included?

* A good logo
* Nice design on the profiler page

This PR aims to pass as the minimum requirement for a cache page. Im happy to do fancy extra features but those should be a different PR.

Commits
-------

7497f1c Added cache data collector and profiler page
@Nyholm
Copy link
Member Author

Nyholm commented Jan 18, 2017

Thank you for merging

@Renrhaf
Copy link

Renrhaf commented Jan 18, 2017

Many thanks @Nyholm :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.