Skip to content

@CacheEvict should allow multiple key values [SPR-10958] #15586

Closed
@spring-projects-issues

Description

@spring-projects-issues
Collaborator

Ashot Golovenko opened SPR-10958 and commented

Currently you can evict only one or all elements in a single cache region.
Allowing something like

@CacheEvict(value = "userCache", key = {"key1", "key2"})

would be really handful.


Affects: 3.2.4

12 votes, 14 watchers

Activity

spring-projects-issues

spring-projects-issues commented on Oct 11, 2013

@spring-projects-issues
CollaboratorAuthor

Phil Webb commented

Usually the key attribute is a SpEL expression that obtains the key from an input parameter.

e.g.:

@Override
@CacheEvict(value = "default", key = "#p0")
public void evict(Object arg1, Object arg2) {
}

What use-case do you have in mind where two keys would need to be evicted?

spring-projects-issues

spring-projects-issues commented on Oct 20, 2013

@spring-projects-issues
CollaboratorAuthor

Ashot Golovenko commented

Well, let's say I have the following logic:

@CacheEvict(value = "userCache", key = "#userId + '/foo1'")
public void updateFoo(long userId, someParams)

@Cacheable(value = "userCache", key = "#userId + '/foo1'")
public void getFoo1(long userId)

@Cacheable(value = "userCache", key = "#userId + '/foo2'")
public void getFoo2(long userId)

The update method changes the database so that calculations of both getFoo1 and getFoo2 methods are obsolete. Now I can evict only one key or all. Ideally I'd really love to have a keymask support so I could evict all keys starting like "#userId*" but array of keys is also fine.

spring-projects-issues

spring-projects-issues commented on Oct 23, 2013

@spring-projects-issues
CollaboratorAuthor

Phil Webb commented

You could consider using different cache stores rather than using the key:

@CacheEvict(value = "foo1Cache", key = "#userId")
public void updateFoo(long userId, someParams)

@Cacheable(value = "foo1Cache", key = "#userId")
public void getFoo1(long userId)

@Cacheable(value = "foo2Cache", key = "#userId")
public void getFoo2(long userId)

You can then use:

@CacheEvict(value = {"foo1Cache", "foo2Cache"}, key = "#userId")
spring-projects-issues

spring-projects-issues commented on Feb 14, 2014

@spring-projects-issues
CollaboratorAuthor

Lives commented

This would be very helpful in different use cases.
For eg : I have a city master which is cached.
The service for city master can accept a collection of cities for creation / modification

Since currently cache evict does not support multiple keys , developer ends up with clearing the entire cache.
Using SPEL developers can pass the list of keys to be removed from cache.If the cache evict aspect can handle it, it would be very useful.

spring-projects-issues

spring-projects-issues commented on Nov 25, 2014

@spring-projects-issues
CollaboratorAuthor

shirish commented

This is a really good feature request. One such use case that I have encountered is to update user messages. Ideally an user can have messages in multiple locales I would like to evict all the messages with key= "{#userid_en, #userid_de}" instead of maintaining a separate cache for each locale.

Also if eviction is possible using regex of the key that would solve this entirely, how ever I am not sure about how big a task this is. some thing like key={"#userid_*"} should evict every thing under that key regex.

spring-projects-issues

spring-projects-issues commented on Apr 10, 2015

@spring-projects-issues
CollaboratorAuthor

Stéphane Nicoll commented

My own personal feeling about it is that if you need to do this you probably reached the boundaries of what's safe to do declaratively. If we're talking about multiple keys and spel then it starts to be awkward for several reasons:

  1. We need this to be consistent so we should support that everywhere we support the key attribute (not only cache evict)
  2. If you can do that with SpEL you must be able to do that programmatically. Yet, the KeyGenerator returns an object so we'll have to deal with the return type and handle a collection of something explicitly (which means you can't use that as a single key anywhere even though I doubt someone was doing that)
  3. Going further down that road, things like pattern matching to retrieve the number of elements comes to mind (there is an example above) and we don't want to do that with SpEL.

Since this adds a lot of complexity I want to be sure that this is really needed so please cast your vote (and add your use case please).

spring-projects-issues

spring-projects-issues commented on Oct 20, 2015

@spring-projects-issues
CollaboratorAuthor

Johannes Kuhs commented

For more advanced caching scenarios, some concept of having multiple keys is definitely necessary in my opinion. I'm not sure it needs to be in the form of allowing multiple key values though. The key is used for retrieving a cache entry but really we just need a way to evict specific entries. So instead, it might make more sense to add a new evictKeys parameter. evictKeys simply point to the key and can thus be used to evict the actual entry.

Another use-case for this kind of functionality would be the caching of different product information. A product might have skus, images, prices, features, facets, and other information attached to it that require more expensive operations (e.g. looping through all images to find a certain image type + view, getting a specific customer price, creating a distinct set of facets across skus, etc.). It would be nice to be able to cache the results of these kind of operations and simply evict them based on the product ID when the product is updated.

Updating the existing annotations to support this could look like this:

@Cacheable(value = "productImages", key = "#product.id + #imageType", evictKeys = { "#product.id" })
public Image getProductImageOfType(Product product, String imageType)

@CacheEvict(value = "productImages", evictKeys = { "product.id" })
public void updateProduct(Product product)
spring-projects-issues

spring-projects-issues commented on Jan 20, 2016

@spring-projects-issues
CollaboratorAuthor

Kyle Lieber commented

We could really use this functionality as well. I think the suggestions made by @jkuhs would work. Here my scenario if it helps:

We have a Policy and a Product.

I have a Policy which is identified by a policyId and each Policy can have multiple Products which are identified by the policyId and the productCode.

So I have a ProductService with a method for getting a product by policyId and productCode.

public class ProductService {
  @Cacheable("products")
  public Product getProduct(Integer policyId, String productCode) {  }
}

Then I have a PolicyService which has a method for deleting a Policy when given the policyId.

public class PolicyService {
  public void deletePolicy(Integer policyId) {  }
}

The PolicyService#deletePolicy will also delete all products for that policy but I have no way to clear the products cache for that policy.

My workaround options are:

  1. Clear all entries in the cache. This is really not an option because this will affect all products for all policies.
  2. Do something really ugly like this:
public class ProductService {
  @Cacheable("products")
  public Product getProduct(Integer policyId, String productCode) {  }

  public List<Product> getAllProducts(Integer policyId) { }

  @CacheEvict("products")
  public void evictFromCache(Integer policyId, String productCode) {
    // this method doesn't do anything and is only here for evicting the cache
  }
}
public class PolicyService {
  public void deletePolicy(Integer policyId) {
    // first delete the policy
    doDelete(policyId);
    // then evict the products
    for (Product product : productService.getAllProducts(policyId)) {
      productService.evictFromCache(policyId, product.code);
    }
  }
}
spring-projects-issues

spring-projects-issues commented on Jan 21, 2016

@spring-projects-issues
CollaboratorAuthor

Johannes Kuhs commented

To add to my previous comment, it would be nice to also have a evictKeysGenerator parameter on the Cacheable annotation. It would follow the same concept as keyGenerator but have a return type of String[] (or even Object[] - the point being that multiple evict keys are supported).

I'm really hoping some support for multiple evict keys will be implemented. In it's current form, the cache invalidation is quite limited for more advanced scenarios.

spring-projects-issues

spring-projects-issues commented on Feb 11, 2016

@spring-projects-issues
CollaboratorAuthor

Stéphane Nicoll commented

We discussed this internally and decided to reject this feature. This is mostly programming by annotations and it would make the API and implementation much more complex. This idea of a separate KeysGenerator interface for evictions is an obvious example.

While we could implement a simpler solution for some cases, the solution would become inconsistent overall as all the other annotation attributes and interface method handle a single value for the key.

spring-projects-issues

spring-projects-issues commented on Feb 11, 2016

@spring-projects-issues
CollaboratorAuthor

Johannes Kuhs commented

It's disappointing to hear that. Would you mind elaborating on why you think this would make the API and implementation "much more complex"? Not sure I understand the argument for a KeysGenerator adding that much complexity. The concept for key generators is not a new one after all.

If this decision is mostly based on not wanting to change the current interfaces, would you consider alternative solutions to support multiple evict keys in some capacity? I haven't thought about alternative solutions a lot, but maybe a separate set of annotations for advanced use, or some hooks that would allow for a more programmatic approach after the logic behind the @Cacheable and @CacheEvict annotations has been executed?

spring-projects-issues

spring-projects-issues commented on Feb 11, 2016

@spring-projects-issues
CollaboratorAuthor

Sarath Akula commented

As @jkuhs mentioned, can it be a separate set of annotations for advanced use.It would avoid writing custom code for clearing up the cache in our application.

spring-projects-issues

spring-projects-issues commented on Apr 10, 2016

@spring-projects-issues
CollaboratorAuthor

Ranadeep Sharma Hidangmayum commented

Hi Team,

The use cases mentioned by everyone here are very common ones, infact.
Here is another usecase -

Let's consider an application where insurance plans for participants belonging to various clients are being read/updated in DB. This application has lots of methods where numerous plan objects have been pumped to cache(s), no matter how many.

@Cacheable(value="participantCache" , key = "{#clientId, #planId, #participantId}")
public List<ParticipantPlanDetails> getParticipantPlanDetails(String clientId, String planId, String participantId)  { ... }

@Cacheable(value="clientCache" , key = "{#clientId, #planId}")
public PlanInfo getEasePlanInfo(String clientId, String planId)  { ... }

Now, I want to delete items from applicable cache(s) where the item's key is matching partially with the collective *clientId * used for deleting all relevant data from DB for a particular client.

@CacheEvict(key = "#clientId")
public void deleteClient(String clientId)  { ... }

In short, I would like the above _CacheEvict_to be able to convey to cache handler that all the objects for this specific clientId be cleared, including the List and PlanInfo objects from participantCache and clientCache respectively.

spring-projects-issues

spring-projects-issues commented on Apr 10, 2016

@spring-projects-issues
CollaboratorAuthor

Ranadeep Sharma Hidangmayum commented

Kindly review this feature against my use case (mentioned in comments) for possible enhancement.

spring-projects-issues

spring-projects-issues commented on Apr 11, 2016

@spring-projects-issues
CollaboratorAuthor

Stéphane Nicoll commented

As I already explained above, we've made up our mind on this feature and decided not to implement it.

14 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

has: votes-jiraIssues migrated from JIRA with more than 10 votes at the time of importin: coreIssues in core modules (aop, beans, core, context, expression)status: declinedA suggestion or change that we don't feel we should currently applytype: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @william-aqn@snicoll@waitpigfly@sergiocard@spring-projects-issues

      Issue actions

        @CacheEvict should allow multiple key values [SPR-10958] · Issue #15586 · spring-projects/spring-framework