this repo has no description
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add multiversion get and fuzz

garrison 52e19601 48c46636

+86 -33
+12 -31
lib/btree/fuzz/model_fuzz.ex
··· 46 46 47 47 # TODO: remove 48 48 :ok = BTree.Writer.apply_batch(state.btree, [{:write, "", ""}]) 49 - simple_kv = SimpleKV.apply_batch(state.simple_kv, [{:write, "", ""}]) 49 + simple_kv = SimpleKV.apply_versioned_batch(state.simple_kv, 0, [{:write, "", ""}]) 50 50 state = %{state | simple_kv: simple_kv} 51 51 52 52 Enum.reduce(1..opts.iterations, state, fn iteration, state -> ··· 76 76 _ -> 77 77 case Enum.random(1..2) do 78 78 1 -> :apply_batch 79 - 2 -> :scan 79 + 2 -> :get 80 80 end 81 81 end 82 82 end ··· 87 87 version: version, 88 88 } = state 89 89 90 - floor_version = version - 5_000_000 90 + floor_version = max(version - 5_000_000, 0) 91 91 92 92 :ok = BTree.Writer.apply_logged_batches_to_storage(btree, floor_version) 93 93 :ok = BTree.Writer.flush_storage_buffer(btree) ··· 110 110 111 111 :ok = BTree.Writer.apply_versioned_batch(btree, version, mutations) 112 112 :ok = BTree.Writer.log_batch(btree, version, mutations) 113 - #simple_kv = SimpleKV.apply_batch(simple_kv, mutations) 113 + simple_kv = SimpleKV.apply_versioned_batch(simple_kv, version, mutations) 114 114 115 115 %{state | 116 116 simple_kv: simple_kv, 117 117 } 118 118 end 119 119 120 - defp execute(:scan, %State{} = state) do 120 + defp execute(:get, %State{} = state) do 121 121 %{ 122 122 simple_kv: simple_kv, 123 123 btree: btree, 124 + version: version, 124 125 opts: %{ 125 126 key_bits: opt_key_bits, 126 127 }, 127 128 } = state 128 129 129 - start_key = make_key(opt_key_bits) 130 - count = Enum.random(1..100) 131 - reverse? = Enum.random([true, false]) 132 - #dbg {start_key, count, reverse?} 133 - #dbg SimpleKV.dump(simple_kv), limit: :infinity 130 + read_version = Enum.random(max(version - 5000, 0)..version) 131 + # TODO: too sparse, literally always nil 132 + key = make_key(opt_key_bits) 134 133 135 - # TODO 136 - _btree_pairs = scan(btree, start_key, count, reverse?) 137 - _skv_pairs = SimpleKV.scan(simple_kv, start_key, count, reverse?) 138 - #assert btree_pairs == skv_pairs 134 + btree_result = Iterator.get(btree, read_version, key) 135 + simple_result = SimpleKV.get(simple_kv, read_version, key) 136 + assert btree_result == simple_result 139 137 140 138 state 141 - end 142 - 143 - defp scan(btree, start_key, count, reverse?) do 144 - it = Iterator.new(btree, start_key, reverse?) 145 - case it.current_pair do 146 - :infinity -> [] 147 - pair -> do_scan(it, [pair], count - 1) 148 - end 149 - end 150 - 151 - defp do_scan(_it, acc, 0), do: Enum.reverse(acc) 152 - defp do_scan(it, acc, count) do 153 - it = Iterator.next(it) 154 - case it.current_pair do 155 - :infinity -> Enum.reverse(acc) 156 - pair -> do_scan(it, [pair | acc], count - 1) 157 - end 158 139 end 159 140 160 141 defp make_batch(opts) do
+50 -2
lib/btree/fuzz/simple_kv.ex
··· 1 1 defmodule Hobbes.BTree.Fuzz.SimpleKV do 2 - @type t :: map 2 + import ExUnit.Assertions, only: [assert: 1] 3 + 4 + @type t :: {[non_neg_integer], %{non_neg_integer => map}} 3 5 4 6 @spec new :: t 5 7 def new do 6 - %{} 8 + {[0], %{0 => %{}}} 9 + end 10 + 11 + @spec get(t, non_neg_integer, binary) :: binary | nil 12 + def get(simple_kv, version, key) do 13 + {versions, kvs_by_version} = simple_kv 14 + 15 + kv = Map.fetch!(kvs_by_version, find_version(versions, version)) 16 + Map.get(kv, key) 17 + end 18 + 19 + # Finds the largest version <= `target_version` 20 + # Invariants: 21 + # - versions list is always in descending order 22 + # - versions list always ends with 0 23 + defp find_version([ver | versions_rest], target_version) do 24 + case ver <= target_version do 25 + true -> ver 26 + false -> find_version(versions_rest, target_version) 27 + end 28 + end 29 + 30 + @spec apply_versioned_batch(t, non_neg_integer, list) :: t 31 + def apply_versioned_batch(simple_kv, version, mutations) do 32 + {[last_version | _] = versions, kvs_by_version} = simple_kv 33 + # TODO: > 34 + assert version >= last_version 35 + 36 + kv = do_writes(mutations, Map.fetch!(kvs_by_version, last_version)) 37 + {[version | versions], Map.put(kvs_by_version, version, kv)} 38 + end 39 + 40 + defp do_writes([], acc), do: acc 41 + defp do_writes([mut | muts_rest], acc) do 42 + case mut do 43 + {:write, k, v} -> 44 + acc = Map.put(acc, k, v) 45 + do_writes(muts_rest, acc) 46 + {:clear, k} -> 47 + acc = Map.delete(acc, k) 48 + do_writes(muts_rest, acc) 49 + {:clear_range, sk, ek} -> 50 + acc = Map.reject(acc, fn {k, _v} -> 51 + k >= sk and k < ek 52 + end) 53 + do_writes(muts_rest, acc) 54 + end 7 55 end 8 56 9 57 @spec apply_batch(t, list) :: t
+24
lib/btree/iterator.ex
··· 5 5 import ExUnit.Assertions, only: [assert: 1] 6 6 import Hobbes.BTree.Utils 7 7 8 + @dialyzer :no_improper_lists 9 + 8 10 @type t :: %__MODULE__{ 9 11 } 10 12 @enforce_keys [ ··· 16 18 :reverse?, 17 19 ] 18 20 defstruct @enforce_keys 21 + 22 + @spec get(BTree.t, non_neg_integer, binary) :: binary | nil 23 + def get(%BTree{} = btree, version, key) do 24 + %{ 25 + versioned_tree: versioned_tree, 26 + } = btree 27 + 28 + case :ets.prev_lookup(versioned_tree, [key | version + 1]) do 29 + {_key, [{[^key | _version], value}]} -> 30 + case value do 31 + :tombstone -> nil 32 + value -> value 33 + end 34 + 35 + _ -> 36 + it = new(btree, key, false) 37 + case it.current_pair do 38 + {^key, value} -> value 39 + _ -> nil 40 + end 41 + end 42 + end 19 43 20 44 @spec new(BTree.t, binary, boolean) :: t 21 45 def new(%BTree{} = btree, start_key, reverse?) do