CacheManager icon indicating copy to clipboard operation
CacheManager copied to clipboard

Implementing Dependencies

Open TMiNus opened this issue 9 years ago • 13 comments

I am currently trying to implement keys dependencies using CacheManager. Until now I have just come up with 2 options, either doing so through cache layers using BlackPlate and the already implemented consistency or using regions somehow.

I think that in the current implementation of the library it is not possible to add new handles dynamically to a manager, you will have to create a new manager all together if you wanted new handles, which I think will not be adequate for the task at hand and even if we could add new handles dynamically I don't know if it will be efficient to have possibly 100+ handles.

In the regions way I suppose it will be possible to define a region called "dependencies" and then subscribe to the events defined in BaseCacheManager and check the "dependencies" region every time an operation is executed in a key but again I don't know if this will be efficient enough.

Is there another way of tackling this problem or am I missing something?

TMiNus avatar Dec 16 '15 17:12 TMiNus

I'd say the only way would be with regions and events at the moment. I did not implement any dependencies in CacheManager because it's not easy, especially with distributed caches, and in many cases it doesn't even makes sense. It also depends a little bit on the use case you want to solve,

Could you maybe describe your use case? That would help me design and at some point add cache dependencies as a core feature.

MichaCo avatar Dec 16 '15 20:12 MichaCo

Basically what I want to have is a file related to a version. Lets say in one key I have a file that is updated regularly and in another key I have the version of that file. I want to link the file to the version so if I update the version key the file key will get invalidated.

Another example that was going through my head when I was thinking in this feature was in the case of having a key representing a tree and then a bunch of keys representing leaves, it would be nice that the leaves keys were related to the tree so if we delete the tree key the leaves keys got invalidated.

I have been checking out in SE Redis how to implement this feature talking directly to Redis and generally they use a SET for this kind of things. I know that in our current implementation we don't have the advance features that Redis offers but anyway I will think a way of adding this features to the library without braking the current status quo (I already now that you don't want partially implemented interfaces or a lot of exceptions ^^)

BTW, can you confirm that in the current implementation there is no way of dynamically add another handle to the already created Cache Manager?

TMiNus avatar Dec 16 '15 21:12 TMiNus

BTW, can you confirm that in the current implementation there is no way of dynamically add another handle to the already created Cache Manager?

No you cannot add other handles to the CacheManager after the instance has been created. That's by design and would otherwise cause any kinds of strange issues.

The thing with distributed caches is that you have no simple way to figure out, if a key got evicted. For example, if you have a key which has sliding expiration, this key can expire at any point unless some client renews the expiration date. So for one client, it is impossible to say if the key got evicted or not and therefore we cannot automatically evict dependent keys.

There is one feature in Redis which fires events on key changes (evicts or sets etc): http://redis.io/topics/notifications. This feature must be enabled because it can cause a lot of overhead in the communication (basically another pub/sub channel). And, this feature is not enabled on Azure for example, which makes it pretty much impossible to use for me... Memcached also doesn't come with any kind of those feature sets, as far as I know.

One possible solution could be, in case of absolute or sliding expiration and dependent keys, that the key stored in redis actually never expires. The CacheManager instance(s) would handle the expiration by storing meta data on the key etc... In this case, each CacheManager instance would know when to expire a dependency graph...

MichaCo avatar Dec 16 '15 23:12 MichaCo

I have put a lot of thinking into this. In case the parent key never expire I think it is pretty straightforward because we delete the parent key and the dependency graph at the same time (in the operation remove). The case of absolute or sliding expiration is a little more tricky.

If we handle the deletion of the dependency graph with the CacheManager library I think we will add a lot of overhead because basically we would have to launch an event when the parent key expire so we can delete the dependency graph. I suppose we will have to check the expiration time of the parent key continuously (in the case of sliding expiration) to be able to launch the event that will invalidate the dependency graph.

I think is a better option to delegate this task to the cache system. For example lets say we have a parent key "tree" with an specific TTL, we could have some "leaves" keys related to that parent with the same TTL as him (always in sync). Of course if the parent key gets his TTL updated we will have to update every TTL in the related keys (in the case of sliding expiration)

In this scenario when the parent key expire the dependency graph will expire with him because they all have the same TTL. We will have to put some restrictions in place for example we can't set sliding expiration on a dependency key if the parent has absolute expiration or the case were the related key wanted to set a TTL higher than the TTL in the parent key. At the end the only task that they library will handle will be the process of updating the TTL on every dependency key (in the case of sliding expiration)

TMiNus avatar Dec 17 '15 15:12 TMiNus

Setting the same expiration for the whole dependency graph is not really a realistic scenario. I totally would see that someone wants some keys never expire, unless the parent expires, but some might have sliding and some others might expire at some fixed date.

So, no, I don't think we could limit the expiration features when using dependencies.

MichaCo avatar Dec 17 '15 21:12 MichaCo

But I mean if we are setting a dependency key in the first place what is the point of having a dependency key without the parent. I didn't say that we couldn't have some keys in sliding and some in fixed date. What I did say was that in any case the expiration of those keys could not be greater than the expiration of the parent of the dependency graph

It is as we were putting a ceiling to the expiration of any key in the dependency graph following this rule -- dependency key TTL <= parent key TTL

TMiNus avatar Dec 17 '15 21:12 TMiNus

Well it could be. ItemA has Sliding of 10minutes, ItemB depends on ItemA and has Sliding of 20minutes. Now as long as ItemA gets refreshed at least every 10minutes, it will not expire ItemB

ItemB could also have an absolute or no expiration at all ;) Would totally work

MichaCo avatar Dec 17 '15 21:12 MichaCo

When I talked about restrictions I was referring to this same scenario that you are proposing. ItemB cannot have an sliding of 20 minutes because is greater that the timing of the ItemA and ItemB depends of ItemA. ItemB can have whatever timing he likes (absolute or sliding) but always below 10 or whatever time ItemA has left (we will have to check for that) :)

With that setting we always guarentee that if ItemA gets evicted ItemB was or is gonna be evicted too because his timing is lower or equal to the timing on ItemA

In what you are proposing we could have the scenario where ItemA gets evicted and ItemB is still kicking around because his timing is greater than ItemA. It doesn't make sense if ItemB depends of ItemA and ItemA is not around anymore

TMiNus avatar Dec 17 '15 21:12 TMiNus

Are there any changes being done for including the dependencies in the CacheItem? Was looking into extending the library to support dependencies. Might help, if something on it has been done.

rkokal avatar Aug 24 '17 06:08 rkokal

@rkokal Hi, And no, I did not spend any time on it yet

MichaCo avatar Aug 31 '17 15:08 MichaCo

@MichaCo Hi, Tnx for your great job, I have to implement cache dependency in our project which it will use CacheManager, so if you need I can help to implement this feature in CacheManager. it will be used in a high-scaled project and completely will be tested.

AlexABD1984 avatar Sep 11 '17 05:09 AlexABD1984

@manonthesky sure. I always appreciate help and contributions ;)

The feature is pretty complex though, especially with distributed caches. Depending on how/what you implement, I might not agree with the solution(s) ;)

That's why it would be best if you, before you start coding, create a new issue here on github, and write down the design you are planning to implement. Then we can discuss it before you spend too much effort into coding

MichaCo avatar Sep 11 '17 09:09 MichaCo

@manonthesky i'm curious myself, i checked out your fork and see you havent pushed any commits yet

Also, can we rename this issue to "Creating Dependent Cache Items", I clicked thinking it was something about dependency injection

ericnewton76 avatar Oct 25 '17 16:10 ericnewton76