Skip to content

feat(local-elasticmq): Add ElasticMQ-backed local queue emulator#57

Open
mnzsss wants to merge 7 commits into
sbalmt:mainfrom
mnzsss:feat/local-elasticmq
Open

feat(local-elasticmq): Add ElasticMQ-backed local queue emulator#57
mnzsss wants to merge 7 commits into
sbalmt:mainfrom
mnzsss:feat/local-elasticmq

Conversation

@mnzsss

@mnzsss mnzsss commented May 18, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds a new @ez4/local-elasticmq provider that lets the local queue emulator run on top of ElasticMQ instead of the in-memory implementation. It also refactors @ez4/local-queue so the emulator can plug in an external client through a trigger, keeping the in-memory mode as the default fallback.

The new package is opt-in: install it next to @ez4/local-queue and local queues talk to a real SQS-compatible service. Don't install it and nothing changes — the in-memory emulator keeps working exactly as before.

Why

The in-memory emulator is great for fast tests but it diverges from how SQS actually behaves. FIFO ordering, deduplication windows, visibility timeouts, DLQ redrive — those are easy to get subtly wrong when the local implementation doesn't share the same semantics as the deployed one.

ElasticMQ ships a real SQS-compatible queue server, so pointing the emulator at it removes that gap. Local development and CI exercise the same wire protocol, the same FIFO/dedup rules, and the same client SDK the AWS provider uses in production. Bugs that only show up against real SQS now also show up locally.

The change is structured so adopting ElasticMQ stays a choice. Projects that don't need SQS-level fidelity keep the zero-setup in-memory mode. Projects that do install one extra package and add a couple of lines of localOptions.

What changed

@ez4/local-elasticmq (new package)

  • SQS SDK client factory with send/receive support and FIFO/fair-mode awareness
  • Queue topology management (standard, FIFO, DLQ wiring, purge)
  • Poller lifecycle that dispatches received messages to the registered subscribers
  • Options resolver with config validation for localOptions.queue (host/port/endpoint)
  • Registers emulator:getClient and emulator:getServices triggers so @ez4/local-queue picks it up automatically when present
  • Unit tests covering options, client, topology, and poller

@ez4/local-queue

  • Replace direct in-memory registration with registerQueueEmulator, which first asks for an external client via the emulator:getClient trigger and falls back to in-memory when none is registered
  • Move the shared message dispatch lifecycle (processQueueMessage, formerly processLambdaMessage) into @ez4/local-common so both emulators reuse it
  • Re-export the renamed handler from @ez4/local-queue for backwards compatibility
  • README documents the two emulator modes

@ez4/local-common

  • New shared processQueueMessage handler (begin/ready/done/error/end lifecycle, validation, scoped runtime)
  • Test coverage for the shared dispatch path

Examples and infra

  • examples/hello-local-elasticmq: standard, FIFO, and FIFO-with-deduplication queue configurations against the ElasticMQ emulator
  • docker-compose.yml: add the softwaremill/elasticmq-native service on port 9324
  • Register @ez4/local-elasticmq in the root workspace

Behavior

Before:

  • Local queues always ran on the in-memory emulator
  • FIFO ordering, deduplication, and DLQ behavior could drift from real SQS without anyone noticing locally
  • Tests that wanted SQS semantics had to either deploy or stand up their own external setup

After:

  • @ez4/local-queue keeps the in-memory emulator as default
  • Installing @ez4/local-elasticmq and configuring localOptions.queue switches local queues to ElasticMQ transparently
  • The same client SDK and the same queue semantics are used locally and in production
  • No code changes required in services or subscriptions to opt in

@mnzsss mnzsss changed the base branch from main to v0.48.0 May 18, 2026 04:53
@sbalmt sbalmt changed the base branch from v0.48.0 to main June 2, 2026 20:43
mnzsss added 6 commits June 23, 2026 17:36
Move processQueueMessage from @ez4/local-queue into @ez4/local-common
so both in-memory and ElasticMQ emulators can use the same dispatch
lifecycle. Rename from processLambdaMessage to processQueueMessage
and keep local-queue compatibility via re-export.
…pport

Replace direct in-memory registration with registerQueueEmulator wrapper
that checks for external clients via emulator:getClient trigger. Falls
back to in-memory mode when no external provider is registered.
New provider package that integrates with @ez4/local-queue via
emulator triggers to provide SQS-compatible local queue emulation
using ElasticMQ. Includes:

- SQS SDK client factory with send/receive message support
- Queue topology management (standard, FIFO, DLQ, purge)
- Poller lifecycle with subscriber dispatch
- Local and test options resolution with config validation
- Unit tests covering options, client, topology, and poller
Example project demonstrating ElasticMQ-backed local queue emulation
with standard, FIFO, and deduplication FIFO queue configurations.
Add elasticmq-native service to docker-compose, register
@ez4/local-elasticmq workspace in root package.json, and
document emulator modes in local-queue README.
The local-queue emulator now returns null instead of creating a
default queue client when no external client is available. The
ElasticMQ emulator registers an 'emulator:getClient' handler to
provide its own client and passes the real service context through
instead of a mock.
@mnzsss mnzsss force-pushed the feat/local-elasticmq branch from 5f4e960 to b25a1c4 Compare June 23, 2026 22:56
local-elasticmq was pinned at 0.47.0 with ^0.47.0 ez4 deps while the
rest of the monorepo is 0.48.0. Since ^0.47.0 does not satisfy 0.48.0,
npm installed the published @ez4/local-common@0.47.0 (predating
processQueueMessage) instead of symlinking the workspace, breaking the
CI build. Bump version and deps to 0.48.0 so the workspace resolves.
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