DDTrace.Task.Supervisor (dd_trace_ex v0.1.0)

Copy Markdown View Source

Task.Supervisor, with the caller's trace carried into the child.

The supervised counterpart of DDTrace.Task, and the same deal: one alias migrates a file, every function keeps its name, arguments, order, defaults and return value.

alias DDTrace.Task

DDTrace.trace "orders.settle_all" do
  Task.Supervisor.async_stream_nolink(MyApp.TaskSup, orders, &settle/1)
  |> Enum.to_list()
end

Aliasing DDTrace.Task is enough for both modules: Task.Supervisor inside that file resolves here.

What is different

Only the spawning functions — async/3,5, async_nolink/3,5, async_stream/4,6, async_stream_nolink/4,6 and start_child/3,5. Each takes a DDTrace.current_context/0 snapshot in the caller, at the call, and attaches it in the child around the work. start_link/1, children/1 and terminate_child/2 are Task.Supervisor, delegated.

Everything DDTrace.Task says about snapshots, streams, raising and what is deliberately absent applies here word for word.

Notes worth having read

A start_child/3,5 given restart: :transient or :permanent is restarted by the supervisor by re-running what it was started with — which is the wrapper, carrying the snapshot taken when the child was first started. A restart minutes later therefore attaches to a trace that ended long ago, and its spans join a chunk nobody will ever see the rest of. That is the honest reading of a snapshot: it says what was true when it was taken. A restarting child that should trace on its own terms should open its own trace rather than be handed one.

An MFA child is spawned as DDTrace.Task.Wrapper, so that is the initial call :proc_lib reports in a crash report. The real module, function and arguments are its arguments, one frame in.

async/5 and async_nolink/5 given an args that is not a list raise FunctionClauseError where Task.Supervisor raises ArgumentError. Both raise at the call site, which is the part that matters, but the class differs: Task.Supervisor reaches its own length(args) to get there, while the arguments this module forwards are always a list — so the raise has to come from a guard here, or not at all. A rescue ArgumentError written around that one call therefore stops catching it; rescue without a class, and every other function of both modules, are unaffected.

child_spec/1 is a plain delegate: a supervision tree is built before there is any trace to carry.

Summary

Functions

Starts a supervised task carrying this process's trace, to be awaited on.

Starts a supervised task carrying this process's trace, to be awaited on.

Starts an unlinked supervised task carrying this process's trace.

Starts an unlinked supervised task carrying this process's trace.

Runs fun over enumerable on supervised tasks, each carrying this process's trace.

Runs module.function(element, ...args) over enumerable on supervised tasks, each carrying this process's trace.

Runs fun over enumerable on unlinked supervised tasks, each carrying this process's trace.

Runs module.function(element, ...args) over enumerable on unlinked supervised tasks, each carrying this process's trace.

Lists a supervisor's running children. See Task.Supervisor.children/1.

Starts a supervised task carrying this process's trace, with nobody awaiting it.

Starts a supervised task carrying this process's trace, with nobody awaiting it.

Starts a task supervisor. See Task.Supervisor.start_link/1.

Starts a task supervisor. See Task.Supervisor.start_link/1.

Functions

async(supervisor, fun, options \\ [])

@spec async(Supervisor.supervisor(), (-> any()), keyword()) :: Task.t()

Starts a supervised task carrying this process's trace, to be awaited on.

See Task.Supervisor.async/3.

Examples

task = DDTrace.Task.Supervisor.async(MyApp.TaskSup, fn -> heavy() end)

async(supervisor, module, function, args, options \\ [])

@spec async(Supervisor.supervisor(), module(), atom(), [term()], keyword()) ::
  Task.t()

Starts a supervised task carrying this process's trace, to be awaited on.

See Task.Supervisor.async/5.

async_nolink(supervisor, fun, options \\ [])

@spec async_nolink(Supervisor.supervisor(), (-> any()), keyword()) :: Task.t()

Starts an unlinked supervised task carrying this process's trace.

The form to reach for when the child may fail and the caller should survive it. See Task.Supervisor.async_nolink/3.

Examples

DDTrace.Task.Supervisor.async_nolink(MyApp.TaskSup, fn -> risky() end)

async_nolink(supervisor, module, function, args, options \\ [])

@spec async_nolink(Supervisor.supervisor(), module(), atom(), [term()], keyword()) ::
  Task.t()

Starts an unlinked supervised task carrying this process's trace.

See Task.Supervisor.async_nolink/5.

async_stream(supervisor, enumerable, fun, options \\ [])

@spec async_stream(
  Supervisor.supervisor(),
  Enumerable.t(),
  (term() -> term()),
  keyword()
) ::
  Enumerable.t()

Runs fun over enumerable on supervised tasks, each carrying this process's trace.

See Task.Supervisor.async_stream/4. The snapshot is taken once, when the stream is built, and shared by every element.

Examples

DDTrace.Task.Supervisor.async_stream(MyApp.TaskSup, orders, &settle/1)

async_stream(supervisor, enumerable, module, function, args, options \\ [])

@spec async_stream(
  Supervisor.supervisor(),
  Enumerable.t(),
  module(),
  atom(),
  [term()],
  keyword()
) :: Enumerable.t()

Runs module.function(element, ...args) over enumerable on supervised tasks, each carrying this process's trace.

See Task.Supervisor.async_stream/6.

async_stream_nolink(supervisor, enumerable, fun, options \\ [])

@spec async_stream_nolink(
  Supervisor.supervisor(),
  Enumerable.t(),
  (term() -> term()),
  keyword()
) :: Enumerable.t()

Runs fun over enumerable on unlinked supervised tasks, each carrying this process's trace.

See Task.Supervisor.async_stream_nolink/4.

Examples

DDTrace.Task.Supervisor.async_stream_nolink(MyApp.TaskSup, urls, &fetch/1)

async_stream_nolink(supervisor, enumerable, module, function, args, options \\ [])

@spec async_stream_nolink(
  Supervisor.supervisor(),
  Enumerable.t(),
  module(),
  atom(),
  [term()],
  keyword()
) :: Enumerable.t()

Runs module.function(element, ...args) over enumerable on unlinked supervised tasks, each carrying this process's trace.

See Task.Supervisor.async_stream_nolink/6.

children(supervisor)

@spec children(Supervisor.supervisor()) :: [pid()]

Lists a supervisor's running children. See Task.Supervisor.children/1.

start_child(supervisor, fun, options \\ [])

@spec start_child(Supervisor.supervisor(), (-> any()), keyword()) ::
  DynamicSupervisor.on_start_child()

Starts a supervised task carrying this process's trace, with nobody awaiting it.

See Task.Supervisor.start_child/3, including what its :restart option means for a trace — the note in this module's documentation.

Examples

DDTrace.Task.Supervisor.start_child(MyApp.TaskSup, fn -> notify(user) end)

start_child(supervisor, module, function, args, options \\ [])

Starts a supervised task carrying this process's trace, with nobody awaiting it.

See Task.Supervisor.start_child/5.

start_link()

@spec start_link() :: Supervisor.on_start()

Starts a task supervisor. See Task.Supervisor.start_link/1.

start_link(options)

@spec start_link(keyword()) :: Supervisor.on_start()

Starts a task supervisor. See Task.Supervisor.start_link/1.

terminate_child(supervisor, pid)

@spec terminate_child(Supervisor.supervisor(), pid()) :: :ok | {:error, :not_found}

Stops one supervised child. See Task.Supervisor.terminate_child/2.