I have read a lot of posts here about the "too many connections" issue.
I am struggling to loop a modest amount of CSV data (10,000 rows) upload to ElephantSQL (psql).
I am on the free plan for ElephantSQL. And while I can upgrade to get more concurrent connections, the issue is that I am not sure how to manage connections.
Here is my code:
First I create individual URLs to pass to axios in the extractToRaw (raw_data table in psql)
readCSV(async (list) => {
const apiURLList = await list.map((item) => {
return `apiDomain=${domain}&api_key=${apiKey}`;
});
for (const url of apiURLList) {
await extractToRaw(url);
}
});
Then:
const extractToRaw = async (url) => {
let records = [];
try {
await axios({
method: "get",
url: url,
params: {
//things here
},
}).then((data) => {
const contactRecord = data.data.data;
const emailData = data.data.data.emails;
const metaData = data.data.meta;
//
if (metaData.results === 0) {
try {
console.log(`no emails for ${contactRecord.domain}`);
upload_no_email(contactRecord.domain);
} catch (err) {
console.log("name: ", err.name, "message: ", err.message);
}
} else
for (const record of emailData) {
console.log(`Writing ${record.value} record...`);
records.push({
firstname: record.first_name,
lastname: record.last_name,
position: record.position,
seniority: record.seniority,
email: record.value,
website: record.value.split("#")[1],
confidence: record.confidence,
});
console.log(records);
}
//upload to table
uploadToRaw(records);
});
} catch (err) {
console.log(err);
}
};
Finally - upload to PSQL
const uploadToRaw = (records) => {
console.log(`uploading from ${records[0].website}`);
for (const record of records) {
const valuesArr = [
record.firstname,
record.lastname,
record.position,
record.seniority,
record.email,
record.website,
record.confidence,
];
pool.query(
`
INSERT INTO raw_data(firstname, lastname, position, seniority, email, website, confidence) VALUES($1, $2, $3, $4, $5, $6, $7)`,
valuesArr
);
}
};
Without fail, I will get a "too many connections" error.
Am I using pool.query wrong?
UPDATE: add node-postgres initialise script
const { Pool, Client } = require("pg");
const connectionString =
"string here";
const pool = new Pool({
connectionString,
});
const client = new Client({
connectionString,
});
The call to create a new pool takes in a parameter call max, which is what the max number of connections in this pool will be set to
Care must be taken that it aligns with the number of connections available on the DB server
Related
I am using express with MongoDB as a database and while I am sending requests using Axios library to my backend, I have found that two documents were created at the time. I figured that out by checking the createdAt field and found the two documents have the exact timestamp. I am creating createdAt & updatedAt fields using mongoose-timestamp2
Here is the code responsible for sending the documents which have isSynchronized flag set to false
const syncExams = async (token) => {
let examSyncError;
await Exam.find({ isSynchronized: false })
.cursor()
.eachAsync(async (exam) => {
// use try/catch inside the loop
// so if problem occured in one document, others will be posted normally
try {
const formData = new FormData();
formData.append("exam", JSON.stringify(exam));
Object.keys(exam.images.toJSON()).forEach((key) => {
formData.append("images", fs.createReadStream(exam.images[key]));
});
const res = await axios.post(`${BACKEND_URL}/exams/`, formData, {
maxContentLength: Infinity,
maxBodyLength: Infinity,
headers: {
...formData.getHeaders(),
Authorization: `Bearer ${token}`,
},
});
console.log(res);
exam.isSynchronized = true;
await exam.save();
} catch (err) {
examSyncError = err;
}
});
if (examSyncError) {
throw examSyncError;
}
};
And here is the controller responsible for handling the coming request and creating these documents in the database.
router.post(
"/exams",
fileUpload.array("images"),
trimmer,
syncControllers.postExam
);
const postExam = async (req, res, next) => {
let createdExam;
let exam;
let patient;
try {
const reqBody = JSON.parse(req.body.exam);
reqBody.operator = reqBody.creator;
delete reqBody._id;
delete reqBody.creator;
const { nationalID } = reqBody;
patient = await Patient.findOne({
"personalData.nationalID": nationalID,
}).exec();
if (!patient) {
return next(errorEmitter("exams.get-patient.not-found", 404));
}
createdExam = new Exam(reqBody);
// insert the new paths of the images
Object.keys(reqBody.images).forEach((key, index) => {
createdExam.images[key.replace("Path", "")].Path = req.files[index].path
.split(path.sep)
.slice(-3)
.join(path.sep);
});
createdExam.patient = patient._id;
const checkDate = new Date(createdExam.examDate);
checkDate.setMonth(checkDate.getMonth() - 3);
const nearExam = await Exam.findOne({
patient: createdExam.patient,
examDate: { $gte: checkDate, $lt: createdExam.examDate },
isGradable: true,
});
if (nearExam) {
createdExam.isGradable = false;
}
exam = await createdExam.save();
patient.exams.push(createdExam);
await patient.save();
} catch (err) {
return next(errorEmitter("exams.create.fail", 500));
}
return res.status(201).json(exam.toObject({ getters: true }));
};
I am using the following package for createdAt & updatedAt records created using this plugin.
const timestamps = require("mongoose-timestamp2");
examSchema.plugin(timestamps);
This problem occurred in two or three cases only and did not happen again until then and I could not replicate the error. I do not what caused this!
What could cause this problem to occur? Is it possible that Axios sent the same request multiple times and mongoose created two documents at the same exact time, or it could be a bug in mongoose-timestamp2 plugin?
Any help would be appreciated, thank you!
The official documentation of the Node.js Driver version 3.6 contains the following example for the .find() method:
const { MongoClient } = require("mongodb");
// Replace the uri string with your MongoDB deployment's connection string.
const uri = "mongodb+srv://<user>:<password>#<cluster-url>?w=majority";
const client = new MongoClient(uri);
async function run() {
try {
await client.connect();
const database = client.db("sample_mflix");
const collection = database.collection("movies");
// query for movies that have a runtime less than 15 minutes
const query = { runtime: { $lt: 15 } };
const options = {
// sort returned documents in ascending order by title (A->Z)
sort: { title: 1 },
// Include only the `title` and `imdb` fields in each returned document
projection: { _id: 0, title: 1, imdb: 1 },
};
const cursor = collection.find(query, options);
// print a message if no documents were found
if ((await cursor.count()) === 0) {
console.log("No documents found!");
}
await cursor.forEach(console.dir);
} finally {
await client.close();
}
}
To me this somewhat implies that I would have to create a new connection for each DB request I make.
Is this correct? If not, then what is the best practise to keep the connection alive for various routes?
You can use mongoose to set a connection with your database.
mongoose.connect('mongodb://localhost:27017/myapp', {useNewUrlParser: true});
then you need to define your models which you will use to communicate with your DB in your routes.
const MyModel = mongoose.model('Test', new Schema({ name: String }));
MyModel.findOne(function(error, result) { /* ... */ });
https://mongoosejs.com/docs/connections.html
It's 2022 and I stumbled upon your post because I've been running into the same issue. All the tutorials and guides I've found so far have setups that require reconnecting in order to do anything with the Database.
I found one solution from someone on github, that creates a class to create, save and check if a client connection exist. So, it only recreates a client connection if it doesn't already exist.
const MongoClient = require('mongodb').MongoClient
class MDB {
static async getClient() {
if (this.client) {
return this.client
}
this.client = await MongoClient.connect(this.url);
return this.client
}
}
MDB.url='<your_connection_url>'
app.get('/yourroute', async (req, res) => {
try {
const client = await MDB.getClient()
const db = client.db('your_db')
const collection = db.collection('your_collection');
const results = await collection.find({}).toArray();
res.json(results)
} catch (error) {
console.log('error:', error);
}
})
I'm writing a simple nodejs CLI tool while learning promises (to avoid callback hell), and every tutorial/stackoverflow example I've found only shows how to make a single call. My use case is as follows:
1. Connect to the Database (this I can do)
2. Perform a SQL select statement (also got this part)
3. Do some stuff with the results
4. Repeat steps 2 & 3 a few more times
I'm collecting the MSSQL user name and password (with hard-coded server 'localhost' and database name 'testdb') so when the app executes I can't just jump into the MSSQL connection.
I can get this via callbacks, but right now I have about 50 queries so you can imagine the ugliness. The full code below does get me the first query, and I strongly suspect I'm not passing the "pool" object to the next "then", but when I try
.then((pool,result) => {
//next command
})
It still doesn't recognize pool
Here is the code (index.js):
const mssql = require('mssql');
const qry1 = "select fieldA from tblA";
const qry2 = "select fieldB from tblB";
const qry3 = "select fieldC from tblC";
var dbuser = '';
var dbpass = '';
var config = {}
function init() {
log('Beginning Audit');
collectDbInfo(); //The reason I don't just included it all here
}
function collectDbInfo() {
//code is irrelevant to the problem, this is where I'm collecting the database credentials
}
function start() {
config = {
user: dbuser,
password: dbpass,
server: 'localhost',
database: 'testdb'
}
mssql.connect(config)
.then(pool => {
//FIRST query
return pool.request().query(qry1)
})
.then(result => {
processQryA(result);
//SECOND query
return pool.request().query(qry2)
})
.then(result => {
processQryB(result);
//THIRD query
return pool.request().query(qry3)
})
.then(result => {
processQryC(result);
})
mssql.on('error',err => {
log('SQL Error:' err)
mssql.close();
process.exit(0);
}
}
processQryA(data) {
console.log(data.recordset[0].fieldA)
}
processQryB(data) {
console.log(data.rcordset[0].fieldB)
}
processQryC(data) {
console.log(data.recordset[0].fieldC)
}
init();
I fully appreciate I may be approaching this all wrong, so any advice or especially examples would be greatly appreciated.
If the queries are absolutely sequential in nature, you can achieve that with async/await:
async function start(){
config = {
user: dbuser,
password: dbpass,
server: 'localhost',
database: 'testdb'
}
try {
pool = await mssql.connect(config);
const res1 = await pool.request().query(qry1);
processQryA(res1);
const res2 = await pool.request().query(qry2);
processQryB(res2);
const res3 = await pool.request().query(qry3);
processQryC(res3);
const res4 = await pool.request().query(qry4);
processQryD(res4);
/*..And so on with rest of sequential queries*/
/*Any of them resulting in error will be caught in (catch)*/
} catch (error) {
console.error("Error in start()::", error);
}
}
Also: I would probably have my pool getting method separately from query executions to handle errors/validations nicely.
I'm writing an API that gets past transactions on the Stellar network for a user, looks up the corresponding users in my database, and put's it all together into a JSON (to respond to the request - later).
Problem: looking up the username corresponding to the accountID ("from" field) is an async method with mongoose and only returns data after the JSON has been assembled.
I've tried to use async/await, promises, .thens but nothing seems to work.
server.payments()
.forAccount(accountId)
.cursor('now')
.order('desc')
.limit(2)
.call()
.then(function (page) {
var history = []
for(var i = 0; i<page.records.length; i++){
var obj = page.records[i]
//Get the username corresponding to the key from my database
//FIXME
var username
findUser(obj["from"]).then(result => {
username = result
})
var payment = {
"currency":obj["asset_code"],
"from": obj["from"],
"username":username,
"amount": obj["amount"],
"timestamp":obj["created_at"]
}
history.push(payment)
}
console.log(history)
//console.log(JSON.stringify(history))
})
.catch((err) => {
console.log(err)
})
async function findUser(senderKey){
var user = await mongoose.User.findOne({publicKey: senderKey})
console.log(user.username)
return user.username
}
Expected result: findUser returns value, payment variable uses it and gets logged together.
What happens: findUser starts looking for data, payment variable gets put together (username as undefined) and logged, findUser returns data.
Here's the log: (spiderman is the actual username from my database)
[ { currency: 'MEUR',
from: 'GACRQARPR2OMWRG6IH7HM5DYTA3FMM6UKA7NKS4BIJIADRIKFRPAIE7G',
username: undefined,
amount: '3.0000000',
timestamp: '2019-05-07T13:37:04Z' },
{ currency: 'MEUR',
from: 'GACRQARPR2OMWRG6IH7HM5DYTA3FMM6UKA7NKS4BIJIADRIKFRPAIE7G',
username: undefined,
amount: '2.0000000',
timestamp: '2019-05-07T13:34:21Z' } ]
spiderman
spiderman
Highlight recommend, you can make it easy with async/await syntax for your case.
You will wait until the server responses the page, then each of obj in page.records (.map or .forEach... will make you some mess), you wait for findUser function returns the username, then do your business.
try {
const page = await server.payments()
.forAccount(accountId)
.cursor('now')
.order('desc')
.limit(2)
.call()
const history = [];
for (const obj of page.records) {
const username = await findUser(obj["from"]);
const payment = {
"currency": obj["asset_code"],
"from": obj["from"],
"username": username,
"amount": obj["amount"],
"timestamp": obj["created_at"]
}
history.push(payment)
}
console.log(history)
} catch (e) {
console.log(e)
}
Remind: await expression only allowed within an async function.
You can use the new "for of" loop along with async await :-
server.payments()
.forAccount(accountId)
.cursor('now')
.order('desc')
.limit(2)
.call()
.then(async function (page) {
var history = []
for(const obj of page.records) {
const username = await findUser(obj["from"]);
var payment = {
"currency":obj["asset_code"],
"from": obj["from"],
"username":username,
"amount": obj["amount"],
"timestamp":obj["created_at"]
}
history.push(payment)
}
console.log(history)
//console.log(JSON.stringify(history))
})
.catch((err) => {
console.log(err)
})
You need a conditional that checks whether the function you are awaiting has completed
async function findUser(senderKey){
var user = await mongoose.User.findOne({publicKey: senderKey})
if (user){
console.log(user.username)
return user.username
}
}
I would like to setup my prepared statements with the mssql module. I created a query file for all user related requests.
const db = require('../databaseManager.js');
module.exports = {
getUserByName: async username => db(async pool => await pool.request()
.input('username', dataTypes.VarChar, username)
.query(`SELECT
*
FROM
person
WHERE
username = #username;`))
};
This approach allows me to require this query file and access the database by executing the query that is needed
const userQueries = require('../database/queries/users.js');
const userQueryResult = await userQueries.getUserByName(username); // call this somewhere in an async function
My database manager handles the database connection and executes the query
const sql = require('mssql');
const config = require('../config/database.js');
const pool = new sql.ConnectionPool(config).connect();
module.exports = async request => {
try {
const result = await request(pool);
return {
result: result.recordSet,
err: null
};
} catch (err) {
return {
result: null,
err
}
}
};
When I run the code I get the following error
UnhandledPromiseRejectionWarning: TypeError: pool.request is not a
function
Does someone know what is wrong with the code?
I think this happens because the pool is not initialized yet... but I used async/await to handle this...
Here is how I made your code work (I did some drastic simplifications):
const sql = require("mssql");
const { TYPES } = require("mssql");
const CONN = "";
(async () => {
const pool = new sql.ConnectionPool(CONN);
const poolConnect = pool.connect();
const getUserByName = async username => {
await poolConnect;
try {
const result = await pool.request()
.input("username", TYPES.VarChar, username)
.query(`SELECT
*
FROM
person
WHERE
username = #username;`);
return {
result: result.recordset,
err: null
};
} catch (err) {
return {
result: null,
err
};
}
};
console.log(await getUserByName("Timur"));
})();
In short, first read this.
You probably smiled when saw that the PR was created just 2 months before your questions and still not reflected in here.
Basically, instead of:
const pool = new sql.ConnectionPool(config).connect();
you do this:
const pool = new sql.ConnectionPool(config);
const poolConnection = pool.connect();
//later, when you need the connection you make the Promise resolve
await poolConnection;