OptimalBits / bull

Premium Queue package for handling distributed jobs and messages in NodeJS.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Bug] The property 'options.family' must be one of: 0, 4, 6. Received '4'

nktnet1 opened this issue · comments

Description

When a REDIS_URL specifies a "family" in the URL, e.g.

redis://@redis:50004/?family=4

it is not type-casted to number, resulting in the error

node:dns:179
          validateOneOf(options.family, 'options.family', validFamilies);
          ^

TypeError [ERR_INVALID_ARG_VALUE]: The property 'options.family' must be one of: 0, 4, 6. Received '4'
    at lookup (node:dns:179:11)
    at emitLookup (node:net:1412:5)
    at defaultTriggerAsyncIdScope (node:internal/async_hooks:464:18)
    at lookupAndConnectMultiple (node:net:1411:3)
    at node:net:1357:7
    at defaultTriggerAsyncIdScope (node:internal/async_hooks:464:18)
    at lookupAndConnect (node:net:1356:5)
    at Socket.connect (node:net:1253:5)
    at connect (node:net:236:17)
    at /home/nktnet/temp/bull/node_modules/ioredis/built/connectors/StandaloneConnector.js:54:66 {
  code: 'ERR_INVALID_ARG_VALUE'
}

Minimal, Working Test code to reproduce the issue.

const Queue = require('bull');

const myQueue = new Queue('myQueue', 'redis://@redis:50004/?family=4');

myQueue.process(() => {});

Bull version

4.12.2

Additional information

1707728023

  • Discovered from nestjs/bull#2001
  • My use case is for a URL that requires family=6, e.g.
    redis://default:c9ee1f12c98411ee8dcf93e49a39cc81@hopper.redis.io:6379/?family=6
    

One potential workaround could be to change the redisOptsFromUrl function

bull/lib/queue.js

Lines 319 to 341 in 60fa88f

function redisOptsFromUrl(urlString) {
let redisOpts = {};
try {
const redisUrl = url.parse(urlString, true, true);
redisOpts.port = parseInt(redisUrl.port || '6379', 10);
redisOpts.host = redisUrl.hostname;
redisOpts.db = redisUrl.pathname ? redisUrl.pathname.split('/')[1] : 0;
if (redisUrl.auth) {
const columnIndex = redisUrl.auth.indexOf(':');
redisOpts.password = redisUrl.auth.slice(columnIndex + 1);
if (columnIndex > 0) {
redisOpts.username = redisUrl.auth.slice(0, columnIndex);
}
}
if (redisUrl.query) {
redisOpts = { ...redisOpts, ...redisUrl.query };
}
} catch (e) {
throw new Error(e.message);
}
return redisOpts;
}

to typecast all numbers, e.g.

function redisOptsFromUrl(urlString) {
  let redisOpts = {};
  try {
    const redisUrl = url.parse(urlString, true, true);
    redisOpts.port = parseInt(redisUrl.port || '6379', 10);
    redisOpts.host = redisUrl.hostname;
    redisOpts.db = redisUrl.pathname ? redisUrl.pathname.split('/')[1] : 0;
    if (redisUrl.auth) {
      const columnIndex = redisUrl.auth.indexOf(':');
      redisOpts.password = redisUrl.auth.slice(columnIndex + 1);
      if (columnIndex > 0) {
        redisOpts.username = redisUrl.auth.slice(0, columnIndex);
      }
    }

    if (redisUrl.query) {
      for (let key in redisUrl.query) {
        if (!isNaN(redisUrl.query[key])) {
          redisOpts[key] = parseInt(redisUrl.query[key], 10);
        } else {
          redisOpts[key] = redisUrl.query[key];
        }
      }
    }
  } catch (e) {
    throw new Error(e.message);
  }
  return redisOpts;
}
17c17,23
<        redisOpts = { ...redisOpts, ...redisUrl.query };
---
>       for (let key in redisUrl.query) {
>         if (!isNaN(redisUrl.query[key])) {
>           redisOpts[key] = parseInt(redisUrl.query[key], 10);
>         } else {
>           redisOpts[key] = redisUrl.query[key];
>         }
>       }

although not too sure about any side effects.

I am not sure this is an issue in Bull or in IORedis. However you should be able to workaround it by just passing the family options separately:

const myQueue = new Queue('myQueue', 'redis://@redis:50004/', { redis: { family: 4 } });

Hi @manast,

Thanks for the reply. I use both IOredis and Bull and IOredis parses it correctly.

The issue is with this line of code for Bull, which parses all query parameters as string:

redisOpts = { ...redisOpts, ...redisUrl.query };

In development, I use family=4, but in production it's family=6, so the workaround I came up with is in the original comment where I just defined my own redisOptsFromUrl function and pass in those parsed options instead :).

Thinking back now, why does bull need the redisOptsFromUrl function to parse the URL at all?
Maybe it's sufficient to simply forward the REDIS_URL to IOredis directly?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

not stale, potentially