2015-05-07 8 views
6

Ho il modello TicketType che ha circa 500 istanze.Non valido per più chiavi di cache raggruppate

Cambia solo poche volte a settimana.

Ma se cambia, devo invalidare tutti i valori memorizzati nella cache che utilizzavano i vecchi TicketTypes.

Sfortunatamente alcune chiavi della cache non sono corrette. Contengono dati calcolati.

vedo queste soluzioni:

utilizzare l'argomento version e aggiornare il valore di versione su un post salvare gestore di TicketType segnale.

Utilizzare un prefisso comune per tutte le chiavi di cache basate su TicketType. Quindi invalidare tutte le chiavi di cache in un gestore di segnale di salvataggio post.

Credo che ci sia una terza, e migliore ...

Esempio:

TicketType è un albero. La visibilità di TicketTypes è legata alle autorizzazioni. Due utenti potrebbero vedere l'albero in un modo diverso, se hanno permessi diversi. Lo memorizziamo in cache, in base alle autorizzazioni. Le autorizzazioni di un utente vengono serializzate e sottoposte a hashing. La chiave di cache viene creato con la creazione di una stringa che contiene l'hash ed una parte fissa:

hash_key='ticket-type-tree--%s' % hashed_permissions 

se l'albero cambia TicketType, abbiamo bisogno di essere sicuri, che nessun dato vecchio viene caricato dalla cache. L'invalidazione attiva non è necessaria, purché non vengano utilizzati vecchi dati.

+0

Potresti per favore elaborare un po 'sul tuo modello e su cosa è memorizzato nella cache, oltre al modo in cui determini le chiavi della cache? – Marcanpilami

+0

@Marcanpilami Ho aggiornato la domanda. – guettli

+1

Mostreresti per favore il tuo modello di TicketType e cosa inserisci esplicitamente nella tua cache? – Charlesthk

risposta

1

È possibile utilizzare l'ora di modifica del ticket come parte della chiave di cache.

hash_key = 'ticket-type-tree--%s-%s' % (hashed_permissions, tree.lastmodified) 

È possibile aggiungere un DateTimeField con auto_now=True. Se ottenere il tempo di modifica dal db è troppo costoso, è possibile memorizzarlo nella cache.

In genere, l'aggiornamento della cache in un gestore di segnale post_save va bene. A meno che non si desideri avere dati coerenti in ogni momento e si desidera pagare il costo aggiuntivo per le transazioni.

+0

Poiché l'albero non cambia spesso. Preferirei * un * timestamp. Se aggiungo un altro 'DateTimeField' ogni riga avrà un valore. Ottenere l'ultima modifica sarebbe facile ('max()') di questa colonna. Suppongo che io usi una versione come questa, ma invece di 'last_modified' prenderò una' versione' che è memorizzata nella cache (senza timeout) e uso 'incr()'. – guettli

+0

sì, puoi usare qualsiasi campo che sai cambierà ogni volta che c'è una modifica. – dnozay

0

Bene, in pratica il problema è semplicemente l'espressività della chiave della cache. Quando devi fare qualcosa di complicato come hash un set per ottenere la chiave, deve essere un suggerimento che manchi qualcosa.

Nel tuo caso, credo che ciò che manca sia semplicemente un oggetto "set di autorizzazioni". Potresti chiamarlo un gruppo, un ruolo (come in RBAC) ... Ecco perché ti ho chiesto se i set fossero ripetitivi - in realtà, la tua chiave hash è semplicemente un modo di ricreare l'ID dell'oggetto set che non esiste.

Quindi una soluzione sarebbe:

  • per creare un modello di ruolo, con un rel M2M per gli utenti e un rel M2M per i permessi (che a quanto ho capito sono legati ai tuoi TicketTypes)
  • utilizzare un gestore di eventi per catturare i salvataggi su TicketType.
    • recuperano tutti i ruoli impattate (attraverso permessi)
    • generare le chiavi (qualcosa come biglietto-tipo-TREEID-ID ruolo) e invalidare le
  • osservazioni

Due finali:

  • Qualche volta cache.clear() è la soluzione - specialmente se non usi la cache per nient'altro
  • Tu dici il tuo SQL qu il conteggio è enorme quando si naviga nell'albero.Se non lo hai già provato, potresti semplicemente voler ottimizzare quello con prefetch e select_related (vedi il documento).
+0

Penso che la modifica dello schema del database per risolvere questo problema non sia una buona soluzione. Sarebbe ridondante, qualcosa che evito negli schemi di database. 'cache.clear()' non è una soluzione. La cache viene usata anche per molte cose diverse. Il conteggio delle query SQL non è enorme. Il conteggio in SQL (postgres) è lento. Ecco perché select_related() o prefetch_related() non aiuterà. – guettli

0

Nella fase di post TicketType salvare gestore di segnale:
a) generare chiave a seconda delle autorizzazioni di tutti gli utenti e invalidare le chiavi
b) generano chiave per ogni permutazione (permesso) (se li si può calcolare) e invalidare la chiavi
c) utilizzare una seconda istanza memcached per archiviare solo queste cache e cancellarla (più semplice)

PS: Suggerimento suggerirebbe di aggiornare le cache invece di invalidarle. Tuttavia, un'eccezione non rilevata in un segnale Django può essere un problema, quindi sii stanco

1

Utilizzare Redis per memorizzare nella cache i vostri modelli

Il modo in cui avrei Cache miei casi sarebbe il seguente:

1-Assicurarsi che si stanno ottenendo un elemento al momento. E.g: Model.objects.get (foo = 'bar'), e si sta utilizzando l'attributo foo ogni volta per ottenere il modello dal database. Questo sarà usato per assicurarsi che i dati vengano invalidati in seguito.

2-Override metodo save() e assicurarsi che esso salva i dati nella cache utilizzando l'attributo foo.

es:

class Model(model.Model): 
    foo = models.CharField() 
    bar = models.CharField() 

    def save(self, *args, **kwargs): 
     redis.set(foo, serialize_model()) 
     super(Model, self).save(*args, **kwargs) 

    def serialize_model(): 
     return serilized_object 

3-override metodo get per ottenere l'oggetto serializzato prima di colpire il database.

Esempio:

class Model(model.Model): 
    ... 
    def get(self, *args, **kwargs): 
     if redis.get(self.foo): 
      return redis.get(self.foo) 
     else: 
      return super(Model).get(*args, **kwargs) 

4-override il metodo di eliminazione per rimuovere la cache nel caso se l'istanza è stata rimossa o cancellata

Eg

class Model(model.Model): 
    ... 
    def delete(self,*args, **kwargs): 
     redis.delete(self.foo) 
     super(Model, self).delete(*args, **kwargs) 

Sostituire classe del modello con il modello , in questo caso sarebbe Ticket Type

Una cosa, sto assumendo non toccherai il database al di fuori della tua app Django. Se stai usando raw sql in qualsiasi altro posto, questo non funzionerà.

Cerca le funzioni di redis sul loro sito Web, hanno una funzione da eliminare, impostare e ottenere. Se stai usando un altro modo di cache. Cerca come impostare, ottenere ed eliminare.

+0

Questo cache troppo. Nel mio caso voglio memorizzare nella cache la struttura ad albero, non tutti i singoli oggetti. La risposta non si adatta al mio caso d'uso. Scusate. – guettli

Problemi correlati