"I could make cache removal very selective, but it seems like more work than its worth. Any tips?"
Dunno if that helps, but here's how I do it:
1.) At the start of every request, generate a hash from the combination of
- user permissions (a bitfield of stuff like "is admin", "is profile owner" etc. )
- GET and POST (used to cache searches)
- configuration cookie
in short, every little thing that has an impact on the output returned needs to be in that hash.
2.) Check if the current request is already cached, return cached result if that is the case. Otherwise:
3.) Register all "items relevant to the cache". If you list a folder for example, any change to that folder, any change to a file, deleting or adding a file will be registered as "relevant" to that cache. For now, the relevant items get stored in an array. If the current requests actually gets cached (has to be turned on explicitly, the default is to not cache), the cache as well as the "relevant items" get stored in the database.
db_cache
hash
content
time_created
time_expire
db_cache_relevant_item
hash
type
id
where "type" is things like "add page to profile", "add subpage to parent page", and "id" is the id of the profile, the parent page etc. that "type" relates to.
4.) In all appropriate places (editing a page, deleting a file, etc.), sprinkle in a function call to delete cached requests that have this item registered as relevant.
cache->invalidate($type, $id)
{
'DELETE cache as c, cache_relevant_item as cri FROM cache as c, cache_relevant_item as cri WHERE c.hash = cri.hash AND cri.type = "' . $type . '" AND cri.id = '.$id
}
The amount of relevant item types and the calls to cache->register_relevant_item() and cache->invalidate() can mushroom quite a bit.... there are more elegant ways to do it, I'm sure.
And of course better ways to explain it ^^ however, it's the best I came up with so far, and it does work very nicely, at least for now.