After digging around some more, the simplest example I've found is the ir_model_data._get_id() method:
@tools.cache()
def _get_id(self, cr, uid, module, xml_id):
ids = self.search(cr, uid, [('module','=',module),('name','=', xml_id)])
if not ids:
raise ValueError('No references to %s.%s' % (module, xml_id))
# the sql constraints ensure us we have only one result
return ids[0]
It seems like you just choose a model method you want to cache and then add the cache as a decorator. If some events should clear the cache like this update() method, you use the cached method as a cache object:
if not result3:
self._get_id.clear_cache(cr.dbname, uid, module, xml_id)
It looks like by default, the first two parameters of the method are ignored when caching (cursor and user id in most cases).
This is all just based on skimming the code. I'd love to hear some feedback from anyone who's actually used it.