Django

Code

Ticket #515 (closed: fixed)

Opened 3 years ago

Last modified 2 years ago

[patch] additional set of cache backends

Reported by: eugene@lazutkin.com Assigned to: jacob
Milestone: Component: Cache system
Version: Keywords:
Cc: Triage Stage: Unreviewed
Has patch: 1 Needs documentation: 0
Needs tests: 0 Patch needs improvement: 0

Description

New Backends

Three more backends are introduced:

  • locmem: - simple thread-safe in-memory cache. Good for small web sites.
    • Example: locmem:///.
  • file: - file-based multi-process/thread-safe cache.
    • Example: file:///tmp/django.cache/lazutkin.com/ - creates directory /tmp/django.cache/lazutkin.com and stores cached items as individual files.
  • sql: - sql-based multi-process/thread-safe cache.
    • Example: sql://cache/ --- uses table cache in current database to store cached items.

All three backends accept arguments described in Django's cache framework. Examples: sql://cache/?timeout=60, locmem:///?max_entries=10&cull_frequency=2.

Additionally simple: cache backend's bug is fixed.

Notes:

  • All backends use pickle (cPickle) as means to save the object. I decided against marshal or custom solutions. For now.
  • locmem:
    • Requires reader-writer lock, which is implemented in synch.py (attached). This file should be placed into django/utils directory.
    • Implements simplified (a-la simple:) cache culling strategy, when each cull_frequency item is removed to make room.
  • file:
    • Implements simplified (a-la simple:) cache culling strategy, when each cull_frequency item is removed to make room.
  • sql:
    • When it is time to cull cache, sql: removes expired items first, then removes oldest items by access time to satisfy cull_frequency. This strategy has a drawback: every time cached item is accessed, its access time filed is updated.
    • It can be speeded up with stored procedures.

Missing django-admin Support

The missing part is django-admin support for:

  • creation of cache table for sql: backend
  • creation of cache indices for sql:
  • cleaning up sql: and file: caches

Pseudo model for cache table is:

class Cache(meta.Model):
    # id is assumed
    keyx       = meta.CharField(maxlength=255, unique=True),
    expiration = meta.DateTimeField(db_index=True),
    accessed   = meta.DateTimeField(db_index=True),
    valx       = meta.TextField(), # should be big enough to hold an item

For TextField on MySQL see #489 --- when you create cache table manually make sure to use longtext instead of text.

If you want to use MySQL, use patch from #463. Otherwise you may have strange random errors.

Testing

Notes:

  • I ran all new backends for several days monitoring cache entries.
  • sql: was tested locally using MySQL and SQLite. I use MySQL/MyISAM-based cache on my web site. I didn't try it with PostgreSQL nor MySQL/InnoDB.

Attachments

cache.patch (11.3 kB) - added by eugene@lazutkin.com on 09/15/05 00:52:55.
patch for django/core/cache.py
synch.py (2.4 kB) - added by eugene@lazutkin.com on 09/15/05 00:55:10.
new file django/utils/synch.py
django.cache.patch (400 bytes) - added by eugene@lazutkin.com on 09/26/05 11:22:01.

Change History

09/15/05 00:52:55 changed by eugene@lazutkin.com

  • attachment cache.patch added.

patch for django/core/cache.py

09/15/05 00:55:10 changed by eugene@lazutkin.com

  • attachment synch.py added.

new file django/utils/synch.py

09/15/05 08:03:36 changed by adrian

Nice! We'll integrate most or all of this...

09/16/05 12:02:10 changed by Simon Willison

This ticket replaces #4.

09/25/05 15:01:36 changed by jacob

(In [686]) Added "locmem" and "file" cache backends. "locmem" is a thread-safe local-memory cache, and "file" is a file-based cache. This refs #515; much thanks to Eugene Lazutkin!

09/25/05 17:03:31 changed by jacob

  • status changed from new to closed.
  • resolution set to fixed.

(In [692]) Added a database-backed cache backend, along with a tool in django-admin to create the necessary table structure. This closes #515; thanks again, Eugene!

09/25/05 21:06:41 changed by eugene@lazutkin.com

  • status changed from closed to reopened.
  • type changed from enhancement to defect.
  • resolution deleted.

Database-backed cache backend doesn't work in MySQL. Reason: 'key' cannot be field name. It is a reserved word. django-admin createcachetable fails. That's why I used goofy 'keyx' in my implementation.

09/25/05 22:14:37 changed by jacob

  • status changed from reopened to closed.
  • resolution set to fixed.

(In [695]) Fixed #515 (again) - renamed "key" field in SQL cache to "cache_key" because MySQL reserves "key".

09/25/05 23:03:34 changed by eugene@lazutkin.com

  • status changed from closed to reopened.
  • resolution deleted.

Unfortunately changes to my patches removed some exception tolerance from caches. For example:

  • file: I got a coredump when somebody purged /tmp. os.listdir() (line 319) aborted with OSError bringing down Django. It should be a normal situation: directory should be recreated and files should be created like nothing happened. That's how you suppose to clear cache of active web sites.
  • db: I got a coredump when two threads checked cache for an item, and both created it. The second thread aborted citing "insertion failure". It should be absolutely normal situation: just continue to do what you are doing.

Unfortunately current implementations of file: and db: are unusable in real world.

09/26/05 07:22:13 changed by jacob

OK, I'll try to reproduce these bugs and check in a fix. I was unclear why you had the bare except clauses sprinkled around your patch; I'm starting to understand now...

09/26/05 07:38:41 changed by jacob

(In [699]) Added exception handlers to take care of the bugs with the file and db backends (refs #515). Eugene, I'm going to leave #515 open; can you check the bug fixes in this revision and mark the ticket as closed if you're satisfied? I don't run Django in a threaded environment, so I'm having issues reproducing your errors.

09/26/05 11:21:30 changed by eugene@lazutkin.com

Now the code looks much better. I tried file: cache. Minor one line patch is attached.

Index: cache.py
===================================================================
--- cache.py	(revision 700)
+++ cache.py	(working copy)
@@ -317,6 +317,7 @@
             filelist = os.listdir(self._dir)
         except (IOError, OSError):
             self._createdir()
+            filelist = []
         if len(filelist) > self._max_entries:
             self._cull(filelist)
         try:

I am running db: cache now and so far so good. Last time I got an error immediatelly. Let me run it for a day.

09/26/05 11:22:01 changed by eugene@lazutkin.com

  • attachment django.cache.patch added.

09/26/05 11:30:14 changed by jacob

  • status changed from reopened to closed.
  • resolution set to fixed.

(In [701]) Quick bug fix to [699] - fixes #515.

09/26/05 11:31:00 changed by jacob

Eugene - feel free to reopen if you find any more bugs. Thanks for your help!


Add/Change #515 ([patch] additional set of cache backends)




Change Properties
Action