···76767777 @spec read_range(pid, non_neg_integer, binary, binary, Keyword.t) :: {:ok, RangeResult.t} | {:error, :read_version_too_old}
7878 def read_range(server, read_version, start_key, end_key, opts \\ []) do
7979+ {timeout, opts} = Keyword.pop(opts, :timeout, 5000)
8080+7981 limit = Keyword.get(opts, :limit, :infinity)
8082 reverse = Keyword.get(opts, :reverse, false)
8181- SimServer.call(server, {:read_range, read_version, start_key, end_key, limit, reverse})
8383+ SimServer.call(server, {:read_range, read_version, start_key, end_key, limit, reverse}, timeout)
8284 end
83858486 @spec get_estimated_size(pid, non_neg_integer, binary, binary) :: {:ok, non_neg_integer} | {:error, :read_version_too_old | :read_version_too_new}
···187189 def handle_info({:tick_import, ref}, %State{} = state) when is_reference(ref) do
188190 {:noreply, tick_import(ref, state)}
189191 end
192192+193193+ # If calls we make time out, we may receive their responses later
194194+ def handle_info(_message, state), do: {:noreply, state}
190195191196 defp up_to_date?(%State{} = state, version) do
192197 cond do
···429434430435 # TODO: assert invariants
431436432432- # TODO: this can deadlock, it needs to be pipelined
433433- too_new_backoff(fn ->
434434- Storage.read_range(from_pid, read_version, si.current_key, si.end_key, limit: 4)
435435- end)
437437+ # TODO: it would be more efficient to pipeline these requests and receive
438438+ # the response in a handle_info so that we can serve reads while waiting
439439+ # (this would also prevent import deadlocks which can currently cause timeouts)
440440+ try do
441441+ too_new_backoff(fn ->
442442+ Storage.read_range(from_pid, read_version, si.current_key, si.end_key, limit: 4, timeout: 1000)
443443+ end)
444444+ catch
445445+ :exit, {{:timeout, _}, _} ->
446446+ {:error, :timeout}
447447+ end
436448 |> case do
437449 {:ok, %RangeResult{pairs: pairs, more: more}} ->
438450 # Write imported KV pairs at `read_version`
···460472 {:error, :read_version_too_old} ->
461473 # This server is lagging by a lot, retry
462474 # TODO: longer interval here?
475475+ SimServer.send_after(self(), {:tick_import, si.ref}, @import_interval_ms)
476476+ state
477477+478478+ {:error, :timeout} ->
479479+ # If the request times out (possibly due to import deadlock), retry
463480 SimServer.send_after(self(), {:tick_import, si.ref}, @import_interval_ms)
464481 state
465482 end