Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 40 additions & 20 deletions test/durable_server_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ defmodule DurableServerTest do
:ets.insert(table, {{:override, key, consistent}, response})
end

defp put_backend_write_override(table, key, response) do
:ets.insert(table, {{:put_override, key}, response})
end

defp insert_running_lock(table, storage_key, supervisor_name, prefix, key, pid) do
node_ref = DurableServer.Supervisor.node_ref(supervisor_name)

Expand Down Expand Up @@ -300,21 +304,27 @@ defmodule DurableServerTest do
@impl true
def put_object(%{table: table} = state, key, data, opts) do
result =
case Keyword.fetch(opts, :etag) do
{:ok, expected_etag} ->
case :ets.lookup(table, {:data, key}) do
[{{:data, ^key}, %{etag: ^expected_etag}}] ->
store_value(table, key, data)

[{{:data, ^key}, _value}] ->
{:error, :conflict}
case :ets.lookup(table, {:put_override, key}) do
[{{:put_override, ^key}, response}] ->
response

[] ->
case Keyword.fetch(opts, :etag) do
{:ok, expected_etag} ->
case :ets.lookup(table, {:data, key}) do
[{{:data, ^key}, %{etag: ^expected_etag}}] ->
store_value(table, key, data)

[{{:data, ^key}, _value}] ->
{:error, :conflict}

[] ->
{:error, :not_found}
end

[] ->
{:error, :not_found}
:error ->
store_value(table, key, data)
end

:error ->
store_value(table, key, data)
end

maybe_after_put(state, key, data, opts, result)
Expand Down Expand Up @@ -2113,22 +2123,32 @@ defmodule DurableServerTest do
end

describe "error handling" do
test "continues operation when sync operations encounter errors", %{
supervisor_name: supervisor_name,
test "explicit sync does not acknowledge a mutation when persistence fails", %{
prefix: _prefix
} do
key = "test-server-#{DurableServer.UUID.uuid4()}"
{supervisor_name, _supervisor_pid, prefix} =
start_test_supervisor(backend: {ConsistencyProbeBackend, owner: self()})

key = "explicit-sync-failure-#{DurableServer.UUID.uuid4()}"

{:ok, {pid, _meta}} =
DurableServer.Supervisor.start_child(
supervisor_name,
{TestServer, key: key, initial_state: %{}}
)

# Basic operations should work
assert GenServer.call(pid, :increment) == 1
assert GenServer.call(pid, :increment_and_sync) == 2
assert GenServer.call(pid, :get_count) == 2
%{storage_backend: backend} = DurableServer.Supervisor.__get_config__(supervisor_name)
put_backend_write_override(backend_table(backend), prefix <> key, {:error, :unavailable})

result =
try do
{:reply, GenServer.call(pid, :increment_and_sync)}
catch
:exit, reason -> {:exit, reason}
end

refute match?({:reply, _reply}, result),
"explicit :sync returned success even though persistence failed"
end

test "auto-sync servers continue operating", %{
Expand Down