Diff for Redis between two databases - redis

What is the best way to see differences between the contents of two different Redis databases? We have a development and production deployment and our production instance doesn't seem to have the same exact data as development but we need a solid easy way to test that - is there a good way to do this without writing too much of our own code?

If your production data has a high rate of change, doing this would be tough. So for this answer let us assume you either don't have a high data churn or you can do this at a quiescent time where the data churn rate is low.
For this to work from a shell script perspective you will need to do the first task in parallel.
Use the RDB save option in redis-cli to pull down a local copy of the data for each server.
Compare a hash of the files, such as an md5sum. If they are the same, the data is the same.
If the churn rate is low enough that you can get a comparable dump, this will identify if they are different, but not what data differs if any. However you can use this as a conditional for avoiding a deeper dive which has to essentially ransack the database.
Alternatively, if they differ you could write something which uses the RDB files and one of the RDB file parsers to compare them and output the differences it finds.
Indeed this method would work far better than ransacking the server as data can change during the process whereas the dump file analysis method compares for a fixed point in time.

Because we are using Redis Sentinel I assumed I couldn't find some code to steal, so wrote my own real quick
the code compares the contents of two different Redis databases, using the ioredis Redis client NPM module. The code works fine for small Redis databases that don't change much, for larger amounts of data or for databases that are seeing a lot of writes per minute, this might not be suitable.
code looks like this:
var async = require('async');
var Redis = require('ioredis');
var REDIS_SENTINEL_MASTER_NAME_STAGING = 'smartconndevredis01';
var REDIS_SENTINEL_MASTER_NAME_PROD = 'smartconnect_curr_master';
var SENTINEL_ENDPOINTS_STAGING = [
{
"host": "XXX.XX.XX.XX",
"port": "26379"
},
{
"host": "XXX.XX.X.XX",
"port": "26379"
},
{
"host": "XXX.XX.X.XX",
"port": "26379"
}
];
var SENTINEL_ENDPOINTS_PROD = [
{
"host": "XXX.XX.X.XX",
"port": "26379"
},
{
"host": "XXX.XX.X.XX",
"port": "26379"
},
{
"host": "XXX.X.XX.XX",
"port": "26379"
}
];
var clientStaging = new Redis({
sentinels: SENTINEL_ENDPOINTS_STAGING,
name: REDIS_SENTINEL_MASTER_NAME_STAGING,
db: 0
});
var clientProd = new Redis({
sentinels: SENTINEL_ENDPOINTS_PROD,
name: REDIS_SENTINEL_MASTER_NAME_PROD,
db: 0
});
var dbStaging = {};
var dbProd = {};
async.parallel([
function (callback) {
clientStaging.keys('*', function (err, keys) {
async.each(keys, function (key, cb) {
clientStaging.get(key, function (err, result) {
//console.log('staging> key: "'+key+'", result:', result);
dbStaging[key] = result;
cb(err);
});
}, function done(err, res) {
callback(err, res);
});
});
},
function (callback) {
clientProd.keys('*', function (err, keys) {
async.each(keys, function (key, cb) {
clientProd.get(key, function (err, result) {
//console.log('production> key: "'+key+'", result:', result);
dbProd[key] = result;
cb(err);
});
}, function done(err, res) {
callback(err, res);
});
});
}
],
function done(err, results) {
if (err) {
throw err;
}
else {
diffDbs();
}
});
function diffDbs() {
Object.keys(dbStaging).forEach(function (key) {
if (!dbProd.hasOwnProperty(key)) {
console.log('staging redis has key, prod redis does not:',key);
}
else {
var stagingVal = dbStaging[key];
var prodVal = dbProd[key];
if(String(stagingVal).valueOf() != String(prodVal).valueOf()){
console.log('staging redis and prod redis have DIFFERENT values for key:',key);
}
}
});
Object.keys(dbProd).forEach(function (key) {
if (!dbStaging.hasOwnProperty(key)) {
console.log('prod redis has key, staging redis does not:',key);
}
else {
var stagingVal = String(dbStaging[key]).replace(/ /g, ''); //remove all whitespace
var prodVal = String(dbProd[key]).replace(/ /g, ''); //remove all whitespace
if(stagingVal.valueOf() != prodVal.valueOf()){
console.log('staging redis and prod redis have DIFFERENT values for key:',key);
}
}
});
process.exit(0);
}

Related

I can't lock value of the redis with redlock. How to lock the value in the redis?

I'd like to lock value in the redis with redlock.
But, It's not locked even though lock is done successfully.
This is my test code. Pushed data is 'refId:1234' and lock it for 4 seconds.
So, pop should be done after 4 seconds. But, it's poped after 1 seconds.
const redis = require('ioredis');
const redLock = require('redlock');
// Connect to Redis
const client = redis.createClient( {
port:6379,
host:'127.0.0.1',
});
const redlock = new redLock([client]);
let count = 0;
let lockT;
function listPush() {
let value = 'refId:1234';
client.rpush('events', value, (err, reply) => {
if (err) {
console.error(err);
}
else {
let now = Date();
let time = Date.parse(now);
console.log('Successfully pushed : ', time);
}
});
}
function listPop() {
let ret;
client.lpop('events', (err, reply) => {
if (err) {
console.error(err);
} else {
let now = Date();
let time = Date.parse(now);
console.log(`Data read from list: ${reply}`, ' ', time);
}
});
}
function lockValue(value, ttl) {
return new Promise((resolve, reject) => {
redlock.lock(`${value}`, ttl)
.then((lock) => {
let now = Date();
let time = Date.parse(now);
console.log("Successfully acquired lock on : ", time);
resolve(lock);
})
.catch((error) => {
console.error(`Failed to acquire lock on "${value}"`);
reject(error);
});
});
}
listPush();
lockValue("refId:1234", 4000);
setTimeout(listPop, 1000);
This is log:
Successfully pushed : 1676204641000
Successfully acquired lock on : 1676204641000
Data read from list: refId:1234 1676204642000
In the log, data read is done within 1 second after lock is done.
The ttl of the lock is 4 seconds. So, data should be accessible after lock time 4 seconds.
It shows that lock is not working
This is partial of lock instance after locking.
resource: [ 'refId:1234' ],
value: 'f9f40e68e9ce3a97fa8b820727317b94',
expiration: 1676205843381,
attempts: 1,
attemptsRemaining: 9
If the expiration value means ttl of lock, it is not matched. It's strange.
But, I'm not sure expiration means ttl.
I'm confusing why the redlock is not working well.
Am I misconfigured the redlock and redis for distributed lock?
Am I misunderstand operation of redlock?
If the value is locked in the redis, the value is not accessible until unlocked.
Access is blocking until unlocked and is possible after timout or release lock.

Is there a way find download/popularity stats for depending packages?

For example I have a driver for elasticsearch and list of modules, which are build with using of this driver. It's about ~800 modules.
Is there a way to sort this list by popularity or download, so I can find the most used dependent package? I guess it will help to find useful libraries or ODM based on native driver.
I have found several community npm-based stats, but I'll never seen anything like it.
You can run some script like this. It is basically fetching all the dependents, then for each dependent, acquiring the score.
const pkg = "#elastic/elasticsearch";
const dependents = [];
const fetchAll = async (offset = 0) => {
const res = await fetch(
`https://www.npmjs.com/browse/depended/${pkg}?offset=${offset}`,
{ headers: { "x-spiferack": "1" }, method: "GET" }
);
const data = await res.json();
dependents.push(...data.packages);
if (data.hasNext) await fetchAll(offset + data.paginationSize);
};
const main = async () => {
await fetchAll();
const result = await Promise.all(
dependents.map(async ({ name }) => {
const res = await fetch(
`https://registry.npmjs.com/-/v1/search?text=${name}&size=1`
);
const data = await res.json();
return { name, score: data.objects[0]?.score };
})
);
// console.log(JSON.stringify(result));
// TODO: sort then display your result here
};
main();
The result will look something like this before sorting:
[
{
"name": "winston-elasticsearch",
"score": {
"final": 0.3617392174769229,
"detail": {
"quality": 0.5468432882273897,
"popularity": 0.23232512016539603,
"maintenance": 0.3324926827166212
}
}
},
{
"name": "mongoosastic",
"score": {
"final": 0.3562854605860561,
"detail": {
"quality": 0.5583413799576047,
"popularity": 0.2060467998060229,
"maintenance": 0.3333333333333333
}
}
},
// ...
]
Note that this is a hack (using NPM's internal APIs and excessively searching the registry), and NPM will likely throw some error like too many requests or connection timeout, especially if your number of packages is too much, and you are not cooling down (you can implement it here, either by changing the logic where I have used Promise.all or using got which already has such features).
Moreover, these API endpoints don't appear to be consistent enough (like the dependents one was giving only ~400 entries for #elastic/elasticsearch while on the site it shows ~800, and the score one was not giving score for certain packages though it should). It will be better for you to use some service like npms.io that properly validates (and caches) the results. The approach will be similar, and pretty much straightforward to implement.
There are also certain packages on NPM that already do/used to do this. Look them up here to see if any is working properly.
Instead of the /-/v1/search hack, you can also fetch https://www.npmjs.com/package/${name} with "x-spiferack": "1" header. But it will only give you download count.

How to setup redis on loopback

I tried Redis on my node.js server before, the script looks like this:
//...
redisClient = redis.createClient();
redisClient.on("connect", function (err) {
console.log("redis terkoneksi");
});
redisClient.on("error", function (err) {
console.log("Redis Error " + err);
});
//...
//redisClient.set(...
//redisClient.get(...
At this time I want to try to install the redis loopback using the 'loopback-connector-redis' plugin. I have installed NPM, but I don't know how to use it. I have tried writing like this:
var DataSource = require('loopback-datasource-juggler').DataSource;
var ds = new DataSource('redis');
ds = redis.createClient(); //script error (createClient is not function)
ds.on("connect", function (err) {
console.log("redis terkoneksi");
});
ds.on("error", function (err) {
console.log("Redis Error " + err);
});
there are two questions I have:
1. how to use redis on loopback?
2. How to write correctly so that it can refer to the redis 'set', 'get', etc?
I will receive all the answers and suggestions, thank you.
best regards.
LoopBack provides two flavors of Redis connector.
Community-maintained loopback-connector-redis which is implementing CRUD-like storage API using Redis as the "database".
loopback-connector-kv-redis providing a key-value API that's closer to how Redis is typically used.
Since you are mentioning set and get commands, my recommendation is to use the KV connector. See https://github.com/strongloop/loopback-example-kv-connectors/tree/master/redis.lb3x for a full working example.
(1)
Create a datasource backed by the Redis KV connector.
Example: server/datasources.json
{
"db": {
"host": "127.0.0.1",
"port": 6379,
"name": "db",
"connector": "kv-redis"
}
}
(2)
Create a new model to represent the KeyValue data and operations.
Example: common/models/color.json
{
"name": "Color",
"base": "KeyValueModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
(3)
Attach the model to the datasource.
Example: server/model-config.json
{
"Color": {
"dataSource": "db",
"public": true
}
}
Now you can access all KeyValueModel methods on your model, either via REST API or from JavaScript:
const Color = app.models.Color;
await Color.set('my key', 'my value', {/*options*/});

Where should be the location for coturn or ice setting for sipjs 0.11.0?

I am moving from sipjs 0.7x to sipjs 0.11
After reading the Git issue https://github.com/onsip/SIP.js/pull/426#issuecomment-312065734
and
https://sipjs.com/api/0.8.0/sessionDescriptionHandler/
I have found that the ice options (coturn, turn, stun) is not in User Agent anymore,
but the problem is that I am not quite understand where should I use the
setDescription(sessionDescription, options, modifiers)
I have seen that the ice is set in options, using
options.peerConnectionOptions.rtcConfiguration.iceServers
below is what I haved tried
session.on('trackAdded', function () {
// We need to check the peer connection to determine which track was added
var modifierArray = [
SIP.WebRTC.Modifiers.stripTcpCandidates,
SIP.WebRTC.Modifiers.stripG722,
SIP.WebRTC.Modifiers.stripTelephoneEvent
];
var options = {
peerConnectionOptions:{
rtcConfiguration:{
iceServers : {
[{urls: 'turn:35.227.67.199:3478',
username: 'leon',
credential: 'leon_pass'}]
}
}
}
}
session.setDescription('trackAdded', options,modifierArray);
var pc = session.sessionDescriptionHandler.peerConnection;
// Gets remote tracks
var remoteStream = new MediaStream();
pc.getReceivers().forEach(function (receiver) {
remoteStream.addTrack(receiver.track);
});
remoteAudio.srcObject = remoteStream;
remoteAudio.play();
// Gets local tracks
// var localStream = new MediaStream();
// pc.getSenders().forEach(function(sender) {
// localStream.addTrack(sender.track);
// });
// localVideo.srcObject = localStream;
// localVideo.play();
});
}
I have tried this and it seems that the traffic is not going to the coturn server.
I have used Trickle Ice "https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/" to test and it is fine, but I have found there is not traffic going through the coturn server. You could also use this one and I do not mind.
There is even no demo on the official website to show how could we use the setDescription(sessionDescription, options, modifiers). In this case can I please ask some recommendations?
Configure STUN/TURN servers in the parameters passed to new UserAgent.
Here is sample, it seems to be working on v0.17.1:
const userAgentOptions = {
...
sessionDescriptionHandlerFactoryOptions: {
peerConnectionConfiguration: {
iceServers: [{
urls: "stun:stun.l.google.com:19302"
}, {
urls: "turn:TURN_SERVER_HOST:PORT",
username: "USERNAME",
credential: "PASSWORD"
}]
},
},
...
};
const userAgent = new SIP.UserAgent(userAgentOptions);
When using SimpleUser - pass it inside SimpleUserOptions:
const simpleUser = new Web.SimpleUser(url, { userAgentOptions })

Send every time new UUID to AWS step function through AWS CloudWatch event rule

How to send a unique value(UUID4) to the step function is the json format, when it was triggered every time from the cloudwatch event rules?
Can any one help on this. Thanks in advance.
AWS Step Functions now provides an intrinsic function to generate a UUIDv4 string in the ASL definition:
https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html#asl-intrsc-func-uuid-generate
Example:
"uuid.$": "States.UUID()"
// output
{ "uuid": "ca4c1140-dcc1-40cd-ad05-7b4aa23df4a8" }
Since AWS recently added Step Functions as a CloudWatch target, you could easily make the first function in the Step Function generate the UUID. And then pass that down to the next function. This would allow the Step Function to be self contained.
"GenerateUuid": {
"Type": "Task",
"Resource": "arn:aws:states:us-east-1:123456789012:activity:GenerateUuid",
"Next": "CreateNewRecord"
},
Another way is to have a Lambda function generate the UUID and pass is into the State Machine which you could again trigger from a CloudWatch event.
var params = {
stateMachineArn: 'MyStateMachine-12345',
input: uuidv4()
};
stepfunctions.startExecution(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
A little late to the party, but in case someone else comes across this...
When you setup your cloudwatch event you can set Input Transformers on it and luckily the event that is fired has an ID that should solve your problem without adding any extra lambdas to your state machine.
Below is an example of setting it up so that the input to your state machine will like this:
{
"meta": {
"uniqueId": "96c80428-14fe-c8d0-f6e3-639384992391"
}
}
Using the AWS console:
Screenshot
Using code (JS):
const targets = [
{
Arn: "my-state-machine-arn",
Id: "my cloud watch rule name",
InputTransformer: {
InputTemplate: '{"meta":{"uniqueId":<id>}}',
InputPathsMap: {
id: '$.id',
},
},
RoleArn: "my-cloudwatch-arn",
},
];
const params = {
Rule: "my cloud watch rule name",
Targets: targets,
};
cloudwatch.putTargets(params, (err, data) => {
if (err) {
// error
} else {
// success
}
});