-
Notifications
You must be signed in to change notification settings - Fork 423
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Start moving filesystems as an initial implementation.
- Loading branch information
Showing
11 changed files
with
274 additions
and
93 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
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
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
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
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
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
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,45 @@ | ||
defmodule Livebook.Storage do | ||
@moduledoc """ | ||
Behaviour defining an interface for storing arbitrary data in | ||
[Entity-Attribute-Value](https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model) fashion. | ||
""" | ||
|
||
@type namespace :: atom() | ||
@type entity_id :: binary() | ||
@type attribute :: atom() | ||
@type value :: binary() | ||
@type timestamp :: non_neg_integer() | ||
|
||
@type entity :: %{required(:id) => entity_id(), optional(attribute()) => value()} | ||
|
||
@doc """ | ||
Returns a map identified by `entity_id` in `namespace`. | ||
fetch(:filesystem, "rand-id") | ||
#=> {:ok, %{id: "rand-id", type: "s3", bucket_url: "/...", secret: "abc", access_key: "xyz"}} | ||
""" | ||
@callback fetch(namespace(), entity_id()) :: {:ok, entity()} | :error | ||
|
||
@doc """ | ||
Returns all values in namespace. | ||
all(:filesystem) | ||
[%{id: "rand-id", type: "s3", bucket_url: "/...", secret: "abc", access_key: "xyz"}] | ||
""" | ||
@callback all(namespace()) :: [entity()] | ||
|
||
@doc """ | ||
Inserts given list of attribute-value paris to a entity belonging to specified namespace. | ||
""" | ||
@callback insert(namespace(), entity_id(), [{attribute(), value()}]) :: :ok | ||
|
||
@doc """ | ||
Deletes an entity of given id from given namespace. | ||
""" | ||
@callback delete(namespace(), entity_id()) :: :ok | ||
|
||
@spec current() :: module() | ||
def current(), do: Application.fetch_env!(:livebook, :storage) | ||
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,100 @@ | ||
defmodule Livebook.Storage.Ets do | ||
@moduledoc """ | ||
Ets implementation of `Livebook.Storage` behaviour. | ||
The module is supposed to be started just once as it | ||
is responsible for managing a named ets table. | ||
`insert` and `delete` operations are supposed to be called using a GenServer | ||
while all the lookups can be performed by directly accessing the named table. | ||
""" | ||
@behaviour Livebook.Storage | ||
|
||
@table_name __MODULE__ | ||
|
||
use GenServer | ||
|
||
@impl Livebook.Storage | ||
def fetch(namespace, entity_id) do | ||
@table_name | ||
|> :ets.lookup({namespace, entity_id}) | ||
|> case do | ||
[] -> | ||
:error | ||
|
||
entries -> | ||
entries | ||
|> Enum.map(fn {_key, attr, val, _timestamp} -> {attr, val} end) | ||
|> Map.new() | ||
|> Map.put(:id, entity_id) | ||
|> then(&{:ok, &1}) | ||
end | ||
end | ||
|
||
@impl Livebook.Storage | ||
def all(namespace) do | ||
@table_name | ||
|> :ets.match({{namespace, :"$1"}, :"$2", :"$3", :_}) | ||
|> Enum.group_by( | ||
fn [entity_id, _attr, _val] -> entity_id end, | ||
fn [_id, attr, val] -> {attr, val} end | ||
) | ||
|> Enum.map(fn {entity_id, attributes} -> | ||
attributes | ||
|> Map.new() | ||
|> Map.put(:id, entity_id) | ||
end) | ||
end | ||
|
||
@impl Livebook.Storage | ||
def insert(namespace, entity_id, attributes) do | ||
GenServer.call(__MODULE__, {:insert, namespace, entity_id, attributes}) | ||
end | ||
|
||
@impl Livebook.Storage | ||
def delete(namespace, entity_id) do | ||
GenServer.call(__MODULE__, {:delete, namespace, entity_id}) | ||
end | ||
|
||
@spec start_link(keyword()) :: GenServer.on_start() | ||
def start_link(opts) do | ||
GenServer.start_link(__MODULE__, opts, name: __MODULE__) | ||
end | ||
|
||
@impl GenServer | ||
def init(_opts) do | ||
table = :ets.new(@table_name, [:named_table, :protected, :duplicate_bag]) | ||
|
||
{:ok, %{table: table}} | ||
end | ||
|
||
@impl GenServer | ||
def handle_call({:insert, namespace, entity_id, attributes}, _from, %{table: table} = state) do | ||
match_head = {{namespace, entity_id}, :"$1", :_, :_} | ||
|
||
guards = | ||
Enum.map(attributes, fn {key, _val} -> | ||
{:==, :"$1", key} | ||
end) | ||
|
||
:ets.select_delete(table, [{match_head, guards, [true]}]) | ||
|
||
timestamp = System.os_time(:millisecond) | ||
|
||
attributes = | ||
Enum.map(attributes, fn {attr, val} -> | ||
{{namespace, entity_id}, attr, val, timestamp} | ||
end) | ||
|
||
:ets.insert(table, attributes) | ||
|
||
{:reply, :ok, state} | ||
end | ||
|
||
@impl GenServer | ||
def handle_call({:delete, namespace, entity_id}, _from, %{table: table} = state) do | ||
:ets.delete(table, {namespace, entity_id}) | ||
|
||
{:reply, :ok, state} | ||
end | ||
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
Oops, something went wrong.