Shigoto - Background job processing

Shigoto is a PostgreSQL-backed background job processing library for Erlang, built on OTP.

Features:

  • Named queues with configurable concurrency
  • Retries with backoff
  • Cron scheduling
  • Job batches with completion callbacks
  • Circuit breakers for failure-heavy queues
  • Telemetry integration

Quick example:

%% Enqueue a job
shigoto:insert(#{
worker => my_email_worker,
args => #{<<“to”>> => <<“user@example.com”>>},
queue => <<“emails”>>
}).


%% Define a worker
-module(my_email_worker).
-behaviour(shigoto_worker).

-export([perform/1]).

perform(#{<<“to”>> := To}) → 
send_email(To),
ok.


Demo app:

We put together shigoto_demo - a small Nova app that exercises the main features: multiple queues, cron jobs, batches, webhook retries with circuit breaking. It comes with a Python traffic simulator to generate load.

The demo also includes two live dashboards built with Arizona (real-time UI framework for Erlang):

  • shigoto_board (/shigoto) - real-time job dashboard with queue stats, job failures, batch progress, and cron entries
  • nova_liveboard (/liveboard) - BEAM VM dashboard with process explorer, ETS tables, supervision trees, and system metrics

Both dashboards are still in active development - expect rough edges and missing features. Feedback and ideas welcome.

Links:

shigoto:

8 Likes

Thank you for building this @Taure. Shigoto is exactly the kind of library the Erlang ecosystem has been missing. This is something Elixir developers have long taken for granted with libraries like Oban, and it is wonderful to see this gap finally being closed on the Erlang side.

This is a meaningful contribution to the community. Looking forward to following its development.

1 Like

Thank you.

I have been working on Nova and tooling around for some time and now felt it is time that we have the same tooling as Elixir.

Erlang have the same things, maybe a bit hidden but you can build web apps the same way as Phoenix and ecto in Erlang. Maybe not as mature but we need to start somewhere. :grin:

4 Likes

Quick update for anyone following the thread. Shigoto is now at v1.2.2 with some notable additions since the original post.

Fanout queues (v1.2.0)

Broadcast delivery mode where a single enqueue fans out to multiple consumers. Useful for webhook delivery, cache invalidation, and any pub/sub shaped workload where you want the durability of a job queue. There is a guide for it in the repo.

Repo config (v1.1.0)

You can now configure Shigoto via a repo module instead of a raw pgo pool name. Plays nicely with Kura if you are already using it.

Auto-migration on startup (v1.2.2)

No need to call shigoto_migration:up/1 manually anymore. Tables are created on application start.

Also worth flagging some things that were already in v1.0.0 but did not make it into the original post:

  • Job dependencies with cycle detection (depends_on chains)
  • Job snoozing, workers can return {snooze, Seconds} without burning a retry attempt
  • Unique jobs with debounce, time windows, and field replacement on conflict
  • Full seki integration: rate limiting (token bucket, sliding window, GCRA, leaky bucket), per-worker and global bulkheads, CoDel load shedding
  • AES-256-GCM encryption at rest with key rotation
  • Stale job rescue via heartbeats
  • Bulk operations: insert_all/1, cancel_by/2, retry_by/2
  • Synchronous drain_queue/1 for deterministic tests
  • OpenTelemetry instrumentation as a separate package, opentelemetry_shigoto

Feedback and issues welcome on the repo.

1 Like

Small follow-up on the metrics side, since someone earlier in the thread pointed out that metrics were DIY via the telemetry contract.

There’s now shigoto_metrics ( GitHub - Taure/shigoto_metrics: Prometheus metrics collectors and Grafana dashboard for shigoto · GitHub , v0.1.0). It’s a companion package, collectors only. Deps are just telemetry and prometheus, no web framework and no bundled HTTP listener. shigoto core doesn’t depend on it at all.

What it does is attach handlers to the [shigoto, …] event surface and turn them into around 20 canonical Prometheus metrics. Job lifecycle counts, duration and queue-wait histograms per queue, the resilience trips (rate-limit, circuit, bulkhead,
load-shed), batches, cron. There’s also a shigoto_testing_mode_armed_total which should always be zero in prod, so it’s a red flag if you ever see it non-zero.

The reason it’s a separate collectors-only package is I didn’t want to force a web layer on anyone or pull a framework into shigoto core. So shigoto_metrics:scrape/0 just returns the Prometheus text and you serve it from your own web layer, behind
whatever auth and routing you already have. Same model as Oban + PromEx, the library emits, the host app owns the endpoint.

On Nova it’s basically a one-line controller:

index(_Req) ->
{status, 200, #{~"content-type" => ~"text/plain; version=0.0.4"},
shigoto_metrics:scrape()}.


Make sure the app is started so the handlers attach:

application:ensure_all_started(shigoto_metrics).

Cardinality is kept disciplined, job_id and batch_id stay out of the labels. And there’s a ready-to-import Grafana dashboard in priv/grafana/shigoto.json if you want something to point at straight away.