Jul232006

Template Caching in TurboStan

Filed under: turbostan 

Having solved the inheritance problem, my next goal is to implement some caching features in TurboStan.There are many issues around caching dynamic content and here's some of my preliminary thoughts with regard to caching TurboStan:

  1. Stan is basically a Python expression, so caching .pyc files is an easy option that will give at least minor improvements.
  2. Caching and serving static HTML is going to be the fastest.
  3. Most pages are a combination of static and dynamic content, so the programmer should be able to specify whether a fragment can be considered static. This way even a dynamic page can benefit from caching the bits of content that are static as HTML.
  4. Even dynamic fragments are quite often temporarily static. That is, they change infrequently, so these should be able to be cached as HTML given there is a trigger to indicate when the cache should be rebuilt.
  5. Session-based caching might be handy as well. Content may be dynamic across users but static per user. Having a mechanism to support this could be beneficial, but cache storage might explode, so this would need to be carefully considered.

Most of this is pretty standard I think, the question becomes how to specify these concepts within the constraints of TurboGears. Since templates are specified via a decorator, that seems one logical place to pass this information, however, due to the fact that inheritance may bypass this decorator that doesn't seem likely to work. Also I'm not certain that would give the fine-grained cache control I'm looking for.

The solution I'm leaning toward is to specify the cache directives directly within the template. While this will add some clutter to the template, it will also allow the programmer/designer to have cache control tied directly to the item being cached. Also, because this puts cache control directly within TurboStan, it will help insulate the caching system and reduce worries about some future caching system in TurboGears conflicting with whatever scheme I come up with.

# proposed caching directives within a fragment

inherits ( 'index' ) [
    override ( 'title' ) [
        cache [ # static cache, expires only on template change
            xml ( ''
                This never changes
            '' )
        ]
    ],

    override ( 'content' ) [
         cache ( expires = vars.cache_expired ) [
             # expires if the content_changed variable is True
             vars.content
         ],
         cache ( expires = vars.time_expired ) [
             # expires based on static time, i.e. at midnight
             vars.remote_rss_feeds
         ]
    ]
]

What I'm thinking here is that the expires keyword will take a function callback that will return a boolean informing it whether to expire the cache or not. TurboStan can then internally keep a table of these callbacks for each fragment and call them prior to rendering the fragment and if they return False, then instead simply return the static HTML from its cache. If no expires keyword is given, then a failed lookup in that table will return False by default (i.e. the content never expires).

I've never implemented a caching system prior to this so I'm sure there's plenty I'm overlooking. Regardless, I suspect implementation will reveal most of these.

Another idea that occurs to me is to simply provide a communication method with an external cache (i.e. Squid) much like Zope does. By this I mean, don't implement caching of actual static content within TurboStan, but rather simply do cache control, notifying the external cache when the cache is dirty. The downside to this is that it requires an external cache, which isn't always a pleasant option for small sites, so perhaps the ultimate solution would be to provide a simple caching hook within TurboStan that lets the developer choose between internal and external caching.

Sounds like fun.



4 comments Leave a comment


Jul312006

The slippery slope of presentation logic

Filed under: turbostan template 

I've been asked, and have previously considered, whether to add logic tags to TurboStan in order to facilitate "presentation logic". I'm still undecided whether this is a good idea or not.

Despite being firmly in the MVC camp, I am also a firm believer that there is such a thing as "presentation logic" that really belongs in the View (template).

For example, in a blog application, a template designer may want to specify that only 10 recent articles are displayed in a sidebar in one situation, and 20 in another. I don't expect a template designer to delve into the controller and add a one-off method to support this. I think the ability to specify things like this is a valid reason to have logic in a template.

On the other hand, once logic flow is added to templates, as it turns out, that is often the easiest place to do logic and it gets abused. Take a look at any moderately sophisticated Smarty template and you'll see what I mean. It quickly becomes a mess and it appears beyond the discipline of programmers and designers alike to avoid it.

So what's the solution? At the moment I've taken to allowing passing extra arguments to renderers in TurboStan, such as:

div ( id = 'sidebar',
      render = render.sequence,
      data = vars.model.Article.data.articles,
      selectby = { 'published': True, 'limit': 10 } )

Of course the controller has to accept these arguments, but that's easily doable.

The question is, is this enough? Is it inherently better than the alternatives? Should I add an "if" tag to TurboStan? You can already (ab)use list comprehensions and any other Python expression within TurboStan, but I wonder if it's wise to open this can of worms.

If you think I should add some sort of logic tag(s) ( I'm talking to you PacoPablo ), maybe a concrete example of something that can't easily be implemented without them would be convincing. An example that either a) cannot be done or b) is just too damn ugly to talk about would be best.

Personally I've found that my desire for such things has dropped pretty much proportionately to how much I've put aside what I'm used to and swallowed the Nevow pill of render and data callbacks, but I'm always willing to at least entertain other's ideas.



2 comments Leave a comment


Aug302006

Adding Memcached Support to TurboStan

Filed under: turbostan memcached 

I got a bit frustrated doing real work tonight, and my experiments with clustering TurboGears instances behind Nginx got me thinking about cache coherency (TurboStan has been using an internal dictionary for caching; efficient for a single instance, but not for clustered applications).

I'd never used memcached before, but I quickly located Sean Reifschneider's python-memcached module in the Cheeseshop. It wouldn't easy_install, but that was no problem since it's a single Python file.

Next I set out to rewire the caching in TurboStan to support an additional configuration directive. If you add:

stan.memcached = "127.0.0.1:11211"

to your TurboGears configuration, TurboStan tries to connect to a memcache at that location.

As documented on the Memcached home page, you start memcached with something like:

memcached -d -m 16 -l 127.0.0.1 -p 11211

This starts memcached with a 16Mb cache (hey, I'm not running LiveJournal) using port 11211 on localhost. At first I thought I could get away with 4Mb of cache, but I spent a while trying to figure out why some objects weren't cached before realizing memcached was out of memory.

In order to support both the internal dictionary cache or the memcache, I created a new class that provides the memcache API for a dictionary and allows you to select one mode or the other.

Memcached is remarkably simple to use. One caveat is that because I'm caching code objects and memcache.py uses cPickle, I was forced to marshal code objects prior to passing them to memcache. This means that the code objects are marshalled and pickled. Not too efficient. I think I'll modify memcache.py to always marshall (the special case code it has for pickling or not pickling objects isn't relevant to this purpose since everything will always need to be pickled). The fact that marshal isn't portable across Python versions is irrelevant too since it's a temporary cache.

Anyway, subjective performance was actually marginally slower, but the point of memcached isn't performance but rather scalability (a different thing altogether athough confused by some). And besides, cache coherence is what I was really after (I have methods for destroying cache entries, but in a cluster only one backend would receive the instruction to destroy a cache entry, leaving the other instances with outdated objects in their caches).

References:

  • Memcached Home
  • Python-Memcached


1 comments Leave a comment


Dec272006

TurboStan is dead... long live SexML?

Filed under: turbostan breve buffet 

I've been meaning to tackle some of the problems inherent in TurboStan for a while. One of the biggest issues is the dependencies on Twisted, Nevow and zope.interfaces. That's a pretty heavy dependency tree for a template engine to have.

I started digging in to the code for nevow.stan to see if I could extract the core of Stan from the jungle, but finally changed my mind and decided on a clean rewrite. Stan has lots of features I don't need and lacks features I want to add. Since the basic concept behind Stan is simple to implement it seemed more reasonable to start from scratch.

I had the basic syntax and flatteners working within a couple hours and moved from there to adding features (inheritance, includes, loadable tag definitions, etc). It took a couple days of hacking but I now have the core of a new template engine. I'm quite pleased as the entire code base is not much larger than the original TurboStan plugin (which was mostly just glue code tying Stan and TurboGears together).

I've been using the working name "sexml" (s-expression markup language) but I've also considered "stanly" (i.e. stan-like) and "sexpress" (s-expressions). I guess it's true men think of sex every 10 seconds.

Anyway, I've got to write some documentation and a Buffet adapter (and test it as a TurboStan replacement on a live site), but expect to see a release before the first of the year (i.e. this week).

Oh, and the only external dependency is the os module from the standard Python library.



0 comments Leave a comment


Aug92006

Using inheritance to eliminate template logic

Filed under: turbostan template 

I recently added a logic tag to TurboStan with some mixed feelings. One of the things I love about using TurboStan is that I'm constantly discovering new ways to utilize its features.

I'm working on a couple of sites that will have one-off pages, that is, pages that don't fit the standard template for the site. In this case, the only difference was that the home page would not have a sidebar, but the other pages all would. In all cases, index.stan defined the overall site layout, and public.stan filled in slots in this template.

The quick and easy solution seemed to be to use the new "case" tag something like this:

#index.stan
html [
    body [
        div ( id = 'left-sidebar' ) [ slot ( 'left-sidebar' ) ],
        div ( id = 'content' ) [ slot ( 'content' ) ]
    ]
]

#public.stan
inherits ( 'index' ) [
    override ( 'left-sidebar' ) [
        case ( vars.page != '/home' ) [
            xml ( 'left sidebar stuff!' )
        ]
    ],
    override ( 'content' ) [
        div ( render = vars.content, data = vars.page )
    ]
]

This worked fine, except I had a problem: even though the left-sidebar was now empty on the /home page, the CSS attached to it was still in effect. "Easy", I thought, "I'll move the div inside the case tag. So now I ended up with something like:

#index.stan
html [
    body [
        slot ( 'left-sidebar' ),
        slot ( 'content' )
    ]
]

#public.stan
inherits ( 'index' ) [
    override ( 'left-sidebar' ) [
        case ( vars.page != '/home' ) [
            div ( id = 'left-sidebar' ) [
                xml ( 'left sidebar stuff!' )
            ]
        ]
    ],
    override ( 'content' ) [
        div ( id='content', render = vars.content, data = vars.page )
    ]
]

This was looking better. I could see how this small alteration gave me a bit more flexibility. However, I still had a problem: in order to make the left-column and content divs sit side-by-side, I was forced to give them both fixed widths in the CSS. If I removed the widths, the content div ended up underneath the left sidebar. The problem was that the /home page needed to be wider than this.

Anyway, I tossed out several ideas that seemed like kludges until it finally dawned on me: inheritance was the answer. I added a new template home.stan, a new controller that utilized that template, and ended up with the following:

#index.stan
html [
    body [
        slot ( 'left-sidebar' ),
        slot ( 'content' )
    ]
]

#public.stan
inherits ( 'index' ) [
    override ( 'left-sidebar' ) [
        div ( id = 'left-sidebar' ) [
            xml ( 'left sidebar stuff!' )
        ]
    ],
    override ( 'content' ) [
        div ( id='content', render = vars.content, data = vars.page )
    ]
]

#home.stan
inherits ( 'public' ) [
    override ( 'left-sidebar' ),
    override ( 'content' ) [
        div ( id = 'home', render = vars.content, data = vars.page )
    ]
]

Much better! Now not only had I removed the need for actual logic in the template, but I was able to have a separate id for the home page which allowed me to set the width for content div to a different value than the rest of the site.



0 comments Leave a comment


Jul312006

Rethinking TurboStan Namespaces

Filed under: turbostan 

I'm seriously entertaining the idea of deprecating the "vars" prefix attached to variables passed to TurboStan templates. There needs to be some sort of prefix to avoid namespace messes, but I'm thinking a bit more separation is in order: I'm considering passing the model as the "model" object and variables passed from the controller as the "view" object, so you'd see stuff like:

h1 [ view.title ],
div ( id = 'content' ) [
    view.content
],
ul ( id = 'articles', render = render.sequence, data = model.Article.data.articles   [
    li ( pattern = 'item', render = model.Article.render.link )
]

I've debated whether to use "view" or "controller" to group variables and methods passed from the controller, but "controller" is awful long and "view" seems a reasonable alternative (especially since TurboGears will be replacing @expose with @view soon).

My main concern is that this would subtly move TurboStan further away from TurboGears in yet another way. Because TurboStan knows nothing about the model, it would be up to the programmer to properly layout the namespaces in the returned dictionary:

return dict = (
    model = model,
    view = dict (
       title = 'A history of the world',
       content = 'Hello, world'
    )
)

Alternatively, I could rely on the fact that TurboGears puts the model in model.py and import that directly in TurboStan. While helping make things a bit more transparent, it also ties TurboStan more closely to TurboGears, which is a downside for people looking to use it outside of TG.

To date, I'm unsure how I feel about this.



0 comments Leave a comment


Jul222006

Inheritance in TurboStan

Filed under: turbostan 

A longstanding limitation in TurboStan has been that inheritance could only be one level deep. I finally got tired of this and made TurboStan recursively render parent templates. The results were surprising.This limitation seemed a minor nit early in TurboStan's development. After all, Stan itself doesn't even support the concept of inheritance[*]. I added inheritance for two reasons: 1) Kid, TurboGear's primary templating engine supports it and I didn't want TurboStan to lack anything users of Kid might be used to and 2) the CherryPy dispatch system really works best using an inheritance rather than an include model for building pages.

However, I've been trying to refine how I organize my TurboGears applications and that includes keeping the template structure tidy and reducing code duplication in templates. With this in mind, I wanted to use the following structure:

index.stan +-- public.stan +--- page1.stan
           |               |
           |               +--- page2.stan
           |
           +--- admin.stan +--- admin1.stan
                           |
                           +--- admin2.stan

By doing this, I could put the general layout in index.stan, fill in general stuff from public.stan, and provide the content and other custom stuff from each page's template. Further, the admin interface inherits from index.stan, and so acquires the same look as the public face, but can remove or add sections in admin.stan, etc.

So far so good.

Except that this model requires that page[12].stan and admin[12].stan be able to inherit from public.stan and admin.stan which in turn inherit from index.stan. This wasn't doable with TurboStan prior to 0.8.6. So I fixed it by having templates recursively render parent templates. All in all only an hour or so of work, not much.

However, the benefits were immediately noticeable. The target structure has become very elegant, with child templates doing the bare minimum to fill in slots in their parents and index.stan truly controlling the layout of the site. I knew this would be the result, but until I'd actually converted my templates to use the new layout I guess I just failed to realize what a boon this would truly be.

# index.stan

html [
    head [
        title [ 'example ],
        link ( href = "/css/style.css", rel = "stylesheet", type = "text/css" ),
        slot ( 'css' )
    ],

    body [
        div ( id = 'menu' ) [ slot ( 'menu' ) ],
        div ( id = 'content' ) [ slot ( 'content' ) ],
        div ( id = 'sidebar' ) [ slot ( 'sidebar' ) ]
    ]
]

The public view fills in the content, menu and sidebar slots:

# public.stan

inherits ( 'index' ) [
    override ( 'css'), # we'll just use the default stylesheet

    override ( 'menu' ) [
        ul [
            li ( href = '/' ) [ 'Home' ],
            li ( href = '/page1' ) [ 'Page 1' ],
            li ( href = '/page2' ) [ 'Page 2' ],
            li ( href = '/admin' ) [ 'Admin' ]
        ]
    ],

    override ( 'content' ) [
        xml ( ''
            <p>
              Hello, world<br />
              This is some content.  It could have also
              been provided via a variable or several other
              methods, but this keeps it simple.
            </p>
        '' )
    ],

     override ( 'sidebar' ) [
         p [ 'This is some sidebar text' ]
     ]
]

The admin view will remove the sidebar and set a custom menu:

# admin.stan

inherits ( 'index' ) [
    override ( 'css' ) [
        # we'll add some custom CSS for the admin interface here
        link ( href = "/css/admin.css", rel = "stylesheet", type = "text/css" )
    ],

    override ( 'menu' ) [
        ul [
            li ( href = '/admin1' ) [ 'Admin 1' ],
            li ( href = '/admin2' ) [ 'Admin 2' ],
            li ( href = '/' ) [ 'Main Site' ]
        ]
    ],

    override ( 'content' ) [
        div [
            h3 [ 'Hello, administrator' ],
            'Please do your admin duties here'
        ]
    ],

    # sidebar will disappear from the generated output
    override ( 'sidebar' )
]

And finally, page1.stan and admin1.stan will replace the content slot but leave the rest intact:

# page1.stan

inherits ( 'public' ) [
    override ( 'content' ) [
        div [ 'This is the page1 template.' ]
    ]
]
# admin1.stan

inherits ( 'admin' ) [
    override ( 'content' ) [
        div [ 'This is the admin1 template.' ]
    ]
]

Because you can inherit to arbitrary depths, children can "delete" slots in parent templates or add new content with ease. Also, note that the overrides on the css slot cause the <link> tags to get injected between the <head> tags as they should be.

You can download the latest TurboStan here.

The current version (as of this writing) is 0.8.6.

  • To be clear, Stan has the slot directive which does the heavy lifting and is what I've leveraged to implement inheritance, but it was meant to be used procedurally from Python code; that is, you would define a slot in Stan, then some Python function would call fillSlots( ). Since a goal for TurboStan was to keep the syntax as a pure s-expression within templates, I added the inherits keyword as a way to express this concept directly within the template without having to resort to doing it procedurally.


0 comments Leave a comment


Jul272006

TurboStan 0.8.7

Filed under: turbostan 

Here's the latest release of TurboStan. It includes arbitrarily deep inheritance, code and content caching, and lots of little fixes.Here's a brief overview of what's new:

  1. Arbitrarily deep levels of inheritance. Earlier versions only supported inheriting from a single master template. Now templates can inherit from templates which in turn inherit from other templates.
  2. Code caching. Templates are precompiled and stored in memory. This gives an enormous performance boost.
  3. Fragment caching. Slots can be cached as static HTML via an argument to the override tag. Further, arbitrary cache expiration schemes can be employed via a callback. Also allows for custom cache identifiers if needed.
  4. Absolute paths to templates. The relative path scheme employed before was a mess. Now you can set stan.templates = 'path/to/templates' in your TurboGears config file and that becomes the root for all template paths.
  5. Passing an arbitrary namespace to included fragments. You can now add locals = dict ( myvar = 'foo' ) and pack the dictionary with variables you want the included fragment to see.

Here's a short example showing how you might implement a time-based caching scheme (i.e. expire the cache at midnight):

controllers.py:

from calendar import weekday, day_name
from time import time, localtime

class Root ( controllers.RootController ):

    @turbogears.expose ( template = 'stan:.public' )
    def index ( self, *args, **kwargs ):

        def render_content ( self ):
            now = tuple ( localtime ( time ( ) ) [ :3 ] )
            today = day_name [ weekday ( *now ) ]

            return ( ''
                This could be pulled from a database or some other source.
                I explain why this is a callback below.
                And to help make the time-based cache expiration make
                a little sense, we'll print what day it is:
                Today is %s.
            '' % today )

        def cache_valid ( self ):
            ''
            Function should return True if the cache content is valid, False if it should
            be flushed and render_content ( ) asked to regenerate the data.
            ''
            return not isMidnight ( )

        return dict (
            render_content = render_content,
            cache_valid = cache_valid
        )

The reason I'm using a callback to generate the content rather than just passing the content back in the return value of the controller is because I don't want to regenerate the content if it's already in the cache. Remember that the controller method is called on every page access prior to rendering the template and prior to checking the cache. This means that if we were to say, pull the content from the database, this database call would be made regardless of whether we later used the cache or not. By implementing this as a callback, we can guarantee that no extraneous functions are called when the cache is used.

Now our index.stan template:

html [
    head [
        title [ 'TurboStan Example' ]
    ],

    html [
        h1 [ 'Welcome to TurboStan!' ],
        div ( id = 'main-content' ) [
            slot ( 'content' )
        ]
    ]
]

And finally, our public.stan template. Note that this is the template referenced in the controller. Index.stan is referenced via inheritance:

inherits ( 'index' ) [
    override ( 'content', cached = vars.cache_valid ) [
        xml ( vars.render_content ( ) )
    ]
]

You can download TurboStan 0.8.7 `here`_.

Feel free to contact me with bug reports, feature requests, etc...

here: http://www.develix.com/downloads/TurboStan/TurboStan-0.8.7.tgz



0 comments Leave a comment




Copyright © 2007, Cliff Wells