Newsgroup: it.comp.www.php
Titolo: Un pattern design per gestire la cache degli oggetti?

Vorrei provare a implementare un sistema di caching un pò avanzato.
Facciamo finta di avere un blog con 2 utenti che possono creare
articoli; Questi articoli sono divisi per categoria (facciamo 2 per
semplificare).
Supponiamo di avere, nel blog, 9 'viste' (visualizzazione di
articoli), così suddivise:
1 Tutti gli articoli di pippo
2 Tutti gli articoli di pluto
3 Tutti gli articoli, di tutti gli scrittori
4 Tutti gli articoli di pippo con categoria 'A'
5 Tutti gli articoli di pippo con categoria 'B'
6 Tutti gli articoli di pluto con categoria 'A'
7 Tutti gli articoli di pluto con categoria 'B'
8 Tutti gli articoli di tutti gli scrittori con categoria 'A'
9 Tutti gli articoli di tutti gli scrittori con categoria 'B'
Ora, mettiamo che io voglia mettere in cache l'oggetto restituito dal
DB contenente i risultati della query, o addirittura direttamente
l'html generato, fin qui tutto ok.
Il problema che mi pongo è: come faccio a sapere quali elementi devo
eliminare dalla cache quando viene effettuata una modifica sugli
articoli?
Esempio, se elimino un articolo di Pippo con categoria 'A', dovrei
eliminare la cache 1, 3, 4 e 8, lasciando invariate le altre che non
sono cambiate.
Ora, in questo esempio ho come 'parametri' di confronto gli utenti e
una categoria, ma mettiamo di avere n parametri: la cosa diventa molto
incasinata, e ovviamente non vorrei che il calcolo di cosa eliminare/
cosa mantenere a ogni modifica di un articolo non diventi più esoso
della generazione dell'elenco in se.
La cosa più semplice è, ovviamente, eliminare la cache di tutti gli
articoli a ogni modifica, ma questo ne ridurrebbe di molto l'efficacia
in se (lasciamo stare che in un blog il 99% degli accessi è in lettura
e solo l'1% in scrittura, è solo un esempio).
Io pensavo di creare un array con memorizzate tutte le chiavi degli
oggetti nella cache, esempio:
'pippo' => array(
    'articoli' => array(
        'categoria_a' => [..dato...]
        'categoria_b' => [..dato...]
        'tutti_post' => [..dato...]
    )
)
Questo però comporta, alla modifica/creazione/eliminazione di un
articolo, di ciclare attraverso la cache di tutti gli utenti ed
eliminare quello che deve essere eliminato.. se avessimo 10 categorie
diverse, sarebbe fattibile?
Voi come vi comportate in questi casi?
Esistono design patterns su cui prendere spunto?

 sono cambiate.
Se usi Zend_Cache puoi taggare ogni cosa cachata con tag multipli:
http://framework.zend.com/manual/en/zend.cache.theory.html
A quel punto ti basta cancellare le cache in base al tag. Come tag puoi 
usare cose tipo "user:pippo", "user:pluto", "cat:A", "cat:B" e dovresti 
risolvere in modo abbastanza semplice.
Puoi usarlo per fare cache sia dei risultati del DB che di qualsiasi 
altra cosa serializzabile.
Bye.


Ti ho preceduto, mi sto già guardando i sorgenti di Zend_Cache ;)
Cavolo, è immenso, davvero può lavorare stand-alone?
Cmq, visto che il progetto è a scopo di apprendimento cercherò di
implementare un mio sistema di tagging, ma non era questo il
problema.. il problema è che, riprendendo l'esempio sopra, metti che
invece di due 'filtri' (creatore e categoria) ne ho 10 (autore,
categoria, area di riferimento, tag, numero di commenti, foo, bar,
baz, etc..) come mi comporto per gestire la cache?
Cioè, se viene eliminato un articolo con categoria A, devo fare il
controllo su tutti gli altri 'filtri' ed eliminare la cache di
conseguenza... ammettiamo di avere 10 filtri sugli utenti, 10 sugli
articoli e altri 10 su, che so, sugli articoli collegati.. il mio
dubbio è dove fermarsi con "l'indicizzazione della cache" per evitare
che la sua gestione sprechi tutte le risorse guadagnate dal caching in
sè.
Ho trovato l'identity map pattern, ma praticamente non accenna a
questo (credo perchè tutto dipende dalle esigenze del software in se)
Adesso mi studio bene Zend_Cache (che, a proposito, mi sta anche
chiarendo un sacco il discorso delle DI) poi vedrò..
Ho anche trovato questo: 
http://code.google.com/p/memcached-tag/

Seguo. Molto interessato (come anche sul discorso apc).

 Seguo. Molto interessato (come anche sul discorso apc).
Dì la tua oltre che a seguire ;)
Il mio problema è più che altro sugli elenchi (gestire il caching di
un singolo oggetto non ha alcun problema), perchè il numero di filtri
è (credo) l'elemento che determina il punto di non ritorno (ovvero
dove gestire la cache è più esoso di non averla proprio).
Ho buttato giù una bozza (la posto su pastebin perchè se google groups
mi scazza la formattazione non si capisce niente):
http://pastebin.com/8jcZpqwj
In teoria funziona; In pratica.. se i filtri aumentano ci si fa del
male..
Faccio qualche prova.
p.s: apc? io non mi muovo da memcache ;)

 sua gestione sprechi tutte le risorse guadagnate dal caching in sè.
Io mi fermerei a autore, data e categoria, onestamente.
Se stai pensando di usare le cache anche per il cosiddetto "advanced 
search", lascia perdere. Ti troveresti una dimensionde della cache 
dell'ordine di n^m, con n=numero di filtri, e m=numero di articoli.
Non ho affrontato gli internals di Zend_Cache, quindi non saprei dirti 
come implementarlo in pratica.
Bye.


Mah non ho ancora approfondito molto la questione.
Più che altro nel mio caso ho diverse categorie ciascuna con un certo 
numero di articoli.
Avevo pensato di fare caching delle pagine intere quando vengono 
richieste, poi se una categoria viene alterata (nuovo articolo, articolo 
rimosso), dichiaro tutte le pagine appartenenti a quella categoria scadute.
Più che altro volevo anche capire il discorso di apc per la cache delle 
pagine semicompilate per far evitare l'onere di dover ricompilare le 
pagine ogni volta.

 pagine ogni volta.
Bhè sia con apc che con memcached puoi mettere in cache sia l'html
final della pagina, sia l'istanza di un oggetto (attento che, quando
lo recuperi, devi prima caricare il file con la classe di riferimento
o se non ricordo male potrai accedere solo alle proprietà pubbliche,
non ai metodi).
Tuttavia anche per le pagine di un articolo singolo non sono poi così
semplici: in base all'utente ci sono pezzi di pagina che non devono
essere mostrati (ad esempio, i link per l'eliminazione/modifica
dell'articolo, tenendo buono l'esempio del blog).
Quindi pensavo o di suddividere la pagina in 'blocchi' e cachare
questi (suicidio?) oppure di cachare l'oggetto finale contenente tutti
i dati e poi i controlli logici farli sempre e comunque, che credo sia
l'unica strada 'sicura'.


A me c'è una parte centrale che contiene tutti gli articoli e dovrebbe 
essere comune a tutti gli utenti, che poi è la più succosa e fa una 
query abnorme su sei tabelle, comprensivo di group by, limit, e diverse 
clausole where.
A dire il vero però stavo pensando una cosa più casalinga, cachare tutta 
la parte centrale (l'html risultante) suddetta in una tabella apposita 
del database, se poi c'è una modifica in una delle sei tabelle della 
query, la pagina in cache è dichiarata spirata e deve essere aggiornata 
appena un utente la ricarica.
Poichè le clausole where sono modificabili dall'utente, metto in cache 
tante varianti di pagine quante me ne vengono richieste, magari 
applicando una politica di swap del tipo Least Recently Used fra un 
numero massimo di pagine.
Insomma, è un bel bordello la cache, ma credo sia anche una 
soddisfazione quando la vedi funzionare (e sono bestemmie che volano 
quando non funziona...).
In questo modo ho il vantaggio di non dover dipendere da moduli esterni 
come apc o memcached. Comunque più che riguardo ai contenuti ero 
maggiormente interessato, ribadisco, alla cache degli opcode.