play-redis

repository·master·Indexed 41 days ago

https://github.com/karelcemus/play-redis

A non-blocking, asynchronous Redis-based cache implementation for the Play Framework built on Pekko actors. It provides standard Play cache APIs and advanced Redis-specific operations, supporting standalone, cluster, sentinel, and master-slave configurations. Version 5.4.0 supports Play Framework 3.0.x with JDK 11 and Scala 2.13 or 3.

Tokens
13.9K
Snippets
38
Records
63
Agent score
51%

What's inside play-redis

  1. Overview of play-redis APIs

    master

    The library provides multiple API implementations depending on your language (Scala or Java) and whether you require blocking or non-blocking operations.

    • Advanced APIs: Provide extended Redis-specific operations (e.g., expire, exists, invalidate, collection support).
    • Basic APIs: Implement the standard Play cacheApi interfaces.

    While the core implementation is built on Pekko actors and is fully non-blocking, many provided facades are blocking wrappers for ease of use.

    |    | 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    |
  2. How named caches and instances work

    master

    The module supports multiple named cache instances via the play.cache.redis.instances node. Each instance is a map where the key is the cache name.

    Key behaviors:

    • Defining any instances under instances automatically disables and ignores the default cache configuration defined at the root play.cache.redis level.
    • Instances inherit configuration from the root play.cache.redis but can override specific properties (like host or source).
    • To use a named cache in Scala, use the @NamedCache("name") qualifier.

    Example of a default cache and a custom named cluster cache:

    play.cache.redis {
      source: standalone
    
      instances {
        play {
          host:       localhost
          port:       6379
        }
        myNamedCache {
          source: cluster
          cluster: [
            { host: localhost, port: 6380 }
          ]
        }
      }
    }
    class MyController @Inject()( @NamedCache( "myNamedCache" ) local: CacheAsyncApi ) {
      // implementation
    }
  3. Understand Data Serialization limitations

    master

    The module provides native serialization for basic types (e.g., String, Int). For all other objects and collections, it uses JavaSerializer by default.

    Warning: JavaSerializer is considered inefficient for production use.

    Recommendation: Use the kryo serializer for better performance and smaller output. You can connect any serialization library through Pekko configuration.

  4. How RecoveryPolicy works

    master

    The RecoveryPolicy determines how the application behaves when a cache request fails. This is useful because cache failures should typically not break the main application flow.

    Available default policies:

    • log-and-default: Logs the error and returns a neutral value (simulating no cache in use). This is the default.
    • log-and-fail: Logs the error and re-throws the RedisException.
    • log-condensed-and-default: Same as log-and-default but with reduced logging.
    • log-condensed-and-fail: Same as log-and-fail but with reduced logging.
    • custom: Allows you to provide your own implementation of the RecoveryPolicy trait.

    Policies can be defined globally under play.cache.redis.recovery or overridden per instance under play.cache.redis.instances.instance-name.recovery.

  5. Handle operation results and errors

    master

    All operations in this module throw an exception if they fail. To check for errors, use the following patterns based on the API type:

    • Synchronous APIs: Use standard try-catch blocks as they throw exceptions directly.
    • Asynchronous APIs: They return a Future wrapping the result. Use .onFailure or .onComplete to handle potential exceptions.
  6. Using Redis and EhCache together

    master

    The library does not enable the Redis module by default to avoid conflicts with Play's default EhCache. This allows you to use EhCache in development and Redis in production, or to use both simultaneously by using named caches for Redis.

    For advanced configurations involving multiple caches, refer to the Named Caches sample.

  7. Migrate from play-redis 2.0.x to 2.1.x

    master

    Upgrading from 2.0.x to 2.1.x involves changes to the invocation policy, annotations, and configuration keys:

    Invocation Policy

    In 2.0.x, the invocation policy was passed as an implicit parameter. In 2.1.x, it is now a static configurable property within the instance configuration.

    Named Cache Annotations

    The @Named annotation used for binding named caches in 2.0.x is deprecated. Use @NamedCache instead to align with the Play framework implementation. Note that @Named will be removed in version 2.2.0.

    Configuration Key Renaming

    The timeout property has been renamed to sync-timeout to avoid ambiguity. The original timeout property is deprecated and will be removed in version 2.2.0.

  8. Configure Redis via Connection String

    master

    If you are running in an environment where the connection details are provided via an environment variable (like Heroku), set the source to connection-string. This allows you to pass a full Redis URL instead of defining individual host/port/password settings.

    Example for Heroku using the REDIS_URL environment variable:

    play.cache.redis {
      # enable connection-string mode
      source: connection-string
    
      # HOCON automatically injects the environmental variable
      connection-string: '${REDIS_URL}'
    }
  9. Migrate from play-redis 2.8.x to 3.0.x

    master

    When upgrading to version 3.0.x, note the following environment and dependency requirements:

    • Play Framework: Requires Play 2.9.0 or higher.
    • JDK: Minimum version is JDK 11.
    • Scala: Support for Scala 2.12 has been dropped. The project is compiled against Scala 2.13.12 to ensure compatibility with Scala 3.
  10. Add play-redis to your SBT project

    master

    To use the library, add the Play cache API dependency (matching your Play version) and the play-redis dependency to your build.sbt file.

    // 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.4.0"
  11. Install play-redis via SBT

    master

    To add play-redis to your Play Framework project, add the following dependencies to your build.sbt file. Ensure you include the appropriate cacheApi based on your Play version.

    Note: This version (5.4.0) supports Play framework 3.0.x with JDK 11 and Scala 2.13 or Scala 3.

    // 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.4.0"
  12. Use Compile-time Dependency Injection with RedisCacheComponents

    master

    For compile-time DI, mix play.api.cache.redis.RedisCacheComponents into your BuiltInComponentsFromContext subclass.

    This provides a cacheApi method that accepts a cache name (a string) and returns a RedisCaches instance. The RedisCaches instance encapsulates the available APIs for that specific cache. You can then expose these APIs (such as async) as needed in your application components.

    // 'play' is the name of the named cache
    // (play is default name of the default cache)
    //
    // the 'play' literal is implicitly converted into
    // the instance but has to be configured in 'application.conf'
    val playCache: RedisCaches = cacheApi( "play" )
    
    // expose `play.api.cache.redis.CacheAsyncApi`
    val asynchronousRedis = playCache.async