redis/ioredis

bug? keyPrefix not added to the KEYS command

Volox opened this issue · 6 comments

Volox commented

Hi,
i have a problem with the KEYS command and the use of keyPrefix. It seems that using a prefixed connection the prefix is not passed to the KEYS command.

I created a simple snippet of code to reproduce the problem.

'use strict';
let Promise = require( 'bluebird' );
let Redis = require( 'ioredis' );

const prefix = 'NS:'
// Create an unprefixed connection
let redisNP = new Redis();
// Create an prefixed connection
let redisP = new Redis( {
  keyPrefix: prefix,
} );


let key = 'test:namespace:test';
let matchKey = 'test:*:test';
const data = {
  mytest: 'true',
  'my test2': 5,
};

// Add a namespaced(prefixed) key
redisP.hmset( key, data )
.then( () => {
  // Issue the keys command to the connections
  return Promise
  .props( {
    // Try the prefixed connection
    prefix: redisP.keys( matchKey ),
    // Try the unprefixed connection
    noPrefix: redisNP.keys( matchKey ),
    // Try to manual add the prefix to the  unprefixed connection
    manualPrefix: redisNP.keys( prefix+matchKey ),
  } );
} )
.then( results => {
  // I have 0 prefixed keys, should be 1
  console.log( 'Should be 1', results.prefix.length );
  // I have 0 unprefixed keys, OK
  console.log( 'Should be 0', results.noPrefix.length );
  // I have 1 manually prefixed keys, OK
  console.log( 'Should be 1', results.manualPrefix.length );
  console.log( 'Results: %j', results );
} )
.catch( err => console.error( err, err.stack ) )
.then( () => {
  return [
    redisP.quit(),
    redisNP.quit(),
  ];
} );
luin commented

Actually it's not a bug. ioredis relies on the COMMAND command of Redis to get the position of the keys in a command's parameters. For keys (as well as scan) command, Redis tells ioredis that it doesn't contain a key, so that ioredis won't prefix it.

Volox commented

Ah ok, i got the point.
But since i am requiring keys it would be nice/useful to use this command transparently no?

How are you able to determine what keys you actually have in redis, if the keys parameter would be prefixed? This is not possible in that case, so if you want to have the keys command prefixed you might consider adding a arguments transformer to it to always prefix the argument. I do not recommend it though.

Volox commented

Ok,
but once i got the keys from the keys command i cannot use them coherently with the other commands.
Here is an example:

// Just pretend that it works without promises/callbacks :)

// prefx: "NS:"
let keys = redisP.keys( '*' ); // keys = [ "key1", "NS:key2" ]

// This will NEVER get "key1" because the get is namespaced
let val = redisP.get( keys[0] );
// also i cannot get "NS:key2" because i will get "NS:NS:key2" instead
luin commented

@Volox I agree with you that it is of course a little inconvenience for people who want to use the keyPrefix option as namespaces in order to have multiple applications share a single redis instance.

However, keyPrefix literally means prefix keys with a string, but the parameter of KEY command is a pattern instead of a key name, so that it won't be prefixed.

It's not hard to make the pattern also be prefixed, but I don't want to let ioredis do too much magic. There are other commands that should be prefixed if we handle the KEYS command, for instance, SCAN.

I don't want to mislead people into thinking they can reply on the keyPrefix as namespaces. For example, FLUSHDB actually deletes all the keys in the redis instead of the keys whose name started with keyPrefix.

The workaround is to create another instance for KEYS and SCAN command or just create a wrapper yourself.

Volox commented

Thanks to all for the support, I see there is no easy solution for this.