GithubHelp home page GithubHelp logo

cream_ex's Introduction

Cache. Rules. Everything. Around. Me.

A Dalli compatible memcached client.

It uses the same consistent hashing algorithm to connect to a cluster of memcached servers.

Table of contents

  1. Features
  2. Installation
  3. Quickstart
  4. Connecting to a cluster
  5. Using modules
  6. Memcachex options
  7. Memcachex API
  8. Ruby compatibility
  9. Supervision
  10. Instrumentation
  11. Documentation
  12. Running the tests
  13. TODO

Features

  • connect to a "cluster" of memcached servers
  • compatible with Ruby's Dallie gem (same consistent hashing algorithm)
  • fetch with anonymous function
  • multi set
  • multi get
  • multi fetch
  • built in pooling via poolboy
  • complete supervision trees
  • fully documented
  • instrumentation with the Instrumentation package.

Installation

In your mix.exs file...

def deps do
  [
    {:cream, ">= 0.1.0"}
  ]
end

Quickstart

# Connects to localhost:11211 with worker pool of size 10
{:ok, cluster} = Cream.Cluster.start_link

# Single set and get
Cream.Cluster.set(cluster, {"name", "Callie"})
Cream.Cluster.get(cluster, "name")
# => "Callie"

# Single fetch
Cream.Cluster.fetch cluster, "some", fn ->
  "thing"
end
# => "thing"

# Multi set / multi get with list
Cream.Cluster.set(cluster, [{"name", "Callie"}, {"buddy", "Chris"}])
Cream.Cluster.get(cluster, ["name", "buddy"])
# => %{"name" => "Callie", "buddy" => "Chris"}

# Multi set / multi get with map
Cream.Cluster.set(cluster, %{"species" => "canine", "gender" => "female"})
Cream.Cluster.get(cluster, ["species", "gender"])
# => %{"species" => "canine", "gender" => "female"}

# Multi fetch
Cream.Cluster.fetch cluster, ["foo", "bar", "baz"], fn missing_keys ->
  Enum.map(missing_keys, &String.reverse/1)
end
# => %{"foo" => "oof", "bar" => "rab", "baz" => "zab"}

Connecting to a cluster

{:ok, cluster} = Cream.Cluster.start_link servers: ["cache01:11211", "cache02:11211"]

Using modules

You can use modules to configure clusters, exactly like how Ecto repos work.

# In config/*.exs

config :my_app, MyCluster,
  servers: ["cache01:11211", "cache02:11211"],
  pool: 5

# Elsewhere

defmodule MyCluster do
  use Cream.Cluster, otp_app: :my_app

  # Optional callback to do runtime configuration.
  def init(config) do
    # config = Keyword.put(config, :pool, System.get_env("POOL_SIZE"))
    {:ok, config}
  end
end

MyCluster.start_link
MyCluster.get("foo")

Memcachex options

Cream uses Memcachex for individual connections to the cluster. You can pass options to Memcachex via Cream.Cluster.start_link/1:

Cream.Cluster.start_link(
  servers: ["localhost:11211"],
  memcachex: [ttl: 3600, namespace: "foo"]
)

Or if using modules:

use Mix.Config

config :my_app, MyCluster,
  servers: ["localhost:11211"],
  memcachex: [ttl: 3600, namespace: "foo"]

MyCluster.start_link

Any option you can pass to Memcache.start_link, you can pass via the :memcachex option for Cream.Cluster.start_link.

Memcachex API

Cream.Cluster's API is very small: get, set, fetch, flush. It may expand in the future, but for now, you can access Memcachex's API directly if you need.

Cream will still provide worker pooling and key routing, even when using Memcachex's API directly.

If you are using a single key, things are pretty straight forward...

results = Cream.Cluster.with_conn cluster, key, fn conn ->
  Memcache.get(conn, key)
end

It gets a bit more complex with a list of keys...

results = Cream.Cluster.with_conn cluster, keys, fn conn, keys ->
  Memcache.multi_get(conn, keys)
end
# results will be a list of whatever was returned by the invocations of the given function.

Basically, Cream will group keys by memcached server and then call the provided function for each group and return a list of the results of each call.

Ruby compatibility

By default, Dalli uses Marshal to encode values stored in memcached, which Elixir can't understand. So you have to change the serializer to something like JSON:

Ruby

client = Dalli::Client.new(
  ["host01:11211", "host2:11211"],
  serializer: JSON,
)
client.set("foo", 100)

Elixir

{:ok, cluster} = Cream.Cluster.start_link(
  servers: ["host01:11211", "host2:11211"],
  memcachex: [coder: Memcache.Coder.JSON]
)
Cream.Cluster.get(cluster, "foo")
# => "100"

So now both Ruby and Elixir will read/write to the memcached cluster in JSON, but still beware! There are some differences between how Ruby and Elixir parse JSON. For example, if you write an integer with Ruby, Ruby will read an integer, but Elixir will read a string.

Supervision

Everything is supervised, even the supervisors, so it really does make a supervision tree.

A "cluster" is really a poolboy pool of cluster supervisors. A cluster supervisor supervises each Memcache.Connection process and one Cream.Cluster.Worker process.

No pids are stored anywhere, but instead processes are tracked via Elixir's Registry module.

The results of Cream.Cluster.start_link and MyClusterModule.start_link can be inserted into your application's supervision tree.

Instrumentation

Cream uses Instrumentation for... well, instrumentation. It's default logging is hooked into this package. You can do your own logging (or instrumentation) very easily.

config :my_app, MyCluster,
  log: false

Instrumentation.subscribe "cream", fn tag, payload ->
  Logger.debug("cream.#{tag} took #{payload[:duration]} ms")
end

Running the tests

Test dependencies:

  • Docker
  • Docker Compose
  • Ruby
  • Bundler

Then run...

bundle install
docker-compose up -d
mix test

# Stop and clean up containers
docker-compose stop
docker-compose rm

TODO

  • Server weights
  • Parallel memcached requests

cream_ex's People

Contributors

cjbottaro avatar deconstrained avatar

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.