developer.jelix.org is not used any more and exists only for history. Post new tickets on the Github account.
developer.jelix.org n'est plus utilisée, et existe uniquement pour son historique. Postez les nouveaux tickets sur le compte github.

Opened 12 years ago

Closed 10 years ago

Last modified 10 years ago

#540 closed new feature (fixed)

jCache

Reported by: sylvain261 Owned by: sylvain261
Priority: normal Milestone: Jelix 1.2 beta
Component: jelix:utils Version: 1.0.3
Severity: normal Keywords:
Cc: bibo, t.ramaroson@… Blocked By:
Blocking: Documentation needed: no
Hosting Provider: Php version:

Description

L'objectif de jCache serait de permettre de réaliser (sur le serveur) du cache de tout contenu quel qu'il soit, à partir du moment où il est sérialisable et donc stockable sous forme de chaine.

Cela serait utile pour (par exemple) :

  • Mettre en cache le résultat d'un appel à un service externe (web service ou autre)
  • Mettre en cache le résultat d'une requête SQL très lentes à s'exécuter

D'une façon générale, il s'agit donc de pouvoir mettre en cache n'importe quelle donnée, à partir du moment où le chargement de cette donnée est coûteuse en performance pour le site.
A plus long terme on peut imaginer que jZone soit revue pour exploiter jCache, on peut également envisager s'en servir pour du cache sur l'intégralité de la réponse (à voir en fonction de l'issu du ticket #4).

En terme d'implémentation, j'envisage :

  1. Que l'utilisation de jCache soit des plus simplistes, cad qu'on aurait une seule méthode statique get qui renverrait le contenu du cache et qui mettrait à jour le cache si nécessaire. Cette fonction recevrait en paramètre :
    • Qqch permettant d'identifier le contenu caché, qui pourra être soit une string, soit un tableau de string. L'utilisation d'un tableau permettant de ranger le cache sous forme arborescente et donc d'en faciliter la gestion, la configuration et son vidage manuel. (par exemple on pourrait avoir comme identifiant de cache array(0=>'fr', 1=>'article', 2=>'hall-of-fame') auquel cas au niveau du stockage on aurait un répertoire /fr/article/hall-of-fame.
    • Le nom de la fonction chargeant les données à cacher. Ce pourra être soit un simple nom de fonction, soit un tableau avec nom de classe + nom de méthode (pour les méthodes statiques) soit un tableau avec object + nom de méthode (pour les méthodes non statiques)
    • Un tableau de paramètres à passer à la fonction/méthode chargeant les données à cacher
      //Un appel à jCache ressemblerait donc à :
      jCache::get(array('article', '1'), 'loadArticle', array(1));
      //ou encore :
      jCache::get('article1', array('myClassName', 'loadArticle'), array(1));
      //ou encore :
      jCache::get('article1', array(&$myObject, 'loadArticle'), array(1));
      
  2. Une configuration du cache entièrement déclarative via defaultConfig.ini.php où on aurait la possibilité
    • De désactiver le cache de façon globale
    • De préciser le TTL du cache de façon globale ou par type de contenu (compte tenu qu'on a une organisation arborescente des données cachée, l'idée et de potentiellement pouvoir définir une durée de cache différentes pour les différentes entrées de l'arborescence)
    • De préciser le driver à utiliser (cf ci dessous) de façon globale ou par type de contenu (même principe que pour le TTL)
  3. Un système de lock et d'attente évitant que le cache soit raffraichit par plusieurs process simultanés
  4. Une méthode clear permettant de supprimer tout ou partie du cache.
  5. Un système de driver (file ou db) permettant dans le cas de db de partager une donnée en cache entre plusieurs frontaux load balancés.

Merci à tous ceux que ça intéressera de me donner leur feedback sur les objectifs et l'approche pour l'implémentation.

Attachments (14)

patchtestapp.diff (16.3 KB) - added by samtahRam 12 years ago.
application test jCache
patchlib.diff (39.5 KB) - added by samtahRam 12 years ago.
plugin jCache
jcache-v2.diff (56.5 KB) - added by laurentj 12 years ago.
mise à jour des patchs précédents
lib.diff (53.4 KB) - added by sylvain261 12 years ago.
JCache approche plus simple/générique
lib20081202.diff (54.2 KB) - added by sylvain261 12 years ago.
MAJ du path sur la lib (corrections incohérence drivers)
testapp20081202.diff (19.1 KB) - added by sylvain261 12 years ago.
Test unitaires de jCache et de ses 3 drivers
lib20090113.diff (57.9 KB) - added by sylvain261 11 years ago.
Ajout de call pour le cache de l'appel de fonction / méthodes (statiques ou non)
testapp20090113.diff (22.1 KB) - added by sylvain261 11 years ago.
MAJ des tests unitaires (tests de la méthode call)
lib20090119.diff (57.4 KB) - added by laurentj 11 years ago.
mise à jour du patch sur le trunk et petites améliorations plugin db
lib20090121.diff (41.5 KB) - added by samtahRam 11 years ago.
mise à jour du plugin jCache sans le driver file
testapp20090121.diff (16.6 KB) - added by samtahRam 11 years ago.
mise à jour tests unitaires jCache
testapp20090121.2.diff (20.9 KB) - added by samtahRam 11 years ago.
patch de l'application testapp + les fichiers de configuration
lib.2.diff (1.7 KB) - added by samtahRam 10 years ago.
changes made in the plugin file and the config template file
testapp.diff (501 bytes) - added by samtahRam 10 years ago.
changes made in the config file

Download all attachments as: .zip

Change History (53)

comment:1 Changed 12 years ago by laurentj

Je ne comprend pas l'usage des arguments "nom de fonction" et "arguments de fonction" à jCache::get

Pour le reste, je suis ok :-)

comment:2 Changed 12 years ago by laurentj

  • Blocking 151 added

comment:3 Changed 12 years ago by sylvain261

L'idée est d'utiliser call_user_func http://www.php.net/manual/en/function.call-user-func.php pour appeller soit la fonction, soit la méthode statique, soit la méthode non statique

comment:4 Changed 12 years ago by Lipki

Ca marcherai pour le cache du plugin image :)
(ouais je parle que de ce plugin, mais c'est le seul validé a mon actif alors :) ...)

comment:5 Changed 12 years ago by laurentj

sylvain: je me suis mal exprimé. Tu n'as pas du tout expliqué l'interet de donner un nom de fonction. Elle sert à quoi cette fonction ?

comment:6 Changed 12 years ago by sylvain261

Bon apparemment j'ai pas bien expliqué la vision que j'avais de jCache.

Le principe est que, si je veut ajouter du cache pour le code suivant :

$data = myClassName::loadArticle(1);

La seule chose que j'ai à faire c'est de changer la façon dont j'appelle la méthode dont je veut cacher le résultat. Çà devient alors :

$data = jCache::get('article1', array('myClassName', 'loadArticle'), array(1));

L'idée est que ce n'est pas à la fonction chargeant les données (ici loadArticle) de se préoccuper des problématiques de cache mais au controller (ou autre) qui appelle cette fonction. En effet, savoir si les choses doivent être cachée ou pas dépendent uniquement du contexte d'appel de la méthode et pas de l'implémentation de la méthode elle même (par exemple une même méthode peut être cachée dans le front mais pas dans le back). C'est donc jCache qui gère tout çà pour nous. C'est pour cela qu'on passe en paramètre à jCache tout ce qui est nécessaire pour lui permettre de faire appel à la fonction chargeant les données lorsqu'il est nécessaire de rafraîchir le cache.

Lipki : Oui, y a pas de raison pour que ce ne soit pas applicable. Par contre il faudra bien sûr adapter un peu ton plugin.

comment:7 Changed 12 years ago by laurentj

mouai.. mais ma classe n'a pas forcément une méthode statique. Et peut être aussi qu'il faut l'instancier avec des paramètres.

Ça pourrait être une utilisation, mais j'aimerai aussi une API plus conventionnelle, avec une méthode set, qu'on puisse utiliser comme ceci :

  $content = jCache::get('article1');
  if($content == null) {
     $content = $myObject->loadArticle('1');
     jCache::set('article1', $content);
  }

On peut ainsi l'utiliser dans de plus grand nombre de cas, plutôt que de restreindre à l'appel d'une méthode statique. Ce sera par exemple plus simple à utiliser dans jZone je pense.

Bref, il faut avoir le choix entre un jCache::get('article1', array(...)..), et un jCache::get('article1');

comment:8 Changed 12 years ago by laurentj

idée de drivers : http://fr.php.net/manual/en/intro.memcache.php memcache, ou encore les fonctions de cache de APC &co il me semble etc.. Regarder leur api et voir si c'est compatible avec l'API que l'on propose dans jCache.

comment:9 Changed 12 years ago by sylvain261

mouai.. mais ma classe n'a pas forcément une méthode statique. Et peut être aussi qu'il faut l'instancier avec des paramètres. => Ça sera pas forcément une méthode statique qu'on appellera ca pourra être une fonction ou une méthode non statique d'un objet déjà instancié.

Ce qui m'ennuie avec cette approche plus conventionnelle c'est la façon (du coup manuelle) dont on va gérer le lock. Il est à mon sens impératif d'avoir ce mécanisme de lock qui évite que 2 process fasse en même temps le chargement de données nécessaire au rafraîchissement du cache. Avec cette approche conventionnelle, pour avoir une gestion du lock il faudrait alors faire :

  $content = jCache::get('article1');
  if($content == null) {
     jCache::lock('article1');
     $content = $myObject->loadArticle('1');
     jCache::set('article1', $content);
     jCache::unlock('article1');
  }

cad qu'on aurait à poser le lock à la main avant de charger les données puis lever le lock à la main une fois les données chargée. (le get se chargeant d'attendre si la donnée cachée est lockée et se chargeant de forcer la libération du lock si l'attente dure trop longtemps).
Qu'est ce que tu en penses ?
On fait les 2 ?

Par ailleurs je me pose la question de comment implémenter le lock... Soit au niveau de chaque driver (mais certains drivers de le permettrons probablement pas (memcache) soit de façon unique quelque soit le driver, à l'aide de fichier sur le disque mais du coup mon lock n'est plus partagé entre mes frontaux...

comment:10 Changed 12 years ago by laurentj

  • Component changed from jelix to jelix:utils

Pourquoi gerer le lock à la main ? La méthode set ne pourrais pas le faire ? Pourquoi mettre un lock avant de lire l'article ?

comment:11 Changed 12 years ago by sylvain261

Comme je l'indiquais, sur les site à forte charge il est fréquent d'avoir plusieurs process simultanés qui demande la rafraîchissement du cache, du coup, sans lock, on charge les données plusieurs fois simultanément. Ce n'est pas viable si ce chargement de données et relativement consommateur en ressource. J'ai rencontré cette problématique pour un site qui fait 1.2 million de pages vues par jour et aujourd'hui je s'apprête à le porter sous Jelix, il m'est donc indispensable d'avoir ce mécanisme de lock évitant que 2 process simultanés décident de charger les données en même temps. (A l'époque où j'avais rencontré ce pb, l'alternative qu'on avait mis en place c'était de rafraîchir le cache via des cron et non pas via le front)

comment:12 Changed 12 years ago by laurentj

Propose ton patch, mais j'aimerais les deux manières d'utilisation. Je ne veux pas qu'on soit obliger d'utiliser une classe ou une fonction.

Je viens aussi de penser à un truc, on pourrait avoir un boolean à jCache::get disant qu'il faut locker si il n'y a pas de cache. Et lors de l'appel du jCache::set, ça enlève le lock automatiquement. Et il faut prévoir un timeout pour le lock, au cas où le lock est mis mais qu'il y a une exception ou autre avant le set, ce qui empeche de lever le lock.

Changed 12 years ago by samtahRam

application test jCache

Changed 12 years ago by samtahRam

plugin jCache

comment:13 Changed 12 years ago by samtahRam

  • Component changed from jelix:utils to jelix:plugins
  • review set to review?

Bonjour, Sylvain m'a confié la tâche de poster le plugin concernant le cache de jelix et de correspondre avec vous en son absence. mon e-mail est t.ramaroson@…. Merci de votre attention

comment:14 Changed 12 years ago by laurentj

  • Component changed from jelix:plugins to jelix:utils
  • review changed from review? to review-

Je suis en train de relire le patch :-)

  • Y a t-il une raison particulière de pouvoir utiliser un driver spécifique pour chaque type de donnée à mettre en cache ? aurais-tu un exemple montrant l'utilité de cette fonctionnalité ?
  • Vu que jCacheSettings ne contient qu'une méthode, ne serait-ce pas mieux de mettre cette méthode directement dans jCache ?
  • je n'ai pas trop compris la raison, dans le driver file, pourquoi ne pas utiliser jFile::removeDir ? si jFile::removeDir lance une exception pour des problèmes de permission, je ne comprend pas pourquoi ça ne serait pas le cas avec la version du plugin.

Il y a cette gestion de lock qui me chiffone :

  • Comment ce lock sera géré par un driver comme memcache par ex ? peut être par une deuxième variable dans memcache 'lock:$id' ?
  • il y a aussi des cas absurdes. ex : on veut mettre en cache un enregistrement que l'on récupère via une requête SQL. Seulement si on choisi le driver db, avec les histoires des locks, on a au minimum 2 requêtes SQL à chaque lecture du cache. Bref, l'utilisation du cache peut finalement être plus gourmand que d'aller executer directement le code métier sans utiliser de cache. Je pense qu'il faut optimiser cette methode get : ne gérer le lock que si on donne une fonction en argument. Si le développeur ne donne pas de fonction, ça veut dire qu'il gère lui même le lock, donc pas besoin de faire toutes ces vérifications de lock à la lecture. M'enfin il n'en reste pas moins que cette gestion de lock avec le driver db ne me satisfait pas trop. N'y a-t-il pas plutôt un moyen dans les bases de donner de locker les enregistrement en lecture, ce qui fait que la base de donnée, quand on fait un select sur cet enregistrement, c'est elle qui attend que l'enregistrement soit délocké pour le lire ? Je pense que ça serait plus efficace. Voir si il n'y a pas ça dans mysql, pgsql et sqlite..

Je vais fournir un nouveau patch, j'y ai fait quelques corrections et au moins il s'appliquera sans erreur sur le trunk ;-)

Changed 12 years ago by laurentj

mise à jour des patchs précédents

comment:15 Changed 12 years ago by laurentj

Changement dans le nouveau patch :

  • adaptation sur le trunk courant
  • le dao est fourni dans le module jelix et non un module jcache
  • renommage des sections de config : cache et cacheAreas.
  • des corrections dans _checkCacheId, _getFormatArea et le driver file : erreurs de syntaxe dans les regexps, et améliorations pour plus d'efficacité.

comment:16 Changed 12 years ago by bballizlife

Well this ticket seems to have moved fast at first, but is now abandonned...

Does the last patch near to be review+ ? Are there a lot to do more ?

comment:17 Changed 12 years ago by sylvain261

I need to discuss with Laurent in order to move ahead. but..not so much time currently. I think we can plan the final implementation of jCache on the end of this month.

comment:18 Changed 12 years ago by bballizlife

Don't wrooy, we're all a little bit short in time ;) If this ticket could end this summer that would be great !

comment:19 Changed 12 years ago by laurentj

  • Milestone set to Jelix 1.1 beta 2

comment:20 Changed 12 years ago by sylvain261

De retour sur ce ticket....
Après mure réflexion on va revoir notre copie pour :

  • Très largement simplifier ça (suppression des notions de lock et des notions d'area) (si on souhaite par la suite avoir ces fonctionnalités plus avancées on le fera en encapsulant jCache )
  • Implémenter le driver memcache
  • Se rapprocher de l'API de memcache pour avoir qqch de cohérent entre les 3 drivers (file, db, memcache) (notamment c'est lors du set qu'on défini une date d'expiration de l'élément caché)

Keep tuned...

comment:21 Changed 12 years ago by laurentj

  • Milestone changed from Jelix 1.1 beta 2 to Jelix 1.2

comment:22 Changed 12 years ago by sylvain261

Ça y est Tahina et moi avons terminé l'implémentation de JCache ! Je poste le patch d'ici peu (quelques dernières petites retouches sont encours). Les tests unitaires viendront ensuite. Je suis plutôt satisfait du résultat, ça valait le coup de vous faire patienter quelque peu...

On a donc une classe jCache qui permet de manipuler le cache et on a implémenté 3 drivers (file, db, memcache), tous reposant sur une interface commune.
Libre à ceux qui le voudront d'implémenter des drivers supplémentaires.

Le cache se paramètre via un fichier cacheProfils.ini.php qui comme dbProfils.ini.php permet de définir pour une même application plusieurs profils de cache.

JCache suit le même esprit que l'API Memcache à savoir :
set, get, add, replace, increment, delete, flush.
On a également une méthode supplémentaire qui est garbage qui permet de supprimer du cache toutes les données expirées (le set permet de définir le ttl de l'info cachée).

JCache s'inspire du Zend Framework (cf http://framework.zend.com/svn/framework/standard/trunk/library/Zend/Cache) sur 3 aspects :

  • "L'automatic cleaning" : A une certaine fréquence (paramétrable) on a, au moment du set, une suppression automatique des données expirées.
  • Le stockage avec plusieurs niveaux de répertoire pour le driver file : L'idée est d'éviter d'avoir des répertoires contenant des milliers de fichier de cache (pour les gros sites...). Pour cela (c'est paramétrable) on peut indiquer à jCache de stocker ça automatiquement dans différent sous répertoires.
  • Le lock optionnel des fichiers pour le driver file

Par la suite je pense qu'il sera pertinent de mettre en place des classes utilitaires permettant de faire des choses plus sophistiquées en encapsulant JCache (mise en cache d'une méthode, etc..).

Changed 12 years ago by sylvain261

JCache approche plus simple/générique

comment:23 Changed 12 years ago by sylvain261

  • review changed from review- to review?

comment:24 Changed 12 years ago by sylvain261

Voici :

  • La mise à jour du pat de la lib. Les modifications effectuées par rappoort à la précédente version concerne essentiellement des corrections sur des différences de comportement entre les 3 drivers (différences décelées en développement les tests unitaires)
  • Le patch de testapp contenant l'ensemble des tests unitaires de jCache et des 3 drivers.

Changed 12 years ago by sylvain261

MAJ du path sur la lib (corrections incohérence drivers)

Changed 12 years ago by sylvain261

Test unitaires de jCache et de ses 3 drivers

comment:25 Changed 12 years ago by laurentj

Merci pour ses patchs. Ne t'inquiète pas si je ne fais pas de review tout de suite, la priorité pour le moment étant la sortie de la version 1.1 :-)

comment:26 Changed 11 years ago by sylvain261

Voici un nouveau patch (lib et tests unitaires)

Il propose une méthode supplémentaire : call (le reste du patch est inchangé.)

Call permet de mettre en cache le résultat de l'appel à une fonction/méthode (ainsi que ses échos éventuels).

Elle s'utilise de la façon suivante :

Cas d'une méthode statique :
$returnData=jCache::call(array($className,$staticMethodName),array($param1,$param2,...))

Cas d'une méthode non statique :
$returnData=jCache::call(array($myObject,$methodName),array($param1,$param2,...))

Cas d'une fonction :
$returnData=jCache::call($myFunctionName),array($param1,$param2,...))

Changed 11 years ago by sylvain261

Ajout de call pour le cache de l'appel de fonction / méthodes (statiques ou non)

Changed 11 years ago by sylvain261

MAJ des tests unitaires (tests de la méthode call)

comment:27 follow-up: Changed 11 years ago by laurentj

J'aimerai savoir un truc : je vois du "copyright zend framework" dans ton patch. Cela veux dire qu'il y a du code de ZF dans ton patch ?

Je me demande si ça ne pose pas de problème, car je crois que la licence BSD utilisée par ZF est incompatible avec la licence LGPL.

J'aimerai donc savoir quelles sont les portions de code prises du ZF.

comment:28 Changed 11 years ago by bibo

  • Cc bibo added

comment:29 in reply to: ↑ 27 Changed 11 years ago by samtahRam

Seulement le principe de Zend Cache pour le système de cache : file. Pas de code du ZF de dupliquer.

Changed 11 years ago by laurentj

mise à jour du patch sur le trunk et petites améliorations plugin db

comment:30 follow-up: Changed 11 years ago by laurentj

  • review review? deleted

Je viens de mettre à jour le patch par rapport au trunk. J'ai amélioré le plugin db, en particulier le fichier dao, dans lequel les anciennes methodes type php peuvent être remplacée par des methodes déclarées en xml.

Pour le plugin file, le code ressemble beaucoup trop à celui du code de zend, il y a donc pour moi du copyright zend dans ce fichier. Vu l'incompatibilité de la licence, je n'incluerai pas ce plugin en l'état.

Question concernant cette méthode call : je n'aime pas trop cet echo et le fait que la fonction appelée puissent afficher quelque chose. Les sorties devraient être faites par les vues/réponses, pas par une classe utilitaire. On verra une fois que j'aurais pris une nuit de sommeil :-)

Autre chose, vu qu'il n'y a qu'une seule classe, il n'est pas utile d'avoir un répertoire spécifique dans lib/jelix. Il faudra la placer dans lib/jelix/utils/.

comment:31 Changed 11 years ago by laurentj

Note: je n'ai pas regardé/relancé/mis à jour les tests unitaires.

comment:32 in reply to: ↑ 30 Changed 11 years ago by samtahRam

Pour le plugin file, le code ressemble beaucoup trop à celui du code de zend, il y a donc pour moi du copyright zend dans ce fichier. Vu l'incompatibilité de la licence, je n'incluerai pas ce plugin en l'état.

Serait-il possible d'indiquer à quel niveau du code la ressemblance mentionné ?

comment:33 Changed 11 years ago by laurentj

par exemple, les methodes getFileContent et setFileContent. La ressemblance est flagrante (la principale grosse différence, ce sont des changements de noms de variables...). Il y a aussi d'autres bouts de code...

Changed 11 years ago by samtahRam

mise à jour du plugin jCache sans le driver file

Changed 11 years ago by samtahRam

mise à jour tests unitaires jCache

comment:34 Changed 11 years ago by samtahRam

G posté les patchs mises à jour sans le driver file. Idem pour les tests

Changed 11 years ago by samtahRam

patch de l'application testapp + les fichiers de configuration

comment:35 Changed 11 years ago by laurentj

  • Documentation needed set
  • Resolution set to fixed
  • review set to review+
  • Status changed from new to closed

patch commité dans le trunk. r1390 et r1391

J'ai renommé le fichier cacheprofils.ini.php en cache.ini.php, et j'ai fais en sorte que les tests unitaires tiennent compte de l'activation ou non des profils dans cache.ini.php afin de ne pas lancer les tests si memcache n'est pas installé.

J'ai finalement intégré le driver file. Après avoir étudié ces histoires de licences, la new BSD utilisé par Zend semble être compatible. Cependant, pour éviter le moins de problème possible, j'ai mis le plugin dans lib/jelix-plugins au lieu de lib/jelix/plugins.

Merci pour ce gros patch qui va nous être très utile !

Changed 10 years ago by samtahRam

changes made in the plugin file and the config template file

Changed 10 years ago by samtahRam

changes made in the config file

comment:36 Changed 10 years ago by samtahRam

  • Cc t.ramaroson@… added
  • Component changed from jelix:utils to jelix:plugins:cache
  • Resolution fixed deleted
  • review changed from review+ to review?
  • Severity changed from normal to major
  • Status changed from closed to reopened
  • Summary changed from jCache to jCache - Bugs fixed for the memcached driver
  • Type changed from new feature to enhancement

using the memcached driver with multiple servers caused some errors in the cache mechanism. some changes have been done in the config file and the plugin file.

comment:37 Changed 10 years ago by laurentj

  • Component changed from jelix:plugins:cache to jelix:utils
  • Resolution set to fixed
  • review review? deleted
  • Severity changed from major to normal
  • Status changed from reopened to closed
  • Summary changed from jCache - Bugs fixed for the memcached driver to jCache
  • Type changed from enhancement to new feature

no. For new enhancement or new bugs, please create a new ticket ! If we use a bug tracker, it's not to put everything in the same ticket. I won't review this new patch on this ticket.

comment:39 Changed 9 years ago by laurentj

  • Blocking 151 removed
Note: See TracTickets for help on using tickets.