Bulk update to Postgres with node js performance issue - node.js

I'm facing performance issue while trying to do bulk update in PostgresDB. It's taking more than 180 seconds to update around 23000 records. PFB the code. I'm using pg-promise library. Is there anything I could do to improve the performance?
const pgp = require('pg-promise')();
const postgresDBConfig = {
host: Config.postgresDBHost,
port: Config.postgresDBPort,
database: Constants.postgresDBName,
user: Config.postgresDBUser,
password: 'pswd'
};
export async function getTransactionDetails(): Promise<any> {
return new Promise<any>(async function (resolve, reject) {
try {
let db = pgp(postgresDBConfig);
db.connect();
let query = "SELECT * FROM table_name";
db.any(query)
.then(data => {
console.log("Executed successfully::");
resolve(data);
})
.catch(error => {
console.log('ERROR:', error);
})
} catch (error) {
log.error("Error::" + error);
throw error;
}
});
}
export async function updateStatus(result: any, status: string) {
try {
let db = pgp(postgresDBConfig);
//db.connect();
let updateData = [];
_.forEach(result, function (row) {
let updateInfo = {};
updateInfo["sessionid"] = row.sessionid;
updateInfo["status"] = status;
updateData.push(updateInfo);
});
console.log("updateData::" + updateData.length);
const tableName = new pgp.helpers.TableName('table_name', 'schema_name');
let columnset = new pgp.helpers.ColumnSet(['?sessionid', 'status'], { table: tableName });
let update = pgp.helpers.update(updateData, columnset);
db.none(update).then(() => {
console.log("Updated successfully");
})
.catch(error => {
console.log("Error updating the status" + error);
});
}
catch (error) {
log.error("Error in function updateStatus::" + error);
throw error;
}
}

The code exhibits problems all over the place
You should initialize the database object only once
You should not use db.connect() at all, which you also use incorrectly for the async code
You again incorrectly use async block, skipping await, so it doesn't execute correctly.
You do not append any UPDATE logic clause, so it is updating everything all over again, unconditionally, which may be resulting in a delayed mess that you're in.
Here's an improved example, though it may need some more work from your side...
const pgp = require('pg-promise')();
const postgresDBConfig = {
host: Config.postgresDBHost,
port: Config.postgresDBPort,
database: Constants.postgresDBName,
user: Config.postgresDBUser,
password: 'pswd'
};
const db = pgp(postgresDBConfig);
const tableName = new pgp.helpers.TableName('table_name', 'schema_name');
const columnSet = new pgp.helpers.ColumnSet(['?sessionid', 'status'], {table: tableName});
export async function getTransactionDetails(): Promise<any> {
try {
const res = await db.any('SELECT * FROM table_name');
console.log('Executed successfully::');
return res;
} catch (error) {
console.log('ERROR:', error);
throw error;
}
}
export async function updateStatus(result: any, status: string) {
try {
let updateData = [];
_.forEach(result, row => {
let updateInfo = {};
updateInfo["sessionid"] = row.sessionid;
updateInfo["status"] = status;
updateData.push(updateInfo);
});
console.log('updateData::', updateData.length);
const update = pgp.helpers.update(updateData, columnSet) +
' WHERE v.sessionid = t.sessionid';
await db.none(update);
console.log('Updated successfully');
}
catch (error) {
console.log('Error in function updateStatus:', error);
throw error;
}
}

Related

Get qldb ledger table data in javascript object

So my issue is simple yet challenging. I am trying to transfer data from one ledger to another. Now for that, I am reading a whole table of data then creating an object for every document than inserting these documents one by one into the other ledger table
Ledger#1 Table1 -> get all data -> convert all data to array of objects -> transfer to Ledger#2 Table1 one by one
The problem is that I cannot create the object from the document. I do this manually by using prototype functions and reading field type and then creating a thing that is messy and causes some data to become null. So I was wondering if there was a better way that is less prone to errors.
I asked a question of migrating ledger but had no luck in getting any response. Please help me in this.
Following is my code. Please copy and paste it inside an IDE so you can better understand
const getValueOfField = (field) => {
const name = field.getType().name;
switch (name) {
case "string":
return field.stringValue();
case "int":
return field.numberValue();
case "null":
return null;
default:
return null;
}
};
const enterDataInNewLedger = async (tableData, tableName) => {
const awsProductionDriver = awsProduction();
console.log(`Starting to insert data inside table ${tableName}`);
try {
for (const data of tableData) {
await awsProductionDriver.executeLambda(async (txn) => {
await txn.execute(`INSERT INTO ${tableName} ?`, data);
});
}
console.log(`Done inserting data inside ${tableName}`);
return { success: true };
} catch (err) {
console.log(err.message);
return { success: false, message: err.message };
}
};
const dataTransferOfTable = async (table) => {
const prodDriver = awsProd();
try {
const allTableData = await prodDriver.executeLambda(async (txn) => {
const result = await txn.execute(`SELECT * FROM ${table.name}`);
const resultList = result.getResultList();
let completeResults = [];
for (const doc of resultList) {
let newDoc = {};
const fields = doc.fields();
for (const field of fields) {
newDoc[field[0]] = getValueOfField(field[1]);
}
completeResults.push(newDoc);
}
return completeResults;
});
const response = await enterDataInNewLedger(allTableData, table.name);
checkForErrors(response);
return { success: true };
} catch (err) {
console.log(err.message);
return { success: false, message: err.message };
}
};
const startDataTransferFromOneLedgerToAnother = async () => {
try {
for (let table of tableName) {
const response = await dataTransferOfTable(table);
checkForErrors(response);
}
} catch (err) {
console.log(err.message);
}
};
startDataTransferFromOneLedgerToAnother();
So apparently I could've done this easily. I was just checking it and figured out the solution.
I can insert the whole fetched document and it will be same so my converted code is as follows
const { awsMainFunction: awsProd } = require("./awsProdConfig");
const { awsMainFunction: awsProduction } = require("./awsProductionConfig");
const { tableNamesAndIndeces: tableName, checkForErrors } = require("./utils");
const enterDataInNewLedger = async (tableData, tableName) => {
const awsProductionDriver = awsProduction();
console.log(`Starting to insert data inside table ${tableName}`);
try {
for (const data of tableData) {
await awsProductionDriver.executeLambda(async (txn) => {
await txn.execute(`INSERT INTO ${tableName} ?`, data);
});
}
console.log(`Done inserting data inside ${tableName}`);
return { success: true };
} catch (err) {
console.log(err.message);
return { success: false, message: err.message };
}
};
const dataTransferOfTable = async (table) => {
const prodDriver = awsProd();
try {
const allTableData = await prodDriver.executeLambda(async (txn) => {
const result = await txn.execute(`SELECT * FROM ${table.name}`);
return result.getResultList();
});
const response = await enterDataInNewLedger(allTableData, table.name);
checkForErrors(response);
return { success: true };
} catch (err) {
console.log(err.message);
return { success: false, message: err.message };
}
};
const startDataTransferFromOneLedgerToAnother = async () => {
try {
for (let table of tableName) {
const response = await dataTransferOfTable(table);
checkForErrors(response);
}
} catch (err) {
console.log(err.message);
}
};
startDataTransferFromOneLedgerToAnother();

firebase document update trigger function error

I am trying to stream insert the data into the Bigquery table and have the below issues with function. Not sure where is an error in the code. I have followed this to achieve with realtime data.
https://github.com/googleapis/nodejs-bigquery/blob/main/samples/insertRowsAsStream.js
Function returned undefined, expected Promise or value
const functions = require("firebase-functions");
const {BigQuery} = require("#google-cloud/bigquery");
exports.onWriteTrigger = functions
.firestore
.document("leaseCompany/{documentId}")
.onWrite((change, context) => {
/*
onCreate: google.firestore.document.create
onUpdate: google.firestore.document.update
onDelete: google.firestore.document.delete
*/
const row = {
// insertId: document.data.id,
// json: {
timestamp: context.timestamp,
name: change.after.data().name,
// documentName: context.resource.name,
documentId: change.after.id,
eventId: context.eventId,
data: change.after.data().country,
};
// console.log(insertRows);
async function insertBigQuery(rows) {
try {
console.log(row);
const datasetName = "firestore_export";
const tableName = "leaseCompany";
const bigqueryClient = new BigQuery();
const ds = bigqueryClient.dataset(datasetName);
const tbl = ds.table(tableName);
await tbl.insert(rows)
.then((data) => {
return true;
})
.catch((err) => {
// An API error or partial failure occurred.
if (err.name === "PartialFailureError") {
console.log("Error Sending Notifications", err);
return false;
}
});
} catch (err) {
console.error(`table.insert: ${JSON.stringify(err)}`);
return err;
}
}
// console.log(row);
insertBigQuery(row);
});
you need to return promise or value from you function
async function insertBigQuery(rows) {
try {
console.log(row);
const datasetName = "firestore_export";
const tableName = "leaseCompany";
const bigqueryClient = new BigQuery();
const ds = bigqueryClient.dataset(datasetName);
const tbl = ds.table(tableName);
return tbl.insert(rows)
.then((data) => {
return true;
})
.catch((err) => {
// An API error or partial failure occurred.
if (err.name === "PartialFailureError") {
console.log("Error Sending Notifications", err);
return false;
}
});
} catch (err) {
console.error(`table.insert: ${JSON.stringify(err)}`);
return err;
}
}
return insertBigQuery(row);

Fastify Plugin Performance

I created a plugin for simple queries with caching and connection pooling. When i respond with that plugin (function), response is slower than before. So i wonder if I got the plugin thing wrong. Is this a correct use or am I making a mistake somewhere?
db.js
const fp = require('fastify-plugin')
const oracledb = require('oracledb');
oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT;
oracledb.autoCommit = true;
module.exports = fp(async function (fastify, opts) {
fastify.decorate('simpleSelectWithCache', async function (key, ttl, sql) {
let cached = await fastify.cache.get(key);
if (cached) {
console.log('Cached:', cached.item);
return cached.item;
} else {
let connection;
try {
connection = await oracledb.getConnection();
const data = await connection.execute(sql);
fastify.cache.set(key, data.rows, ttl);
console.log('Real:', data.rows);
return data.rows;
// oracledb.getPool()._logStats(); // show pool statistics. _enableStats must be true
} catch (error) {
console.error(err);
} finally {
if (connection) await connection.close();
}
}
})
})
api.js
module.exports = async function (fastify, opts) {
fastify.get(
'/cached',
{
schema: {
description: 'Shared Api',
tags: ['Shared'],
},
},
async function (req, reply) {
const data = await fastify.simpleSelectWithCache('shared-cached', 60*1000, 'SELECT id FROM users WHERE id < 50')
reply.send(data);
}
);
};
Is this a correct use or am I making a mistake somewhere?
The connection is a heavy operation and, for every query, a new connection (aka a new socket) is created between your server and DB.
To optimize your plugin you need to create the connection pool at start:
module.exports = fp(async function (fastify, opts) {
await oracledb.createPool({
user: opts.user,
password: opts.password,
connectString: opts.connectString
})
fastify.decorate('simpleSelectWithCache', async function (key, ttl, sql) {
const cached = await fastify.cache.get(key)
if (cached) {
console.log('Cached:', cached.item)
return cached.item
} else {
let connection
try {
connection = await oracledb.getConnection()
const data = await connection.execute(sql)
fastify.cache.set(key, data.rows, ttl)
console.log('Real:', data.rows)
return data.rows
// oracledb.getPool()._logStats(); // show pool statistics. _enableStats must be true
} catch (error) {
console.error(error)
} finally {
if (connection) await connection.close()
}
}
})
fastify.addHook('onClose', (instance, done) => {
oracledb.getPool().close(10)
.then(done)
.catch(done)
})
})
// then register your plugin
fastify.register(myOraclePlugin, {
user: 'ora'
password: '1234',
connectString: 'foo'
})

How to get redis value for a given redis key using Nodejs + Redis

I am using Nodejs + redis to Set and Get key:value pairs. I have written a sample code to set a key:value pair and then fetch it using the default readme from npm redis plugin.
My goal here is to get value from the redis server using any given key. I have followed the steps as given by npm redis plugin. I am able to log it, but since the plugin is async cant figure out a way to get a synchronous client.get request.
My code is
var redis = require("redis"),
client = redis.createClient();
const bluebird = require("bluebird");
bluebird.promisifyAll(redis.RedisClient.prototype);
bluebird.promisifyAll(redis.Multi.prototype);
const redisKey = "redis-set-key";
const redisValue = "hello-world";
client.set(redisKey, redisValue);
function getData(key) {
return client.get(key, function(err, result) {
console.log("1. Get key from redis - ", result.toString());
return result.toString();
});
}
const getRedisdata = getData(redisKey);
console.log("2. getRedisdata", getRedisdata);
Result
2. getRedisdata false
1. Get key from redis - hello-world
My goal is to get the result like this
1. Get key from redis - hello-world
2. getRedisdata hello-world
Please help me resolve this.
Found a solution, here is my resolved code
const redis = require("redis");
const client = redis.createClient();
const bluebird = require("bluebird");
bluebird.promisifyAll(redis.RedisClient.prototype);
bluebird.promisifyAll(redis.Multi.prototype);
const redisKey = "redis-set-key";
const redisValue = "hello-world";
client.set(redisKey, redisValue);
async function setKey(key, value, expire = "EX", time = 300) {
return new Promise((resolve, reject) => {
return client.set(key, value, function(err, result) {
if (result === null) {
reject("set key fail promise");
} else {
resolve(result);
}
});
});
}
async function getKey(key) {
return new Promise((resolve, reject) => {
return client.getAsync(key).then(function(res) {
if (res == null) {
reject("fail promise");
} else {
resolve(res);
}
});
});
}
async function hashGetKey(hashKey, hashvalue) {
return new Promise((resolve, reject) => {
return client.hget(hashKey, hashvalue, function(err, res) {
if (res == null) {
reject("hash key fail promise");
} else {
resolve(res.toString());
}
});
});
}
async function hashGetAllKey(hashKey) {
return new Promise((resolve, reject) => {
return client.hgetall(hashKey, function(err, res) {
if (res == null) {
reject("hash key all fail promise");
} else {
resolve(res);
}
});
});
}
async function delKey(key) {
return new Promise((resolve, reject) => {
return client.del(key, function(err, result) {
if (result === null) {
reject("delete fail promise");
} else {
resolve(result);
}
});
});
}
(async () => {
// get single key value
try {
const keyData = await getKey("string key");
console.log("Single key data:-", keyData);
} catch (error) {
console.log("Single key data error:-", error);
}
// get single hash key value
try {
const hashKeyData = await hashGetKey("hashkey", "hashtest 1");
console.log("Single hash key data:-", hashKeyData);
} catch (error) {
console.log("Single hash key data error:-", error);
}
// get all hash key values
try {
const allHashKeyData = await hashGetAllKey("hashkey");
console.log("All hash key data:-", allHashKeyData);
} catch (error) {
console.log("All hash key data error:-", error);
}
// delte single key
try {
const checkDel = await delKey("XXYYZZ!!!!");
console.log("Check key delete:-", checkDel);
} catch (error) {
console.log("Check key delete error:-", error);
}
// set single key
try {
const checkSet = await setKey("XXYYZZ", "AABBCC");
console.log("Check data setkey", checkSet);
} catch (error) {
console.log("Check data setkey error", error);
}
})();
// hget hashkey "hashtest 1"
client.hset("hashkey", "hashtest 1", "some value", redis.print);
client.hset(["hashkey", "hashtest 2", "some other value"], redis.print);
Haven't you read the Redis module's readme, it provides another way to use async/await way to make the async redis get process as sync.
const { promisify } = require("util");
const getAsync = promisify(client.get).bind(client);
getAsync.then(console.log).catch(console.error);

Handling promises inside the forEach loop

I am trying to run a series of tasks. Each task is dynamic, and could have different rules to follow. This will be executed on AWS-Lambda.
I have an array of JSON. It has a body with task name in it, and it also has attributes.
I need to dynamically load a javascript file with the name inside the body.
I need to wait until all is finished inside that task. Or it failed (regardless where). If the fail happens, I will need to write that data inside the current record inside the forEach loop.
I have old issue, where my forEach is finished first without waiting for the task to complete.
This is the forEach loop:
const jobLoader = require('./Helpers/jobLoader');
event.Records.forEach(record => {
const { body: jobName } = record;
const { messageAttributes } = record;
const job = jobLoader.loadJob(jobName);
job.runJob(messageAttributes).then(res => {
console.log('Show results');
return; // resume another record from forEach
}).catch(err => {
record.failed = true;
record.failureMessage = err.message;
console.log('I errored');
});
console.log('All Done');
});
The problem is that message All Done is printed, and then the message show results is printed. I get results from the database once it comes for execution.
This is the file that loads a task:
exports.loadJob = (jobName) => {
const job = require(`../Tasks/${jobName}`);
return job;
};
This is the file that contains actual task:
const mySqlConnector = require('../Storage/mySql');
exports.runJob = async (params) => {
let payload = {};
let dataToSend = await getUserName(params.userId.stringValue);
payload.dataToSend = dataToSend;
let moreDataToSend = await getEvenMoreData(params.userId.stringValue);
payload.moreDataToSend = moreDataToSend;
return await sendData(payload);
};
const getUserName = async (userId) => {
const query = 'SELECT * FROM user_data';
return await mySqlConnector.handler(query);
};
const getEvenMoreData = async (userId) => {
const query = 'SELECT * FROM user_data';
return await mySqlConnector.handler(query);
};
const sendData = (payload) => {
//this should be Axios sending data
};
And this is the mySql connector itself:
const mysql = require('promise-mysql');
exports.handler = async (query) => {
return mysql.createConnection({
host : '127.0.0.1',
user : 'root',
password : '',
database: 'crm'
}).then(conn =>{
let result = conn.query(query);
conn.end();
return result;
}).then(rows => {
//console.log("These are rows:" + rows);
return rows;
}).catch(error => {
return error;
});
};
The task file can have any number of things it needs to complete, which will be different when I start adding tasks.
I need that job.runJob completes, or that it catches an error, from whatever location it originated, so I can continue with the forEach.
I have tried using map and what not, but the end result is always the same.
What am I doing wrong?
You can use Promise.all method :
const promises = event.Records.map(record => {
const { body: jobName } = record;
const { messageAttributes } = record;
const job = jobLoader.loadJob(jobName);
return job.runJob(messageAttributes).then(res => {
console.log('Show results', res);
}).catch(err => {
record.failed = true;
record.failureMessage = err.message;
console.log('I errored');
throw new Error('Your error !');
});
});
try {
const results = await Promise.all(promises);
console.log('All done');
} catch (e) {
console.log('Something has an error', e);
}
don't forget to make your function async !
I managed to solve it, and still keep details about the execution:
Something like this:
for (let prop in event.Records){
const { body: jobName } = event.Records[prop];
const { messageAttributes } = event.Records[prop];
const job = jobLoader.loadJob(jobName);
await job.runJob(messageAttributes).then(res => {
console.log('Show results', res);
}).catch(err => {
event.Records[prop].failed = true;
event.Records[prop].failed = err.message;
console.log('I errored');
});
}

Resources