feat: add serve and moveInto to blocking
This commit is contained in:
1 parent
933c3503cc
commit
2775b58d35
1 file changed
+123
@@ -155,3 +155,126 @@ defmodule Excache.Blocking do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp serve({:move, fromSide, toSide, destination}, key) do
|
||||||
|
case popOne(key, fromSide) do
|
||||||
|
{:ok, value} ->
|
||||||
|
moveInto(key, destination, fromSide, toSide, value)
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
:empty
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
{:error, errorTerm(reason)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp moveInto(source, destination, fromSide, toSide, value) do
|
||||||
|
case pushOne(destination, toSide, value) do
|
||||||
|
{:ok, _length} ->
|
||||||
|
{:ok, {:bulk, value}, [destination],
|
||||||
|
moveWrites(source, destination, fromSide, toSide, value)}
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
pushOne(source, fromSide, value)
|
||||||
|
{:error, errorTerm(reason)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp popOne(key, :left), do: Store.lpop(key, nil)
|
||||||
|
defp popOne(key, :right), do: Store.rpop(key, nil)
|
||||||
|
|
||||||
|
defp popMany(key, :left, count), do: Store.lpop(key, count)
|
||||||
|
defp popMany(key, :right, count), do: Store.rpop(key, count)
|
||||||
|
|
||||||
|
defp pushOne(key, :left, value), do: Store.lpush(key, [value])
|
||||||
|
defp pushOne(key, :right, value), do: Store.rpush(key, [value])
|
||||||
|
|
||||||
|
defp popName(:left), do: "LPOP"
|
||||||
|
defp popName(:right), do: "RPOP"
|
||||||
|
|
||||||
|
defp pushName(:left), do: "LPUSH"
|
||||||
|
defp pushName(:right), do: "RPUSH"
|
||||||
|
|
||||||
|
defp moveWrites(source, destination, :right, :left, _value),
|
||||||
|
do: [{"RPOPLPUSH", [source, destination]}]
|
||||||
|
|
||||||
|
defp moveWrites(source, destination, fromSide, toSide, value),
|
||||||
|
do: [{popName(fromSide), [source]}, {pushName(toSide), [destination, value]}]
|
||||||
|
|
||||||
|
defp missReply({:move, _fromSide, _toSide, _destination}), do: {:bulk, nil}
|
||||||
|
defp missReply(_op), do: {:array, nil}
|
||||||
|
|
||||||
|
defp errorTerm(:wrongtype), do: Reply.wrongtype()
|
||||||
|
|
||||||
|
defp deliver(served, inlineRef) do
|
||||||
|
Enum.reduce(served, nil, fn {waiter, term}, inline ->
|
||||||
|
if waiter.ref == inlineRef do
|
||||||
|
term
|
||||||
|
else
|
||||||
|
send(waiter.pid, {:excacheUnblocked, waiter.ref, term})
|
||||||
|
inline
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp headWaiter(state, key) do
|
||||||
|
with {:ok, queue} <- Map.fetch(state.queues, key),
|
||||||
|
{:value, ref} <- :queue.peek(queue) do
|
||||||
|
Map.fetch!(state.waiters, ref)
|
||||||
|
else
|
||||||
|
_missing -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp enqueue(state, waiter) do
|
||||||
|
queues =
|
||||||
|
Enum.reduce(Enum.uniq(waiter.keys), state.queues, fn key, acc ->
|
||||||
|
Map.put(acc, key, :queue.in(waiter.ref, Map.get(acc, key, :queue.new())))
|
||||||
|
end)
|
||||||
|
|
||||||
|
%{state | queues: queues, waiters: Map.put(state.waiters, waiter.ref, waiter)}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp dropWaiter(state, waiter) do
|
||||||
|
armed = Map.fetch!(state.waiters, waiter.ref)
|
||||||
|
if armed.monitor, do: Process.demonitor(armed.monitor, [:flush])
|
||||||
|
if armed.timer, do: Process.cancel_timer(armed.timer)
|
||||||
|
|
||||||
|
queues =
|
||||||
|
Enum.reduce(Enum.uniq(armed.keys), state.queues, fn key, acc ->
|
||||||
|
dropFromQueue(acc, key, armed.ref)
|
||||||
|
end)
|
||||||
|
|
||||||
|
%{
|
||||||
|
state
|
||||||
|
| queues: queues,
|
||||||
|
waiters: Map.delete(state.waiters, armed.ref),
|
||||||
|
monitors: Map.delete(state.monitors, armed.monitor)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp dropFromQueue(queues, key, ref) do
|
||||||
|
pruned = :queue.filter(&(&1 != ref), Map.fetch!(queues, key))
|
||||||
|
|
||||||
|
if :queue.is_empty(pruned) do
|
||||||
|
Map.delete(queues, key)
|
||||||
|
else
|
||||||
|
Map.put(queues, key, pruned)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp arm(state, waiter, timeout) do
|
||||||
|
monitor = Process.monitor(waiter.pid)
|
||||||
|
|
||||||
|
timer =
|
||||||
|
if timeout > 0, do: Process.send_after(self(), {:blockingTimeout, waiter.ref}, timeout)
|
||||||
|
|
||||||
|
armed = %{waiter | monitor: monitor, timer: timer}
|
||||||
|
|
||||||
|
%{
|
||||||
|
state
|
||||||
|
| waiters: Map.put(state.waiters, waiter.ref, armed),
|
||||||
|
monitors: Map.put(state.monitors, monitor, waiter.ref)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in new issue
Block a user