[Twisted-web] caching branch

Andrea Arcangeli andrea at cpushare.com
Thu Feb 17 22:37:35 MST 2005

I ported the html caching on top of ICache and I left the tags.cached
part unchanged and as expcted the ICache generates a significant
slowdown, -20%. I get around 180 req/sec while I was getting 220 (never
less than 200) req per second with my previous patch that avoided the
ICache adapters from compy.

I really don't want this ICache abstraction slowdown that hurts
performance in the core html caching.

This below is the code that I benchmarked (against trunk + caching

The ICache is a nice abstraction for lower-performance caches like
tags.cache to keep code clean, but the html caching has to run raw w/o
slowdowns. So I'll resurrect my original code to get that bit of
performance back. I exclude I'll switch over to the the current caching
branch for the global rand.Page caching even if it would work, since
that one will be even slower than the below patch that only gets
disturbed by a single ICache lookup without even taking into account all
the flattener (I never run the flattener).

BTW, this confirms my theory that compy is the worst offender

I'll post an updated patch suitable for merging with the
high-performance code as soon as I finish to port it and test it.

--- Nevow/nevow/rend.py.~1~	2005-02-18 04:39:54.208615490 +0100
+++ Nevow/nevow/rend.py	2005-02-18 06:29:54.440482008 +0100
@@ -30,6 +30,7 @@ from nevow import tags
 from nevow import flat
 from nevow.util import log
 from nevow import util
+from nevow import url
 import formless
 from formless import iformless
@@ -387,11 +388,41 @@ class Page(Fragment, ConfigurableFactory
     beforeRender = None
     afterRender = None
     addSlash = None
     cache = False
     lifetime = -1
     flattenFactory = flat.flattenFactory
+    def hasCache(self, ctx):
+        if not self.cache:
+            return
+        c = self.lookupCache(ctx)
+        if c:
+            return c
+        from twisted.internet.defer import Deferred
+        d = Deferred()
+        # do only this rendering, others will wait the deferred
+        self.storeCache(ctx, d)
+    def chainDeferredCache(self, ctx, d):
+        if not self.cache:
+            return d
+        from twisted.internet.defer import Deferred
+        c = self.lookupCache(ctx)
+        if isinstance(c, Deferred):
+            # we're the thread that went ahead to refresh the cache
+            d.chainDeferred(c)
+        return d
+    def storeCache(self, ctx, c):
+        inevow.ICache(ctx).set(c, self.cacheIDX(ctx))
+    def lookupCache(self, ctx):
+        return inevow.ICache(ctx).get(self.cacheIDX(ctx), self.lifetime)
+    def cacheIDX(self, ctx):
+        return str(url.URL.fromContext(ctx))
     def renderHTTP(self, ctx):
         ## XXX request is really ctx now, change the name here
         request = inevow.IRequest(ctx)
@@ -413,11 +444,18 @@ class Page(Fragment, ConfigurableFactory
             if self.afterRender is not None:
-        if self.buffered:
+        c = self.hasCache(ctx)
+        if c:
+            finishRequest()
+            return c
+        if self.buffered or self.cache:
             io = StringIO()
             writer = io.write
-            def finisher(result):                
-                request.write(io.getvalue())
+            def finisher(result):
+                c = io.getvalue()
+                self.storeCache(ctx, c)
+                request.write(c)
                 return result
@@ -427,12 +465,9 @@ class Page(Fragment, ConfigurableFactory
                 return result
         doc = self.docFactory.load()
-        if self.cache:
-            name = url.URL.fromContext(ctx).path
-            doc = tags.cached(name, self.lifetime)[doc]
         ctx =  WovenContext(ctx, tags.invisible[doc])
-        return self.flattenFactory(doc, ctx, writer, finisher)
+        return self.chainDeferredCache(ctx, self.flattenFactory(doc, ctx, writer, finisher))
     def rememberStuff(self, ctx):
         Fragment.rememberStuff(self, ctx)
@@ -504,7 +539,6 @@ class Page(Fragment, ConfigurableFactory
                 ## Use the redirectAfterPost url
                 ref = str(redirectAfterPost)
-            from nevow import url
             refpath = url.URL.fromString(ref)
             magicCookie = str(now())
             refpath = refpath.replace('_nevow_carryover_', magicCookie)

More information about the Twisted-web mailing list