aboutsummaryrefslogtreecommitdiff
path: root/shard/lib/data/store.ex
blob: ce5618c29bf78cfc2282e09d20a557cb610301f1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
defprotocol SData.Page do
  @moduledoc"""
  Protocol to be implemented by objects that are used as data pages
  in a pagestore and that may reference other data pages by their hash.
  """

  @fallback_to_any true

  @doc"""
  Get hashes of all pages referenced by this page.
  """
  def refs(page)
end

defimpl SData.Page, for: Any do
  def refs(_page), do: []
end


defprotocol SData.PageStore do
  @moduledoc"""
  Protocol to be implemented for page stores to allow their
  manipulation.

  This protocol may also be implemented by store proxies that track
  operations and implement different synchronization or caching mechanisms.

  A page store is an object that stores data pages (arbitrary Erlang terms) and
  identifies them by their hash. Dependencies may exist between pages, in which
  case they form a Merkle DAG.
  """

  @doc"""
  Put a page. Argument is the content of the page, returns the
  hash that the store has associated to it.

  Returns {hash, store}
  """
  def put(store, page)

  @doc"""
  Get a page referenced by its hash.

  Returns page
  """
  def get(store, hash)

  @doc"""
  Copy to the store a page and all its references from the other store.
  In the case of pages on the network in a distributed store, this may
  be lazy.

  Returns store
  """
  def copy(store, other_store, hash)

  @doc"""
  Free a page referenced by its hash, marking it as no longer needed.

  Returns store
  """
  def free(store, hash)
end


defmodule SData.LocalStore do
  @moduledoc"""
  A page store that saves all pages locally in RAM. The store is basically a dictionnary
  of hash to term mappings, which is mutated by put operations.
  """
  defstruct [:pages] 

  @doc"""
  Create empty LocalStore.
  """
  def new() do
    %SData.LocalStore{ pages: %{} }
  end
end

defimpl SData.PageStore, for: SData.LocalStore do
  def put(store, page) do
    hash = SData.term_hash page
    store = %{ store | pages: Map.put(store.pages, hash, page) }
    { hash, store }
  end

  def get(store, hash) do
    store.pages[hash]
  end

  def copy(store, other_store, hash) do
    page = SData.PageStore.get(other_store, hash)
    refs = SData.Page.refs(page)
    store = Enum.reduce(refs, store, fn x, acc -> copy(acc, other_store, x) end)
    %{ store | pages: Map.put(store.pages, hash, page) }
  end

  def free(store, hash) do
    %{ store | pages: Map.delete(store.pages, hash) }
  end
end