test: cover Excache.EvictionTest
This commit is contained in:
1 parent
7d26219f61
commit
b65f902d27
1 file changed
+154
@@ -0,0 +1,154 @@
|
|||||||
|
defmodule Excache.EvictionTest do
|
||||||
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
|
alias Excache.Eviction
|
||||||
|
alias Excache.Eviction.Tracker
|
||||||
|
alias Excache.Memory
|
||||||
|
alias Excache.Store
|
||||||
|
|
||||||
|
@now 1_000_000
|
||||||
|
|
||||||
|
setup do
|
||||||
|
Store.flushdb()
|
||||||
|
on_exit(&Store.flushdb/0)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
test "policy names round trip through the Redis spelling" do
|
||||||
|
for policy <- Eviction.policies() do
|
||||||
|
assert {:ok, ^policy} = Eviction.parsePolicy(Eviction.policyName(policy))
|
||||||
|
end
|
||||||
|
|
||||||
|
assert Enum.sort(Enum.map(Eviction.policies(), &Eviction.policyName/1)) ==
|
||||||
|
Enum.sort(~w(
|
||||||
|
noeviction allkeys-lru allkeys-lfu allkeys-random
|
||||||
|
volatile-lru volatile-lfu volatile-ttl volatile-random
|
||||||
|
))
|
||||||
|
|
||||||
|
assert Eviction.parsePolicy("lru") == :error
|
||||||
|
assert Eviction.volatile?(:volatileTtl)
|
||||||
|
assert Eviction.allkeys?(:allkeysLfu)
|
||||||
|
assert Eviction.random?(:volatileRandom)
|
||||||
|
refute Eviction.volatile?(:allkeysLru)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "every policy picks a different victim from the same keyspace" do
|
||||||
|
opts = [now: @now, metadata: mixedMetadata(), sampleSize: 8, sampler: firstSampler()]
|
||||||
|
entries = mixedEntries()
|
||||||
|
|
||||||
|
assert Eviction.selectVictim(entries, [policy: :allkeysLru] ++ opts) == {:ok, "idle-all"}
|
||||||
|
assert Eviction.selectVictim(entries, [policy: :allkeysLfu] ++ opts) == {:ok, "cold-all"}
|
||||||
|
assert Eviction.selectVictim(entries, [policy: :allkeysRandom] ++ opts) == {:ok, "random-all"}
|
||||||
|
assert Eviction.selectVictim(entries, [policy: :volatileLru] ++ opts) == {:ok, "idle-ttl"}
|
||||||
|
assert Eviction.selectVictim(entries, [policy: :volatileLfu] ++ opts) == {:ok, "cold-ttl"}
|
||||||
|
assert Eviction.selectVictim(entries, [policy: :volatileTtl] ++ opts) == {:ok, "soon-ttl"}
|
||||||
|
|
||||||
|
assert Eviction.selectVictim(entries, [policy: :volatileRandom] ++ opts) ==
|
||||||
|
{:ok, "random-ttl"}
|
||||||
|
|
||||||
|
assert Eviction.selectVictim(entries, [policy: :noeviction] ++ opts) ==
|
||||||
|
{:error, :noCandidates}
|
||||||
|
|
||||||
|
victims =
|
||||||
|
for policy <- Eviction.policies() -- [:noeviction] do
|
||||||
|
{:ok, key} = Eviction.selectVictim(entries, [policy: policy] ++ opts)
|
||||||
|
key
|
||||||
|
end
|
||||||
|
|
||||||
|
assert length(Enum.uniq(victims)) == 7
|
||||||
|
end
|
||||||
|
|
||||||
|
test "an untracked key is evicted before any tracked one" do
|
||||||
|
entries = stringEntries(["tracked", "unseen"], "payload")
|
||||||
|
metadata = %{"tracked" => %{lastAccess: @now - 500_000, counter: 5}}
|
||||||
|
opts = [now: @now, metadata: metadata, sampleSize: 8, sampler: firstSampler()]
|
||||||
|
|
||||||
|
assert Eviction.selectVictim(entries, [policy: :allkeysLru] ++ opts) == {:ok, "unseen"}
|
||||||
|
assert Eviction.selectVictim(entries, [policy: :allkeysLfu] ++ opts) == {:ok, "unseen"}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "noeviction refuses to free anything and reports OOM" do
|
||||||
|
entries = stringEntries(~w(k1 k2 k3), "payload")
|
||||||
|
{table, keyspace} = fixtureKeyspace(entries)
|
||||||
|
|
||||||
|
assert {:error, report} =
|
||||||
|
Eviction.run(policy: :noeviction, maxmemory: 1, keyspace: keyspace, now: @now)
|
||||||
|
|
||||||
|
assert report.reason == :noeviction
|
||||||
|
assert report.evicted == []
|
||||||
|
assert report.evictedCount == 0
|
||||||
|
assert report.freedBytes == 0
|
||||||
|
assert report.usedMemory == Memory.usedMemory(entries)
|
||||||
|
assert :ets.info(table, :size) == 3
|
||||||
|
end
|
||||||
|
|
||||||
|
test "volatile policies leave a keyspace without any TTL untouched" do
|
||||||
|
entries = stringEntries(~w(k1 k2 k3), "payload")
|
||||||
|
{table, keyspace} = fixtureKeyspace(entries)
|
||||||
|
|
||||||
|
for policy <- [:volatileLru, :volatileLfu, :volatileTtl, :volatileRandom] do
|
||||||
|
assert {:error, report} =
|
||||||
|
Eviction.run(
|
||||||
|
policy: policy,
|
||||||
|
maxmemory: 1,
|
||||||
|
keyspace: keyspace,
|
||||||
|
now: @now,
|
||||||
|
metadata: %{}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert report.reason == :noVolatileKeys
|
||||||
|
assert report.evictedCount == 0
|
||||||
|
assert report.freedBytes == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
assert :ets.info(table, :size) == 3
|
||||||
|
end
|
||||||
|
|
||||||
|
test "maxmemory of zero means no limit at all" do
|
||||||
|
entries = stringEntries(~w(k1 k2 k3), "payload")
|
||||||
|
{table, keyspace} = fixtureKeyspace(entries)
|
||||||
|
|
||||||
|
assert {:ok, report} =
|
||||||
|
Eviction.run(policy: :allkeysRandom, maxmemory: 0, keyspace: keyspace, now: @now)
|
||||||
|
|
||||||
|
assert report.reason == :withinLimit
|
||||||
|
assert report.evicted == []
|
||||||
|
assert :ets.info(table, :size) == 3
|
||||||
|
end
|
||||||
|
|
||||||
|
test "the loop stops the moment usage drops under the limit" do
|
||||||
|
keys = for index <- 1..10, do: "k#{String.pad_leading("#{index}", 2, "0")}"
|
||||||
|
entries = stringEntries(keys, "payload")
|
||||||
|
perEntry = Memory.entryBytes(hd(entries))
|
||||||
|
{table, keyspace} = fixtureKeyspace(entries)
|
||||||
|
|
||||||
|
assert {:ok, report} =
|
||||||
|
Eviction.run(
|
||||||
|
policy: :allkeysRandom,
|
||||||
|
maxmemory: 7 * perEntry,
|
||||||
|
keyspace: keyspace,
|
||||||
|
now: @now,
|
||||||
|
seed: {2, 4, 8}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert report.reason == :evicted
|
||||||
|
assert report.evictedCount == 3
|
||||||
|
assert report.freedBytes == 3 * perEntry
|
||||||
|
assert report.usedMemory == 7 * perEntry
|
||||||
|
assert report.passes == 3
|
||||||
|
assert :ets.info(table, :size) == 7
|
||||||
|
assert Enum.all?(report.evicted, fn key -> :ets.lookup(table, key) == [] end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "the candidate pool carries a strong candidate between passes" do
|
||||||
|
entries = stringEntries(~w(k1 k2 k3 k4), "payload")
|
||||||
|
perEntry = Memory.entryBytes(hd(entries))
|
||||||
|
{_table, keyspace} = fixtureKeyspace(entries)
|
||||||
|
|
||||||
|
metadata = %{
|
||||||
|
"k1" => %{lastAccess: @now - 90_000, counter: 9},
|
||||||
|
"k2" => %{lastAccess: @now - 80_000, counter: 9},
|
||||||
|
"k3" => %{lastAccess: @now - 20_000, counter: 9},
|
||||||
|
"k4" => %{lastAccess: @now - 30_000, counter: 9}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user