README.md

# ShieldedCache

![ShieldedCache](https://github.com/SpotIM/shielded-cache/blob/master/assets/logo.png?raw=true)

ShieldedCache is a caching module for Elixir.

## What Makes It Special?
ShieldedCache enables a type of CDN Shielding on your cache that allows you to return stale results while simultaneously refreshing your data.
This allows you to always return data to your client incredibly fast after the first request is made, while still refreshing data in an async way to keep
subsequent requests fresh.

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `shielded_cache` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:shielded_cache, ">= 0.0.0"}
  ]
end
```

Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://spotim.hexdocs.pm). Once published, the docs can
be found at [https://spotim.hexdocs.pm/shielded_cache](https://spotim.hexdocs.pm/shielded_cache).

## Usage

To use the ShieldedCache, simply add `use ShieldedCache` along with the necessary options to the top of your module,
and define the `fetch/1` callback, which is used to fetch the data for the cache.

There is an additional `key/1` callback available, which lets you define what your key will be in the cache.
The default functionality for this is to use the `serialize!/1` function and serialize any key you give it so that it can be used as a key.

There are two options you must define: `cache_name` and `caching_module`, and four optional options, `ttl`, `compressed`, `fetch_task_timeout_seconds`, and `max_fetch_task_retries`.

`cache_name` is the identifier of your cache, and should be an atom (in the form of a module name, see the example below).

`caching_module` is the module used for cache storage. This module should conform to the `ShieldedCache.Cache` behaviour, which will enforce
the necessary functions for the cache, and what their return types should be.

`ttl` is the number of milliseconds until the values in the cache expire, which defaults to `10_000`.

`compressed` is a boolean value that defines whether or not the data stored in the cache will be compressed, which defaults to `false`.

`fetch_task_timeout_seconds` is the number of seconds until an async fetch task has a timeout, which defaults to `60`.

`max_fetch_task_retries` is the number of retries that an async fetch task will attempt before being cancelled, which defaults to `5`.

There are currently 6 caching modules built in to the `ShieldedCache` library: `ShieldedCache.Cache.LocalAgent`, `ShieldedCache.Cache.Redis`, `ShieldedCache.Cache.RedisCluster`, `ShieldedCache.Cache.RedixSingleton`, `ShieldedCache.Cache.Memcached`, and `ShieldedCache.Cache.MemcachedCluster`.

**Note: If using `ShieldedCache.Cache.RedixSingleton`, there is a third required option: `caching_module_name`. This defines the name of the `Redix` process that
you would like to use as your client to your Redis instance.**

### `ShieldedCache.Cache.LocalAgent`

This caching module stores your caching data in an Elixir Agent process supervised by the cache supervisor.

### `ShieldedCache.Cache.Redis`

This caching module initializes a pool of `Redix` clients for the specified cache,
and stores your caching data in Redis.

#### Configuration Options

There are two configuration options available here: `redis_pool_size`, which defaults to 1, and `redis_url`, which defaults to `redis://localhost:6379`.

To configure these, add the following to your `config.exs`:

```elixir
config :shielded_cache,
  redis_url: "<YOUR REDIS URL>",
  redis_pool_size: <YOUR REDIS POOL SIZE AS INTEGER>,
```

### `ShieldedCache.Cache.RedisCluster`

This caching module initializes a pool of `Redix` clients for the specified cache,
and stores your caching data in a Redis cluster (see [Redis Cluster](https://redis.io/topics/cluster-tutorial)).

#### Configuration Options

This caching module is built on top of the redix_cluster library, which was forked, modernized, fixed, and refactored recently by Spot.IM.

With this library, there are various configuration options available: `cluster_nodes`, which defaults to an empty list, `pool_size`, which defaults to 5, and `pool_max_overflow`, which defaults to 0.

To configure these, add the following to your `config.exs` (__Note the name of the application is `redix_cluster` and not `shielded_cache`__):

```elixir
config :redix_cluster,
  cluster_nodes: [
    %{host: "myredishost", port: 6379}
  ],
  pool_size: 5,
  pool_max_overflow: 0
```

### `ShieldedCache.Cache.RedixSingleton`

This caching module assumes that you have already created a `Redix` client, and utilizes it when making calls to the cache.

If using this caching module, you must also define a third option to your cache: `caching_module_name`. This defines the name of the `Redix` process that
you would like to use as your client to your Redis instance.

### `ShieldedCache.Cache.Memcached`

This caching module initializes a `Memcache` client for the specified cache,
and stores your caching data in Memcached.

#### Configuration Options

There are two configuration options available here: `memcached_hostname`, which defaults to `localhost`, and `memcached_port`, which defaults to `11211`.

To configure these, add the following to your `config.exs`:

```elixir
config :shielded_cache,
  memcached_hostname: "<YOUR MEMCACHED HOSTNAME>",
  memcached_port: <YOUR MEMCACHED PORT AS INTEGER>,
```

### `ShieldedCache.Cache.MemcachedCluster`

This caching module initializes a `Cream` client for the specified cache,
and stores your caching data in a Memcached Cluster.

#### Configuration Options

There is one configuration option available here: `memcached_cluster_servers`, which defaults to `[localhost:11211]`.

To configure this, add the following to your `config.exs`:

```elixir
config :shielded_cache,
  memcached_cluster_servers: "<YOUR MEMCACHED CLUSTER SERVERS AS A LIST OF HOSTNAMES AND PORTS>",
```

### Simple Caching Example

The following is a simple caching module that uses the built in `ShieldedCache.Cache.LocalAgent` caching module.

```elixir
defmodule ExampleShieldedCache do
  use ShieldedCache,
    cache_name: ExampleShieldedCache,
    caching_module: ShieldedCache.Cache.LocalAgent

  def fetch(cache_request) do
    {:ok, "WOW #{DateTime.utc_now()}"}
  end
end
```