Skip to content

feat: fanout queue mode for broadcast delivery#26

Merged
Taure merged 1 commit into
mainfrom
feat/fanout-queues
Mar 30, 2026
Merged

feat: fanout queue mode for broadcast delivery#26
Taure merged 1 commit into
mainfrom
feat/fanout-queues

Conversation

@Taure
Copy link
Copy Markdown
Owner

@Taure Taure commented Mar 30, 2026

Summary

  • New shigoto_fanout_queue module — every node processes every job (vs single-consumer standard queues)
  • Time-window polling with ETS dedup — no cursor, no node registration, stateless
  • Auto-prunes expired jobs (2x window) to prevent unbounded table growth
  • Config via {fanout_queues, [{Name, Concurrency, #{window => Seconds}}]}

Design

Standard queues use FOR UPDATE SKIP LOCKED — one node claims each job. Fanout queues use a simple SELECT with inserted_at >= now() - window — all nodes read the same rows. Each node tracks seen IDs in a local ETS set to avoid re-processing within a session.

On node restart, the ETS is empty so recent jobs within the window are re-processed. Workers must be idempotent. The source of truth is always the database — fanout is best-effort push.

Use case

Session revocation, notifications, chat delivery in cloud-native deployments where distributed Erlang is not available.

Test plan

  • All 84 existing tests pass
  • Compiles clean
  • xref clean
  • Dialyzer clean
  • rebar3 fmt clean
  • ELP lint clean (no new warnings)
  • Integration test with multi-node fanout delivery

Fanout queues let every node process every job, unlike standard queues
where one node claims each job via FOR UPDATE SKIP LOCKED. Useful for
broadcast events like session revocation, notifications, and cross-node
chat in cloud-native deployments without distributed Erlang.

Each node polls with a time window (default 120s) and tracks seen job
IDs in a local ETS table for dedup. Workers must be idempotent since
jobs may be re-processed on node restart. Old jobs are pruned
automatically after 2x the window.

Config: {fanout_queues, [{<<"broadcast">>, 5, #{window => 120}}]}
@github-actions
Copy link
Copy Markdown

🔴 Code Coverage — 49.9%

640 of 1282 lines covered.


✅ ELP Lint

No diagnostics.


ℹ️ 11 OTP CVEs auto-ignored (already fixed in running version)

These CVEs are patched in the installed OTP version but NVD data
has not been updated to reflect this. They are excluded from the
scan via an auto-generated .trivyignore.

CVE Details
CVE-2026-23943 Fixed in 28.4.1, running 28.4.1 — Pre-auth SSH DoS via unbounded zlib inflate
CVE-2026-23942 Fixed in 28.4.1, running 28.4.1 — SFTP root escape via component-agnostic prefix check in ssh_sftpd
CVE-2026-23941 Fixed in 28.4.1, running 28.4.1 — Request smuggling via first-wins Content-Length parsing in inets httpd
CVE-2026-21620 Fixed in 28.3.2, running 28.4.1 — TFTP Path Traversal
CVE-2016-1000107 Fixed in 28.0.4, running 28.4.1 — Httpd CGI Scripts Environment Variable Pollution AKA "httpoxy"
CVE-2025-58050 Fixed in 28.0.3, running 28.4.1 — Buffer Read Overflow on Regular Expressions with (*scs:) and (*ACCEPT)
CVE-2025-48038 Fixed in 28.0.3, running 28.4.1 — SSH Unverified File Handles can Cause Excessive Use of System Resources
CVE-2025-48039 Fixed in 28.0.3, running 28.4.1 — SSH Unverified Paths can Cause Excessive Use of System Resources
CVE-2025-48040 Fixed in 28.0.3, running 28.4.1 — SSH Malicious Key Exchange Messages may Lead to Excessive Resource Consumption
CVE-2025-48041 Fixed in 28.0.3, running 28.4.1 — SSH_FXP_OPENDIR may Lead to Exhaustion of File Handles
CVE-2025-4748 Fixed in 28.0.1, running 28.4.1 — Absolute Path in Zip Module

@Taure Taure merged commit 7245c6e into main Mar 30, 2026
17 checks passed
@Taure Taure deleted the feat/fanout-queues branch March 30, 2026 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant