Redis - inserting values ​​from an array into a transaction using promises (bluebird)

I have an array like this:

var names = ['Irina', 'Michael', 'Carl'];

      

I want to inject them into redis using transactions with promises (I don't know any other way). But I don't understand how to do this; this is my code:

var Promise = require("bluebird");
var redis = require("redis");

Promise.promisifyAll(redis.RedisClient.prototype);
Promise.promisifyAll(redis.Multi.prototype);

var client = redis.createClient(), multi;

var names = ['Irina', 'Michael', 'Carl'];

var result = names.map(function(item) {
    client.watch('user:id');
    client.getAsync('user:id', function(err, data) {
      var multi = client.multi();
      var user_id = parseInt(data) + 1;
      multi.hmsetAsync('user:' + user_id, 'username', item, 'about', 'love to coding');
      multi.incrAsync('user:id');
      multi.execAsync(function(err,data){
        console.log(data);
      });
    });
});

Promise.all(result).then(function(response) {
    console.log(response);
});

      

But it doesn't work (:

EDIT: This is the error caused by the application:

Unhandled Rejection Error: ERR Invalid number of arguments for get command

Someone can help me!


EDIT 2: I changed my code, but now only stores the last value of the array:

client.watch('user:id');
var result = names.map(function(item) {        
    var multi = client.multi();
    client.getAsync('user:id').then(function(value) {
        var user_id = parseInt(value) + 1;
        return user_id;
    }).then(function(user_id) {
        multi.hmsetAsync('user:' + user_id, 'username', item, 'about', 'love to coding');
        multi.incrAsync('user:id');
    }).then(function() {
        multi.execAsync().spread(function(err,data){
            console.log(data);
        });
    });
});

      


EDIT 3:

When used, redis' MONITOR

this is the result:

[0 127.0.0.1:54439] "info"
[0 127.0.0.1:54439] "watch" "user:id"
[0 127.0.0.1:54439] "get" "user:id"
[0 127.0.0.1:54439] "get" "user:id"
[0 127.0.0.1:54439] "get" "user:id"
[0 127.0.0.1:54439] "MULTI"
[0 127.0.0.1:54439] "hmset" "user:88" "username" "Irina" "about" "love to coding"
[0 127.0.0.1:54439] "incr" "user:id"
[0 127.0.0.1:54439] "EXEC"
[0 127.0.0.1:54439] "MULTI"
[0 127.0.0.1:54439] "hmset" "user:88" "username" "Michael" "about" "love to coding"
[0 127.0.0.1:54439] "incr" "user:id"
[0 127.0.0.1:54439] "EXEC"
[0 127.0.0.1:54439] "MULTI"
[0 127.0.0.1:54439] "hmset" "user:88" "username" "Carl" "about" "love to coding"
[0 127.0.0.1:54439] "incr" "user:id"
[0 127.0.0.1:54439] "EXEC"

      

Performs get user:id

3 times and then other methods. Why?

+3


source to share


2 answers


I solved the problem this way:

client.watch('user:id');
Promise.each(names, function(item) {
    var multi = client.multi();
    return client.getAsync('user:id').then(function(value) {
        var user_id = parseInt(value) + 1;
        return user_id;    
    }).then(function(user_id) {
        multi.hmsetAsync('user:' + user_id, 'username', item, 'about', 'love to coding');
        multi.incrAsync('user:id');
    }).then(function() {
        return multi.execAsync().spread(function(err,data){
            console.log(data);
        });
    });
})
.then(function() {
    console.log("Ended process ...");
});

      

The trick changed:

var result = names.map(function(item) {...});

with a promise:



Promise.each(names, function(item) {...});

and then return client.getAsync(user:id)

to it.

And this is the output from redis' MONITOR:

[0 127.0.0.1:53290] "info"
[0 127.0.0.1:53290] "watch" "user:id"
[0 127.0.0.1:53290] "get" "user:id"
[0 127.0.0.1:53290] "MULTI"
[0 127.0.0.1:53290] "hmset" "user:88" "username" "Irina" "about" "love to coding"
[0 127.0.0.1:53290] "incr" "user:id"
[0 127.0.0.1:53290] "EXEC"
[0 127.0.0.1:53290] "get" "user:id"
[0 127.0.0.1:53290] "MULTI"
[0 127.0.0.1:53290] "hmset" "user:89" "username" "Michael" "about" "love to coding"
[0 127.0.0.1:53290] "incr" "user:id"
[0 127.0.0.1:53290] "EXEC"
[0 127.0.0.1:53290] "get" "user:id"
[0 127.0.0.1:53290] "MULTI"
[0 127.0.0.1:53290] "hmset" "user:90" "username" "Carl" "about" "love to coding"
[0 127.0.0.1:53290] "incr" "user:id"
[0 127.0.0.1:53290] "EXEC"

      

Surprisingly, it's true! (Hint: fooobar.com/questions/450619 / ... )

+2


source


Disclaimer: not JS maven, but I'll try to help :)

It sounds like you want a unique identifier for the name. In the spirit of KISS, I would try to make the following flow, which does not require any transactional behavior:

  • id = INCR user: id
  • HMSET user: ...

The "risk" here is that if your worker dies between 1 and 2, you "waste" the ID. If you can live with it, perhaps you could look something like this (sorry, untested - see Disclaimer;)):



var result = names.map(function(item) {
    client.incrAsync('user:id').then(function(user_id) {
        client.hmsetAsync('user:' + parseInt(user_id), 'username', item, 'about', 'love to coding');
    }).then(function(err) {
        console.log(err);
    });
});

      

Update 1 : but since you will change the position for the transaction, I am guessing that the last name update, specifically the INCR call, breaks the HOURS of the previous ones. Since you are not throwing an error, you cannot see this. You can also debug behavior using Redis MONITOR

at runtime.

Update 2 . I guess this is how Node does async operations - it starts the GET first and stops when it needs to wait for a response. Then the first response (with the first GET) is processed, then the second ... Apart from using your intended logic, this also causes only the first part to be able to use MULTI - the other two don't have a MULTI instruction in front of their relative EXEC ...

0


source







All Articles