Install
$ agentstack add skill-agent-sh-glidemq-glide-mq-migrate-bee ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Migrate from Bee-Queue to glide-mq
When to Apply
Use this skill when:
- Replacing bee-queue with glide-mq in an existing project
- Converting Bee-Queue's chained job API to glide-mq's options API
- Updating connection configuration from ioredis to valkey-glide
- Upgrading from bee-queue due to Node.js compatibility or maintenance issues
Step-by-step guide for converting Bee-Queue projects to glide-mq. Bee-Queue uses a chained job builder pattern - this migration requires rewriting job creation and separating producer/consumer concerns.
Why Migrate
- Unmaintained - last release 2021, accumulating Node.js compatibility issues
- No cluster support - cannot scale beyond a single Redis instance
- No TLS - requires manual ioredis workarounds for encrypted connections
- No native TypeScript - community
@types/bee-queueonly, often outdated - No priority queues - workaround is multiple queues
- No workflows - no parent-child jobs, no DAGs, no repeatable/cron jobs
- No rate limiting, batch processing, or broadcast
- glide-mq provides all Bee-Queue features plus 35%+ higher throughput
Breaking Changes Summary
| Feature | Bee-Queue | glide-mq | |---------|-----------|----------| | Queue + Worker | Single Queue class | Separate Queue (producer) and Worker (consumer) | | Job creation | queue.createJob(data).save() (chained) | queue.add(name, data, opts) (single call) | | Job name | Not used - no name parameter | Required first argument to queue.add() | | Job options | Chained: .timeout(ms).retries(n) | Options object: { attempts, backoff, delay } | | Retries | .retries(n) | { attempts: n } (different name!) | | Processing | queue.process(concurrency, handler) | new Worker(name, handler, { concurrency }) | | Connection | { host, port } or redis URL | { addresses: [{ host, port }] } | | Progress | job.reportProgress(anyJSON) | job.updateProgress(number \| object) (number 0-100 or object) | | Per-job events | job.on('succeeded', ...) | QueueEvents class (centralized) | | Stall detection | Manual checkStalledJobs() | Automatic on Worker | | Batch save | queue.saveAll(jobs) | queue.addBulk(jobs) | | Producer-only | { isWorker: false } | Producer class or just Queue |
Queue Settings Mapping
| Bee-Queue Setting | Default | glide-mq Equivalent | Notes | |-------------------|---------|---------------------|-------| | redis | {} | connection: { addresses: [...] } | Array of { host, port } objects | | isWorker | true | Use Producer or Queue class | Separate classes replace flag | | getEvents | true | Use QueueEvents class | Separate class for event subscription | | sendEvents | true | events: true on Worker | Controls lifecycle event emission | | storeJobs | true | Always true | glide-mq always stores jobs | | ensureScripts | true | Automatic | Server Functions loaded automatically | | activateDelayedJobs | false | Automatic | Server-side delayed job activation | | removeOnSuccess | false | { removeOnComplete: true } | Per-job option on queue.add() | | removeOnFailure | false | { removeOnFail: true } | Per-job option on queue.add() | | stallInterval | 5000 | lockDuration on Worker | Lock-based stall detection | | nearTermWindow | 20min | N/A | Valkey-native delayed processing | | delayedDebounce | 1000 | N/A | Server-side scheduling | | prefix | 'bq' | prefix on Queue | Default: 'glide' | | quitCommandClient | true | Automatic | Handled by graceful shutdown | | redisScanCount | 100 | N/A | Different key strategy |
Queue Method Mapping
| Bee-Queue Method | glide-mq Equivalent | Notes | |------------------|---------------------|-------| | queue.createJob(data) | queue.add(name, data, opts) | Name is required; returns Job not builder | | queue.process(n, handler) | new Worker(name, handler, { concurrency: n }) | Separate class | | queue.checkStalledJobs(interval) | Automatic on Worker | No manual call needed | | queue.checkHealth() | queue.getJobCounts() | Returns { waiting, active, completed, failed, delayed } | | queue.close() | gracefulShutdown([...]) | Or individual .close() calls | | queue.ready() | worker.waitUntilReady() | On Worker, not Queue | | queue.isRunning() | worker.isRunning() | On Worker | | queue.getJob(id) | queue.getJob(id) | Same API | | queue.getJobs(type, page) | queue.getJobs(type, start, end) | Range-based pagination | | queue.removeJob(id) | (await queue.getJob(id)).remove() | Via Job instance | | queue.saveAll(jobs) | queue.addBulk(jobs) | Different input format | | queue.destroy() | queue.obliterate() | Removes all queue data |
Event Mapping
| Bee-Queue Event | Source | glide-mq Equivalent | Source | |-----------------|--------|---------------------|--------| | queue.on('ready') | Queue | worker.waitUntilReady() | Worker | | queue.on('error', err) | Queue | worker.on('error', err) | Worker | | queue.on('succeeded', job, result) | Queue (local) | worker.on('completed', job) | Worker | | queue.on('retrying', job, err) | Queue (local) | worker.on('failed', job, err) | Worker (with retries remaining) | | queue.on('failed', job, err) | Queue (local) | worker.on('failed', job, err) | Worker | | queue.on('stalled', jobId) | Queue | worker.on('stalled', jobId) | Worker | | queue.on('job succeeded', id, result) | Queue (PubSub) | events.on('completed', { jobId }) | QueueEvents | | queue.on('job failed', id, err) | Queue (PubSub) | events.on('failed', { jobId }) | QueueEvents | | queue.on('job retrying', id, err) | Queue (PubSub) | No direct equivalent | Use events.on('failed') + retry check | | queue.on('job progress', id, data) | Queue (PubSub) | events.on('progress', { jobId, data }) | QueueEvents | | job.on('succeeded', result) | Job | events.on('completed', { jobId }) | QueueEvents (filter by jobId) | | job.on('failed', err) | Job | events.on('failed', { jobId }) | QueueEvents (filter by jobId) | | job.on('progress', data) | Job | events.on('progress', { jobId }) | QueueEvents (filter by jobId) |
Per-job events (job.on(...)) do not exist in glide-mq. Use QueueEvents and filter by jobId, or use queue.addAndWait() for request-reply patterns.
Step-by-Step Conversion
1. Connection
// BEFORE (Bee-Queue)
const Queue = require('bee-queue');
const queue = new Queue('tasks', {
redis: { host: 'localhost', port: 6379 }
});
// AFTER (glide-mq)
import { Queue, Worker } from 'glide-mq';
const connection = { addresses: [{ host: 'localhost', port: 6379 }] };
const queue = new Queue('tasks', { connection });
2. Job Creation (Biggest Change)
Bee-Queue uses chained builder with no job name. glide-mq uses a single call with a required name.
// BEFORE (Bee-Queue) - chained builder, no name
const job = await queue.createJob({ email: 'user@example.com' })
.retries(3)
.backoff('exponential', 1000)
.delayUntil(Date.now() + 60000)
.setId('unique-123')
.save();
// AFTER (glide-mq) - options object, name required
await queue.add('send-email',
{ email: 'user@example.com' },
{
attempts: 3, // NOT "retries" - different name!
backoff: { type: 'exponential', delay: 1000 },
delay: 60000,
jobId: 'unique-123',
}
);
3. Worker
// BEFORE (Bee-Queue)
queue.process(10, async (job) => {
return { processed: true };
});
queue.on('succeeded', (job, result) => console.log('Done:', result));
// AFTER (glide-mq) - separate Worker class
const worker = new Worker('tasks', async (job) => {
return { processed: true };
}, { connection, concurrency: 10 });
worker.on('completed', (job) => console.log('Done:', job.returnValue));
4. Batch Save
// BEFORE (Bee-Queue)
const jobs = items.map(item => queue.createJob(item));
await queue.saveAll(jobs);
// AFTER (glide-mq) - each entry needs a name
await queue.addBulk(items.map(item => ({
name: 'process',
data: item
})));
5. Producer-Only
// BEFORE (Bee-Queue) - disable worker mode
const queue = new Queue('tasks', {
isWorker: false, getEvents: false, sendEvents: false,
redis: { host: 'localhost', port: 6379 }
});
// AFTER (glide-mq) - Producer class
import { Producer } from 'glide-mq';
const producer = new Producer('tasks', { connection });
await producer.add('job-name', data);
await producer.close();
6. Progress Reporting
// BEFORE (Bee-Queue) - arbitrary JSON
queue.process(async (job) => {
job.reportProgress({ percent: 50, message: 'halfway' });
return result;
});
// AFTER (glide-mq) - number (0-100) or object
const worker = new Worker('tasks', async (job) => {
await job.updateProgress(50);
await job.updateProgress({ page: 3, total: 10 }); // objects also supported
await job.log('halfway done'); // structured info goes to job.log()
return result;
}, { connection });
7. Stall Detection
// BEFORE (Bee-Queue) - manual setup required
const queue = new Queue('tasks', { stallInterval: 5000 });
queue.checkStalledJobs(5000); // must call manually!
// AFTER (glide-mq) - automatic on Worker
const worker = new Worker('tasks', processor, {
connection,
lockDuration: 30000,
stalledInterval: 30000,
maxStalledCount: 2
});
// Stall detection runs automatically - no manual call
8. Health Check
// BEFORE (Bee-Queue)
const health = await queue.checkHealth();
// { waiting, active, succeeded, failed, delayed, newestJob }
// AFTER (glide-mq)
const counts = await queue.getJobCounts();
// { waiting, active, completed, failed, delayed }
9. Web UI (Arena to Dashboard)
// BEFORE (Bee-Queue) - Arena
const Arena = require('bull-arena');
app.use('/', Arena({ Bee: require('bee-queue'), queues: [{ name: 'tasks' }] }));
// AFTER (glide-mq) - Dashboard
import { createDashboard } from '@glidemq/dashboard';
app.use('/dashboard', createDashboard([queue]));
What You Gain
Features Bee-Queue does not have that are available after migration:
| Feature | glide-mq API | |---------|-------------| | Priority queues | { priority: 0 } (lower = higher, 0 is highest) | | FlowProducer | Parent-child job trees and DAG workflows | | Broadcast | Fan-out with subscriber groups | | Batch processing | Process multiple jobs per worker call | | Deduplication | Simple, throttle, and debounce modes | | Schedulers | Cron patterns and interval repeatable jobs | | Rate limiting | limiter: { max: 100, duration: 60000 } on Worker | | LIFO mode | Process newest jobs first with { lifo: true } | | Dead letter queue | deadLetterQueue: { name: 'dlq' } on Queue | | Serverless pool | Connection caching for Lambda/Edge | | HTTP proxy | Cross-language queue access via REST | | OpenTelemetry | Automatic span emission | | Testing utilities | TestQueue/TestWorker without Valkey | | Cluster support | Hash-tagged keys, AZ-affinity routing | | TLS / IAM auth | useTLS: true, IAM credentials for ElastiCache | | Native TypeScript | Full generic type support throughout | | AI usage tracking | job.reportUsage({ model, tokens, costs, ... }) | | Token streaming | job.stream() / queue.readStream() for real-time LLM output | | Suspend/resume | job.suspend() / queue.signal() for human-in-the-loop | | Flow budget | flow.add(tree, { budget: { maxTotalTokens } }) | | Fallback chains | opts.fallbacks: [{ model, provider }] | | Dual-axis rate limiting | tokenLimiter for RPM + TPM compliance | | Vector search | queue.createJobIndex() / queue.vectorSearch() |
Migration Checklist
- [ ] Install glide-mq, uninstall bee-queue and @types/bee-queue
- [ ] Create connection config (addresses array format)
- [ ] Convert queue.createJob().save() to queue.add(name, data, opts)
- [ ] Add job names to every queue.add() call (Bee-Queue had none)
- [ ] Convert .retries(n) to { attempts: n } (different name!)
- [ ] Convert .backoff(strategy, delay) to { backoff: { type, delay } }
- [ ] Convert .delayUntil(date) to { delay: ms }
- [ ] Convert .setId(id) to { jobId: id }
- [ ] Convert queue.process() to new Worker()
- [ ] Convert queue.saveAll() to queue.addBulk()
- [ ] Separate producer queues (isWorker:false to Producer class)
- [ ] Convert job.reportProgress(json) to job.updateProgress(number | object)
- [ ] Remove manual checkStalledJobs() calls (automatic on Worker)
- [ ] Convert checkHealth() to getJobCounts()
- [ ] Update event listeners (queue.on to worker.on or QueueEvents)
- [ ] Convert per-job events (job.on) to QueueEvents
- [ ] Keep the project's existing module system (CommonJS or ESM)
- [ ] Run full test suite
- [ ] Confirm queue counts: await queue.getJobCounts()
- [ ] Confirm no jobs stuck in active state
- [ ] Smoke-test QueueEvents or SSE listeners if the app exposes them
- [ ] Confirm workers, queues, and connections close cleanly
Troubleshooting
| Error | Cause | Fix | |-------|-------|-----| | queue.createJob is not a function | API changed | Use queue.add(name, data, opts) | | queue.process is not a function | Separated producer/consumer | Use new Worker(name, handler, opts) | | Cannot use require() | Module system mismatch | Keep the project's existing module system; glide-mq supports CommonJS and ESM | | job.reportProgress is not a function | API renamed | Use job.updateProgress(number) | | Cannot find module 'bee-queue' | Leftover import | grep -r "bee-queue" src/ to find remaining | | Missing job name | Bee-Queue had no name | Add a name as first arg to queue.add() | | retries option not recognized | Different name | Use attempts not retries | | No stall detection | Bee-Queue needed manual start | glide-mq runs it automatically on Worker | | Progress type changed | Bee-Queue accepted any JSON | Use job.updateProgress(number \| object) - numbers (0-100) or objects supported | | Per-job events not working | No per-job events in glide-mq | Use QueueEvents class and filter by jobId |
Quick Start Commands
npm uninstall bee-queue @types/bee-queue
npm install glide-mq
References
| Document | Content | |----------|---------| | [references/api-mapping.md](references/api-mapping.md) | Complete method-by-method API mapping | | [references/new-features.md](references/new-features.md) | Features available after migration |
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: agent-sh
- Source: agent-sh/glidemq
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.