actually we are using keys method to retrieve retained messages from redis. This is generally a bad approach as stated here.
I also think we could someway improving scan to do some pattern matching (when possible).
Example:
async getRetainedMessages(patterns) {
const qOpts = qlobberOptions;
const packets = [];
const wildcardPatterns = [];
const qlobber = new QlobberTrue(qlobberOptions);
for (const p of patterns) {
if (!p.includes(qOpts.wildcard_some) || !p.includes(qOpts.wildcard_one)) {
const packet = await this.getRetainedMessage(p);
if (packet) {
packets.push(packet);
}
} else {
qlobber.add(p);
wildcardPatterns.push(p);
}
}
patterns = wildcardPatterns;
if (patterns.length === 0) return packets;
// use hscan to iterate over all RETAINEDKEY keys
let cursor = '0';
const patternsKey = this.getPatternsKey(patterns);
do {
// in NON-CLUSTER mode we store using `HSET` so we have to use `HSCAN` to iterate over the keys
const [newCursor, keys] = this.isCluster
? await this.redisClient.scanBuffer(cursor, 'MATCH', patternsKey)
: await this.redisClient.hscanBuffer(
RETAINEDKEY,
cursor,
'MATCH',
patternsKey
);
cursor = newCursor.toString();
for (let i = 0; i < keys.length; i += 2) {
const topic = this.isCluster
? decodeURIComponent(keys[i].toString().split(':')[1])
: keys[i];
if (qlobber.test(topic.toString())) {
const p = this.decodeRetainedPacket(keys[i + 1]);
if (p) {
packets.push(p);
}
}
}
} while (cursor !== '0');
return packets;
}
/**
* Returns an optimized redis key to look for when matching provided patterns
* prevents to iterate over ALL retained keys when looking for a pattern
*/
getPatternsKey(patterns): string {
let topic = '';
let stop = false;
let charIndex = 0;
while (!stop) {
const char = patterns[0][charIndex];
// ensure all patterns have the same char at the same index
// stop if pattern ends or char is different or is a wildcard
for (const pattern of patterns) {
if (
pattern.length <= charIndex ||
pattern[charIndex] !== char ||
[
qlobberOptions.wildcard_one,
qlobberOptions.wildcard_some,
].includes(char)
) {
stop = true;
break;
}
}
if (!stop) {
topic += char;
charIndex++;
}
}
return (this.isCluster ? this.retainedKey(topic) : topic) + '*';
}
@seriousme thoughts on this?
actually we are using
keysmethod to retrieve retained messages from redis. This is generally a bad approach as stated here.I also think we could someway improving scan to do some pattern matching (when possible).
Example:
@seriousme thoughts on this?