[PATCH] added set_cache_without_associations

1 view
Skip to first unread message

kevin y. kim

unread,
Apr 10, 2008, 12:53:06 PM4/10/08
to acts_as_cached
We use this message to help keep the size of the objects we store
in memcache down. I was going to use some inverse of the test
for set_cache_with_associations, but I couldn't find it.

-kevin


Signed-off-by: kevin y. kim <kyki...@gmail.com>
---
lib/acts_as_cached/cache_methods.rb | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/lib/acts_as_cached/cache_methods.rb b/lib/acts_as_cached/
cache_methods.rb
index cd5690b..22c4071 100644
--- a/lib/acts_as_cached/cache_methods.rb
+++ b/lib/acts_as_cached/cache_methods.rb
@@ -277,6 +277,11 @@ module ActsAsCached
set_cache
end

+ def set_cache_without_associations
+ self.clear_association_cache
+ set_cache
+ end
+
# Lourens Naud?
def expire_cache_with_associations(*associations_to_sweep)
(Array(cache_options[:include]) +
associations_to_sweep).flatten.uniq.compact.each do |assoc|
--
1.5.4.4

Eli Miller

unread,
Apr 22, 2008, 1:43:01 AM4/22/08
to acts_as...@googlegroups.com
Kevin,

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

kevin y. kim

unread,
Apr 23, 2008, 11:36:39 PM4/23/08
to acts_as_cached
Eli - thanks for the feedback...

I've done a lot digging with cache_fu since I first posted.
I think that we actually don't want this methods, but rather
should be using "reset_cache"....

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.

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.

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?

-kevin

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...

(1) http://groups.google.com/group/acts_as_cached/browse_thread/thread/c54f489fe6d29e84



On Apr 22, 1:43 am, "Eli Miller" <elijah.mil...@gmail.com> wrote:
> Kevin,
>
> 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
>
> On Thu, Apr 10, 2008 at 12:53 PM, kevin y. kim <kykim...@gmail.com> wrote:
>
>
>
> > We use this message to help keep the size of the objects we store
> > in memcache down. I was going to use some inverse of the test
> > for set_cache_with_associations, but I couldn't find it.
>
> > -kevin
>
> > Signed-off-by: kevin y. kim <kykim...@gmail.com>

Eli Miller

unread,
Apr 24, 2008, 1:00:11 AM4/24/08
to acts_as...@googlegroups.com
On Wed, Apr 23, 2008 at 11:36 PM, kevin y. kim <kyki...@gmail.com> wrote:
> I've done a lot digging with cache_fu since I first posted.
> I think that we actually don't want this methods, but rather
> should be using "reset_cache"....

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

Reply all
Reply to author
Forward
0 new messages