Aggressive cache how to deal with cache refreshes?

685 views
Skip to first unread message

Chris Marisic

unread,
Jun 6, 2012, 12:52:52 PM6/6/12
to rav...@googlegroups.com
When using aggressively cache, how do you inform RavenDB properly that it's current cache is stale?

Like on a list of things that almost never changes and are used many places so it's aggressively cached, but at some point a new item is added to the list, it still has the original aggressively cached values and does not contain the new value.

Matt Warren

unread,
Jun 6, 2012, 5:17:03 PM6/6/12
to rav...@googlegroups.com
I've just looked through the code and I can't see how you can do that, but I guess it could be added?

At the moment, you can clear the entire cache, see https://github.com/ravendb/ravendb/blob/master/Raven.Client.Lightweight/Connection/HttpJsonRequestFactory.cs#98

         store.JsonRequestFactory.ResetCache()

But unless I'm missing something, that's about it. The internal cache isn't exposed any other way and there's no other code that removes items from it (which might be a problem in of itself)

Matt Warren

unread,
Jun 6, 2012, 5:35:06 PM6/6/12
to rav...@googlegroups.com
Okay I got one bit wrong:

"there's no other code that removes items from it (which might be a problem in of itself)"

The cache expires items on a LRU basis (once it's limit is reached), but I still can't see how you manually get the cache to replace an existing item with a newer copy.

Chris Marisic

unread,
Jun 6, 2012, 8:59:09 PM6/6/12
to rav...@googlegroups.com
That seems a bit way beyond aggressive. That's more like murderlycachce.

Beyers Cronje

unread,
Jun 7, 2012, 6:12:17 AM6/7/12
to rav...@googlegroups.com
I can also see a definite requirement to invalidate specific objects in the cache. Should this feature be added to the 1.2 feature request list?

Itamar Syn-Hershko

unread,
Jun 7, 2012, 6:41:06 AM6/7/12
to rav...@googlegroups.com
using (DocumentStore.DisableAggressiveCaching())
{
session.Load<Foo>("id");
}

or a Query

Will force Raven to issue the query to the server, and get the new version if it indeed exists

Chris Marisic

unread,
Jun 7, 2012, 9:25:09 AM6/7/12
to rav...@googlegroups.com
Does that actually invalidate the cache? Or does that only make requests issued with in that context of

using (DocumentStore.DisableAggressiveCaching())
{...
}

to bypass caching?

Matt Warren

unread,
Jun 7, 2012, 9:54:56 AM6/7/12
to rav...@googlegroups.com
That's how I read it as well and I've put together a gist that shows the issue.

DisableAggressiveCaching() doesn't prevent an existing item from being pulled from the cache. It just seems to prevent a new item from being added to the cache.

The code here https://gist.github.com/2888887 prints:

Session Version:
        customers/225: Name "Matt", Age=32

Database Version:
        customers/225: Name "Matt Warren", Age=99

Session Version (without caching):
        customers/225: Name "Matt", Age=32
Press any key to continue . . .

you can see that the Load inside the "DisableAggressiveCaching" is still pulling the "stale" value from the cache (because the original TimeSpan hasn't expired) even though the value of the doc on the server is different? 

Matt Warren

unread,
Jun 7, 2012, 10:09:50 AM6/7/12
to rav...@googlegroups.com
Whoops, ignore my last post, I was doing everything inside the same session, I need to look at this more, I don't think I understand the interaction between the session cache and the doc store aggressive caching!

David Boike

unread,
Jun 7, 2012, 10:26:43 AM6/7/12
to rav...@googlegroups.com
What is needed is something like...

DocumentStore.RemoveFromCache<ModelType>(documentId);

I'm not sure if it should even need a session.

We use a lot of NServiceBus, and this would be helpful for a message handler reacting to a SomethingBigHasChangedEvent by ejecting the normally very long-lived data from the agressive cache so that the NEXT request will go back to Raven for a fresh copy.

Chris Marisic

unread,
Jun 7, 2012, 11:06:03 AM6/7/12
to rav...@googlegroups.com
This is exactly what is needed. I also think it should support

DocumentStore.RemoveFromCache<ModelType>

Users should be able to forcefully eject either a specific object, or all objects of a type.

Matt Warren

unread,
Jun 7, 2012, 11:29:39 AM6/7/12
to rav...@googlegroups.com
Okay, I get it now!! I've put an updated gist at https://gist.github.com/2888887. DisableAggressiveCaching() works as Itamar said.

BTW, does the name "AggressivelyCacheFor" confuse anyone else?

When I see:

        using (store.AggressivelyCacheFor(TimeSpan.FromMinutes(30)))
	{
        }

I read that as, "anything that gets loaded inside the using statement can be cached for the next 30 mins" But I don't think that matches what is actually happening.

What about if it was (or something similar):
        using (store.UseCachedItemsFrom(TimeSpan.FromMinutes(30)))
	{
        }

So that reads as, "inside the using statement, use any items that have been put in the cache in the last 30 mins", which is closer to what's actually happening (in the way I understand it and as it's explained at http://ayende.com/blog/25601/ravendb-aggressive-caching-mode)

Kijana Woodard

unread,
Jun 7, 2012, 11:52:02 AM6/7/12
to rav...@googlegroups.com
Thanks for the gist. I got it running and verified that loading the object through disableAggressiveCaching refereshes the cache.

I also learned about DatabaseCommands.Get. 
And I learned that aggressive caching doesn't work for embedded. :-)

Matt Warren

unread,
Jun 7, 2012, 11:55:05 AM6/7/12
to rav...@googlegroups.com
Yeah, it doesn't help in embedded, it's only worth doing when you are in client/server because it saves network trips. In embedded moving between client/server is just a function call, so it's not worth caching.

Kijana Woodard

unread,
Jun 7, 2012, 11:56:14 AM6/7/12
to rav...@googlegroups.com
Makes sense, I just set it up like I did my unit tests and was confused for 0.9 seconds. :-)

Oren Eini (Ayende Rahien)

unread,
Jun 7, 2012, 3:43:54 PM6/7/12
to rav...@googlegroups.com
Chris,
You use: DisableAggressiveCaching and make the query that you want to refresh.
That would go to the DB, and also load it to the cache.

On Wed, Jun 6, 2012 at 7:52 PM, Chris Marisic <ch...@marisic.com> wrote:

Oren Eini (Ayende Rahien)

unread,
Jun 7, 2012, 3:44:47 PM6/7/12
to rav...@googlegroups.com
It invalidate the cache for the current scope.
The it loads the new results into the cache.

Oren Eini (Ayende Rahien)

unread,
Jun 7, 2012, 3:47:22 PM6/7/12
to rav...@googlegroups.com
The session cache is always there, and it will not go to the server to get a value that it already has.
The reasoning behind it is that you already loaded the object, and you might have changed it in some manner, so we don't want to touch it, so we just give you the already loaded object.
Pretty standard UoW stuff.

Chris Marisic

unread,
Jun 7, 2012, 5:11:06 PM6/7/12
to rav...@googlegroups.com
Got it. Sounds like there was alot of confusion around this.

Matt Warren

unread,
Jun 8, 2012, 4:38:47 AM6/8/12
to rav...@googlegroups.com
Makes sense, thanks for clearing that up

Itamar Syn-Hershko

unread,
Jun 8, 2012, 8:47:27 AM6/8/12
to rav...@googlegroups.com

Actually there are 2 perspectives you could look at it

The one we used when naming it considers what happens when this code is executed for the first time. The doc retrieved will be cached for that period of time and no further calls to the server will be made by that code block

David Boike

unread,
Jun 8, 2012, 10:48:51 AM6/8/12
to rav...@googlegroups.com
So then my NServiceBus event handler scenario from above could be trivially implemented by an extension method as:

public static void RemoveFromCache<TModel>(this IDocumentStore store, string documentId)
{
    using(var session = store.OpenSession())
    {
        using(store.DisableAggressiveCaching())
        {
            session.Load<TModel>(documentId);
        }
    }
}

This carries the advantage that the refresh is done asynchronously within the event handler, and the next request needing it will already have it without having to pay the price.

However, I still worry about the situation where you have a lot of events that force a web application into loading a bunch of stuff into aggressive cache that they may or may not need to have resident in memory.


On Thursday, June 7, 2012 2:43:54 PM UTC-5, Oren Eini wrote:
Chris,
You use: DisableAggressiveCaching and make the query that you want to refresh.
That would go to the DB, and also load it to the cache.

Oren Eini (Ayende Rahien)

unread,
Jun 8, 2012, 12:11:33 PM6/8/12
to rav...@googlegroups.com
David,
It is a bit more complex than that. What you said works for simple entities loads. But it doesn't work for more complex scenarios.
For example:

session.Include<Customer>(x=>x.Company).Load(1);
session.Include<Customer>(x=>x.Client).Load(1); 

Are actually two _different_ cache entries.

We quite intentionally did NOT provide any sort of access to the aggressive caching system. We have seen what happened in the NHibernate world.
Aggressive caching is elective, and it requires that you take special steps. That is on purpose, you should use it for stuff that very rarely change.
In RaccoonBlog case, we use it for things like the config, the sections, etc.
Reply all
Reply to author
Forward
0 new messages