# `Zizq.JobKind`
[🔗](https://github.com/zizq-labs/zizq-elixir/blob/v0.8.0/lib/zizq/job_kind.ex#L4)

Defines a kind of job: its name on the server, the enqueue options it
defaults to, and what running it does.

    defmodule MyApp.SendEmail do
      use Zizq.JobKind,
        type: "send_email",
        queue: "emails",
        retry_limit: 5

      @impl Zizq.JobKind
      def perform(%{"user_id" => id, "template" => template}) do
        MyApp.Mailer.deliver(id, template)
      end
    end

`use` generates `type/0`, `new/1` and `new/2` on the module. Building
an enqueue and sending it stay separate, so enqueues compose:

    MyApp.SendEmail.new(%{"user_id" => 42})
    |> Zizq.enqueue(MyApp.Zizq)

    users
    |> Enum.map(&MyApp.SendEmail.new(%{"user_id" => &1.id}))
    |> Zizq.enqueue_all(MyApp.Zizq)

`new/2` takes per-enqueue overrides on top of the module's defaults:

    MyApp.SendEmail.new(%{"user_id" => 42}, priority: 10)

## Options

`:type` is the job's name on the server — the `"type"` field a
producer written in any language sends to reach this handler. It is
**required and never inferred from the module name**, so renaming
`MyApp.SendEmail` cannot silently change the wire contract and strand
every queued job.

Everything else is optional and is any key `Zizq.Enqueue` accepts
except `:payload`, which belongs to the individual enqueue rather
than the kind. Options are validated when the module compiles, so a
malformed `:backoff` is a build failure rather than a surprise at the
first enqueue.

`:queue` defaults to `"default"` in the client. Anything left unset
is omitted from the request entirely, so the server's own defaults
apply and keep tracking its configuration.

## `perform/1` and `perform/2`

Define whichever you need. `perform/2` also receives the
`Zizq.Job`, for the attempt count, id, queue, etc:

    @impl Zizq.JobKind
    def perform(payload, %Zizq.Job{attempts: attempts}) when attempts >= 3 do
      MyApp.Mailer.deliver_without_retry(payload)
    end

    def perform(payload, _job), do: MyApp.Mailer.deliver(payload)

`:attempts` counts attempts that have already finished, so it is `0`
while a job runs for the first time and the guard above first matches
on the fourth run. Note that `Zizq.ErrorRecord`'s `:attempt` numbers
the attempt a failure belongs to, so it reads one higher than the
`:attempts` a handler saw during that same run.

Defining both is fine — `perform/2` wins. Defining neither fails at
compile time. The choice is resolved while the module compiles, so
dispatch costs nothing at runtime.

See `Zizq.Worker` for what a return value does to the job.

## Producing without consuming

A module is only worth defining where the job is *run*. An
application that merely enqueues work handled elsewhere — by another
service, or another language — has no `perform` to write, and should
build enqueues with `Zizq.Enqueue` directly.

# `result`

```elixir
@type result() ::
  :ok
  | {:ok, term()}
  | {:error, term()}
  | {:cancel, term()}
  | {:snooze, non_neg_integer() | DateTime.t()}
```

# `perform`
*optional* 

```elixir
@callback perform(payload :: term()) :: result()
```

Run the job, given its payload.

# `perform`
*optional* 

```elixir
@callback perform(payload :: term(), job :: Zizq.Job.t()) :: result()
```

Run the job, given its payload and the `Zizq.Job` it came from.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
