Giter Site home page Giter Site logo

karelcemus / play-redis Goto Github PK

View Code? Open in Web Editor NEW
165.0 14.0 42.0 1.17 MB

Play framework 2 cache plugin as an adapter to redis-server

License: Mozilla Public License 2.0

Scala 100.00%
scala redis-cache play-framework reactive asynchronous play-cache play-redis playframework

play-redis's Introduction

Redis Cache module for Play framework

This version supports Play framework 3.0.x with JDK 11 and Scala 2.13 and Scala 3.
For previous versions see older releases.

Travis CI: Status Coverage Status Maven Central

About the Project

Play framework is delivered with SyncCacheApi and AsyncCacheApi. This module provides implementation of a cache over Redis server, i.e., key/value storage.

Besides the compatibility with all Play's cache APIs, it introduces more evolved API providing lots of handful operations. Besides the basic methods such as get, set and remove, it provides more convenient methods such as expire, exists, invalidate and much more.

The implementation builds on the top of Pekko actor system, it is completely non-blocking and asynchronous under the hood, though it also provides blocking APIs to ease the use. Furthermore, the library supports several configuration providers to let you easily use play-redis on localhost, Heroku, as well as on your premise.

Features

Provided APIs

This library delivers a single module with following implementations of the API. While the core of the framework is fully non-blocking, most of the provided facades are only blocking wrappers.

Trait Language Blocking Features
1. play.api.cache.redis.CacheApi Scala blocking advanced
2. play.api.cache.redis.CacheAsyncApi Scala non-blocking advanced
3. play.cache.redis.AsyncCacheApi Java non-blocking advanced
4. play.api.cache.SyncCacheApi Scala blocking basic
5. play.api.cache.AsyncCacheApi Scala non-blocking basic
6. play.cache.SyncCacheApi Java blocking basic
7. play.cache.AsyncCacheApi Java non-blocking basic

First, the CacheAsyncApi provides extended API to work with Redis and enables non-blocking connection providing results through scala.concurrent.Future. Second, the CacheApi is a thin blocking wrapper around the asynchronous implementation. Third, there are other implementations supporting contemporary versions of the CacheApis bundled within Play framework. Finally, play-redis also supports Java version of the API, though it is primarily designed for and more efficient with Scala.

Documentation and Getting Started

The full documentation is in the doc directory. The documentation for a particular version is under the particular tag in the Git history or you can use shortcuts in the table below.

To use this module:

  1. Add this library into your project and expose APIs
  2. See the configuration options
  3. Browse examples of use

If you come from older version, you might check the Migration Guide

Samples

To ease the initial learning, there are several sample projects intended to demonstrate the most common configurations. Feel free to study, copy or fork them to better understand the play-redis use.

  1. Getting Started is a very basic example showing the minimal configuration required to use the redis cache

  2. Named Caches is the advanced example with custom recovery policy and multiple named caches.

  3. EhCache and Redis shows a combination of both caching provides used at once. While the EhCache is bound to unqualified APIs, the Redis cache uses named APIs.

How to add the module into the project

To your SBT build.sbt add the following lines:

// enable Play cache API (based on your Play version)
libraryDependencies += play.sbt.PlayImport.cacheApi
// include play-redis library
libraryDependencies += "com.github.karelcemus" %% "play-redis" % "5.0.0"

Compatibility matrix

play framework play-redis documentation
3.0.x 5.0.0 see here
2.9.x 3.0.0 see here
2.8.x 2.7.0 see here
2.7.x 2.5.1 see here
2.6.x 2.3.0 see here (Migration Guide)
2.5.x 1.4.2 see here
2.4.x 1.0.0 see here
2.3.x 0.2.1 see here

Contribution

If you encounter any issue, have a feature request, or just like this library, please feel free to report it or contact me.

Changelog

For the list of changes and migration guide please see the Changelog.

Caveat

The library does not enable the redis module by default. It is to avoid conflict with Play's default EhCache and let the user define when use Redis. This allows you to use EhCache in your dev environment and Redis in production. You can also combine the modules using named caches.

play-redis's People

Contributors

alexmihailov avatar easel avatar karelcemus avatar pangwa avatar pcejrowski avatar solicode avatar tanacasino avatar tmscer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

play-redis's Issues

repository not available

Hi,

The Brando repository is not avaible when I run the compilation. Any idea ?

My application.conf :

resolvers += "chrisdinn" at "http://chrisdinn.github.io/releases/"
libraryDependencies += "com.digital-achiever" %% "brando" % "3.0.2"
libraryDependencies += "com.github.karelcemus" %% "play-redis" % "0.3-SNAPSHOT"

SBT error :

sbt.ResolveException: unresolved dependency: com.github.karelcemus#play-redis_2.11;0.3-SNAPSHOT: not found

Supporting scala version 2.10.x

I just spent a couple of hours with figuring out some compilation error, which turned out play-redis doesn't release binary compiled with scala 2.10.x. Is there any specific reason for not supporting 2.10.x though 2.10.x is still actively developed and used in common?

Flexible Configuration By Mode

I would like to easily switch my caching approach based on whether I'm in Play's dev, test, or prod mode.

In a perfect world, I would like the option to use Ehcache in dev and Redis in test and prod.

If I can't do that, then I would like to have configuration be local in dev (after installing Redis locally) and heroku in test and prod. Because the configuration is defined statically in application.conf, I think the only way I can toggle between these is with environment variables. I think it would be better if I could take a more dependency-injection-based approach where I bind the appropriate Configuration implementation according to mode. I guess I could do that with none, but it seems to me that it's better to remove static configuration from application.conf entirely and instead have a default binding to the local instance of Configuration and allow other bindings to override the default as needed.

Does this make sense? Does the current codebase offer this sort of flexibility already? I might be missing it.

Thanks.

Do not wait for confirmation

There are various scenarios where it is not necessary to wait for actual invocation of the redis command. Among these cases belong set and remove operations especially within getOrElse and getOrFuture operations.

This issue proposes a new methods or policy enabling us to execute these methods with the expectation of a positive result and without waiting for a confirmation.

This issue raised from #94.

Excessive logging with akka.pattern.AskTimeoutException

When requests to redis timeouts due to, e.g., disconnection, there is unnecessary excessive logging. Such exceptions should be handled with more condensed log messages

10:40:54 [error] play.api.cache.redis SETEX command failed for key 'mykey'.
akka.pattern.AskTimeoutException: Ask timed out on [Actor[akka://application/user/$b#1419961090]] after [1000 ms]. Sender[null] sent message of type "brando.Request".
    at akka.pattern.PromiseActorRef$$anonfun$1.apply$mcV$sp(AskSupport.scala:604)
    at akka.actor.Scheduler$$anon$4.run(Scheduler.scala:122)
    at scala.concurrent.Future$InternalCallbackExecutor$.unbatchedExecute(Future.scala:601)
    at scala.concurrent.BatchingExecutor$class.execute(BatchingExecutor.scala:109)
    at scala.concurrent.Future$InternalCallbackExecutor$.execute(Future.scala:599)
    at akka.actor.LightArrayRevolverScheduler$TaskHolder.executeTask(LightArrayRevolverScheduler.scala:322)
    at akka.actor.LightArrayRevolverScheduler$$anon$4.executeBucket$1(LightArrayRevolverScheduler.scala:274)
    at akka.actor.LightArrayRevolverScheduler$$anon$4.nextTick(LightArrayRevolverScheduler.scala:278)
    at akka.actor.LightArrayRevolverScheduler$$anon$4.run(LightArrayRevolverScheduler.scala:230)
    at java.lang.Thread.run(Thread.java:745)

java.util.concurrent.TimeoutException: Futures timed out after [10 seconds]

Hi folks,
I'm hoping to get some help. Maybe it's an issue, maybe I'm missing something.

I'm using the sync version of the API and I get the timeout errors quite often (10 seconds timeout). Not always.
At the beginning I thought it was just slow code calculating the entry on cache.getOrElse. But, then I even saw a timeout on removing a key. Why we would get a timeout here at all??

We run on AWS and use a single ElasticCache node. Before the cache was in memory and was just fine.

I know Brando is used under the hood. I'm wondering If I should change something on the akka system.

P/S: I can't provide a test for this as it's not deterministic. It's a simple use case with a small single server with a single redis node.

Thanks for your help

Platform-independent configuration

Just a small suggestion. Your classes and traits named Heroku* maybe should be renamed to something more generic like with Url* or Env*. Heroku is the cloud provider I am using, but those who deploy to other providers like Microsoft Azure may also have to deal with the same issue (configuration via url). This way it will be more intuitive that the code isn't platform-dependent but rather env var dependent. Of course this is your decision.

Well that's true but I'm not sure it would work in other envs, I haven't read their specification. There might be different env variable, no authentication etc. I considered the current impl a bit safer. But I'll think about your suggestion and possibly provide a bit more generic solution.


Goal is to provide more generic and configuration providers not to be platform-specific. It would be best to have configurable ENV variable, optional authentication and possibly more.

Unordered sets

Redis has a support for unordered sets, which preserves the set property: value uniqueness.

This issue proposes implementation of related commands.

TimeoutException with Latest Build

With the latest build of play-redis, I had an error on Heroku that emanated from play-redis. It could be an issue with Brando, but I am curious why that Await call happens:

java.util.concurrent.TimeoutException: Futures timed out after [1 second]
at scala.concurrent.impl.Promise$DefaultPromise.ready(Promise.scala:219)
at scala.concurrent.impl.Promise$DefaultPromise.result(Promise.scala:223)
at scala.concurrent.Await$$anonfun$result$1.apply(package.scala:190)
at akka.dispatch.MonitorableThreadFactory$AkkaForkJoinWorkerThread$$anon$3.block(ThreadPoolBuilder.scala:169)
at scala.concurrent.forkjoin.ForkJoinPool.managedBlock(ForkJoinPool.java:3640)
at akka.dispatch.MonitorableThreadFactory$AkkaForkJoinWorkerThread.blockOn(ThreadPoolBuilder.scala:167)
at scala.concurrent.Await$.result(package.scala:190)
at play.api.cache.redis.Implicits$Synchronizer.sync(Implicits.scala:38)
at play.api.cache.redis.Builders$SynchronousBuilder$.toResult(Builders.scala:50)
at play.api.cache.redis.Implicits$class.build(Implicits.scala:43)
at play.api.cache.redis.RedisCache.build(RedisCache.scala:17)
at play.api.cache.redis.RedisCache.get(RedisCache.scala:32)
at play.api.cache.redis.SyncRedis.get(Cache.scala:12)
at play.api.cache.CachedBuilder$$anonfun$build$1$$anonfun$apply$7.apply(Cached.scala:197)
at play.api.cache.CachedBuilder$$anonfun$build$1$$anonfun$apply$7.apply(Cached.scala:197)
at scala.Option.orElse(Option.scala:289)
at play.api.cache.CachedBuilder$$anonfun$build$1.apply(Cached.scala:195)
at play.api.cache.CachedBuilder$$anonfun$build$1.apply(Cached.scala:184)
at play.api.mvc.EssentialAction$$anon$2.apply(Action.scala:55)
at play.api.mvc.EssentialAction$$anon$2.apply(Action.scala:54)
at play.core.routing.TaggingInvoker$$anon$2.apply(HandlerInvoker.scala:37)
at play.core.routing.TaggingInvoker$$anon$2.apply(HandlerInvoker.scala:36)
at play.filters.gzip.GzipFilter$$anon$1.apply(GzipFilter.scala:54)
at play.filters.gzip.GzipFilter$$anon$1.apply(GzipFilter.scala:51)
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$9.apply(PlayDefaultUpstreamHandler.scala:155)

Here is what I saw in the logs:

a.a.OneForOneStrategy - queue empty 
java.util.NoSuchElementException: queue empty 
    at scala.collection.mutable.Queue.dequeue(Queue.scala:66) ~[org.scala-lang.scala-library-2.11.7.jar:na] 
    at brando.Connection$$anonfun$receive$1$$anonfun$applyOrElse$4.apply(Connection.scala:91) ~[com.digital-achiever.brando_2.11-3.0.3.jar:3.0.3]  
    at brando.Connection$$anonfun$receive$1$$anonfun$applyOrElse$4.apply(Connection.scala:78) ~[com.digital-achiever.brando_2.11-3.0.3.jar:3.0.3]  
    at brando.ReplyParser$class.parseReply(ReplyParser.scala:145) ~[com.digital-achiever.brando_2.11-3.0.3.jar:3.0.3] 
    at brando.Connection.parseReply(Connection.scala:27) ~[com.digital-achiever.brando_2.11-3.0.3.jar:3.0.3] 
    at brando.Connection$$anonfun$receive$1.applyOrElse(Connection.scala:78) ~[com.digital-achiever.brando_2.11-3.0.3.jar:3.0.3] 
    at akka.actor.Actor$class.aroundReceive(Actor.scala:467) ~[com.typesafe.akka.akka-actor_2.11-2.3.13.jar:na] 
    at brando.Connection.aroundReceive(Connection.scala:27) ~[com.digital-achiever.brando_2.11-3.0.3.jar:3.0.3] 
    at akka.actor.ActorCell.receiveMessage(ActorCell.scala:516) [com.typesafe.akka.akka-actor_2.11-2.3.13.jar:na] 
    at akka.actor.ActorCell.invoke(ActorCell.scala:487) [com.typesafe.akka.akka-actor_2.11-2.3.13.jar:na]

Any ideas what the issue is? Perhaps a bug in the latest version of Brando?

Configuration on Heroku

This is not an "issue," but I was curious if you could provide guidance on how to configure play-redis on Heroku since you only get the full Redis URL rather than the components. I imagine I have to use a provider (I am using Scaldi for dependency injection) to parse the Redis URL, configure play-redis with the components, and inject it as the the CacheApi implementation. Is there anything special I need to do or any suggestions you'd have for doing this while still having play-redis remain the CacheApi implementation?

Also, is it possible to use CacheApi backed by play-redis in production but CacheApi backed by Ehcache in development?

Thanks.

Release 1.2.0

  • upgrade README to 1.2.0
  • simplify motivation in README
  • clickable older releases
  • clean up Checking operation result
  • describe Kryo serializer
  • update changelog

Docs

Hi Karel,

Play 2.5.8
Scala 2.11.8

I was just upgrading to your latest milestone and got the following error:

/development/code/jonjack/fyb-server/build.sbt:31: error: not found: value PlayImport
libraryDependencies += PlayImport.cache exclude("net.sf.ehcache", "ehcache-core")
                       ^
[error] Type error in expression

I tried both the following versions of PlayScala 2.5.0 and 2.5.8 and same error.

It is fixed by specifying package for PlayImport which for me was play.sbt.PlayImport:

So following is successful.

libraryDependencies += play.sbt.PlayImport.cache exclude("net.sf.ehcache", "ehcache-core")

Anyway, maybe I am missing some config. which meant I had to specify fully qualified name. But just in case others have the same problem as me I thought I would let you know in case you thought it useful to update your README. Maybe my config. is not the norm though.

Keep up the great work.
Jon

Counters

Counters are considered to be one of basic features of cache implementations. See play#5048

Is Disabling Ehcache Module OK?

Quick question.

Just saw this in the Play documentation:

"Note: If you are working on a library, it is highly discouraged to use play.modules.disabled to disable modules, as it can lead to undetermistic results when modules are loaded by the application (see this issue for reasons on why you should not touch play.modules.disabled). In fact, play.modules.disabled is intended for end users to be able to override what modules are enabled by default."

I know you have unit tests supporting your approach, but I just wanted to bring this to your attention in case you hadn't seen it. It is buried in Play's documentation.

Thanks.

Support for Redis HGET, HSET and HDEL

hi @KarelCemus

Does play-redis support HGET, HSET and HDEL right now?

I searched hget as keyword in your repo, and I found only RedisConnectorImpl.scala contains the hget in hashGet().

I tried to inject RedisConnector(see below), but in the runtime, I got error:

[ExecutionFailedException: Execution of 'HGET mayclass ....' for key 'HGET' failed]

My question is does it support HGET right now, or I need inject another class?

Or even, I need wait for the implementation, like the implementation of MGET in #81, #82 ?

Thanks

import play.api.cache.redis.connector.RedisConnector
import com.google.inject.Inject

class MyClass @Inject() (
  cache: RedisConnector
)(implicit ec: ExecutionContext) {

  def get(key: String): Future[Option[String]] = {
    cache.hashGet("myclass", key)
  }
}

Scala 2.12

Compile the lib against scala 2.12 by default and cross-compile against 2.11. This issue requires all lib dependencies compiled against 2.12.

  • play framework #6691
  • scredis #23
  • specs2

Subsequent tasks

  • Update header in README, add Scala 2.12 support note
  • Update current version in README
  • Add scala versions into versions table in README
  • Release 1.5.0 - will be done in another issue
  • ensure publishing scredis for Scala 2.12 (it is not published yet)

Hash objects

Redis supports key-value maps known as hashes. They are efficient and allows Object-Oriented Development. Under one key, we may store many field-value pairs

Configuration provider

There are various scenarios accepting Redis connection settings from different sources (#15). First, in development environment we might be interested in configuring Redis through application.conf file. Second, we might deploy our application on Heroku providing Redis connection link in REDIS_URL environment variable. To cover all these cases we design a ConfigurationProvider trait which we may implement for all environments we want. Out of the box we provide configuration file and Heroku providers.

Redis connector

  • design advanced non-blocking cache API
  • integrate non-blocking redis-server connector Brando

Expiration as "cache due"

Instead of cache in seconds support due date for both java.util.Date and org.joda.DateTime

There might be some support in scala.concurrent.duration

APPEND key value

APPEND command

If key already exists and is a string, this command appends the value at the end of the string. If key does not exist it is created and set as an empty string, so APPEND will be similar to SET in this special case.

Play 2.6.x

Update to Play 2.6.x, more specifically support its new cacheApi module introducing sync and async API.

Inaccurate and outdated README

There are several issues in README

  • provide more detailed description of all 4 APIs
  • set no longer returns Future[ Try[ String ] ]

Brando Transitive Dependency

README should note that you have to add

resolvers += "chrisdinn" at "http://chrisdinn.github.io/releases/"

to build.sbt so that the Brando transitive dependency can be found.

Async API throws an exception when serialization fails

AsyncCache throws an actual exception instead of returning failing Future as is expected.
It happens when serialization fails at:

	at play.api.cache.redis.exception.package$.serializationFailed(package.scala:18)
	at play.api.cache.redis.connector.RedisConnectorImpl$$anonfun$play$api$cache$redis$connector$RedisConnectorImpl$$encode$1.applyOrElse(RedisConnectorImpl.scala:87)
	at play.api.cache.redis.connector.RedisConnectorImpl$$anonfun$play$api$cache$redis$connector$RedisConnectorImpl$$encode$1.applyOrElse(RedisConnectorImpl.scala:86)
	at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
	at scala.util.Failure$$anonfun$recover$1.apply(Try.scala:216)
	at scala.util.Try$.apply(Try.scala:192)
	at scala.util.Failure.recover(Try.scala:216)
	at play.api.cache.redis.connector.RedisConnectorImpl.play$api$cache$redis$connector$RedisConnectorImpl$$encode(RedisConnectorImpl.scala:86)
	at play.api.cache.redis.connector.RedisConnectorImpl.set(RedisConnectorImpl.scala:82)
	at play.api.cache.redis.impl.RedisCache$$anonfun$set$1.apply(RedisCache.scala:23)
	at play.api.cache.redis.impl.RedisCache$$anonfun$set$1.apply(RedisCache.scala:23)
	at play.api.cache.redis.impl.Builders$AsynchronousBuilder$.toResult(Builders.scala:33)
	at play.api.cache.redis.impl.Builders$AsynchronousBuilder$.toResult(Builders.scala:28)
	at play.api.cache.redis.impl.Implicits$RecoveryUnitFuture.recoverWithUnit(Implicits.scala:39)
	at play.api.cache.redis.impl.RedisCache.set(RedisCache.scala:23)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.