The current best practice with cache_fu is to let it call find_cache
and expire_cache, and let it do all the setting behind the scenes.
If your method has use for objects that have just been retrieved from
the database then we should probably look into using it by default or
at least providing it as an easy option. My guess is that no
associations should be loaded after most finds, unless they were
loaded by eager loading, after_find or after_initialize. Please feel
free to prove me wrong :)
Eli
I only use get_cache and expire_cache in my projects. This makes
things a lot easier and then you just have to deal with concurrent
writes and StaleObjectError. I strongly suggest using only those
methods if you can.
> First, a caveat - our version of cache is out of sync with cache_fu.
> Among other things, it appears we are using a set of patches that
> you (Eli) submitted a few months back (1) - I'm not sure I understand
> the implications of the patch on our code and what it's doing.
If this data is of any real importance, you probably should be using
optimistic locking through the lock_version column. This will alert
you to any stale objects very very loudly.
As some have noticed, my after_save method fires even if a save fails.
This is not a huge concern but it does degrade performance slightly.
It's an easy fix but I haven't had any time to play with the code
lately.
> So it appears that set_cache_without_associations was written
> to counter writing large objects to memcache. If we have a class
> like this
> class Foo
> acts_as_cached
> after_save :set_cache
>
> belongs_to_cached :bar
> has_many :things
> end
> It appears that the data from the associations get written to the
> cache
> as well. Which makes for a big object.
That set_cache call would only save the associations if they had been
loaded within the lifetime of the object. I'm of the opinion that
"related objects" should only be saved with another object explicitly
through eager loading as this can have huge cache expiration issues if
you're not careful.
@user = User.get_cache(1)
@user.posts.size
@user.set_cache
Post.destroy_all
User.get_cache(1).posts.size != 0
> Now, I'm thinking that we should really have "after_save :reset_cache"
> instead - which will probably accomplish what we really want - which
> is
> to update attribute data and not necessarily cache the associated
> objects.
> Is this thinking correct?
The problem with these association clearing methods is that they clear
the associations on your objects, causing duplicate SELECTs. Normally
Rails' query cache would stop that, but we're doing an UPDATE
operation which will cause the query cache to be cleared.
@user User.get_cache(1)
@user.posts # SELECT ...
@user.set_cache_without_associations
@user.posts # SELECT ...
I haven't seen t his set_cache_without_associations method but it
should probably duplicate the current object and clear those
associations as it would be faster than most SELECTs.
Realistically, just use get_cache and quit worrying about these
association headaches! Caching is fraught with many dangerous
situations that are subtle and difficult to debug. You can get 90% of
the benefit of caching with 10% of the headache by just using
get_cache and expire_cache.
> p.s. I don't know the status of "belongs_to_cached" and
> "has_XXX_cached", but
> I've also written a different implementation which I will try to put
> on Github...
I've re-implemented these as simply belongs_to and has_one, but the
code now notices when a cached model is being referenced and
automatically uses get_cache when it can. For updating an existing
code base this is great but it doesn't provide distinct ways for
getting from cache and finding from the database. I'll be re-releasing
my code in the near future.
Eli