defmodule Loader do @moduledoc "README.md" |> File.read!() |> String.split("") |> Enum.fetch!(1) @external_resource "README.md" defmodule WorkResponse do @moduledoc """ Internal data structure used to represent the results of executing a `WorkSpec` ## Properties - `:data` - whatever important data is returned by the work - `:response_time` - **must be an integer number, in microseconds**, which is the "client-side" view of how long the work took. I recommend using `System.monotonic_time/0` or `:timer.tc/1` """ defstruct [:data, :kind, :response_time] @type t :: %__MODULE__{ data: any(), kind: :ok | :error, response_time: integer() } end defmodule WorkSpec do @moduledoc """ A specification for some "work" to do, to generate load. """ # TODO: should a user be able to specify a task that does not take a count? could we batch them ourselves? # is it a good design to force the user to tell us how to execute their work in # batches? # maybe they can give us either `arity-0 func`, `arity-1 func`, or `{arity-0, arity-1}` and we # handle batching as best we can? # TODO: should a `reason` be attached to the `is_success?` callback? so that a user can do something like `{false, "too slow"}`? defstruct [:task, :is_success?] @type t :: %__MODULE__{ task: (() -> term()) | mfa(), is_success?: (Loader.WorkResponse.t() -> boolean()) } end @doc """ Execute tasks based on the `work_spec`, scheduled based on the parameters in the `load_profile` """ @spec execute_profile(Loader.LoadProfile.t(), Loader.WorkSpec.t()) :: DynamicSupervisor.on_start_child() def execute_profile(load_profile, work_spec) do DynamicSupervisor.start_child( Loader.DynamicSupervisor, Loader.ScheduledLoader.child_spec(load_profile: load_profile, work_spec: work_spec) ) end @doc """ Execute several `LoadProfile`s simultaneously, each with their supplied `WorkSpec` """ @spec execute_profiles([{Loader.LoadProfile.t(), Loader.WorkSpec.t()}]) :: [ DynamicSupervisor.on_start_child() ] def execute_profiles(profile_spec_pairs) do Enum.map(profile_spec_pairs, fn {profile, spec} -> execute_profile(profile, spec) end) end end