From b67b1585c2be71d7d42517668ae5dbfa1f4d4780 Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Tue, 4 Aug 2026 16:17:21 +0200 Subject: [PATCH] CB-517: deploy LavinMQ as a pinned, durable, self-restarting broker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CB-307 durable ReplyInbox needs an AMQP broker, but the one behind it was run ad hoc and had simply vanished from the host — which takes the whole daemon with it, since AmqpReplyInbox.open throws and Bridged.java:187 does not guard it. A missing broker is a hard startup failure, not a degraded mode, so 'how the broker runs' is part of the system, not a local detail. Pinned to 2.9.1 (:latest would move the broker under a running daemon), data on a named volume (held-but-unacked replies are the entire point of Stage 2 — a plain 'compose down' would discard exactly what durability protects), and restart: unless-stopped so it comes back after a reboot instead of disappearing again. Ports are bound to 127.0.0.1 deliberately: LavinMQ ships a default guest/guest account, which is only acceptable while nothing off-host can reach it. Verified by driving the production AmqpReplyInbox against this deployment (publish/peek/dedup/FIFO/ack, then reconnect): 8/8 including redelivery of the unacked message. That pairing had never been exercised — the @Tag("contract") test runs against a RabbitMQ container, and is excluded from the default build, so mvn clean install covers the broker path zero times. --- deploy/lavinmq/compose.yaml | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 deploy/lavinmq/compose.yaml diff --git a/deploy/lavinmq/compose.yaml b/deploy/lavinmq/compose.yaml new file mode 100644 index 0000000..0e5fd36 --- /dev/null +++ b/deploy/lavinmq/compose.yaml @@ -0,0 +1,60 @@ +# LavinMQ — the AMQP broker behind bridged's durable ReplyInbox (CB-307 Stage 2). +# +# Why this file exists: the broker was previously run ad hoc and simply vanished from the host, +# which takes bridged down with it — AmqpReplyInbox.open throws on an unreachable broker and +# Bridged.java:187 does not guard it, so a missing broker is a hard startup failure, not a +# degraded mode. This pins the version, keeps the data, and brings itself back after a reboot. +# +# Usage: +# docker compose -f deploy/lavinmq/compose.yaml up -d +# docker compose -f deploy/lavinmq/compose.yaml ps +# docker compose -f deploy/lavinmq/compose.yaml logs -f +# docker compose -f deploy/lavinmq/compose.yaml down # keeps the volume +# docker compose -f deploy/lavinmq/compose.yaml down -v # DESTROYS held replies +# +# Management UI: http://127.0.0.1:15672 (guest / guest) +# +# This is bridged's OWN broker. Do not point bridged at any other AMQP server on this host — +# notably not the `local-rabbitmq` container, which belongs to a different project and would end +# up carrying this project's queues. + +name: bridged-broker + +services: + lavinmq: + # Pinned deliberately: :latest silently moves the broker under a running daemon. + image: cloudamqp/lavinmq:2.9.1 + container_name: bridged-lavinmq + + # The failure this deployment exists to prevent — survive reboots and Docker restarts, but + # stay down if it was stopped on purpose. + restart: unless-stopped + + # Loopback-bound on purpose. LavinMQ ships a default guest/guest account, which is only + # acceptable because nothing off-host can reach it. bridged connects over 127.0.0.1, and + # binding 0.0.0.0 here would expose a broker with default credentials to the network. + ports: + - "127.0.0.1:5672:5672" # AMQP — bridged.yaml broker.uri points here + - "127.0.0.1:15672:15672" # HTTP management API + UI + + # The whole point of Stage 2. Held-but-unacked replies live here; without a named volume a + # `docker compose down` would discard exactly what the durable inbox exists to protect. + volumes: + - lavinmq-data:/var/lib/lavinmq + + healthcheck: + test: ["CMD", "lavinmqctl", "status"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + +volumes: + lavinmq-data: + name: bridged-lavinmq-data