Cache APIs do not support atomic actions from JSR-107

Description

See KNL-1162
There are JSR-107 methods in the Cache API which are atomic (or nearly so anyway) that could help with making invalidating, dependent, or linked caches work.

//JSR-107 V getAndPut(K key, V value);
//JSR-107 boolean putIfAbsent(K key, V value);
//JSR-107 boolean remove(K key, V oldValue);
//JSR-107 V getAndRemove(K key);
//JSR-107 boolean replace(K key, V oldValue, V newValue);
//JSR-107 boolean replace(K key, V value);
//JSR-107 V getAndReplace(K key, V value);

NOTE: these were left out to make it more likely to be compatible with caching systems like memcached that do not really support this capability (at least, not completely)

NOTE: I added Matthew Buckett's related comments below (from other tickets)

Activity

Show:

Neal Caidin April 2, 2015 at 10:03 AM

Sakai core team - not critical to get Sakai 11 out the door.

Neal Caidin March 9, 2015 at 8:21 AM

Checked with Bob Long. Apparently Unicon is no longer working on this.

Aaron Zeckoski May 1, 2014 at 6:39 AM

Matthew Buckett commented on KNL-1230:
--------------------------------------

If we take the example of a 2 node cluster using a distributed cache and 2 users accessing the same site but on different nodes. And if we use 2 caches, one to hold the values and one to hold the values to be invalidated.

Before any users have accessed the site the caches are empty:

Then if user1 attempts to access the !gateway site on node1 the authz check will happen against the DB (and it's allowed) and this gets put into the cache:

unlock@user1@site.visit@/site/!gateway => MRCE[true,refs=/site/!gateway, /realm/!site.helper, /realm/!user.template, /realm//site/!gateway]

but before node 1 puts an entry in the second cache for invalidation user2 on node 2 tries to access the same site and again there isn't anything in the cache for this user so the query goes to the DB and the following value gets put in the cache:

unlock@user2@site.visit@/site/!gateway => MRCE[true,refs=/site/!gateway, /realm/!site.helper, /realm/!user.template, /realm//site/!gateway]

now both these nodes want to create entries in the second cache for invalidation (showing as a set (at the moment it's a map with duplicate key/values)):

node 1 needs to put in:

/site/!gateway => {unlock@user1@site.visit@/site/!gateway}
/realm/!site.helper => {unlock@user1@site.visit@/site/!gateway}
/realm/!user.template => {unlock@user1@site.visit@/site/!gateway}
/realm//site/!gateway => {unlock@user1@site.visit@/site/!gateway}

and node 2 needs put in:

/site/!gateway => {unlock@user2@site.visit@/site/!gateway}
/realm/!site.helper => {unlock@user2@site.visit@/site/!gateway}
/realm/!user.template => {unlock@user2@site.visit@/site/!gateway}
/realm//site/!gateway => {unlock@user2@site.visit@/site/!gateway}

so that the final invalidation cache values are:

/site/!gateway => {unlock@user1@site.visit@/site/!gateway, unlock@user2@site.visit@/site/!gateway}
/realm/!site.helper => {unlock@user1@site.visit@/site/!gateway, unlock@user2@site.visit@/site/!gateway}
/realm/!user.template => {unlock@user1@site.visit@/site/!gateway, unlock@user2@site.visit@/site/!gateway}
/realm//site/!gateway => {unlock@user1@site.visit@/site/!gateway, unlock@user2@site.visit@/site/!gateway}

But how should the 2 nodes put the /site/!gateway entry into the invalidation cache without overwriting each others values? If they do end up overwriting each others values then cache invalidation won't correctly happens when one of the realms gets updated. Without the cache supporting a one of the concurrent primitives such as boolean replace(K key, V oldValue, V newValue) they can't manage todo this.

Aaron Zeckoski May 1, 2014 at 6:38 AM

Matthew Buckett added a comment - 24-Apr-2014 06:46
To replace MultiRefCache with 2 caches I think we need to add the additional JAR-107 methods to the org.sakaiproject.memory.api.Cache interface.

If a cache is used to hold a set of references that should be invalidated the set of references will need to be updated by multiple nodes in the cluster and we don't want one node replacing changes made by another node. One possible solution is to use:

boolean replace(K key, V oldValue, V newValue);

Details

Priority

Affects versions

Components

Assignee

Reporter

Created May 1, 2014 at 6:37 AM
Updated April 25, 2018 at 3:18 PM