-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
637 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,25 @@ | ||
# The directory Mix will write compiled artifacts to. | ||
/_build | ||
|
||
# If you run "mix test --cover", coverage assets end up here. | ||
/cover | ||
|
||
# The directory Mix downloads your dependencies sources to. | ||
/deps | ||
|
||
# Where 3rd-party dependencies like ExDoc output generated docs. | ||
/doc | ||
|
||
# If the VM crashes, it generates a dump, let's ignore it too. | ||
erl_crash.dump | ||
|
||
# Also ignore archive artifacts (built via "mix archive.build"). | ||
*.ez | ||
|
||
# Others | ||
*.o | ||
*.beam | ||
*.plt | ||
erl_crash.dump | ||
.DS_Store | ||
._* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,86 @@ | ||
# kvx | ||
Simple in-memory KV store written in Elixir using `cabol/shards` | ||
# KVX | ||
|
||
This is a simple/basic [Elixir](http://elixir-lang.org/) in-memory Key/Value Store | ||
using [Shards](https://github.com/cabol/shards) – which is the default adapter. | ||
|
||
## Usage | ||
|
||
Add `kvx` to your Mix dependencies: | ||
|
||
```elixir | ||
defp deps do | ||
[{:kvx, "~> 0.1.0"}] | ||
end | ||
``` | ||
|
||
In an existing or new module: | ||
|
||
```elixir | ||
defmodule MyTestMod do | ||
use KVX.Bucket | ||
end | ||
``` | ||
|
||
Now let's play with `kvx`: | ||
|
||
```elixir | ||
> MyTestMod.new(:mybucket) | ||
:mybucket | ||
|
||
> MyTestMod.set(:mybucket, :k1, 1) | ||
:mybucket | ||
|
||
> MyTestMod.mset(:mybucket, k2: 2, k3: "3") | ||
:mybucket | ||
|
||
> MyTestMod.get(:mybucket, :k1) | ||
1 | ||
|
||
> MyTestMod.mget(:mybucket, [:k2, :k3]) | ||
[2, "3"] | ||
|
||
> MyTestMod.find_all(:mybucket) | ||
[k3: "3", k2: 2, k1: 1] | ||
|
||
> MyTestMod.delete(:mybucket, :k1) | ||
:mybucket | ||
|
||
> MyTestMod.get(:mybucket, :k1) | ||
nil | ||
|
||
> MyTestMod.flush!(:mybucket) | ||
:mybucket | ||
|
||
> MyTestMod.find_all(:mybucket) | ||
[] | ||
``` | ||
|
||
## Configuration | ||
|
||
Most of the configuration that goes into the `config` is specific to the adapter. | ||
But there are some common/shared options such as: `:adapter` and `:ttl`. E.g.: | ||
|
||
```elixir | ||
config :kvx, | ||
adapter: KVX.Bucket.Shards, | ||
ttl: 1 | ||
``` | ||
|
||
Now, in case of Shards adapter `KVX.Bucket.Shards`, it has some extra options | ||
like `:shards_mod`. E.g.: | ||
|
||
```elixir | ||
config :kvx, | ||
adapter: KVX.Bucket.Shards, | ||
ttl: 1, | ||
shards_mod: :shards | ||
``` | ||
|
||
In case of Shards adapter, run-time options when calling `new/2` function, are | ||
the same as `shards:new/2`. E.g.: | ||
|
||
```elixir | ||
MyModule.new(:mybucket, [n_shards: 4]) | ||
``` | ||
|
||
> **NOTE:** For more information check [KVX.Bucket.Shards](./lib/kvx/adapters/shards/bucket_shards.ex). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# This file is responsible for configuring your application | ||
# and its dependencies with the aid of the Mix.Config module. | ||
use Mix.Config | ||
|
||
# KVX config | ||
config :kvx, | ||
adapter: KVX.Bucket.Shards | ||
|
||
# Import environment specific config. | ||
import_config "#{Mix.env}.exs" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
use Mix.Config | ||
|
||
# KVX config | ||
config :kvx, | ||
adapter: KVX.Bucket.Shards, | ||
ttl: 300 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
use Mix.Config | ||
|
||
# KVX config | ||
config :kvx, | ||
adapter: KVX.Bucket.Shards, | ||
ttl: 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
defmodule KVX do | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
defmodule KVX.Bucket.Shards do | ||
@moduledoc """ | ||
Shards adapter. This is the default adapter supported by `KVX`. | ||
Shards adapter only works with `set` and `ordered_set` table types. | ||
Shards extra config options: | ||
* `:shards_mod` - internal Shards module to use. By default, `:shards` | ||
module is used, which is a wrapper on top of `:shards_local` and | ||
`:shards_dist`. | ||
Run-time options when calling `new/2` function, are the same as | ||
`shards:new/2`. For example: | ||
MyModule.new(:mybucket, [n_shards: 4]) | ||
For more information about `shards`: | ||
* [GitHub](https://github.com/cabol/shards) | ||
* [Blog Post](http://cabol.github.io/posts/2016/04/14/sharding-support-for-ets.html) | ||
""" | ||
|
||
@behaviour KVX.Bucket | ||
|
||
@shards (Application.get_env(:kvx, :shards_mod, :shards)) | ||
@default_ttl (Application.get_env(:kvx, :ttl, 3600)) | ||
|
||
require Ex2ms | ||
|
||
## Setup Commands | ||
|
||
def new(bucket, opts \\ []) do | ||
case Process.whereis(bucket) do | ||
nil -> new_bucket(bucket, opts) | ||
_ -> bucket | ||
end | ||
end | ||
|
||
defp new_bucket(bucket, opts) do | ||
{^bucket, _} = @shards.new(bucket, opts) | ||
bucket | ||
end | ||
|
||
## Storage Commands | ||
|
||
def add(bucket, key, value, ttl \\ @default_ttl) do | ||
case get(bucket, key) do | ||
nil -> set(bucket, key, value, ttl) | ||
_ -> raise KVX.ConflictError, key: key, value: value | ||
end | ||
end | ||
|
||
def set(bucket, key, value, ttl \\ @default_ttl) do | ||
ttl = if is_integer(ttl) do | ||
seconds_since_epoch(ttl) | ||
end | ||
true = @shards.insert(bucket, {key, value, ttl}) | ||
bucket | ||
end | ||
|
||
def mset(bucket, kv_pairs, ttl \\ @default_ttl) when is_list(kv_pairs) do | ||
kv_pairs |> Enum.each(fn({key, value}) -> | ||
^bucket = set(bucket, key, value, ttl) | ||
end) | ||
bucket | ||
end | ||
|
||
## Retrieval Commands | ||
|
||
def get(bucket, key) do | ||
case @shards.lookup(bucket, key) do | ||
[{^key, value, ttl}] -> | ||
case ttl > seconds_since_epoch(0) do | ||
true -> | ||
value | ||
_ -> | ||
true = @shards.delete(bucket, key) | ||
nil | ||
end | ||
_ -> | ||
nil | ||
end | ||
end | ||
|
||
def mget(bucket, keys) when is_list(keys) do | ||
for key <- keys do | ||
get(bucket, key) | ||
end | ||
end | ||
|
||
def find_all(bucket, query \\ nil) do | ||
do_find_all(bucket, query) | ||
end | ||
|
||
defp do_find_all(bucket, nil) do | ||
find_all(bucket, Ex2ms.fun do object -> object end) | ||
end | ||
defp do_find_all(bucket, query) do | ||
bucket | ||
|> @shards.select(query) | ||
|> Enum.reduce([], fn({k, v, ttl}, acc) -> | ||
case ttl > seconds_since_epoch(0) do | ||
true -> | ||
[{k, v} | acc] | ||
_ -> | ||
true = @shards.delete(bucket, k) | ||
acc | ||
end | ||
end) | ||
end | ||
|
||
## Cleanup functions | ||
|
||
def delete(bucket, key) do | ||
true = @shards.delete(bucket, key) | ||
bucket | ||
end | ||
|
||
def flush!(bucket) do | ||
true = @shards.delete_all_objects(bucket) | ||
bucket | ||
end | ||
|
||
## Private functions | ||
|
||
defp seconds_since_epoch(diff) do | ||
{mega, secs, _} = :os.timestamp() | ||
mega * 1000000 + secs + diff | ||
end | ||
end |
Oops, something went wrong.