Distributed lock of Hazelcast using nodejs - node.js

I have the Hazelcast cluster server running in the 172.30.56.60, 61, 62
(i.e)
[ Member {
address: Address { host: '172.30.56.60', port: 5701, type: 4 },
uuid: 'bd6428f0-e888-453f-872f-6fe8296d751d',
isLiteMember: false,
attributes: {} },
Member {
address: Address { host: '172.30.56.61', port: 5701, type: 4 },
uuid: 'e0cd795a-0ca5-41ab-907a-492b61690a18',
isLiteMember: false,
attributes: {} },
Member {
address: Address { host: '172.30.56.62', port: 5701, type: 4 },
uuid: '0a834ae8-e707-4b5b-945b-362bfea08cf5',
isLiteMember: false,
attributes: {} } ]
I try to implement the Hazelcast distributed locking using nodejs using the following code,
// Initialize the hazelcast client instance.
var HazelcastClient = require('hazelcast-client').Client;
var Config = require('hazelcast-client').Config;
var config = new Config.ClientConfig();
config.networkConfig.addresses = [{host: '172.30.56.60', port: '5701'},{host: '172.30.56.61', port: '5701'}, {host: '172.30.56.62', port: '5701'}];
var lock = {};
var sleep = require('sleep');
HazelcastClient
.newHazelcastClient(config)
.then(function (hazelcastClient) {
lock = hazelcastClient.getLock("lock1");
// do stuff with lock
lock.lock();
console.log('Am locked in node with lock1...will be locked for 20 seconds');
sleep.sleep(20);
console.log('Unlocked now...');
lock.unlock();
process.exit();
});
I started the script node by node, I expected to establish the lock node by node, but instead it locks all the nodes in the same time. So it is not working as a distributed lock, so all the script started and ending in the same time (NOTE : For testing I provided 20 seconds sleep)
Please let me know, How to establish the distributed lock using node js in Hazelcast.

I found the answer myself, I didn't realize the return of promise, my bad (new to nodejs)
// Initialize the hazelcast client instance.
var HazelcastClient = require('hazelcast-client').Client;
var Config = require('hazelcast-client').Config;
var config = new Config.ClientConfig();
config.networkConfig.addresses = [{host: '172.30.56.60', port: '5701'},{host: '172.30.56.61', port: '5701'}, {host: '172.30.56.62', port: '5701'}];
var sleep = require('sleep');
// Test process
HazelcastClient
.newHazelcastClient(config)
.then(function (hazelcastClient) {
var lock = hazelcastClient.getLock('rowId_tablename');
// lock the code here
lock.lock().then(function() {
console.log('Am locked in node with lock3...will be locked for 20 seconds');
sleep.sleep(20);
// unlock after process
return lock.unlock();
}).then(function() {
console.log('unlocked now');
});
});

Related

socket.io-redis adapter timeout reached while waiting for clients response error

In my project, I'm using (socket.io - 2.3.0) & (socket.io-redis - 5.2.0) for data broadcasting between servers. In that scenario, I'm having the redis timeout issue from time to time, and I'm not sure why. In my server I just run a single node process, and I use Redis to store data and share it with other connections. Is it correct that I use almost 5 dbs in redis out of 15? In production, I encountered this problem more than ten times in a single day. Please assist us in resolving this issue.
Stack Trace:
Error: timeout reached while waiting for clients response
at Timeout._onTimeout (/var/www/html/project/node_modules/socket.io-redis/index.js:485:48)
at listOnTimeout (internal/timers.js:555:17)
at processTimers (internal/timers.js:498:7)
Here it's my node entry point.
var port = 12300;
var io = require('socket.io')(port);
var redis = require('redis');
const redis_adap = require("socket.io-redis");
io.adapter(redis_adap({
host: '127.0.0.1',
port: 6379,
requestsTimeout: 5000, // i tried upto 20000 but still the issue is occured
}));
io.on('connection', function(socket) {
var player = new Player();
var Redis = new redis();
var roomId;
player.id = socket.id;
socket.on('ping', function() {
socket.emit('pong');
});
socket.on('message', function(data) {
let _messageIndex = data.e;
let _id = player.id;
let returnData = {
i: _id,
e: _messageIndex
}
socket.broadcast.to(player.roomid).emit('message', returnData);
});
socket.on('UpdateHorn', function() {
let _id = player.id;
let returnData = {
i: _id
}
socket.broadcast.to(player.roomid).emit('UpdateHorn', returnData);
})
socket.on('UpdateTiles', function(data) {
let returnData = {
t: data.t
}
socket.broadcast.to(player.roomid).emit('UpdateTiles', returnData);
});
socket.on('getLobbyDetails', function() {
socket.emit('getLobbyDetails', { lobby: CONFIG.getLobbyDetails() });
})
})

How gRPC-web handles bytes data from server-side streaming?

I want to transmit a sample video file from backend grpc service to the browser using grpc-web, I did some tweaks based on official hello world tutorial. Btw, nothing changed in the envoy configuration. The video file is split into 17 chunks, I can receive 17 messages in browser, however there is nothing inside, what should do so I can get the data?
protobuf definition:
syntax = "proto3";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (stream HelloReply);
}
message HelloRequest {}
message HelloReply {
bytes message = 1;
}
server.js:
var PROTO_PATH = __dirname + '/helloworld.proto';
var grpc = require('grpc');
var fs = require('fs');
var protoLoader = require('#grpc/proto-loader');
var packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
var protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
var helloworld = protoDescriptor.helloworld;
function doSayHello(call) {
let count = 0;
let videoDataStream = fs.createReadStream('./sample.mp4');
videoDataStream.on('data',function(chunk){
console.log(chunk);
console.log(++count);
call.write({videoStream: chunk});
// call.write(chunk);
}).on('end',function(){
call.end();
});
}
function getServer() {
var server = new grpc.Server();
server.addService(helloworld.Greeter.service, {
sayHello: doSayHello,
});
return server;
}
if (require.main === module) {
var server = getServer();
server.bind('0.0.0.0:9090', grpc.ServerCredentials.createInsecure());
server.start();
}
exports.getServer = getServer;
client.js:
const {HelloRequest, HelloReply} = require('./helloworld_pb.js');
const {GreeterClient} = require('./helloworld_grpc_web_pb.js');
var client = new GreeterClient('http://localhost:8080');
var request = new HelloRequest();
client.sayHello(request).on('data', function(chunk){
//console.log(chunk.getMessage());
console.log(chunk);
});
Anyway, in case there is problem with proxy, below is my envoy.yaml:
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address: { address: 0.0.0.0, port_value: 9901 }
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 8080 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
codec_type: auto
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match: { prefix: "/" }
route:
cluster: greeter_service
max_grpc_timeout: 0s
cors:
allow_origin_string_match:
- prefix: "*"
allow_methods: GET, PUT, DELETE, POST, OPTIONS
allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
max_age: "1728000"
expose_headers: custom-header-1,grpc-status,grpc-message
http_filters:
- name: envoy.grpc_web
- name: envoy.cors
- name: envoy.router
clusters:
- name: greeter_service
connect_timeout: 0.25s
type: logical_dns
http2_protocol_options: {}
lb_policy: round_robin
hosts: [{ socket_address: { address: host.docker.internal, port_value: 9090 }}]
the bytes logged on server side:
and below console output in browser:
Have you tried testing with a gRPC (rather than gRPC Web) client to eliminate the possibility that the proxy is the problem?
I'm not super familiar with the Node.JS implementation but...
Should it not be server.addProtoService(...)?
Also the message streamed by the server is:
message HelloReply {
bytes message = 1;
}
But you:
call.write({videoStream: chunk});
Should it not be:
call.write({message: chunk});

Correct way to implement a single instance in a module

I am trying to initialize a DB connection in a module and pass to other modules.
My DB connection module (rdsconnection.js) is:
const { Pool } = require('pg');
let dbParams = require('./configparams').getDbParams();
let schema = (dbParams.db_schema === "" || dbParams.db_schema === undefined) ? "public." : dbParams.db_schema + ".";
// Connect to AWS RDS
let pool;
if(!pool)
{
console.log("PG pool not initialized. Initializing now.");
pool = new Pool({
user: dbParams.username,
host: dbParams.host,
database: dbParams.dbname,
password: dbParams.password,
port: dbParams.port
});
}
else
{
console.log("PG already initialized.");
}
module.exports = { pool, schema }
I want to do to
const { pool, schema } = require('../modules/rdsconnection');
in multiple modules and want to return only a single pool connection object.
I am assuming the if/else I am doing is wrong way to go about this.
What is the proper way to initialize an object only once in a module?
Does the code in the module gets run each time another module calls require()?
Thank you very much.
You're overcomplicating it. Node.js modules are only loaded once and then cached for reuse if they are requireed multiple times in a program. You can simplify it to this:
const { Pool } = require('pg');
const dbParams = require('./configparams').getDbParams();
const schema = `${dbParams.db_schema || 'public'}.`;
// Connect to AWS RDS
const pool = pool = new Pool({
user: dbParams.username,
host: dbParams.host,
database: dbParams.dbname,
password: dbParams.password,
port: dbParams.port
});
module.exports = { pool, schema };

How to share connection pool between modules in node.js?

I have a little or no knowledge at how to properly implement postgres connection pool in node.js modules.
My question is : Is it problem when I am creating new Pool in every module where I need a connection to pg ?
Is there a way to create pool globally for a whole app ?
Thanks.
Define the pool in a file say pgpool.js
var pg = require('pg');
var pool;
var config = {
user: 'foo',
database: 'my_db',
password: 'secret',
port: 5432,
max: 10,
idleTimeoutMillis: 30000,
};
module.exports = {
getPool: function () {
if (pool) return pool; // if it is already there, grab it here
pool = new pg.Pool(config);
return pool;
};
Use it like this:
var db = require('./a/path/to/pgpool.js');
var pool = db.getPool();
I would suggest to not export the pool, but a function that provides a connection to it. Using promise-mysql
var mysql = require('promise-mysql');
var connectionPool = mysql.createPool({
host : "127.0.0.1",
user : '******',
password : '******',
database : '******',
port : '3306'
});
module.exports={
getConnection: function(){
return new Promise(function(resolve,reject){
connectionPool.getConnection().then(function(connection){
resolve(connection);
}).catch(function(error){
reject(error);
});
});
}
}
I have made a simple blog post about MySQL pool sharing, where you can read more

register new xmpp account with node-xmpp ( node.js )

I'm looking at 'XEP-0077 in-band registration' about how to register a new XMPP account. Here is my code. I use node-xmpp to connect my node.js application to an ejabberd server.
var net = require('net');
var xmpp = require('node-xmpp');
var cache = new Object();
net.createServer( function(socket) {
socket.setEncoding('utf8');
socket.addListener('data',function(data) {
data = data.substr(0,data.length-2);
if(cache.admin==undefined && data=='login') {
var ejabberd =new xmpp.Client({jid:"admin#mine",password:'12345',host:'192.168.7.202',port:'5222'});
cache.admin = ejabberd;
cache.admin.addListener('online',function() {
cache.admin.send(new xmpp.Element('presence',{type:'chat'}).c('show').c('status').t('mine status'));
cache.admin.send(new xmpp.Element('iq',{type:'get',id:'reg1'}).c('query',{xmlns:'jabber:iq:register'}));
})
cache.admin.addListener('stanza',function(stanza) {
if(stanza.is('iq')) {
console.log(stanza.children[1]);
}
})
cache.admin.addListener('end',function() {
cache.admin.end();
cache.admin = undefined;
})
}
if(cache.admin!=undefined && data=='logout') {
cache.admin.end();
cache.admin = undefined;
} else if(cache.admin!=undefined && data=='register') {
cache.admin.send(new xmpp.Element('iq',{type:'set',id:'reg1'}).c('query',{xmlns:'jabber:iq:register'}).c('username').t('alow').up().c('password').t('test'));
}
});
}).listen(5000);
If i run this code, I get this error:
{ name: 'error',
parent:
{ name: 'iq',
parent: null,
attrs:
{ from: 'admin#mine',
to: 'admin#mine/20108892991316770090454637',
id: 'reg1',
type: 'error',
xmlns: 'jabber:client',
'xmlns:stream': 'http://etherx.jabber.org/streams' },
children: [ [Object], [Circular] ] },
attrs: { code: '403', type: 'auth' },
children:
[ { name: '**forbidden**',
parent: [Circular],
attrs: [Object],
children: [] } ] }
In 'XEP-0077: In-Band Registration' it says that the forbidden reason means that "The sender does not have sufficient permissions to cancel the registration".
How can I get such permissions?
I have been strugling with something similar, I wanted to register a new user account via in-band registraton from nodejs to an ejabberd server running in ubuntu. Here is what I did and worked for me:
//Dependencies
var xmpp = require('node-xmpp');
//Host configuration
var host = "localhost";
var port = "5222";
var admin = "sebastian#localhost";
var adminPass = "adminPass";
var connection = new xmpp.Client({
jid: admin,
password: adminPass,
host: host,
port: port
});
//user to be registered name & pass
var newUserName = "pepe";
var newUserPass = "pepePass";
//Stream
var iq = "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:component:accept' to='localhost'><iq type='set' id='reg2'><query xmlns='jabber:iq:register'><username>" + newUserName + "</username><password>" + newUserPass + "</password></query></iq></stream>";
//Send
connection.send(iq);
//End
connection.end();
The var iq is kind of messy, I suppose that if you know how to use Strophe.js in a propper way that part could look a little bit nicer and cleaner. I was missing the section of the xml, it seems that if you want to send a stream, you have to provide a valid ejabberd namespace, that was what was failing for me. Hope this helps you sort your problem out.
Which server are you using? Are you sure it has XEP-77 enabled? Test with an existing client. Ensure that the account you're trying to create does not already exist. Ensure that the account has the correct domain name.

Resources