elixir-concurrency
Elixir concurrency patterns: Tasks, GenStage pipelines, Broadway data ingestion, Flow parallel processing, Oban background jobs. Use when building concurrent or parallel data processing systems.
Best use case
elixir-concurrency is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Elixir concurrency patterns: Tasks, GenStage pipelines, Broadway data ingestion, Flow parallel processing, Oban background jobs. Use when building concurrent or parallel data processing systems.
Teams using elixir-concurrency should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/elixir-concurrency/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How elixir-concurrency Compares
| Feature / Agent | elixir-concurrency | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Elixir concurrency patterns: Tasks, GenStage pipelines, Broadway data ingestion, Flow parallel processing, Oban background jobs. Use when building concurrent or parallel data processing systems.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Elixir Concurrency Patterns
Concurrency and parallel processing patterns for the BEAM VM.
## When to Use
- Running concurrent operations (API calls, file processing)
- Building data ingestion pipelines from message queues
- Parallel processing of large datasets
- Background job processing
- Fan-out/fan-in computation patterns
## Tasks
### Async/Await (Concurrent Computation)
```elixir
task1 = Task.async(fn -> fetch_user_profile(user_id) end)
task2 = Task.async(fn -> fetch_user_orders(user_id) end)
task3 = Task.async(fn -> fetch_user_notifications(user_id) end)
profile = Task.await(task1)
orders = Task.await(task2)
notifications = Task.await(task3)
```
### Async Stream (Parallel with Back-Pressure)
```elixir
urls
|> Task.async_stream(&HTTPClient.get/1, max_concurrency: 10, timeout: 30_000)
|> Enum.map(fn
{:ok, {:ok, response}} -> response
{:ok, {:error, reason}} -> {:error, reason}
{:exit, reason} -> {:error, {:crashed, reason}}
end)
```
### Supervised Tasks (Fault-Isolated)
```elixir
# In supervision tree
{Task.Supervisor, name: MyApp.TaskSupervisor}
# Fire-and-forget (supervised)
Task.Supervisor.start_child(MyApp.TaskSupervisor, fn ->
send_welcome_email(user)
end)
# Async with isolation (won't crash caller)
task = Task.Supervisor.async_nolink(MyApp.TaskSupervisor, fn ->
expensive_computation()
end)
case Task.yield(task, 5_000) || Task.shutdown(task) do
{:ok, result} -> result
nil -> {:error, :timeout}
end
```
## GenStage (Custom Pipelines)
Demand-driven producer-consumer with built-in back-pressure:
```elixir
# Producer
defmodule MyApp.DataProducer do
use GenStage
def init(:ok), do: {:producer, %{}}
def handle_demand(demand, state) when demand > 0 do
events = fetch_batch(demand)
{:noreply, events, state}
end
end
# Consumer
defmodule MyApp.DataConsumer do
use GenStage
def init(:ok) do
{:consumer, %{}, subscribe_to: [{MyApp.DataProducer, max_demand: 50, min_demand: 10}]}
end
def handle_events(events, _from, state) do
Enum.each(events, &process_event/1)
{:noreply, [], state}
end
end
```
## Broadway (Data Ingestion)
High-level pipeline for message queue ingestion:
```elixir
defmodule MyApp.Pipeline do
use Broadway
def start_link(_opts) do
Broadway.start_link(__MODULE__,
name: __MODULE__,
producer: [
module: {BroadwaySQS.Producer, queue_url: System.fetch_env!("SQS_QUEUE_URL")},
concurrency: 2
],
processors: [
default: [concurrency: 10]
],
batchers: [
default: [batch_size: 100, batch_timeout: 1_000, concurrency: 5]
]
)
end
@impl true
def handle_message(_processor, message, _context) do
message
|> Broadway.Message.update_data(&Jason.decode!/1)
|> Broadway.Message.put_batcher(:default)
end
@impl true
def handle_batch(:default, messages, _batch_info, _context) do
rows = Enum.map(messages, fn msg -> msg.data end)
MyApp.Repo.insert_all(Event, rows)
messages
end
end
```
Broadway supports: SQS, Kafka, RabbitMQ, Google Cloud PubSub out of the box.
## Flow (Parallel Data Processing)
MapReduce-style for large enumerables:
```elixir
# Word frequency count across files
File.ls!("./data")
|> Flow.from_enumerable(max_demand: 5)
|> Flow.flat_map(fn file ->
File.read!("./data/#{file}")
|> String.split(~r/\s+/)
|> Enum.map(&String.downcase/1)
end)
|> Flow.partition()
|> Flow.reduce(fn -> %{} end, fn word, acc ->
Map.update(acc, word, 1, &(&1 + 1))
end)
|> Enum.to_list()
```
Flow works in batches of 500 by default — only shows improvement on larger datasets.
## Oban (Background Jobs)
PostgreSQL-backed job queue with ACID guarantees:
```elixir
# Define a worker
defmodule MyApp.Workers.SendEmail do
use Oban.Worker, queue: :mailers, max_attempts: 3
@impl true
def perform(%Oban.Job{args: %{"to" => to, "template" => template}}) do
case MyApp.Mailer.deliver(to, template) do
{:ok, _} -> :ok
{:error, reason} -> {:error, reason} # Will retry
end
end
end
# Enqueue a job
%{to: "user@example.com", template: "welcome"}
|> MyApp.Workers.SendEmail.new()
|> Oban.insert()
# Schedule for later
%{report_id: 123}
|> MyApp.Workers.GenerateReport.new(schedule_in: 3600)
|> Oban.insert()
```
### Oban Configuration
```elixir
config :my_app, Oban,
repo: MyApp.Repo,
queues: [
default: 10,
mailers: 5,
critical: 20,
reports: 3
],
plugins: [
{Oban.Plugins.Pruner, max_age: 60 * 60 * 24 * 7}, # Clean jobs older than 7 days
{Oban.Plugins.Cron, crontab: [
{"0 * * * *", MyApp.Workers.HourlySync},
{"0 0 * * *", MyApp.Workers.DailyReport}
]}
]
```
### Key Rules
- Keep job args small: pass IDs, not full records
- Design workers to be **idempotent** (safe for retries)
- Use `Oban.Testing` for test assertions
## Choosing the Right Tool
| Need | Tool |
|------|------|
| 2-10 concurrent operations | `Task.async/await` |
| Process a list in parallel | `Task.async_stream` |
| Fire-and-forget background work | `Task.Supervisor.start_child` |
| Custom producer-consumer pipeline | `GenStage` |
| Message queue ingestion (SQS/Kafka) | `Broadway` |
| Parallel data crunching | `Flow` |
| Reliable background jobs with retries | `Oban` |
| Scheduled/cron jobs | `Oban` with Cron plugin |
## Anti-Patterns
- **Processes for code organization**: Processes model runtime concerns, not code structure
- **Unbounded spawning**: Always use supervisors; prefer `Task.Supervisor` over raw `Task.async`
- **Ignoring back-pressure**: Raw message passing can overflow mailboxes; use GenStage/Broadway
- **Large Oban payloads**: Pass IDs, not data blobs
- **Non-idempotent workers**: Jobs may retry; design for safe re-execution
- **`Task.async` without `await`/`yield`**: Linked tasks crash the caller on failure
## Key Libraries
- `Task`, `Task.Supervisor` (stdlib)
- `GenStage`, `Flow` (Dashbit)
- `Broadway` + producers: `broadway_sqs`, `broadway_kafka`, `broadway_rabbitmq`
- `Oban` (background jobs), `Oban.Pro` (workflows, batch jobs, Web UI)Related Skills
go-concurrency-patterns
Master Go concurrency with goroutines, channels, sync primitives, and context. Use when building concurrent Go applications, implementing worker pools, or debugging race conditions.
elixir-testing
Elixir testing patterns: ExUnit, Mox behaviour-based mocking, StreamData property testing, Phoenix integration tests. Use when writing tests for Elixir/Phoenix applications.
elixir-pro
Write idiomatic Elixir code with OTP patterns, supervision trees, and Phoenix LiveView. Masters concurrency, fault tolerance, and distributed systems.
elixir-otp-patterns
OTP architecture patterns for Elixir: GenServer, Supervisor, DynamicSupervisor, Registry, Application. Use when designing fault-tolerant process architectures, supervision trees, or stateful services.
elixir-deployment
Elixir deployment patterns: Mix releases, Docker multi-stage builds, clustering with libcluster, runtime config, health checks, observability. Use when deploying Elixir/Phoenix to production.
wordpress-penetration-testing
This skill should be used when the user asks to "pentest WordPress sites", "scan WordPress for vulnerabilities", "enumerate WordPress users, themes, or plugins", "exploit WordPress vu...
php-pro
Write idiomatic PHP code with generators, iterators, SPL data structures, and modern OOP features. Use PROACTIVELY for high-performance PHP applications.
moodle-external-api-development
Create custom external web service APIs for Moodle LMS. Use when implementing web services for course management, user tracking, quiz operations, or custom plugin functionality. Covers parameter va...
laravel-expert
Senior Laravel Engineer role for production-grade, maintainable, and idiomatic Laravel solutions. Focuses on clean architecture, security, performance, and modern standards (Laravel 10/11+).
voice-ai-engine-development
Build real-time conversational AI voice engines using async worker pipelines, streaming transcription, LLM agents, and TTS synthesis with interrupt handling and multi-provider support
voice-ai-development
Expert in building voice AI applications - from real-time voice agents to voice-enabled apps. Covers OpenAI Realtime API, Vapi for voice agents, Deepgram for transcription, ElevenLabs for synthesis...
voice-agents
Voice agents represent the frontier of AI interaction - humans speaking naturally with AI systems. The challenge isn't just speech recognition and synthesis, it's achieving natural conversation flo...