Query was already executed, MoongoseError - node.js

I am making a transaction controller in NodeJS but when I send data through postman I get this error:
MongooseError: Query was already executed: Customer.updateOne({ name: 'Axel' }, { '$set': { balance: 98...
at model.Query._wrappedThunk [as _updateOne] (C:\Users\m4afy\Desktop\the spark foundation\Banking system\node_modules\mongoose\lib\helpers\query\wrapThunk.js:23:19)
at C:\Users\m4afy\Desktop\the spark foundation\Banking system\node_modules\kareem\index.js:494:25
at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
originalStack: 'Error\n' +
' at model.Query._wrappedThunk [as _updateOne] (C:\\Users\\m4afy\\Desktop\\the spark foundation\\Banking system\\node_modules\\mongoose\\lib\\helpers\\query\\wrapThunk.js:27:28)\n' +
' at C:\\Users\\m4afy\\Desktop\\the spark foundation\\Banking system\\node_modules\\kareem\\index.js:494:25\n' +
' at process.processTicksAndRejections (node:internal/process/task_queues:77:11)'
}
my Transaction code goes as follow:
const Transaction = require('../models/transaction')
const Customer = require('../models/customers')
const cashTransaction = async (req, res, next) => {
const {from, to, amount} = req.body
try {
let sender = await Customer.findOne({'name' : `${from}`})
let senderBalance = Number(sender.balance) - Number(amount)
await Customer.updateOne({name : from}, {balance : senderBalance}, err =>{
if (err){
console.log(err)
res.status(500).send('Could not update sender information')
} else {
console.log('Sender information updated');
}
})
let receiver = await Customer.findOne({name : to})
let receiverBalance = Number(receiver.balance) + Number(amount)
await Customer.updateOne({name : to}, {balance : receiverBalance}, err =>{
if (err){
console.log(err);
res.status(500).send('Could not update receive ver information')
} else{
console.log('receiver information updated');
}
})
const transaction = new Transaction({
from,
to,
amount
})
await transaction.save()
res.status(200).json({transaction : {transaction} , message : 'transaction saved'})
} catch (error) {
console.log(error);
res.status(500).send('An Error occured');
}
}
how can I update it multiple times?
It worked one time but then am getting this error, any help?

Using await and a callback simultaneously will result in the query executing twice.
The Model.updateOne method returns a query object. Passing a callback function causes the query to be immediately executed and then the callback is called. Await will likewise cause the query to be executed, and will return the result.
When you use both at the same time, both try to execute the query, but a specific instance of a query can only be executed once, hence the error.
You might try using await inside of a try/catch instead of a callback.
Each call to updateOne instantiates a new query object, so you should be able to do both updates

Related

Sequelize not retrieving all data after insert

I have noticed that my backend is not retrieving the expected data after an insert.
In my React application, I have one function which inserts data into the database and after getting a response, a new request is sent to update the current component state with the newly fetched data.
All my functions are using await/async and in the backend, all transactions are correctly used and committed in order.
My client is calling the following endpoints:
-POST: api/ticket ( INSERT AN ITEM)
-GET: api/ticket (GET ALL ITEMS)
Here is what the backend is showing which looks correct to me, the problem is that in the 'SELECT' statement, the inserted item is not retrieved.
The transactions are started from two different routes but I don't see why it should be an issue.
In addition, I tried to change the AddItem function to output the same findAll statement which is called when using the GET method and the data returned are correct.
So why if I separate these two flows I do not get all the items? I always need to refresh the page to get the added item.
START TRANSACTION;
Executing (a9d14d5c-c0ac-4821-9b88-293b086debaa): INSERT INTO `messages` (`id`,`message`,`createdAt`,`updatedAt`,`ticketId`,`userId`) VALUES (DEFAULT,?,?,?,?,?);
Executing (a9d14d5c-c0ac-4821-9b88-293b086debaa): COMMIT;
Executing (9ee9ddaa-294e-41d1-9e03-9f02a2737030): START TRANSACTION;
Executing (9ee9ddaa-294e-41d1-9e03-9f02a2737030): SELECT `ticket`.`id`, `ticket`.`subject`, `ticket`.`status`, `ticket`.`createdAt`, `ticket`.`updatedAt`, `ticket`.`deletedAt`, `ticket`.`userId`, `messages`.`id` AS `messages.id`, `messages`.`message` AS `messages.message`, `messages`.`sender` AS `messages.sender`, `messages`.`createdAt` AS `messages.createdAt`, `messages`.`updatedAt` AS `messages.updatedAt`, `messages`.`deletedAt` AS `messages.deletedAt`, `messages`.`ticketId` AS `messages.ticketId`, `messages`.`userId` AS `messages.userId`, `messages->user`.`id` AS `messages.user.id`, `messages->user`.`firstname` AS `messages.user.firstname`, `messages->user`.`surname` AS `messages.user.surname`, `messages->user`.`email` AS `messages.user.email`, `messages->user`.`password` AS `messages.user.password`, `messages->user`.`stripeId` AS `messages.user.stripeId`, `messages->user`.`token` AS `messages.user.token`, `messages->user`.`birthDate` AS `messages.user.birthDate`, `messages->user`.`status` AS `messages.user.status`, `messages->user`.`confirmationCode` AS `messages.user.confirmationCode`, `messages->user`.`createdAt` AS `messages.user.createdAt`, `messages->user`.`updatedAt` AS `messages.user.updatedAt`, `messages->user`.`deletedAt` AS `messages.user.deletedAt` FROM `tickets` AS `ticket` LEFT OUTER JOIN `messages` AS `messages` ON `ticket`.`id` = `messages`.`ticketId` AND (`messages`.`deletedAt` IS NULL) LEFT OUTER JOIN `users` AS `messages->user` ON `messages`.`userId` = `messages->user`.`id` AND (`messages->user`.`deletedAt` IS NULL) WHERE (`ticket`.`deletedAt` IS NULL);
Executing (9ee9ddaa-294e-41d1-9e03-9f02a2737030): COMMIT;
-- POST '/api/ticket
exports.addMessage = async (req, res) => {
try {
const result = await sequelize.transaction(async (t) => {
var ticketId = req.body.ticketId;
const userId = req.body.userId;
const message = req.body.message;
const subject = req.body.subject;
// Validate input - If new ticket, a subject must be provided
if (!ticketId && !subject) {
return res
.status(400)
.send({ message: "New ticket must have a subject" });
}
// Validate input - If ticket exists, userId and message must be provided
if (!userId && !message && ticketId) {
return res
.status(400)
.send({ message: "UserID and message are required" });
}
// Create ticket is no ticketID was provided
if (!ticketId) {
const [ticket, created] = await Ticket.findOrCreate({
where: {
subject: subject,
userId: userId,
},
transaction: t,
});
ticketId = ticket.id;
}
// Create a new message object
const messageObject = await db.message.create(
{
message: message,
userId: userId,
ticketId: ticketId,
},
{ transaction: t }
);
// Output message object
return res.send(messageObject);
});
} catch (err) {
console.log(err);
return res.status(500).send({
message:
err.message || "Some error occurred while creating the ticket message.",
});
}
};
-- GET: api/ticket
exports.findAll = async (req, res) => {
try {
const result = await sequelize.transaction(async (t) => {
const tickets = await db.ticket.findAll(
{
include: [{ model: db.message, include: [db.user] }],
},
{ transaction: t }
);
tickets.forEach((ticket) => {
console.log(JSON.stringify(ticket.messages.length));
});
return res.send(tickets);
});
} catch (err) {
console.log(err);
res.status(500).send({
message: err.message || "Some error occurred while retrieving Tickets.",
});
}
};
You sent a response to a client before the transaction actually was committed. You just need to move res.send(messageObject); outside the transaction call.
You can try to look what's going on in the current version of your code if you add several console.log with messages to see what the actual order of actions is (I mean a couple of messages in POST (the last statement inside transaction and after transaction before res.send) and at least one at the beginning of GET).
Actually if the transaction was rolled back you'd send an uncommited and already removed object/record that I suppose is not your goal.

MongoError: pool destroyed when fetching all data without conditions

I am new to mongoDb, as I am trying to query from different collection and in order to do that, when I am fetching data from category collection I mean when I am running select * from collection it is throwing error, MongoError: pool destroyed.
As per my understanding it is because of some find({}) is creating a pool and that is being destroyed.
The code which I am using inside model is below,
const MongoClient = require('mongodb').MongoClient;
const dbConfig = require('../configurations/database.config.js');
export const getAllCategoriesApi = (req, res, next) => {
return new Promise((resolve, reject ) => {
let finalCategory = []
const client = new MongoClient(dbConfig.url, { useNewUrlParser: true });
client.connect(err => {
const collection = client.db(dbConfig.db).collection("categories");
debugger
if (err) throw err;
let query = { CAT_PARENT: { $eq: '0' } };
collection.find(query).toArray(function(err, data) {
if(err) return next(err);
finalCategory.push(data);
resolve(finalCategory);
// db.close();
});
client.close();
});
});
}
When my finding here is when I am using
let query = { CAT_PARENT: { $eq: '0' } };
collection.find(query).toArray(function(err, data) {})
When I am using find(query) it is returning data but with {} or $gte/gt it is throwing Pool error.
The code which I have written in controller is below,
import { getAllCategoriesListApi } from '../models/fetchAllCategory';
const redis = require("redis");
const client = redis.createClient(process.env.REDIS_PORT);
export const getAllCategoriesListData = (req, res, next, query) => {
// Try fetching the result from Redis first in case we have it cached
return client.get(`allstorescategory:${query}`, (err, result) => {
// If that key exist in Redis store
if (false) {
res.send(result)
} else {
// Key does not exist in Redis store
getAllCategoriesListApi(req, res, next).then( function ( data ) {
const responseJSON = data;
// Save the Wikipedia API response in Redis store
client.setex(`allstorescategory:${query}`, 3600, JSON.stringify({ source: 'Redis Cache', responseJSON }));
res.send(responseJSON)
}).catch(function (err) {
console.log(err)
})
}
});
}
Can any one tell me what mistake I am doing here. How I can fix pool issue.
Thanking you in advance.
I assume that toArray is asynchronous (i.e. it invokes the callback passed in as results become available, i.e. read from the network).
If this is true the client.close(); call is going to get executed prior to results having been read, hence likely yielding your error.
The close call needs to be done after you have finished iterating the results.
Separately from this, you should probably not be creating the client instance in the request handler like this. Client instances are expensive to create (they must talk to all of the servers in the deployment before they can actually perform queries) and generally should be created per running process rather than per request.

Transaction error using node-mssql library

Hello I'm using the mssql library to insert some data into an SQL Server DB, the call is being executed inside a queue so if it fails it will retry in 1 min * retryAttemp up until 5 attempts
however I keep getting this weird behavior where I get this error message:
ERROR: Error inserting into BASIS OLAP TransactionError: Can't acquire connection for the request. There is another request in progress.
and I don't know why this is happening... this is my code:
static async insertPresale(req, res) {
logger.info('Inserting new Pre-Sale');
const { CAB, DET } = req.body.basisOlapStructure;
const { country } = req.body;
if (country.toLowerCase() !== 'cl') return res.status(404).json({ success: false, message: 'Other countries are not currently supported' });
let pool;
let transaction;
try {
pool = await Databases.ClSQLServerPreventa;
if (!pool) throw new errors.UNEXPECTED_ERROR({ message: 'Error inserting pre-sale in BASIS OLAP. Could not get a connection pool' });
transaction = new mssql.Transaction(pool);
await new Promise((resolve, reject) => transaction.begin(err => (err ? reject(err) : resolve())));
const request = new mssql.Request(transaction);
logger.info('Inserting data into PVRCabecera');
let queryStr = msql.insert().into(bTables.PVRCabecera.toString()).setFields(CAB);
logger.info(queryStr.toString());
let result = await request.query(queryStr.toString());
if (result.rowsAffected > 0) logger.info('Data successfully inserted into PVRCabecera');
logger.info('Inserting data into PVRDetalle');
const queries = DET.map((d) => {
queryStr = msql.insert().into(bTables.PVRDetalle.toString()).setFields(d);
logger.info(queryStr.toString());
return request.query(queryStr.toString());
});
result = await Promise.all(queries);
if (result[0].rowsAffected > 0) logger.info('Data successfully inserted into PVRDetalle');
await transaction.commit();
logger.info('BASIS_OLAP Transaction completed successfully');
return res.status(201).json({ success: true });
} catch (error) {
logger.error(`Error inserting into BASIS OLAP ${error}`);
if (transaction) {
await transaction.rollback();
}
logger.info('Transaction has been rolled back');
throw new errors.UNEXPECTED_ERROR({ message: `Error inserting pre-sale in BASIS OLAP. ${error.toString()}` });
}
}
what can I do to solve this problem?

nodejs mongodb official node package - async function won't return data

I am trying to write a simple function to grab the id of a specific instance based on matching criteria from mongodb using the official node package 'mongodb'.
My function works as I can console log the data but I am unable to return the data to use it as I intended to do as you can see.
const mongo = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
// Function for finding database id of device based on deviceKey, The database is written into
// the code under the const 'db' as is the collection.
async function fetchId(deviceKey) {
const client = await mongo.connect(url, { useNewUrlParser: true });
const db = client.db('telcos');
const collection = db.collection('device');
try {
await collection.find({"deviceKey": deviceKey}).toArray((err, response) => {
if (err) throw err;
console.log(response[0]._id); // << works logs _id
return response[0]._id; // << does nothing... ?
})
} finally {
client.close();
}
}
// # fetchId() USAGE EXAMPLE
//
// fetchId(112233); < include deviceKey to extract id
//
// returns database id of device with deviceKey 112233
// Run test on fetchId() to see if it works
fetchId("112233")
.then(function(id) {
console.dir(id); // << undefined
})
.catch(function(error) {
console.log(error);
});
Why does my test return undefined but my console.log() inside the function works?
It looks like you're combining callback code with async/await code in an odd way. Your function fetchId isn't returning anything at all, which is why you don't see id after fetching.
try {
const response = await collection.find(...).toArray()
return response[0]._id
}...
If we weren't able to await collection.find(...).toArray() and needed to manually convert this from using callbacks to promises, we'd have to do something like:
function fetchId (id) {
// this function returns a promise
return new Promise((resolve, reject) => {
...
collection.find(...).toArray((err, response) => {
// within the callback, returning values doesn't do anything
if (err) return reject(err);
return resolve(response[0]._id);
})
});
}
You are returning a value but handled like a promise is being returned.Please try this code.I had not tested it.
const mongo = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
// Function for finding database id of device based on deviceKey, The database is written into
// the code under the const 'db' as is the collection.
async function fetchId(deviceKey) {
return new Promise((resolve,reject)=>{
const client = await mongo.connect(url, { useNewUrlParser: true });
const db = client.db('telcos');
const collection = db.collection('device');
try {
await collection.find({"deviceKey": deviceKey}).toArray((err, response) => {
if (err) throw err;
console.log(response[0]._id); // << works logs _id
return resolve(response[0]._id); // << does nothing... ?
})
}
catch(error){
return reject(error);
}
finally {
client.close();
}
});
}
// # fetchId() USAGE EXAMPLE
//
// fetchId(112233); < include deviceKey to extract id
//
// returns database id of device with deviceKey 112233
// Run test on fetchId() to see if it works
fetchId("112233")
.then(function(id) {
console.dir(id); // << undefined
})
.catch(function(error) {
console.log(error);
});

Firebase cloud-function multiple executions

I have a firebase (Google) cloud-function as follows
// Initialize the Auth0 client
var AuthenticationClient = require('auth0').AuthenticationClient;
var auth0 = new AuthenticationClient({
domain: 'familybank.auth0.com',
clientID: 'REDACTED'
});
function getAccountBalance(app) {
console.log('accessToken: ' + app.getUser().accessToken);
auth0.getProfile(app.getUser().accessToken, function (err, userInfo) {
if (err) {
console.error('Error getting userProfile from Auth0: ' + err);
}
console.log('getAccountBalance userInfo:' + userInfo)
let accountowner = app.getArgument(PARAM_ACCOUNT_OWNER);
// query firestore based on user
var transactions = db.collection('bank').doc(userInfo.email)
.db.collection('accounts').doc(accountowner)
.collection('transactions');
var accountbalance = transactions.get()
.then( snapshot => {
var workingbalance = 0
snapshot.forEach(doc => {
workingbalance = workingbalance + doc.data().amount;
});
app.tell(accountowner + " has a balance of $" + workingbalance)
})
.catch(err => {
console.log('Error getting transactions', err);
app.tell('I was unable to retrieve your balance at this time.')
});
});
}
actionMap.set(INTENT_ACCOUNT_BALANCE, getAccountBalance);
app.handleRequest(actionMap);
When this executes, I see the following logs
Notice that parts of the function are being executed multiple times, and the second execution is failing. If I close out the auth0.getProfile call after logging userInfo, then the function works, but obviously doesn't have userInfo.
Any idea why parts of this function are executing multiple times and why some calls would fail?
The userInfo is undefined at point (2) because there has been an error (reported on the line right beneath it, which was the previous logged message). Your error block does not leave the function, so it continues to run with an invalid userInfo object.
But that doesn't explain why the callback is getting called twice - once with a valid userInfo and once with an err. The documentation (although not the example) for AuthenticationClient.getProfile() indicates that it returns a Promise (or undefined - although it doesn't say why it might return undefined), so I am wondering if this ends up calling the callback twice.
Since it returns a promise, you can omit the callback function and just handle it with something like this:
function getAccountBalance(app) {
let accountowner = app.getArgument(PARAM_ACCOUNT_OWNER);
console.log('accessToken: ' + app.getUser().accessToken);
var accessToken = app.getUser().accessToken;
auth0.getProfile( accessToken )
.then( userInfo => {
console.log('getAccountBalance userInfo:' + userInfo)
// query firestore based on user
var transactions = db.collection('bank').doc(userInfo.email)
.db.collection('accounts').doc(accountowner)
.collection('transactions');
return transactions.get();
})
.then( snapshot => {
var workingbalance = 0
snapshot.forEach(doc => {
workingbalance = workingbalance + doc.data().amount;
});
app.tell(accountowner + " has a balance of $" + workingbalance)
})
.catch( err => {
console.error('Error:', err );
app.tell('I was unable to retrieve your balance at this time.')
})
});
}

Resources