feat: add mergePool and score to eviction
This commit is contained in:
1 parent
d3fe77badb
commit
bcd5503f80
1 file changed
+102
@@ -155,3 +155,105 @@ defmodule Excache.Eviction do
|
||||
{sampled, randState} = config.sampler.(remaining, config.sampleSize, config.randState)
|
||||
config = %{config | randState: randState}
|
||||
|
||||
if random?(config.policy) do
|
||||
{hd(sampled), pool, config}
|
||||
else
|
||||
metadata = config.metaFun.(Enum.map(sampled, & &1.key), config.now)
|
||||
|
||||
scored =
|
||||
Enum.map(sampled, fn candidate ->
|
||||
{score(config.policy, candidate, Map.get(metadata, candidate.key), config.now),
|
||||
candidate}
|
||||
end)
|
||||
|
||||
[{_score, victim} | rest] = mergePool(pool, scored, config.poolSize)
|
||||
{victim, rest, config}
|
||||
end
|
||||
end
|
||||
|
||||
defp mergePool(pool, scored, poolSize) do
|
||||
(scored ++ pool)
|
||||
|> Enum.uniq_by(fn {_score, candidate} -> candidate.key end)
|
||||
|> Enum.sort_by(fn {candidateScore, _candidate} -> candidateScore end, :desc)
|
||||
|> Enum.take(poolSize)
|
||||
end
|
||||
|
||||
defp score(policy, _candidate, meta, now) when policy in @lruPolicies, do: lruScore(meta, now)
|
||||
|
||||
defp score(policy, _candidate, meta, _now) when policy in @lfuPolicies,
|
||||
do: @lfuMaxCounter - counterOf(meta)
|
||||
|
||||
defp score(:volatileTtl, candidate, _meta, _now), do: -candidate.expiresAt
|
||||
|
||||
defp lruScore(nil, _now), do: {1, 0}
|
||||
defp lruScore(meta, now), do: {0, max(now - meta.lastAccess, 0)}
|
||||
|
||||
defp counterOf(nil), do: 0
|
||||
defp counterOf(meta), do: meta.counter
|
||||
|
||||
defp eligible?(_candidate, :noeviction), do: false
|
||||
|
||||
defp eligible?(candidate, policy) do
|
||||
if volatile?(policy), do: candidate.expiresAt != nil, else: true
|
||||
end
|
||||
|
||||
defp emptyReason(policy) do
|
||||
if volatile?(policy), do: :noVolatileKeys, else: :noCandidates
|
||||
end
|
||||
|
||||
defp configure(opts) do
|
||||
%{
|
||||
policy: Keyword.fetch!(opts, :policy),
|
||||
maxmemory: Keyword.get(opts, :maxmemory, 0),
|
||||
now: Keyword.get_lazy(opts, :now, fn -> System.system_time(:millisecond) end),
|
||||
sampleSize: Keyword.get(opts, :sampleSize, @defaultSampleSize),
|
||||
poolSize: Keyword.get(opts, :poolSize, @defaultPoolSize),
|
||||
sampler: Keyword.get(opts, :sampler, &sampleRandom/3),
|
||||
randState: :rand.seed_s(:exsss, Keyword.get(opts, :seed, @defaultSeed)),
|
||||
tracker: Keyword.get(opts, :tracker),
|
||||
metaFun: metaFun(opts)
|
||||
}
|
||||
end
|
||||
|
||||
defp metaFun(opts) do
|
||||
cond do
|
||||
Keyword.has_key?(opts, :metadata) ->
|
||||
metadata = Keyword.fetch!(opts, :metadata)
|
||||
fn keys, _now -> Map.take(metadata, keys) end
|
||||
|
||||
Keyword.has_key?(opts, :tracker) ->
|
||||
tracker = Keyword.fetch!(opts, :tracker)
|
||||
fn keys, now -> Tracker.metadata(tracker, keys, now) end
|
||||
|
||||
true ->
|
||||
fn _keys, _now -> %{} end
|
||||
end
|
||||
end
|
||||
|
||||
defp sampleRandom(pool, count, randState) when length(pool) <= count, do: {pool, randState}
|
||||
|
||||
defp sampleRandom(pool, count, randState) do
|
||||
{taken, _rest, _size, nextState} =
|
||||
Enum.reduce(1..count, {[], pool, length(pool), randState}, fn _index,
|
||||
{acc, left, size, state} ->
|
||||
{index, drawn} = :rand.uniform_s(size, state)
|
||||
{picked, rest} = List.pop_at(left, index - 1)
|
||||
{[picked | acc], rest, size - 1, drawn}
|
||||
end)
|
||||
|
||||
{Enum.reverse(taken), nextState}
|
||||
end
|
||||
|
||||
defp report(config, acc, reason) do
|
||||
%{
|
||||
reason: reason,
|
||||
policy: config.policy,
|
||||
maxmemory: config.maxmemory,
|
||||
usedMemory: acc.used,
|
||||
freedBytes: acc.freed,
|
||||
evicted: Enum.reverse(acc.evicted),
|
||||
evictedCount: length(acc.evicted),
|
||||
passes: acc.passes
|
||||
}
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user