Source de données : Memcache


1Présentation

Le cache sert habituellement à stocker des informations qui sont souvent accédées en lecture. Il est alors préférable de ne pas aller les lire en base de données à chaque accès, mais plutôt de les agréger et de les placer en cache ; les lectures suivantes seront plus rapides.

Il faut comprendre que, contrairement aux sessions, les variables de cache sont communes à l'ensemble de l'application. Il est donc important de réfléchir soigneusement au nommage des variables, pour pouvoir les retrouver facilement.
Autre différence, les variables de cache ont pour vocation de rester temporaires. Elles expirent au bout d'un certain temps, qui peut être défini de manière globale ou précisément pour chaque variable. Par défaut, la durée d'expiration est de 24 heures.

Si vous avez correctement configuré les paramètres de connexion au serveur Memcached, Temma crée automatiquement un objet de type \Temma\Datasources\Memcache. Par convention, nous partirons du principe que vous avez nommé cette connexion cache dans le fichier etc/temma.php (voir la documentation de la configuration).

Dans les contrôleurs, la connexion au cache est alors disponible en écrivant :

$this->cache

Dans les autres objets gérés par le le composant d'injection de dépendances, la connexion au cache est accessible en écrivant :

$this->_loader->dataSources['cache']

2Configuration

Dans le fichier etc/temma.php (voir la documentation de la configuration), vous déclarez le DSN (Data Source Name) qui permet de se connecter au(x) serveur(s) Memcache.

Lorsque les données sont réparties sur plusieurs serveurs Memcache, c'est aux clients de connaître l'ensemble des serveurs, et leur liste doit être configurée à l'identique sur tous les clients qui doivent se connecter aux serveurs.

Le DSN de connexion à un serveur Memcache s'écrit de la forme : memcache://SERVEUR[:PORT]
Le numéro de port est le 11211 par défaut.

Lorsqu'il y a plusieurs serveurs Memcache, il faut les séparer par un point-virgule.
Exemple : memcache://localhost;otherhost:11000;anotherhost

Si le serveur Memcache s'exécute sur la même machine, il est possible de se connecter en utilisant une socket Unix, permettant d'éviter les latences réseau. Dans ce cas, le DSN est de la forme : memcache://CHEMIN:0
Le chemin doit mener à la socket Unix (souvent /var/run/memcached.sock).
Le numéro de port doit obligatoirement être 0 (zéro) par défaut.
Exemple : memcache:///var/run/memcached.sock:0


3Appels unifiés

3.1Accès de type tableau

// vérification de l'existence d'une donnée
if (isset($this->cache['key1']))
    doSomething();

// lecture de données (désérialisées)
$data = $this->cache['key1'];

// écriture de données (sérialisées)
$this->cache['key1'] = $value;

// effacement de données
unset($this->cache['key1']);

// nombre d'éléments
$nbr = count($this->cache);

3.2Méthodes générales

// vérification de l'existence d'une donnée
if ($this->cache->isSet('user:1'))
    doSomething();

// effacement de donnée
$this->cache->remove('user:1');

// effacement de plusieurs données
$this->cache->mRemove(['user:1', 'user:2', 'user:3']);

// effacement de données à patir d'un préfixe
$this->cache->clear('user:');

// effacement de toutes les données
$this->cache->flush();

3.3Gestion de données complexes sérialisées

Memcache ne supporte pas la méthode search().

Par défaut, les données enregistrées dans Memcache ont une durée de vie de 24 heures. Il est possible de spécifier le nombre de secondes de mise en cache, jusqu'à 30 jours (soit en spécifiant 2592000 secondes, soit en indiquant une valeur de -1).

// lecture de données (désérialisées)
$user = $this->cache->get('user:1');
// lecture de données avec valeur par défaut
$color = $this->cache->get('color', 'blue');
// lecture de données avec création de données si nécessaire
$user = $this->cache->get("user:$userId", function() use ($userId) {
    return $this->dao->get($userId);
});

// lecture de plusieurs données (désérialisées)
$users = $this->cache->mGet(['user:1', 'user:2', 'user:3']);

// écriture de données (sérialisées)
$this->cache->set('user:1', $userData);
// écriture de données (sérialisées) avec une durée de vie d'une heure
$this->cache->set('user:1', $userData, 3600);

// écriture de plusieurs données (sérialisées)
$this->cache->mSet([
    'user:1' => $user1data,
    'user:2' => $user2data,
    'user:3' => $user3data,
]);
// écriture de plusieurs données, avec une durée de vie de 30 jours
$this->cache->mSet([
    'user:1' => $user1data,
    'user:2' => $user2data,
], -1);

3.4Gestion de données brutes

Memcache ne supporte pas la méthode find().

Par défaut, les données enregistrées dans Memcache ont une durée de vie de 24 heures. Il est possible de spécifier le nombre de secondes de mise en cache, jusqu'à 30 jours (soit en spécifiant 2592000 secondes, soit en indiquant une valeur de -1).

// lecture de données (brutes)
$html = $this->cache->read('page:home');
// lecture de données avec valeur par défaut
$html = $this->cache->read('page:home',
                           '<html><body><h1>Homepage</h1><body><html>');
// lecture de données avec création de données si nécessaire
$html = $this->cache->read('page:home', function() {
    return file_get_contents('/path/to/homepage.html');
});

// lecture de plusieurs données (brutes)
$pages = $this->cache->mRead(['page:home', 'page:admin', 'page:products']);

// copie d'une donnée dans un fichier local
$this->cache->copyFrom('page:home', '/path/to/newpage.html');
// copie d'une donnée dans un fichier local, avec valeur par défaut
$this->cache->copyFrom('page:home', '/path/to/newpage.html', $defaultHtml);
// copie d'une donnée dans un fichier local,
// avec création de données si nécessaire
$this->cache->copyFrom('page:home', '/path/to/newpage.html', function() {
    return file_get_contents('/path/to/oldpage.html');
});
// copie d'une donnée dans un fichier local,
// avec création de données si nécessaire (avec une durée de vie d'une heure)
$this->cache->copyFrom('page:home', '/path/to/newpage.html', function() {
    return file_get_contents('/path/to/oldpage.html');
}, 3600);

// écriture de donnée (brute)
$this->cache->write('color:blue', '#0000ff');
// écriture de donnée (brute) avec une durée de vie d'une heure
$this->cache->write('color:blue', '#0000ff', 3600);

// écriture de plusieurs données (brutes)
$this->cache->mWrite({
    'color:blue'  => '#0000ff',
    'color:red'   => '#ff0000',
    'color:green' => '#00ff00',
]);
// écriture de plusieurs données avec une durée de vie de 30 jours
$this->cache->mWrite({
    'color:blue'  => '#0000ff',
    'color:red'   => '#ff0000',
], -1);

// écriture d'une donnée (brute) à partir d'un fichier local
$this->cache->copyTo('page:home', '/path/to/homepage.html');
// écriture d'une donnée à partir d'un fichier local, avec une durée de vie d'une heure
$this->cache->copyTo('page:home', '/path/to/homepage.html', 3600);

// écriture de plusieurs données (brutes) à partir de fichiers locaux
$this->cache->mCopyTo([
    'page:home'     => '/path/to/homepage.html',
    'page:admin'    => '/path/to/admin.html',
    'page:products' => '/path/to/products.html',
]);
// écriture de plusieurs données à partir de fichiers locaux
// avec une durée de vie de 30 jours
$this->cache->mCopyTo([
    'page:home'  => '/path/to/homepage.html',
    'page:admin' => '/path/to/admin.html',
], -1);

4Appels spécifiques

4.1Durée de mise en cache

La méthode setExpiration() sert à définir la durée de mise en cache par défaut. Cela permet d'éviter de le définir explicitement à chaque fois qu'une donnée est écrite en cache.
Elle prend un paramètre, qui est la durée de vie maximale des données en cache, exprimée en secondes. Par défaut, la durée est de 86400 secondes (soit 24 heures).
Cette méthode retourne l'instance de l'objet de cache.

// expiration par défaut d'une heure
$this->cache->setExpiration(3600);

// expiration de 5 minutes, suivi d'un ajout de donnée
$this->cache->setExpiration(300)->set('aa', 'bb');

Vous pouvez utiliser la méthode getExpiration() pour connaître la durée de mise en cache actuellement configurée.

// on veut s'assurer que l'expiration est d'au moins 1 heure
$exp = $this->cache->getExpiration();
if ($exp < 3600)
    $this->cache->setExpiration(3600);

4.2Gestion de préfixe

Ce que nous nommons "préfixes" est un label qui est ajouté aux noms des variables de cache. Leur intérêt est de pouvoir gérer toutes les variables de cache possédant le même préfixe, pour les invalider en une seule opération.

La méthode setPrefix([string $prefix]) sert à définir le préfixe des variables qui vont être traitées lors des appels à get() et set() qui suivront. En l'appelant sans paramètre, on supprime l'utilisation des préfixes.
Elle retourne l'instance de l'objet de cache.

La méthode clear(string $prefix) sert à invalider toutes les variables de cache qui ont le préfixe passé en paramètre.
Elle retourne aussi l'instance de l'objet de cache.

Exemple d'utilisation :

// définition d'un préfixe
$this->cache->setPrefix('sites');
// ajout de variables de cache
$this->cache->set('A', $siteA);
$this->cache->set('B', $siteB);
$this->cache->set('C', $siteC);

// définition d'un second préfixe
$this->cache->setPrefix('articles');
// ajout de variables de cache
$this->cache->set('Y', $articleY);
$this->cache->set('Z', $articleZ);

// effacement des variables appartenant au premier préfixe
// ("A", "B" et "C")
$this->cache->clear('sites');

// arrêt de l'utilisation du préfixe "articles"
$this->cache->setPrefix();

// récupération d'une variable préfixée
$data = $this->cache->setPrefix('articles')->get('Z');

Il est possible de connaître le préfixe en cours grâce à la méthode getPrefix() :

$prefix = $this->cache->getPrefix();
if ($prefix != 'sites')
    $this->cache->setPrefix('sites');

4.3Activation/désactivation du cache

La méthode disable() sert à désactiver temporairement l'utilisation du cache. Tous les appels subséquents ne retourneront pas d'erreur, mais aucun accès au cache ne sera effectué.
La méthode enable() permet de réactiver l'utilisation du cache (après un appel à disable(), par exemple).
Ces deux méthodes retournent l'instance de l'objet de cache.

Exemple d'utilisation :

// désactivation du cache
$this->cache->disable();

// la fonction anonyme sera systématiquement exécutée,
// car le cache est désactivé
$article = $this->cache->get(
    "article:$articleId",
    function () use ($articleId, $dao) {
        return ($dao->get($articleId));
    }
);

// réactivation du cache et enregistrement d'une variable
$this->_cache->enable()->set('name', $value);

La méthode enable() réactive le cache qui a été temporairement désactivé. Elle retourne aussi l'instance de l'objet de cache.

$this->cache->enable();

Pour savoir si le cache est activé ou désactivé, vous pouvez utiliser la méthode isEnabled(), qui retourne true si le cache est activé, et false sinon.

if (!$this->cache->isEnabled())
    print("Cache désactivé");