Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using play-redis AND ehcache? #138

Closed
fooblahblah opened this issue Jan 3, 2018 · 22 comments
Closed

Using play-redis AND ehcache? #138

fooblahblah opened this issue Jan 3, 2018 · 22 comments

Comments

@fooblahblah
Copy link

This isn't an issue, but a question. Is it possible to use both ehcache and play-redis? I was goofed around with the configuration for a bit, but was never able to make play-redis and ehcache run together. A subset of what I had configured is as follows.

I have a file called redis.conf:

play.cache.redis {
  bind-default = false

  # source property; standalone is default
  source: standalone

  instances {
    history-cache {
      host: localhost
      port: 6379
    }
  }
}

In application.conf I've enabled the module as follows:

play.modules.enabled += "play.api.cache.redis.RedisCacheModule"

The problem is when I try to inject an instance of CacheAsyncApi I get an error indicating No implementation for play.api.cache.redis.CacheAsyncApi was bound.. Presumably this is doe to the bind-default = false statement? If I remove that I get various other issues related to having to define the play cache, which is the default as mentioned in the docs.

Is there an example anywhere of running both caches?

@sfelt-carecloud
Copy link

I have the same issue and added a new attribute in my application.conf and IF stmt in the bindings. I am going to create a PR to push the code up.

@KarelCemus
Copy link
Owner

I'm going to write an example hopefully later today. I haven't tested using both implementations simultaneously but I believe, it should work.

@sfelt-carecloud
Copy link

@KarelCemus I have it working locally and can create a PR or send you the code, whichever you like. I also created a "discovery" method for the cluster to gather the slots. There is an issue with rediscala where dns resolution does not work properly to map out the nodes in the RedisPool when working with AWS. Just let me know.

@KarelCemus
Copy link
Owner

I'm not aware of the issue within AWS cluster. Maybe you can create a separate issue/PR for it to open a discussion if you think it should be included in the lib. However, it sounds more like a workaround for the rediscala issue.

I'll let you know regarding the ehcache integration, I'll look at it myself at first.

Thanks.

@fooblahblah
Copy link
Author

Thanks for the quick reply @KarelCemus and @sfelt-carecloud. If someone can share an example of default Play ehcache and play-redis running in same app that'd be great. I'd be looking to keep the default play cache in ehcache and create some new NamedCache's using play-redis.

@KarelCemus
Copy link
Owner

KarelCemus commented Jan 3, 2018

Alright, I created the example and it works as expected, I haven't seen any bug. Though, I have to admit there is incomplete documentation at one point. That needs to be fixed for these cases, I'll do it.

Example

Let's use both EhCache and Redis together. Have EhCache bound to the default APIs and Redis cache bound only to the named instance redis. Otherwise let's assume defaults such as redis on localhost:6379 etc.

build.sbt

libraryDependencies += ehcache
libraryDependencies += "com.github.karelcemus" %% "play-redis" % "2.0.1"

application.conf

# enable redis cache module
play.modules.enabled += "play.api.cache.redis.RedisCacheModule"

play.cache.redis {
  # do not bind default unqualified APIs
  bind-default: false

  # name of the instance in simple configuration,
  # i.e., not located under `instances` key
  default-cache: "redis"
}

Controller Home.scala

package controllers

import javax.inject._

import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future}

import play.api.cache.AsyncCacheApi
import play.api.mvc._

/**
  * This controller creates an `Action` to handle HTTP requests to the
  * application's home page.
  */
@Singleton
class Home @Inject()( ehcache: AsyncCacheApi, @Named( "redis" ) redis: AsyncCacheApi, cc: ControllerComponents )( implicit ec: ExecutionContext ) extends AbstractController( cc ) {

  def index = Action {
    Ok( views.html.index() )
  }

  val ehImpl = ehcache.getClass.getName

  val redisImpl = redis.getClass.getName


  def testEhCache( ) = Action.async {
    var hit = true
    ehcache.getOrElseUpdate( "key", 2.seconds ) {
      hit = false
      Future successful "value"
    }.map { _ =>
      Ok( if ( hit ) s"Hit in EhCache ($ehImpl)" else s"Miss on EhCache ($ehImpl). New value computed and stored." )
    }
  }

  def testRedis( ) = Action.async {
    var hit = true
    redis.getOrElseUpdate( "key", 2.seconds ) {
      hit = false
      Future successful "value"
    }.map { _ =>
      Ok( if ( hit ) s"Hit in Redis ($redis)" else s"Miss on Redis ($redis). New value computed and stored." )
    }
  }
}

I hope this answers your question. Even if so, please keep the issue opened to indicate the documentation bug.

@fooblahblah
Copy link
Author

Thanks. I notice a few differences with the @Named annotation and I think I was using the wrong AysncCacheApi. I blew out that branch, but will get back to it tomorrow and see if I can get it working.

@KarelCemus
Copy link
Owner

This is the API provided by Play thus shared by both implementations. There is also another API provided by the play-redis, but it's not implemented by EhCache so the demonstration would not be so obvious.

@tech-sam
Copy link

tech-sam commented Jan 5, 2018

Hi Karel, may be I have done something wrong I am using play 2.6.2 and my application.conf is like

play.cache.bindCaches = ["db-cache", "user-cache", "session-cache", "options-cache"]

play {
    modules {
        enabled += "play.api.cache.redis.RedisCacheModule"
        disabled += "play.api.cache.ehcache.EhCacheModule"
    }
}
play.cache.redis {
  bind-default: false
  source: standalone
  instances {
    options-cache {
	  host:       localhost
	  port:       6379
      prefix: 	  options
    }
  }
}

I am getting error

 No implementation for play.api.cache.SyncCacheApi was bound.
  while locating play.api.cache.SyncCacheApi

Guide me to implement named cache also

@KarelCemus
Copy link
Owner

KarelCemus commented Jan 5, 2018

Hi, well, it seems you have/want to have several named caches but what which one is the default and bound to the unqualified traits?

It's not options-cache managed by play-redis, you've disabled it by bind-default: false

By the way, play-redis does not support bindCaches, it's used by EhCache.

Regarding the issue - you are somewhere injecting unqualified SyncCacheApi trait but no one registered its implementation. What instance do you expect there?

@tech-sam
Copy link

tech-sam commented Jan 5, 2018

Thanks for the quick reply , Yes I want several named caches , default is the play cache which I have updated as below

play.cache.redis {
  bind-default: true
  source: standalone
  instances {
    play {
	  host:       localhost
	  port:       6379
      
    }
  
    options-cache {
	  host:       localhost
	  port:       6379
      prefix: 	  options
    }
  }
}

so I put bind-default true also , error what I got is like

No implementation for play.cache.SyncCacheApi annotated with @play.cache.NamedCache(value=options-cache) was bound.
  while locating play.cache.SyncCacheApi annotated with @play.cache.NamedCache(value=options-cache)
    for the 1st parameter of GeneralOptions.<init>(GeneralOptions.java:23)

below is sample of my Implementation class

import javax.inject.Inject;
import javax.inject.Singleton;
import play.cache.NamedCache;
import play.cache.SyncCacheApi;


@Singleton
public class GeneralOptions extends BaseOptions {

    @Inject
    public GeneralOptions(@NamedCache("options-cache") SyncCacheApi cache) {
        super(cache);
    }
}

@KarelCemus
Copy link
Owner

KarelCemus commented Jan 5, 2018

Hmm, you've hit a tricky place I didn't know about (or didn't notice earlier). You are using @NamedCache annotation while I designed play-redis to use @Named annotation.

That is the reason why this does not work but I'm thinking about the compatibility with, e.g., ehcache because use of the different annotation breaks the interchangeability of the implementation.

@tech-sam
Copy link

tech-sam commented Jan 5, 2018

Thanks Karel , truly appreciate your contribution towards community !!

@KarelCemus
Copy link
Owner

I'm glad it helped. I'm probably gonna deprecate @Named annotation in 2.1.0 and introduce NamedCache to be consistent with play framework and remove it completely in 2.2.0 or so.

@tech-sam
Copy link

tech-sam commented Jan 9, 2018

@KarelCemus , Is there any way to override the implementation of DefaultSyncCacheApi class . The reason behind it I want to use play-redis in multi-tenant environment so while setting the key , I want to add some unique id with each and every key and this key is different for every client.
cache.set(key + 'unique-Id' , item); So whenever somebody will call cache.set or cache.get it should come to my implemented class instead of DefaultSyncCacheApi.
Thanking you in anticipation

@KarelCemus
Copy link
Owner

Well, that's not much common requirement so it is not directly supported by play-redis. The binding is quite hard-coded in the module. Right now, I see only two ways:

  1. Replace the RedisCacheModule by your own implementation changing the binding. This is very simple but involves a lot of code duplication through copy-paste
  2. The other solution is cleaner but more complicated. Imagine having @Named("redis") a vanilla redis cache, i.e., without any of your modifications. Then you can create your own instance of CacheApi accepting the vanilla implementation as a parameter. This instance would modify the keys and delegate requests to the internal vanilla instance. Finally, you can register this instance as, e.g., @Named("tenant-friendly") CacheApi. This requires much more efforts that is for sure. But it keeps the vanilla instance, introduces new tenant-friendly cache so it can be used where necessary and finally does not involve any code duplication.

Does this answer your question?

@KarelCemus
Copy link
Owner

Hey all, I started a new project with play-redis samples. Could you review them and give me some feedback if something is missing or there is anything else you'd appreciate?

Thanks

@KarelCemus
Copy link
Owner

I'm closing the issue

  • deprecated @Named for caches in 2.1.0
  • started a new project with samples including EhCache

If there is still anything unaddressed, please, let me know

@tech-sam
Copy link

@KarelCemus , Definitely I would like to be part of new project . It is good if we can provide dynamic prefix provider also for named cache .

@KarelCemus
Copy link
Owner

@ImportSumit Open a new issue, please, and propose how it should work. I'm not sure how to tailor the prefixed considering the prefix might be request-specific, user-specific, etc., i.e., context-specific. The question is how to access the context in the prefixer. I'm not sure that the context-unaware prefixer is worth implementing as there is not much added value.

@aledeulo
Copy link

aledeulo commented Mar 24, 2021

Hi @KarelCemus, I'm new with Play, and I need exactly this, Ehcache and Redis cache working togheter. You has mentioned about an example that uses this approach, but I wasn't able to find it, at least not for Java. Could you please let me know where that is? Thank you so much!

@aledeulo
Copy link

Hi @KarelCemus, following the Scala sample was very easy to transport it into Java code, I have the full integration working without any issues at the moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants