feat(command): add encodingName and compact? to keyspace
This commit is contained in:
1 parent
76e425bf21
commit
29b3774426
1 file changed
+190
@@ -312,3 +312,193 @@ defmodule Excache.Command.Keyspace do
|
||||
if compact?(value, length(value)), do: "listpack", else: "quicklist"
|
||||
end
|
||||
|
||||
defp encodingName({:hash, value, _ea}) do
|
||||
flat = Enum.flat_map(value, fn {field, item} -> [field, item] end)
|
||||
if compact?(flat, map_size(value)), do: "listpack", else: "hashtable"
|
||||
end
|
||||
|
||||
defp encodingName({:set, value, _ea}) do
|
||||
members = MapSet.to_list(value)
|
||||
size = MapSet.size(value)
|
||||
|
||||
cond do
|
||||
size <= @intsetEntries and Enum.all?(members, &integerString?/1) -> "intset"
|
||||
compact?(members, size) -> "listpack"
|
||||
true -> "hashtable"
|
||||
end
|
||||
end
|
||||
|
||||
defp encodingName({:zset, value, _ea}) do
|
||||
members = value |> SkipList.to_list() |> Enum.map(fn {_score, member} -> member end)
|
||||
if compact?(members, SkipList.size(value)), do: "listpack", else: "skiplist"
|
||||
end
|
||||
|
||||
defp encodingName({:stream, _value, _ea}), do: "stream"
|
||||
|
||||
defp compact?(values, size) do
|
||||
size <= @listpackEntries and Enum.all?(values, &(byte_size(&1) <= @listpackValue))
|
||||
end
|
||||
|
||||
defp refcount({:string, value, _ea}) do
|
||||
if integerString?(value) and String.to_integer(value) in 0..9999 do
|
||||
@sharedRefcount
|
||||
else
|
||||
1
|
||||
end
|
||||
end
|
||||
|
||||
defp refcount(_entry), do: 1
|
||||
|
||||
defp integerString?(value) do
|
||||
case Integer.parse(value) do
|
||||
{number, ""} -> Integer.to_string(number) == value and byte_size(value) <= 20
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
defp parseIntercard(numkeysRaw, rest) do
|
||||
case Integer.parse(numkeysRaw) do
|
||||
{numkeys, ""} when numkeys <= 0 ->
|
||||
{:reply, {:error, "ERR numkeys should be greater than 0"}}
|
||||
|
||||
{numkeys, ""} when numkeys > length(rest) ->
|
||||
{:reply, {:error, "ERR Number of keys can't be greater than number of args"}}
|
||||
|
||||
{numkeys, ""} ->
|
||||
{keys, tail} = Enum.split(rest, numkeys)
|
||||
|
||||
case parseIntercardLimit(tail) do
|
||||
{:ok, limit} -> {:ok, keys, limit}
|
||||
{:reply, term} -> {:reply, term}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:reply, Reply.not_integer()}
|
||||
end
|
||||
end
|
||||
|
||||
defp parseIntercardLimit([]), do: {:ok, 0}
|
||||
|
||||
defp parseIntercardLimit([token, raw]) do
|
||||
if String.upcase(token) == "LIMIT" do
|
||||
case Integer.parse(raw) do
|
||||
{limit, ""} when limit >= 0 -> {:ok, limit}
|
||||
{_negative, ""} -> {:reply, {:error, "ERR LIMIT can't be negative"}}
|
||||
_ -> {:reply, Reply.not_integer()}
|
||||
end
|
||||
else
|
||||
{:reply, Reply.syntax()}
|
||||
end
|
||||
end
|
||||
|
||||
defp parseIntercardLimit(_tail), do: {:reply, Reply.syntax()}
|
||||
|
||||
defp parseCopyOptions([], replace), do: {:ok, replace}
|
||||
|
||||
defp parseCopyOptions([token | rest], replace) do
|
||||
case {String.upcase(token), rest} do
|
||||
{"REPLACE", tail} ->
|
||||
parseCopyOptions(tail, true)
|
||||
|
||||
{"DB", [indexRaw | tail]} ->
|
||||
case Integer.parse(indexRaw) do
|
||||
{0, ""} -> parseCopyOptions(tail, replace)
|
||||
{_other, ""} -> {:reply, {:error, "ERR DB index is out of range"}}
|
||||
_ -> {:reply, Reply.not_integer()}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:reply, Reply.syntax()}
|
||||
end
|
||||
end
|
||||
|
||||
defp parseExpireFlags(tokens) do
|
||||
tokens
|
||||
|> Enum.reduce_while({:ok, %{nx: false, xx: false, gt: false, lt: false}}, fn token,
|
||||
{:ok, flags} ->
|
||||
case String.upcase(token) do
|
||||
"NX" -> {:cont, {:ok, %{flags | nx: true}}}
|
||||
"XX" -> {:cont, {:ok, %{flags | xx: true}}}
|
||||
"GT" -> {:cont, {:ok, %{flags | gt: true}}}
|
||||
"LT" -> {:cont, {:ok, %{flags | lt: true}}}
|
||||
other -> {:halt, {:reply, {:error, "ERR Unsupported option #{other}"}}}
|
||||
end
|
||||
end)
|
||||
|> validateExpireFlags()
|
||||
end
|
||||
|
||||
defp validateExpireFlags({:reply, term}), do: {:reply, term}
|
||||
|
||||
defp validateExpireFlags({:ok, flags}) do
|
||||
incompatible = (flags.nx and (flags.xx or flags.gt or flags.lt)) or (flags.gt and flags.lt)
|
||||
|
||||
if incompatible do
|
||||
{:reply, {:error, "ERR NX and XX, GT or LT options at the same time are not compatible"}}
|
||||
else
|
||||
{:ok, flags}
|
||||
end
|
||||
end
|
||||
|
||||
defp expireAt(key, msAbs, flags) do
|
||||
case Store.expiretime(key, :milliseconds) do
|
||||
-2 -> 0
|
||||
current -> if expireAllowed?(flags, current, msAbs), do: Store.expireat(key, msAbs), else: 0
|
||||
end
|
||||
end
|
||||
|
||||
defp expireAllowed?(flags, current, msAbs) do
|
||||
cond do
|
||||
flags.nx -> current == -1
|
||||
flags.xx and current == -1 -> false
|
||||
flags.gt -> current != -1 and msAbs > current
|
||||
flags.lt -> current == -1 or msAbs < current
|
||||
true -> true
|
||||
end
|
||||
end
|
||||
|
||||
defp zrevrange(key, startRaw, stopRaw, withscores, state) do
|
||||
Reply.with_two_ints(startRaw, stopRaw, state, fn start, stop ->
|
||||
Reply.zrange_reply(Store.zrevrange(key, start, stop), withscores)
|
||||
end)
|
||||
end
|
||||
|
||||
defp scoreEntry(nil), do: Reply.map_score(nil)
|
||||
defp scoreEntry(score), do: Reply.map_score({:ok, score})
|
||||
|
||||
defp withType(key, expected, state, fun) do
|
||||
case Store.type(key) do
|
||||
"none" -> reply(fun.(), state)
|
||||
^expected -> reply(fun.(), state)
|
||||
_ -> reply(Reply.wrongtype(), state)
|
||||
end
|
||||
end
|
||||
|
||||
defp singleReply({:ok, [value | _rest]}), do: {:bulk, value}
|
||||
defp singleReply({:ok, []}), do: {:bulk, nil}
|
||||
defp singleReply({:error, reason}), do: errorTerm(reason)
|
||||
|
||||
defp arrayReply({:ok, values}), do: {:array, Reply.bulks(values)}
|
||||
defp arrayReply({:error, reason}), do: errorTerm(reason)
|
||||
|
||||
defp integerReply({:ok, value}), do: {:integer, value}
|
||||
defp integerReply({:error, reason}), do: errorTerm(reason)
|
||||
|
||||
defp bulkReply({:ok, value}), do: {:bulk, value}
|
||||
defp bulkReply(nil), do: {:bulk, nil}
|
||||
defp bulkReply({:error, reason}), do: errorTerm(reason)
|
||||
|
||||
defp rankReply({:ok, rank}), do: {:integer, rank}
|
||||
defp rankReply(nil), do: {:bulk, nil}
|
||||
defp rankReply({:error, reason}), do: errorTerm(reason)
|
||||
|
||||
defp statusReply({:ok, "OK"}), do: {:simple, "OK"}
|
||||
defp statusReply({:error, reason}), do: errorTerm(reason)
|
||||
|
||||
defp errorTerm(:wrongtype), do: Reply.wrongtype()
|
||||
defp errorTerm(:not_integer), do: Reply.not_integer()
|
||||
defp errorTerm(:not_float), do: Reply.not_float()
|
||||
defp errorTerm(:nokey), do: {:error, "ERR no such key"}
|
||||
defp errorTerm(:range), do: {:error, "ERR index out of range"}
|
||||
|
||||
defp reply(term, state), do: {[term], state}
|
||||
end
|
||||
Reference in new issue
Block a user