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

Added the documentation for the Cache component #6515

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
116 changes: 116 additions & 0 deletions components/cache/cache_items.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
.. index::
single: Cache Item
single: Cache Expiration
single: Cache Exceptions

Cache Items
===========

Cache items are each one of the information units stored in the cache as a
Copy link
Member

Choose a reason for hiding this comment

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

Cache items are each one of the information units (or looks strange to me)

key/value pair. In the Cache component they are represented by the
:class:`Symfony\\Component\\Cache\\CacheItem` class.

Cache Item Keys and Values
--------------------------

The **key** of a cache item is a UTF-8 encoded string which acts as its
identifier, so it must be unique for each cache pool. The PSR-6 standard limits
the key length to 64 characters, but Symfony allows to use longer keys (they are
encoded internally to reduce their size).
Copy link
Member

Choose a reason for hiding this comment

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

They are not encoded for all adapters but still the length is not limited. I suggest removing these brackets.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd advocate for keeping to 64 characters anyway, for interoperability (the whole point of PSR interfaces...)

Copy link
Member Author

Choose a reason for hiding this comment

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

If Symfony follows this 64-char limit internally and it doesn't enforce it externally, I think we should remove this information.

Copy link
Contributor

Choose a reason for hiding this comment

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

It does not matter what Symfony Cache does internally. The interface only guarantees that keys up to 64 characters will work. Say, the user swap out Symfony Cache for another PSR-6 cache, things might break if they go over that limit. And we should encourage users to stay within the limit - warn them of the risk of doing otherwise.


You can freely chose the keys, but they can only contain letters (A-Z, a-z),
Copy link
Member

Choose a reason for hiding this comment

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

they can -> they should? (because we allow any other non-reserved chars)

Copy link
Contributor

Choose a reason for hiding this comment

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

chose -> choose

numbers (0-9) and the ``_`` and ``.`` symbols. Other common symbols (such as
``{``, ``}``, ``(``, ``)``, ``/``, ``\`` and ``@``) are reserved for future uses.
Copy link
Member

Choose a reason for hiding this comment

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

by PSR-6?

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we link to the relevant sections in PSR-6?


The **value** of a cache item can be any data represented by a type which is
serializable by PHP, such as basic types (strings, integers, floats, boolean,
nulls), arrays and objects.
Copy link
Member

Choose a reason for hiding this comment

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

nulls (not s)


Creating Cache Items
--------------------

Cache items are created with the ``getItem($key)`` method of the cache pool. The
argument is the key of the item::

// $cache pool object was created before
$cachedNumProducts = $cache->getItem('stats.num_products');
Copy link
Member

Choose a reason for hiding this comment

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

$pool->getItem(...?

Copy link
Contributor

Choose a reason for hiding this comment

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

Does num stand for a total quantity or an identifier? In case of quantity I'd name it 'stats.count_products'.


Then, use the ``set($value)`` method to set the data stored in the cache item::
Copy link
Member

Choose a reason for hiding this comment

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

let's use an API link here


// storing a simple integer
$cachedNumProducts->set(4711);

// storing an array
$cachedNumProducts->set(array(
'category1' => 4711,
'category2' => 2387,
));

.. note::

Creating a cache item and setting its value is not enough to save it in the
cache. You must execute the ``save($cacheItem)`` method explicitly on the
Copy link
Member

Choose a reason for hiding this comment

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

we don't put arguments in the parenthesis when refering to methods

cache pool.

The key and the value of any given cache item can be obtained with the
corresponding *getter* methods::

$cacheItem = $cache->getItem('logged_users');
// ...
$key = $cacheItem->getKey();
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a difference between $key and 'logged_users'?

$value = $cacheItem->get();

Cache Item Expiration
~~~~~~~~~~~~~~~~~~~~~

By default cache items are stored "permanently", which in practice means "as long
as allowed by the cache implementation used".
Copy link
Member

Choose a reason for hiding this comment

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

as long as the cache pool is not cleared also?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that's already understood. This is talking about expiration. Clearing is a different matter.


However, in some applications it's common to use cache items with a shorter
lifespan. Consider for example an application which caches the latest news just
for one minute. In those cases, use the ``expiresAfter()`` method to set the
number of seconds to cache the item::

$latestNews = $cache->getItem('latest_news');
$latestNews->expiresAfter(60); // 60 seconds = 1 minute

// this method also accepts \DateInterval instances
$latestNews->expiresAfter(DateInterval::createFromDateString('1 hour'));

Cache items define another related method called ``expiresAt()`` to set the
exact date and time when the item will expire::

$mostPopularNews = $cache->getItem('popular_news');
$mostPopularNews->expiresAt(new \DateTime('tomorrow'));

Cache Item Hits and Misses
--------------------------

Using a cache mechanism is important to improve the application performance, but
it should not be required to make the application work. In fact, the Cache
Copy link
Member

Choose a reason for hiding this comment

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

What's the "cache standard"?

standard states that caching errors should not result in application failures.

In practice this means that the ``getItem()`` method always returns an object
which implements the ``Psr\Cache\CacheItemInterface`` interface, even when the
cache item doesn't exist. Therefore, you don't have to deal with ``null`` values.
Copy link
Member

Choose a reason for hiding this comment

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

This allows caching e.g. null or false values?


In order to decide if the returned object is correct or not, caches use the
concept of hits and misses:

* **Cache Hits** occur when the requested item is found in the cache, its value
is not corrupted or invalid and it hasn't expired;
* **Cache Misses** are the opposite of hits, so they occur when the item is not
found in the cache, its value is corrupted or invalid for any reason or the
item has expired.

Cache item objects define a boolean ``isHit()`` method which returns ``true``
for cache hits::

$latestNews = $cache->getItem('latest_news');
$latestNews->expiresAfter(60);
Copy link
Member

Choose a reason for hiding this comment

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

not sure this example is the good one here, i'd suggest something like the following, which is the canonical snippet when dealing with arbitrary cached values to me:

$latestNews = $cache->getItem('latest_news');

if (!$latestNews->isHit()) {
   $value = //... do some heavy computation
    $pool->save($latestNews->set($value));
} else {
    $value = $latestNews->get();
}


// check the item a few seconds after creating it
$isHit = $latestNews->isHit(); // true

// check the item 10 minutes after creating it
$isHit = $latestNews->isHit(); // false
250 changes: 250 additions & 0 deletions components/cache/cache_pools.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
.. index::
single: Cache Pool
single: APC Cache, APCu Cache
single: Doctrine Cache
single: Redis Cache

Cache Pools
===========

Cache Pools are the logical repositories of cache items. They perform all the
common operations on items, such as saving them or looking for them. Cache pools
are independent from the actual cache implementation. Therefore, applications
can keep using the same cache pool even if the underlying cache mechanism
changes from a filesystem based cache to a Redis or database based cache.

Creating Cache Pools
--------------------

Cache Pools are created through the **cache adapters**, which are classes that
implement the :class:`Psr\\Cache\\CacheItemPoolInterface` interface. This
Copy link
Member

Choose a reason for hiding this comment

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

"which" refers to "cache adapters", these implement AdapterInterface (which extends CacheItemPoolInterface).

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 don't understand this comment. Please tell me how to reword this paragraph:

Cache Pools are created through the **cache adapters**, which are classes that
implement the :class:`Psr\\Cache\\CacheItemPoolInterface` interface. This
component provides several adapters ready to use in your applications.

Thanks.

Copy link
Member

Choose a reason for hiding this comment

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

With the current situation, it seems like I need to implement CacheItemPoolInterface when I want to create a custom adapter. However, I have to to implement AdapterInterface (unless I'm wrong of course).

Copy link
Member

Choose a reason for hiding this comment

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

true for symfony derivated adapters (AdapterInterface extends CacheItemPoolInterface)

component provides several adapters ready to use in your applications.

Array Cache Adapter
~~~~~~~~~~~~~~~~~~~

This adapter is only useful for testing purposes because contents are stored in
memory and no persisted in any way. Besides, some features explained later are
Copy link
Member

Choose a reason for hiding this comment

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

not persisted

not available, such as the deferred saves::

use Symfony\Component\Cache\Adapter\ArrayAdapter;

$cache = new ArrayAdapter($defaultLifetime = 0, $storeSerialized = true);

``defaultLifetime``
**type**: integer, **default value**: ``0``
The default lifetime, in seconds, applied to cache items that don't define
their own lifetime. The default value (``0``) means an "infinite" lifetime,
but this adapter destroys the cache once the current PHP execution finishes.

``storeSerialized``
**type**: boolean, **default value**: ``true``
If ``true``, the values saved in the cache are serialized before storing them.

Filesystem Cache Adapter
~~~~~~~~~~~~~~~~~~~~~~~~

This adapter is useful when you want to improve the application performance but
can't install in the server tools like APC or Redis::
Copy link
Member

Choose a reason for hiding this comment

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

It stores cached items in a set of directories on the local file system.


use Symfony\Component\Cache\Adapter\FilesystemAdapter;

$cache = new FilesystemAdapter($namespace = '', $defaultLifetime = 0, $directory = null);
Copy link
Member

Choose a reason for hiding this comment

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

$pool?


``namespace``
**type**: string, **default value**: ``''`` (an empty string)
The subdirectory created inside the main cache directory (defined in the
third argument) to store the cache items.

``defaultLifetime``
**type**: integer, **default value**: ``0``
The default lifetime, in seconds, applied to cache items that don't define
their own lifetime. The default value (``0``) means an "infinite" lifetime,
which this adapter respects because items are actually persisted.

``directory``
**type**: string, **default value**: ``null``
The directory where the cache items are stored as files. Make sure that this
directory has read-write permissions for your application. If no directory
is defined, a new directory called ``symfony-cache/`` is created in the
system's temporary directory.

APCu Cache Adapter
~~~~~~~~~~~~~~~~~~

This adapter can increase the application performance very significantly, because
contents are cached in the memory of your server, which is much faster than the
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 shared memory

filesystem. It requires to have installed and enabled the PHP APC extension::
Copy link
Member

Choose a reason for hiding this comment

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

Note that APCu should be recommended for mostly-append populating scenarios. Having lots of delete&writes creates fragmentation which can degrade performance significantly.


use Symfony\Component\Cache\Adapter\ApcuAdapter;

$cache = new ApcuAdapter($namespace = '', $defaultLifetime = 0);

``namespace``
**type**: string, **default value**: ``''`` (an empty string)
The string prefixed to the keys of the items stored in this cache.

``defaultLifetime``
**type**: integer, **default value**: ``0``
The default lifetime, in seconds, applied to cache items that don't define
their own lifetime. The default value (``0``) means an "infinite" lifetime,
which in this adapter ends when the web server is restarted or the APC memory
is deleted in any other way.

Redis Cache Adapter
~~~~~~~~~~~~~~~~~~~

This adapter, similarly to APCu adapter, can increase the application performance
very significantly, because contents are cached in the memory of your server. It
Copy link
Member

Choose a reason for hiding this comment

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

This description makes it as if Redis is like APCu (both store content "in the memory of your server").
But of course the difference is much more than that: Redis is accessed through network protocols, which means cached items can be shared by several PHP fronts, whearas APCu is accessed through shared memory, which is local to one single PHP server.

requires to have installed Redis and have created a connection that implements
``\Redis`` class::
Copy link
Member

Choose a reason for hiding this comment

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

the Redis


use Symfony\Component\Cache\Adapter\RedisAdapter;

$cache = new RedisAdapter(\Redis $redisConnection, $namespace = '', $defaultLifetime = 0);
Copy link
Member

Choose a reason for hiding this comment

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

$pool?


``redisConnection``
**type**: ``\Redis``, **default value**: (none, this argument is mandatory)
The object that represents a valid connection to your Redis system.

``namespace``
**type**: string, **default value**: ``''`` (an empty string)
The string prefixed to the keys of the items stored in this cache.

``defaultLifetime``
**type**: integer, **default value**: ``0``
The default lifetime, in seconds, applied to cache items that don't define
their own lifetime. The default value (``0``) means an "infinite" lifetime,
which in this adapter ends when the server is restarted or the Redis memory
is deleted in any other way.

Chain Cache Adapter
~~~~~~~~~~~~~~~~~~~

This adapter allows to combine any number of the previous adapters. Cache items
are fetched from the first adapter which contains them. Besides, cache items are
saved in all the given adapters, so this is a quick way of creating a cache
replication::

use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;

$apcCache = new ApcuAdapter();
$fileCache = new FilesystemAdapter();

$cache = new ChainAdapter(array($apcCache, $fileCache));

The second optional argument of ``ChainAdapter`` is the ``maxLifetime`` (default
``0``) which is the maximum lifetime of items propagated from lower adapters to
upper ones.

.. TODO: I don't understand the previous phrase, which is copied from the ChainAdapter code.
Copy link
Member

Choose a reason for hiding this comment

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

when an item is not found in the first pool but is found in the second, the ChainAdapter ensures that the just fetched item is saved in the first pool where it were missing. But with which lifetime? Since there is no way to know of much time the item will remain in the second adapter where it were found, we need a default value, here comes $maxLifetime

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for the comment. I understand this now.

However, I don't understand why we need this. If an item is found in the second pool, we now when expires it. Why cannot we create the item in the first pool and set its expiration with expiresAt() ?

Copy link
Member

Choose a reason for hiding this comment

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

we do not know when it expires, that's exactly the point

Copy link
Member Author

Choose a reason for hiding this comment

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

How can't you know when an existing item expires?

Copy link
Member

Choose a reason for hiding this comment

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

How can you know when an existing item expires? :)


Proxy Cache Adapter
~~~~~~~~~~~~~~~~~~~

.. TODO: what is this adapter useful for?
Copy link
Member

Choose a reason for hiding this comment

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

Two use cases: PSR-6 interop of cache items implementations. Without it, you can't fetch a cache item from a non-symfony pool and save it in a Symfony one. The second use case if getting stats about hits/misses in the current script context (see getHits/getMisses functions).


Doctrine Cache Adapter
~~~~~~~~~~~~~~~~~~~~~~

This adapter wraps any `Doctrine Cache`_ provider so you can use them in your
application as if they were Symfony Cache adapters::

use Doctrine\Common\Cache\SQLite3Cache;
use Symfony\Component\Cache\Adapter\DoctrineAdapter;

$doctrineCache = new SQLite3(__DIR__.'/cache/data.sqlite');
$symfonyCache = new DoctrineAdapter($doctrineCache);

This adapter also defines two optional arguments called ``namespace`` (default:
``''``) and ``defaultLifetime`` (default: ``0``) and adapts them to make them
work in the underlying Doctrine cache.

Looking for Cache Items
-----------------------

Cache Pools define three methods to look for cache items. The most common method
is ``getItem($key)``, which returns the cache item identified by the given key::

use Symfony\Component\Cache\Adapter\FilesystemAdapter;

$cache = new FilesystemAdapter('app.cache')
$latestNews = $cache->getItem('latest_news');

If no item is defined for the given key, the method doesn't return a ``null``
value but an empty object which implements the :class:`Symfony\\Component\\Cache\\CacheItem`
class.

If you need to fetch several cache items simultaneously, use instead the
``getItems(array($key1, $key2, ...))`` method::

// ...
$stocks = $cache->getItems(array('AAPL', 'FB', 'GOOGL', 'MSFT'));

Again, if any of the keys doesn't represent a valid cache item, you won't get
a ``null`` value but an empty ``CacheItem`` object.

The last method related to fetching cache items is ``hasItem($key)``, which
returns ``true`` if there is a cache item identified by the given key::

// ...
$hasBadges = $cache->hasItem('user_'.$userId.'_badges');

Saving Cache Items
------------------

The most common method to save cache items is ``save($item)``, which stores the
Copy link
Member

@wouterj wouterj Jun 14, 2016

Choose a reason for hiding this comment

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

let's use API links (same below for the other methods)

item in the cache immediately (it returns ``true`` if the item was saved or
``false`` if some error occurred)::

// ...
$userFriends = $cache->get('user_'.$userId.'_friends');
$userFriends->set($user->getFriends());
$isSaved = $cache->save($userFriends);

Sometimes you may prefer to not save the objects immediately in order to
increase the application performance. In those cases, use the
``saveDeferred($item)`` method to mark cache items as "ready to be persisted"
and then call to ``commit()`` method when you are ready to persist them all::

// ...
$isQueued = $cache->saveDeferred($userFriends);
// ...
$isQueued = $cache->saveDeferred($userPreferences);
// ...
$isQueued = $cache->saveDeferred($userRecentProducts);
// ...
$isSaved = $cache->commit();

The ``saveDeferred()`` method returns ``true`` when the cache item has been
successfully added to the "persist queue" and ``false`` otherwise. The ``commit()``
method returns ``true`` when all the pending items are successfully saved or
``false`` otherwise.

Removing Cache Items
--------------------

Cache Pools include methods to delete a cache item, some of them or all of them.
The most common is ``deleteItem($key)``, which deletes the cache item identified
by the given key (it returns ``true`` when the item is successfully deleted or
doesn't exist and ``false`` otherwise)::

// ...
$isDeleted = $cache->deleteItem('user_'.$userId);

Use the ``deleteItems(array($key1, $key2, ...))`` method to delete several cache
items simultaneously (it returns ``true`` only if all the items have been deleted,
even when any or some of them don't exist)::

// ...
$areDeleted = $cache->deleteItems(array('category1', 'category2'));

Finally, to remove all the cache items stored in the pool, use the ``clear()``
method (which returns ``true`` when all items are successfully deleted)::

// ...
$cacheIsEmpty = $cache->clear();

.. _`Doctrine Cache`: https://github.com/doctrine/cache
9 changes: 9 additions & 0 deletions components/cache/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Cache
=====

.. toctree::
:maxdepth: 2

introduction
cache_items
cache_pools
Loading