node-redis
node-redis copied to clipboard
REDIS waited for Delete appears to not be immediately effective
Environment:
- Node.js Version: v14.19.3
- Redis Server Version: redis-3.2.12-2.el7.x86_64
- Node Redis Version: ^2.8.0
- Platform: Centos 7
The code at issue runs every 5 minutes and about 7% of the time it would appear that the delete does not throw but is not effective since the key can still be obtained with a subsequent get.
I had thought that with this code the client and the server actions would be synchronous and therefore if the delete returned from the await then it must have been successful.
Any thoughts? - thanks.
Code at issue
` const redis = require("./redis"); ... await redis.setAsync("HealthTestKey", "HealthTestValue");
const healthTestKey = await redis.getAsync("HealthTestKey");
if (healthTestKey !== "HealthTestValue") {
...
// not going here
} else {
// Delete key value pair
await redis.delAsync("HealthTestKey");
const healthTestKey = await redis.getAsync("HealthTestKey");
if (!healthTestKey) {
...
// not going here
} else {
...
// goes in here around 7% of the time - suggests the delete was not immediately effective
}
}
} catch (e) { ... // does not go in here`
Client library code:
` const { promisify } = require("util");
const logger = require("./apiLogger");
const redis = require("redis");
require("dotenv").config();
const redisUrl =
process.env.REDIS_PASSWORD && process.env.REDIS_HOST
? `redis://:${process.env.REDIS_PASSWORD}@${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`
: undefined;
const redisClient = redis.createClient(redisUrl);
redisClient.on("error", async (e) => {
const { recordInternalError } = require("./healthCheck");
logger.error(`Failed to initialise REDIS Client: ${e.message} ${e.stack}`);
await recordInternalError(
{
email: "",
entryPoint: "/services/redis"
},
e
); });
module.exports = Object.assign(redisClient, {
getAsync: promisify(redisClient.get).bind(redisClient),
setAsync: async (key, value, flag, duration) => {
if (!key.length || !value) {
throw new Error(
REDIS setAsync has missing arguments: key: ${key} value: ${value}
);
}
return new Promise((resolve, reject) => {
try {
if (flag !== undefined && duration !== undefined) {
redisClient.set(key, value, flag, duration);
} else {
redisClient.set(key, value);
}
resolve(true);
} catch (err) {
reject(err);
}
});
},
delAsync: async (key) => {
return new Promise((resolve, reject) => {
redisClient.del(key, (err, result) => {
if (err) {
return reject(err);
}
return resolve(result);
});
});
},
expireAsync: promisify(redisClient.expire).bind(redisClient)
});
`
Just to add to this. On success AND perceived possible failure the delete returns a 1. Can I assume this is some kind of latency issue, since I am doing the check on literally the next line of code?
Is the DEL command even getting executed ? even though it returned 1, it did not delete the key.
Yes 1 is always being returned, what I am saying is that a small % of the time it "appears" to be ineffective, since the immediate read back finds it still present. The rest of the time it behaves as expected.
// Delete key value pair await redis.delAsync("HealthTestKey"); const healthTestKey = await redis.getAsync("HealthTestKey"); if (!healthTestKey) { ... // not going here } else { ... // goes in here around 7% of the time
This got me to thinking is there some lag going on? Though I did think that if 1 was returned then the action was fully complete.
I have tried adding a pause (1000ms) in a test environment between the set and the readback and the delete and the readback. Currently a small % of the time following the set and the 1000ms pause the subsequent get returns null.
I'm getting a similar issue. client.get()
and client.set()
work perfectly but for some reason client.del()
doesn't actually delete the item despite returning a 1
. Manually using the DEL command in console does the trick but for some reason this one either works sometimes after the 2nd run or not at all.
I might downgrade to see if that fixes anything, but definitely disheartening.
This code dose not reproduce it:
import { createClient } from '@redis/client';
import { equal } from 'node:assert/strict';
const client = createClient();
client.on('error', err => console.error(err));
await client.connect();
for (let i = 0; i < 1_000_000; i++) {
equal(
await client.set('key', 'value'),
'OK'
);
equal(
await client.del('key'),
1
);
equal(
await client.get('key'),
null
);
}
Have you tried using the "MONITOR" command to check maybe there is a "SET" command from another client that gets executed in between the "DEL" and "GET" commands?
Huh, ran the same successfully. So hopefully it's just a weird issue on my express flow that I can fix, but looking into it.
Thanks for the check!