Seite 1 von 1
Cache Objekt resetten im thread Umfeld...
Verfasst: Mittwoch 29. September 2010, 15:41
von jens
Um SQL Abfragen zu vermeiden packe ich Daten in ein cache Dictionary. Beim ändern der Datenbank Daten wird das dict einfach mit .clear() "geleert"...
Das Problem: Mit fastCGI laufen ja mehrere Python Prozesse unabhängig voneinander. Das cache Dictionary also auch. Wird im einem Prozess der cache geleert "überträgt" sich das ja nicht auf die anderen...
Wie könnte man das lösen???
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Mittwoch 29. September 2010, 16:42
von noisefloor
Hallo,
Memcached oder ein KV-Store, welches im RAM läuft, als Cache-Server?
Gruß, noisefloor
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Freitag 1. Juli 2011, 22:56
von jens
Der einfachste Weg veraltete Caches zu verhindern, ist es ja alle Threads/Prozesse neu zu starten.
Nur wie?
Die Hammer Methode:
killall python 
...was ich sogar in einem externen Plugin über die Web Oberfläche zugänglich gemacht habe, siehe:
https://github.com/jedie/PyLucid-system ... er.py#L274
Mit killall erschlägt man natürlich alle seine python Prozesse, was nicht immer gut ist
Eine neue Idee: Der Prozess die Daten ändern, hält diesen Zeitpunkt in der Datenbank fest. Jeder Prozess schaut als erstes in die Datenbank nach und unternimmt was.
Genauer: Man könnte man das ganze in einer WSGI/Django middleware machen. Mit einem
STARTUP = time.time() auf Modulebene, weiß der Thread seinen Startzeitpunkt.
Vor der Request Abarbeitung schaut er in der DB nach, ob es dort einen neueren Timecode gibt, wenn ja, schießt er sich selbst mit os.kill(pid) ab.
Einige Nachteile hat das ganze allerdings: Es gibt mehr DB-Queries und das abschießen mit os.kill() hat wahrscheinlich Nebenwirkungen. Führt der aktuelle Request zu einem Fehler beim Client oder gleicht das fastCGI/mod_wsgi automatisch aus?
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Samstag 2. Juli 2011, 10:01
von deets
Das killen von Prozessen wird 100%ig zu einem Fehler fuehren. Und ausserdem deine Cache-Performance deutlich mehr belasten, als noetig. Denn so kommen ja die gesamten setup-Kosten immer wieder auf dich zu - bloss um den Cache zu leeren?!?
Dein DB-Ansatz ist schon ok, ist eine Abart einer simplen message-queue. Und genau sowas koennte deine Loesung sein - zeromq zB.
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Samstag 2. Juli 2011, 10:48
von sma
Python-Prozesse zu töten, weil ein Dict nicht passt, halte ich für Wahnsinn

Mach es wie noisefloor vorschlägt. Redis oder memcached sind hier gute Kandidaten.
Oder aber baue selbst etwas in Python. Für gute Performance bietet sich z.B. Tornado an. Da reicht dann wohl ein Prozess. Sieht ungefähr so (ungetestet, aus dem Kopf aufgeschrieben) aus:
Code: Alles auswählen
import tornado.ioloop, tornado.web
class CacheHandler(tornado.web.RequestHandler):
def initialize(self):
self.cache = {}
def get(self, key):
self.finish(self.cache[key])
def put(self, key):
self.cache[key] = self.request.body
self.finish()
def delete(self, key):
del self.cache[key]
Application([('/(.*)', CacheHandler)]).listen(8888)
tornado.ioloop.IOLoop.instance().start()
Jetzt will man wahrscheinich noch einbauen, das Dinge nur eine gewisse Zeitspanne im Cache bleiben oder das der Cache nach einem LRU-Algorithmus eine maximale Speichergröße nicht überschreitet. Ich würde dafür einfach alle Größen der Bytestrings aufaddieren.
Nutze dann den asynchronen Tornado-HTTP-Client, um aus deinem Webserver auf diesen Caching-Server zuzugreifen.
Natürlich müsste man mit diesem Server nicht per HTTP sprechen, aber so war es extrem einfach.
Schließlich kann ich nicht enden, um darauf hinzuweisen, dass so ein Dienst auch eine nette Gelegenheit ist, mal nodejs auszuprobieren, denn node ist genau für derartige kleine Server-Prozesse ideal geeignet - und JavaScript ist auch nicht viel komplizierter als Python
Stefan
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Montag 4. Juli 2011, 10:48
von jens
Noch ein paar Worte zum Anwendungsfall:
Es geht nicht um HTML-Seiten cache. Dazu nutzte ich das Django cache framework.
Es geht um kleinere Cache Objekte, die DB-Queries einsparen oder evtl. um den url-cache von Django.
sma hat geschrieben:Python-Prozesse zu töten, weil ein Dict nicht passt, halte ich für Wahnsinn

Das es keine Optimale Lösung ist, war klar.
Am besten wäre es natürlich, wenn man gezielt die Objekte (Liste/tuple/dict) in jedem Thread/Prozess "resetten" könnte.
Welche Möglichkeiten hat man mit anderen Threads/Prozessen zu kommunizieren? (Außer über die Datenbank)
Das der Cache veraltet ist, kommt recht selten vor. Nur dann, wenn man eine Seite löscht oder im Baum verschiebt oder umbenennt. Das macht man ja nicht jede Sekunde
Bei einer Lösung mit Datenbank sehe ich zwei Möglichkeiten:
1. Ein DB-Query pro Request: Jeder Prozess schaut am Anfang eines Requests nach, ob alle Caches aktuell sind. Bzw. ob es einen Cache resetten muß:
- * Jeder Prozess merkt sich seine Startzeit (START=time.time() auf Modulebene)
* Der Prozess der die Daten ändert, schreibt in die DB die Änderungszeit
* Bei Request-Start holt sich jeder Prozess aus der DB die letzte Änderungszeit und vergleicht sie mit der eigenen Startzeit.
2. DB-Query nur beim App-Start + Änderung: Jeder Prozess schreibt seine pid + Startzeit in die DB.
- * Der Prozess der die Daten ändert, weiß welche pids veraltete Daten hat -> abschießen oder irgendwie mit allen pids kommunizieren, das die Caches resetten werden müßen.
Eine Abwandlung der ersten Variante wäre es, nicht die Datenbank, sondern die Informationen in den Django-Cache zu speichern.
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Montag 4. Juli 2011, 12:42
von sparrow
Was spricht denn dagegen, dass du dafür auch das Django-Cache-Framework verwendest?
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Montag 4. Juli 2011, 13:07
von jens
Ja, das hab ich mich auch schon gefragt
Schätze mal die Reihenfolge nach Kosten/Nutztenfaktor ist so:
1. lokales dict/tuple/liste etc.
2. Django cache
3. Datenbank
Wobei es beim Django cache auf das verwendete Backend ankommt:
- 'django.core.cache.backends.db.DatabaseCache'
'django.core.cache.backends.dummy.DummyCache'
'django.core.cache.backends.filebased.FileBasedCache'
'django.core.cache.backends.locmem.LocMemCache'
'django.core.cache.backends.memcached.MemcachedCache'
'django.core.cache.backends.memcached.PyLibMCCache'
Doch auch beim LocMemCache oder Memcache dürfte ein lokales dict schneller sein. Denn die Daten im cache werden durch pickle gejagt.
Wenn ich mir
die Sourcen von LocMemCache ansehe, dürfte man auch da Probleme haben, das ein Cache in einem Thread/Prozess nicht mehr aktuell ist, wenn ein andere die Basisdaten verändert hat... In der Doku steht's auch:
Note that each process will have its own private cache instance, which means no cross-process caching is possible. This obviously also means the local memory cache isn't particularly memory-efficient, so it's probably not a good choice for production environments. It's nice for development.
Diese Probleme wird man wohl nicht bei Memcache, DatabaseCache und FileBasedCache haben, oder?
Wie wäre eine Kombination?
Die Daten sind eigentlich in einem lokalen dict. Die Informationen, ob die Cache Daten aktuell sind speichert man aber im Django cache backend.
Das dürfte sich dann lohnen, wenn mehr mehrmals auf den Cache innerhalb eines Requests zugreift. Und wenn man wesentlich öfters "lesend" und selten "schreibend" auf den Cache zugreift.
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Montag 4. Juli 2011, 13:14
von deets
Es ist doch im Grunde eine ganz einfache Sache:
- du hast mehrere Prozesse
- du hast prozess-lokale Caches
- du hast konkurrierende Aenderungen der Daten hinter diesen Caches
Und da gibt es jetzt nur 2 Moeglichkeiten:
a) du informierst im Falle einer Aenderung alle Prozesse darueber, was sich geaendert hat. Oder mindestens *das* sich was geaendert hat.
b) die Prozesse fummeln das selbst raus, und dazu muessen sie irgendwie auf eine geteilte Resource wie zB die Datenbank, einen shared-memory oder ein Dateisystem zugreifen.
Loesung a ist theoretisch das "eleganteste", weil du - wenn keine Aenderungen am System stattfinden - keinerlei Kosten hast - vorrausgesetzt, das IPC-System hat keine versteckten Kosten. Praktisch ist es aber ziemlich komplex, nicht zuletzt, weil du ueberhaupt erstmal wissen musst, wer alle deine Prozesse sind, die muessen sich dann anmelden an dem IPC-System usw.
Fuer Loesung b spricht daher, dass sie simpel zu implementieren ist. Einfach die geteilte Resource bestimmen, und da zB ueber einen Timestamp oder Counter kommunizieren.
Und weil du eh schon eine geteilte Resource hast, naemlich die Datenbank, wuerde ich auch die einfach nehmen. Pro request einmal zu fragen "gab's Aenderungen?" geht im Rauschen unter, und damit ist der Drops gelutscht.
Ob einer der django-cache-Backends sowas implementiert kann ich nicht sagen. Aber zur Not bastelst du das basierend auf dem LocMemCache halt selbst, das sollte sich in ein paar Zeilen erledigt haben. Und dafuer ist die Nutzung dann uniform.
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Montag 4. Juli 2011, 13:32
von jens
Jep, du hast die Sache genau verstanden...
deets hat geschrieben: a) du informierst im Falle einer Aenderung alle Prozesse darueber, was sich geaendert hat. Oder mindestens *das* sich was geaendert hat.
b) die Prozesse fummeln das selbst raus, und dazu muessen sie irgendwie auf eine geteilte Resource wie zB die Datenbank, einen shared-memory oder ein Dateisystem zugreifen.
Genau!
Lösung A ist besser. Doch wie alle Prozesse informieren? Somit ist Lösung B attraktiver.
Wenn man als "geteilte Resource" ein cache Backend von Django nimmt, kann der Anwender sogar ab Django v1.3 sich separat dafür ein Backend wählen.
Vielleicht lohnt es sich daraus ein kleines Allgemeine Django-App zu machen...
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Montag 4. Juli 2011, 14:24
von sparrow
Vielleicht habe ich das überlesen, aber was steht denn eigentlich in diesem dict das du unbedingt cachen willst?
Wenn das nur eine einfache Key-Value Zuordnung ist, ist das natürlich genau das was du über das Django-Caching-System abbilden kannst.
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Montag 4. Juli 2011, 14:43
von jens
Wie schon geschrieben, im Django-Cache kann man alles schmeißen, was mit pickle verarbeitet werden kann
In einem Anwendungsfall, ist es allerdings nur ein ID-URL dict:
https://github.com/jedie/PyLucid/blob/c ... ee.py#L341
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Montag 4. Juli 2011, 20:31
von jens
Hab eine erste Implementierung:
https://github.com/jedie/django-tools/c ... 5e78d52f3d
So ganz bin ich davon aber noch nicht überzeugt... Irgendwelchen Kommentare?
EDIT: Und die Änderungen in einem PyLucid branch dazu:
https://github.com/jedie/PyLucid/commit ... c718c97fea
Re: Cache Objekt resetten im thread Umfeld...
Verfasst: Dienstag 5. Juli 2011, 11:20
von jens
Hab es nochmal überarbeitet:
https://github.com/jedie/django-tools/c ... 5bcb10d9cf
Neben Dokumentation habe ich auch noch eine weitere Funktionalität eingebaut: Komischerweise bleibt die Information des letzten reset Zeitpunktes nicht immer im django cache. Warum weiß nicht nicht genau.
Deswegen speichre ich den reset Zeitpunkt nochmal lokal in self._OWN_RESET_TIMES. Beim Request start, also in LocalSyncCache.check_state(), wird der timestamp nochmal in den cache eingetragen, wenn er nicht mehr existiert...
Eigentlich unschön. Aber besser als wenn der Reset Zeitpunk verloren geht und die anderen Threads mit veraltetem Cache weiter machen würden...