Skip to content

feat(infra): use a message queue service for IPC#477

Open
wpf500 wants to merge 1 commit into
mainfrom
feat/better-broadcast
Open

feat(infra): use a message queue service for IPC#477
wpf500 wants to merge 1 commit into
mainfrom
feat/better-broadcast

Conversation

@wpf500
Copy link
Copy Markdown
Member

@wpf500 wpf500 commented Feb 16, 2026

This is part of a series of PR to make beabee ready for Kubernetes.

To move to Kubernetes we need to remove the fixed Docker-style references to other services (e.g. api_app, webhook_app). This could be simply replaced with Kubernetes-style references, but to keep compatibility with Docker stacks (e.g. for self hosters), and because in general it's a better solution, this PR adds Redis as a message queue service for IPC.

@wpf500 wpf500 force-pushed the feat/better-broadcast branch 2 times, most recently from c17cc47 to 7d4c819 Compare February 16, 2026 13:43
@wpf500 wpf500 force-pushed the feat/better-broadcast branch from 7d4c819 to 0be4966 Compare February 16, 2026 13:51
@wpf500 wpf500 marked this pull request as ready for review February 17, 2026 08:27
@wpf500 wpf500 added this to the Kubernetes migration milestone Mar 10, 2026
@wpf500 wpf500 requested a review from JumpLink March 12, 2026 10:46
Copy link
Copy Markdown
Collaborator

@JumpLink JumpLink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good approach overall 👍

A few issues should be addressed before merging — see inline comments.

Additional notes (not on diff lines)

serviceSecret is now unused (packages/core/src/config/config.ts:166)
BEABEE_SERVICE_SECRET / config.serviceSecret was only used by the removed NetworkCommunicatorService for JWT signing. Since Redis Pub/Sub doesn't need JWT auth, this config entry and env var can be removed — either here or as a follow-up.

log.error('Uncaught error', err);
}
await db.close();
await mq.close();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: SIGTERM handler does not close Redis connections

mq.close() is correctly called here in runApp(), but the startServer() SIGTERM handler (line 30) only calls db.close()mq.close() is missing there. This can cause hanging Redis connections during Kubernetes pod shutdowns.

const log = mainLogger.child({ app: 'message-queue' });

const subClient = createClient({ url: config.messageQueueUrl });
const pubClient = subClient.duplicate();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error event handlers on Redis clients

The redis client emits error events, and unhandled EventEmitter errors will crash the Node.js process. Both clients need error handlers:

subClient.on("error", (err) => log.error("Redis sub client error", err));
pubClient.on("error", (err) => log.error("Redis pub client error", err));


const log = mainLogger.child({ app: 'message-queue' });

const subClient = createClient({ url: config.messageQueueUrl });
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Module-level side effects

Redis clients are created immediately on import. This means any code that imports message-queue (including transitively via OptionsService) requires a valid Redis URL at import time, which makes testing harder.

Consider deferring client creation into connect(). Not a blocker for this PR.

Comment thread .env.example
BEABEE_DATABASE_URL=postgres://membership_system:membership_system@db/membership_system

# Redis connection URL [REQUIRED]
BEABEE_MESSAGEQUEUE_URL=redis://redis:6379
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Host mismatch with docker-compose service name

The Docker Compose service is named mq, not redis. This default URL will not resolve within the Docker network.

Suggested change
BEABEE_MESSAGEQUEUE_URL=redis://redis:6379
BEABEE_MESSAGEQUEUE_URL=redis://mq:6379


export async function connect(): Promise<void> {
await subClient.connect();
await subClient.subscribe('service:broadcast', handleMessage);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reconnect handling for subscriber

The redis v5 client has auto-reconnect, but in subscriber mode subscriptions need to be re-established after reconnect. If the connection drops, the service:broadcast subscription will be lost silently. Not critical for dev, but important for production reliability.

Comment thread packages/core/src/services/OptionsService.ts
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.

2 participants