···11defmodule Hobbes.HybridKV do
22- alias Hobbes.{HybridKV, MemKV}
22+ alias Hobbes.{HybridKV, MemKV, RangeForest}
33+ alias Hobbes.RangeForest.RangeTree
34 alias Hobbes.KV.FlatKV
45 alias Hobbes.Structs.RangeResult
5666- @enforce_keys [:mem_kv, :storage_kv]
77+ @enforce_keys [:mem_kv, :storage_kv, :deleted_forest]
78 defstruct @enforce_keys
89910 def new do
1010- %__MODULE__{
1111+ %HybridKV{
1112 mem_kv: MemKV.new(),
1213 storage_kv: FlatKV.new(),
1414+ deleted_forest: RangeForest.new(),
1315 }
1416 end
15171616- @spec put(%HybridKV{}, non_neg_integer, binary, binary) :: :ok
1818+ @spec put(%HybridKV{}, non_neg_integer, binary, binary) :: %HybridKV{}
1719 def put(%HybridKV{} = kv, version, key, value)
1820 when is_integer(version) and version >= 0 and is_binary(key) and is_binary(value) do
1921 :ok = MemKV.put(kv.mem_kv, version, key, value)
2222+2323+ case RangeForest.split_at(kv.deleted_forest, version, key) do
2424+ {:updated, deleted_forest} -> %HybridKV{kv | deleted_forest: deleted_forest}
2525+ :noop -> kv
2626+ end
2027 end
21282229 @spec delete(%HybridKV{}, non_neg_integer, binary) :: :ok
···2431 :ok = MemKV.delete(kv.mem_kv, version, key)
2532 end
26333434+ @spec delete_range(%HybridKV{}, non_neg_integer, binary, binary) :: %HybridKV{}
3535+ def delete_range(%HybridKV{} = kv, version, start_key, end_key) do
3636+ %HybridKV{kv | deleted_forest: RangeForest.add_range(kv.deleted_forest, version, start_key, end_key)}
3737+ end
3838+2739 @spec get(%HybridKV{}, non_neg_integer, binary) :: binary | nil
2840 def get(%HybridKV{} = kv, version, key)
2941 when is_integer(version) and version >= 0 and is_binary(key) do
3030- case MemKV.get(kv.mem_kv, version, key) do
4242+ floor_version =
4343+ case RangeForest.tree_at(kv.deleted_forest, version) |> RangeTree.intersect_key(0, key) do
4444+ {_sk, _ek, v} -> v + 1
4545+ nil -> 0
4646+ end
4747+4848+ case MemKV.get(kv.mem_kv, version, floor_version, key) do
3149 nil -> FlatKV.get(kv.storage_kv, key)
3250 :deleted -> nil
3351 value -> value
···219237 %{
220238 mem_kv: MemKV.dump(kv.mem_kv),
221239 storage_kv: FlatKV.dump(kv.storage_kv),
240240+ deleted_forest: RangeForest.dump(kv.deleted_forest),
222241 }
223242 end
224243end
+11
lib/kv/test_kv.ex
···3838 %TestKV{kv | maps: Map.put(kv.maps, version, updated), version: version}
3939 end
40404141+ def delete_range(%TestKV{} = kv, version, start_key, end_key) do
4242+ if version < kv.version, do: raise "TestKV cannot write at old versions!"
4343+4444+ updated =
4545+ Map.fetch!(kv.maps, kv.version)
4646+ |> Enum.reject(fn {k, _v} -> k >= start_key and k < end_key end)
4747+ |> Map.new()
4848+4949+ %TestKV{kv | maps: Map.put(kv.maps, version, updated), version: version}
5050+ end
5151+4152 defp read_version_for(%TestKV{maps: maps}, version) do
4253 # Newest version which is <= `version`
4354 maps
+4-4
lib/mem_kv.ex
···2626 :ok
2727 end
28282929- @spec get(:ets.table, non_neg_integer, binary) :: binary | :deleted | nil
3030- def get(table, version, key)
2929+ @spec get(:ets.table, non_neg_integer, non_neg_integer, binary) :: binary | :deleted | nil
3030+ def get(table, version, floor_version, key)
3131 when is_integer(version) and version >= 0 and is_binary(key) do
3232 case :ets.prev(table, {key, version + 1}) do
3333- {^key, _version} = k ->
3434- [{^k, value}] = :ets.lookup(table, k)
3333+ {^key, version} = full_key when version >= floor_version ->
3434+ [{^full_key, value}] = :ets.lookup(table, full_key)
3535 value
36363737 _ ->
+1-1
lib/meta_store.ex
···8080 end
81818282 defp get(kv, version, key) do
8383- MemKV.get(kv, version, @special_prefix <> key)
8383+ MemKV.get(kv, version, 0, @special_prefix <> key)
8484 end
85858686 defp scan(kv, version, start_key, end_key, opts \\ []) do
+8-1
lib/range_forest.ex
···8282 def split_at(tree, version, key) do
8383 next = key <> "\x00"
8484 case :gb_trees.smaller(next, tree) do
8585- {sk, {ek, ^version}} ->
8585+ {^key, {ek, ^version}} ->
8686+ tree = :gb_trees.delete(key, tree)
8787+ case ek do
8888+ ^next -> {:updated, tree}
8989+ ek -> {:updated, :gb_trees.insert(next, {ek, version}, tree)}
9090+ end
9191+9292+ {sk, {ek, ^version}} when ek > key ->
8693 tree = :gb_trees.update(sk, {key, version}, tree)
8794 case ek do
8895 ^next -> {:updated, tree}
+30-10
test/hybrid_kv_test.exs
···22222323 alias Verifier
24242525- @enforce_keys [:hybrid_kv, :test_kv, :version, :durable_version]
2525+ @enforce_keys [:hybrid_kv, :test_kv, :version, :durable_version, :seed]
2626 defstruct @enforce_keys
27272828- def new do
2828+ def new(seed) do
2929+ :rand.seed(:exsss, seed)
3030+2931 %Verifier{
3032 hybrid_kv: HybridKV.new(),
3133 test_kv: TestKV.new(),
3234 version: 1,
3335 durable_version: 0,
3636+ seed: seed,
3437 }
3538 end
3639···4245 perform(random_op(), verifier)
4346 rescue
4447 e in [ExUnit.AssertionError] ->
4545- e = Map.update!(e, :message, &(&1 <> " (at op #{i})"))
4848+ e = Map.update!(e, :message, &(&1 <> " (at op #{i}, seed=#{inspect(verifier.seed)})"))
4649 reraise e, __STACKTRACE__
4750 end
4851 end)
···5659 ], limit: :infinity
5760 end
58615959- @ops [:put, :delete, :get, :scan]
6262+ @ops [:put, :delete, :delete_range, :get]
6063 defp random_op do
6164 case Enum.random(1..100) do
6262- 1 -> :flush
6565+ #1 -> :flush
6366 _ -> Enum.random(@ops)
6467 end
6568 end
···7174 key = rand_hash()
7275 value = rand_hash()
73767474- :ok = HybridKV.put(hkv, version, key, value)
7777+ hkv = HybridKV.put(hkv, version, key, value)
7578 tkv = TestKV.put(tkv, version, key, value)
76797780 %Verifier{verifier | hybrid_kv: hkv, test_kv: tkv}
···8992 %Verifier{verifier | hybrid_kv: hkv, test_kv: tkv}
9093 end
91949595+ defp perform(:delete_range, %Verifier{hybrid_kv: hkv, test_kv: tkv} = verifier) do
9696+ verifier = inc_version(verifier)
9797+9898+ version = verifier.version
9999+ start_key = rand_hash()
100100+ end_key = rand_hash()
101101+ {start_key, end_key} =
102102+ cond do
103103+ start_key < end_key -> {start_key, end_key}
104104+ start_key > end_key -> {end_key, start_key}
105105+ start_key == end_key -> {start_key, start_key <> "\x00"}
106106+ end
107107+108108+ hkv = HybridKV.delete_range(hkv, version, start_key, end_key)
109109+ tkv = TestKV.delete_range(tkv, version, start_key, end_key)
110110+111111+ %Verifier{verifier | hybrid_kv: hkv, test_kv: tkv}
112112+ end
113113+92114 defp perform(:get, %Verifier{hybrid_kv: hkv, test_kv: tkv} = verifier) do
93115 read_version = rand_read_version(verifier)
94116···166188 describe "verify HybridKV" do
167189 @tag :hkv_verify
168190 test "operations" do
169169- :rand.seed(:exsss, {100, 101, 102})
170170- Verifier.new()
191191+ Verifier.new({101, 101, 102})
171192 |> Verifier.run(1000)
172193 end
173194···176197 test "operations multi (slow)" do
177198 # 300 verification tests (takes about 1.3s)
178199 Enum.map(1..300, fn s ->
179179- :rand.seed(:exsss, {100 + s, 101, 102})
180180- Verifier.new()
200200+ Verifier.new({100 + s, 101, 102})
181201 |> Verifier.run(1000)
182202 end)
183203 end