Packages

Aerospike driver for Elixir with an OTP-native cluster runtime

Current section

Files

Jump to
aerospike_driver lib aerospike async_task.ex
Raw

lib/aerospike/async_task.ex

defmodule Aerospike.AsyncTask do
@moduledoc """
Behaviour for pollable async server operations.
Some Aerospike server operations (secondary index creation, UDF registration) complete
asynchronously. The server accepts the request immediately and performs the work in the
background. `AsyncTask` provides a uniform interface for checking and waiting on these
operations.
Two modules implement this behaviour:
- `Aerospike.IndexTask` — tracks secondary index build progress
- `Aerospike.RegisterTask` — tracks UDF package registration progress
## Callbacks
Implementing modules must define `status/1`. A default `wait/2` implementation is injected
by `use Aerospike.AsyncTask` and polls `status/1` in a loop; implementors may override it.
## Usage
task = Aerospike.create_index(:my_conn, "test", "demo",
bin: "age", name: "age_idx", type: :numeric
)
# Poll manually
{:ok, :complete} = Aerospike.IndexTask.status(task)
# Or block until done
:ok = Aerospike.IndexTask.wait(task, timeout: 30_000)
"""
@doc """
Blocks until the async operation completes or the timeout is exceeded.
## Options
- `:poll_interval` — milliseconds between status checks (default: `1_000`)
- `:timeout` — maximum milliseconds to wait (default: no timeout)
Returns `:ok` when complete, `{:error, %Aerospike.Error{}}` on failure or timeout.
"""
@callback wait(struct(), keyword()) :: :ok | {:error, Aerospike.Error.t()}
@doc """
Returns the current status of the async operation.
Returns `{:ok, :complete}` when finished, `{:ok, :in_progress}` while running,
or `{:error, %Aerospike.Error{}}` on failure.
"""
@callback status(struct()) :: {:ok, :complete | :in_progress} | {:error, Aerospike.Error.t()}
@doc false
defmacro __using__(_opts) do
quote do
@behaviour Aerospike.AsyncTask
@impl Aerospike.AsyncTask
def wait(task, opts \\ []) do
Aerospike.AsyncTask.poll(__MODULE__, task, opts)
end
defoverridable wait: 2
end
end
@doc false
def poll(module, task, opts) do
poll_interval = Keyword.get(opts, :poll_interval, 1_000)
timeout_ms = Keyword.get(opts, :timeout, nil)
deadline = if timeout_ms, do: System.monotonic_time(:millisecond) + timeout_ms, else: nil
do_poll(module, task, poll_interval, deadline)
end
defp do_poll(module, task, poll_interval, deadline) do
case module.status(task) do
{:ok, :complete} ->
:ok
{:ok, :in_progress} ->
if deadline && System.monotonic_time(:millisecond) >= deadline do
{:error, Aerospike.Error.from_result_code(:timeout)}
else
Process.sleep(poll_interval)
do_poll(module, task, poll_interval, deadline)
end
{:error, _} = error ->
error
end
end
end