this repo has no description
2
fork

Configure Feed

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

Set max epoch on tables instead of inserting tombstones (much simpler)

garrison 61042f5d b785329c

+47 -35
+47 -35
lib/xks/manifest.ex
··· 79 79 {fk_key, fk_version} = first_key 80 80 {lk_key, lk_version} = last_key 81 81 82 - id = next_table_id(manifest) 83 - :ets.insert(manifest, {{level, fk_key, fk_version, epoch}, {lk_key, lk_version, block_index, block_checksum, id}}) 82 + epoch_max = :infinity 83 + :ets.insert(manifest, {{level, fk_key, fk_version, epoch}, {lk_key, lk_version, block_index, block_checksum, epoch_max}}) 84 84 :ok 85 85 end 86 86 87 + @spec delete_table(t, non_neg_integer, Table.t) :: :ok 88 + def delete_table(manifest, epoch, %{manifest_key: manifest_key}) do 89 + case :ets.lookup(manifest, manifest_key) do 90 + [{_key, {lk_key, lk_version, block_index, block_checksum, :infinity}}] -> 91 + # Mark table as deleted at `epoch` by updating the table's `epoch_max` 92 + new_value = {lk_key, lk_version, block_index, block_checksum, epoch} 93 + :ets.insert(manifest, {manifest_key, new_value}) 94 + :ok 95 + 96 + [] -> 97 + raise "Table not found" 98 + end 99 + end 100 + 87 101 def level_start_sentinel(level), do: {level, "", 0, -1} 88 102 # TODO: standardize last key of keyspace 89 103 def level_end_sentinel(level), do: {level, "\xFF\xFF\xFF\xFF", :infinity, :infinity} ··· 95 109 # E.g. if we passed epoch=10 we could get {level, key, version + 1, 9} when we want to get {level, key, version, some_epoch} 96 110 prev_key = {level, key, version + 1, 0} 97 111 98 - do_seek_prev_table(manifest, epoch, level, prev_key, false) 112 + do_seek_prev_table(manifest, epoch, level, prev_key) 99 113 end 100 114 101 - defp do_seek_prev_table(manifest, epoch, level, prev_key, prev_tombstone?) do 115 + defp do_seek_prev_table(manifest, epoch, level, prev_key) do 102 116 case :ets.prev_lookup(manifest, prev_key) do 103 - {{^level, _k, _ver, _ep} = key, _obj} when prev_tombstone? -> 104 - # The previous entry we saw (which is *next* in the keyspace) was a tombstone, 105 - # so this entry has been deleted at `epoch` 106 - do_seek_prev_table(manifest, epoch, level, key, false) 107 - 108 - {{^level, _k, _ver, ep} = key, _obj} when ep > epoch -> 117 + {{^level, _k, _ver, ep} = key, [{_key, {_, _, _, _, ep_max}}]} when epoch < ep or epoch >= ep_max -> 109 118 # This entry is not visible at `epoch` 110 - do_seek_prev_table(manifest, epoch, level, key, false) 111 - 112 - {{^level, _k, _ver, _ep} = key, [{_key, :tombstone}]} -> 113 - # This entry is visible and is a tombstone, 114 - # so we set the prev_tombstone? flag to cancel out the next (previous in the keyspace) entry 115 - do_seek_prev_table(manifest, epoch, level, key, true) 119 + do_seek_prev_table(manifest, epoch, level, key) 116 120 117 121 {{^level, _sk_key, _sk_ver, _ep} = key, [{_key, value}]} -> 118 - # This entry is visible 122 + # This is the first visible entry we have seen 119 123 {:ok, table_from_kv(key, value)} 120 124 121 125 _ -> 122 - # Hit the start of the level without finding a table 126 + # Hit the start of the level without finding a visible entry 123 127 :error 124 128 end 125 129 end 126 130 127 131 @spec seek_next_table(t, non_neg_integer, non_neg_integer, binary, non_neg_integer) :: {:ok, Table.t} | :error 128 132 def seek_next_table(manifest, epoch, level, key, version) do 129 - prev_key = 130 - case seek_prev_table(manifest, epoch, level, key, version) do 131 - {:ok, %{manifest_key: key}} -> key 132 - :error -> level_start_sentinel(level) 133 - end 133 + # We first seek backwards to see if there is a table containing `{key, version}` 134 + # 135 + # We have to look back because tables are keyed by `start_key` 136 + # If tables were keyed by `end_key` then we would have to look forward in `seek_prev_table` 137 + # (i.e. it's mirrored one way or the other) 138 + case seek_prev_table(manifest, epoch, level, key, version) do 139 + {:ok, %Table{} = table} -> 140 + seek_key = {key, version} 141 + case seek_key >= table.start_key and seek_key < table.end_key do 142 + # We have already found the table containing the key 143 + true -> {:ok, table} 144 + # There was no table containing the key, so we return the *next* table 145 + # (because this is `seek_next_table`) 146 + false -> next_table(manifest, epoch, level, table) 147 + end 134 148 135 - next_table(manifest, epoch, level, %{manifest_key: prev_key}) 149 + :error -> 150 + # There was no table containing the key *or any previous table*, 151 + # so we return the first table in the level 152 + # (Note that if the level is empty next_table() will also return :error) 153 + next_table(manifest, epoch, level, %{manifest_key: level_start_sentinel(level)}) 154 + end 136 155 end 137 156 138 157 @spec next_table(t, non_neg_integer, non_neg_integer, map) :: {:ok, Table.t} | :error ··· 142 161 143 162 defp do_next_table(manifest, epoch, level, prev_key) do 144 163 case :ets.next_lookup(manifest, prev_key) do 145 - {{^level, _k, _ver, ep} = key, _obj} when ep > epoch -> 164 + {{^level, _k, _ver, ep} = key, [{_key, {_, _, _, _, ep_max}}]} when epoch < ep or epoch >= ep_max -> 146 165 # This entry is not visible at `epoch` 147 166 do_next_table(manifest, epoch, level, key) 148 167 149 - {{^level, sk_key, sk_ver, _ep} = key, [{_key, value}]} -> 150 - case :ets.next_lookup(manifest, key) do 151 - {{^level, ^sk_key, ^sk_ver, _ep} = next_key, [{_key, :tombstone}]} -> 152 - # The entry was deleted, skip past the tombstone 153 - do_next_table(manifest, epoch, level, next_key) 154 - 155 - _ -> 156 - # The next entry is *not* a tombstone, so this entry is the next table visible at `epoch` 157 - {:ok, table_from_kv(key, value)} 158 - end 168 + {{^level, _sk_key, _sk_ver, _ep} = key, [{_key, value}]} -> 169 + # This is the first visible entry we have seen 170 + {:ok, table_from_kv(key, value)} 159 171 160 172 _ -> 161 173 # Hit the end of the level without finding a table