redis / ioredis

🚀 A robust, performance-focused, and full-featured Redis client for Node.js.
MIT License
14.39k stars 1.2k forks source link

select being called after subscribe on autopipeline + enableOfflineQueue #1505

Open pjpoole opened 2 years ago

pjpoole commented 2 years ago

Redis version: 4.28.5

I have a pretty simple singleton subscriber instance. It is used on a redis instance (underlying running AWS ElastiCache, non-clustered, v6.0.5) only for subscribe queries.

The pattern used is, I create or retrieve the singleton, not waiting for the ready event, call subscribe, and listen for responses.

Between 3 and 20ms after the ready event fires, I get an error from redis due to the select query being fired presumably after the subscribe query that would have been placed in the offline queue.

The configuration is

{
  port: options.port,
  host: options.host,
  password: options.password,
  db: options.database, // value between 0 and 4
  enableReadyCheck: true,
  enableAutoPipelining: true
}

When enableAutoPipelining is removed or set to false, the error does not reproduce.

[In order to work around this issue, I'm going to wire up a queue outside ioredis and only feed queries in after ready fires... or, at least, that'll be my next attempt]

Error: ReplyError: ERR Can't execute 'select': only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT are allowed in this context

Stack:

    at Redis.emit (events.js:314:20)
    at Redis.EventEmitter.emit (domain.js:483:12)
    at Redis.silentEmit (/home/ec2-user/bubble/node_modules/ioredis/built/redis/index.js:553:26)
    at /home/ec2-user/bubble/node_modules/ioredis/built/redis/event_handler.js:42:22
    at processTicksAndRejections (internal/process/task_queues.js:97:5)

Edit: ~minimal reproduction

const Redis = require('ioredis')

const options = {
  port: 6379,
  host: 'localhost',
  db: 6,
  enableReadyCheck: true,
  enableAutoPipelining: true
}

function frob () {
  const redis = new Redis(options)
  redis.on('error', function (err) {
    console.log('Error connecting to redis')
    console.log(err)
  })
  redis.subscribe('frob')
}

frob()

Edit 2: After a good deal of investigation, I have reason to believe that non-subscriber clients on dbs other than 0 are sending some portion of their commands to db 0 before the first select gets issued under the same conditions as this issue.

spirin22 commented 1 year ago

Today I faced same issue, when upgraded bull and ioredis in one of my projects. I previously used enableAutoPipelining: true both for Redis storage and queue connections. Disabling enableAutoPipelining for my queues solved the issue.

pjpoole commented 1 year ago

[In order to work around this issue, I'm going to wire up a queue outside ioredis and only feed queries in after ready fires... or, at least, that'll be my next attempt]

Turns out the select is still firing even though we wait for the ready event with an external queue, just not as often