Reputation: 1551
Using the relevant files below, I am trying to implement Knex transactions into my project. However, I get the following error:
KnexTimeoutError: Knex: Timeout acquiring a connection. The pool is probably full. Are you missing a .transacting(trx) call? at Client_SQLite3.acquireConnection (/c/Users/Raj/Projects/testserver/node_modules/knex/lib/client.js:347:26)
auth/methods.js
const bcrypt = require("bcrypt");
const knex = require("../db/connection");
const passport = require("./passportLocalStrategy");
async function createUser(req, res) {
console.log("we're in createUser");
try {
const errors = validateData(req);
if (errors.length > 0) throw errors;
const salt = bcrypt.genSaltSync();
const hash = bcrypt.hashSync(req.body.password, salt);
console.log("trying to insert user with knex");
const trxProvider = knex.transactionProvider();
const trx = await trxProvider();
return trx("users").insert({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: hash,
});
} catch (errors) {
console.log("error when inserting user with knex");
// return 400 error
return errors;
}
}
function validateData(req) {
console.log("validating data");
let errors = [];
if (!req.body.name) errors.push({ text: "Please enter name" });
if (!req.body.email) errors.push({ text: "Please enter email" });
if (!req.body.username) errors.push({ text: "Please enter username"})
if (req.body.password != req.body.password_confirm)
errors.push({ text: "Passwords do not match" });
if (req.body.password < 4)
errors.push({ text: "Password must be at least 4 characters" });
return errors;
}
async function login(req, res, next) {
console.log("logging in");
await passport.authenticate("local", function (err, user, info) {
if (err) {
console.log("error 1")
return next(err);
}
if (!user) {
console.log("error 2")
return res.redirect("/login");
}
req.login(user, function (err) {
if (err) {
console.log("error 3")
return next(err);
}
const user_data = {
email: req.user.email,
username: req.user.username,
name: req.user.name,
user_since: req.user.created_at,
};
console.log("returning user_data")
return user_data;
});
})(req, res, next);
}
module.exports = {
createUser,
selectByUsername,
login,
};
auth/passportLocalStrategy.js
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const init = require("./passportSessionConfig");
const knex = require("../db/connection");
const authUtils = require("./utils");
// const options = {};
init();
console.log("passport localstrategy loaded");
passport.use(
new LocalStrategy(
{
usernameField: "email",
passwordField: "password",
},
function (username, password, done) {
console.log("we're in passportLocalStrategy");
console.log(username);
// check to see if the username exists
knex("users")
.where({ email: username })
.orWhere({ username })
.first()
.then((results) => {
console.log("line 24 ", results);
console.log("within passportstrategy");
if (!results) {
console.log("no results");
return done(null, false);
}
if (!authUtils.comparePass(password, results.password)) {
console.log("PASSWORD DOESNT MATCH!");
return done(null, false);
} else {
console.log("PASSWORD MATCHES!!");
return done(null, results);
}
})
.catch((err) => {
console.log("caught an error!!!");
return done(err);
});
}
)
);
module.exports = passport;
routes/users.js
const express = require("express");
const router = express.Router();
const knex = require("../db/connection");
const passport = require("../auth/passportLocalStrategy");
const authUtils = require("../auth/utils");
const authMethods = require("../auth/methods");
router.post("/api/register", async (req, res, next) => {
try {
const ids = await authMethods.createUser(req, res);
console.log("got ids: ")
console.log(ids)
const currentUser = authMethods.login(req, res, next);
return res.status(200).send({ currentUser });
} catch (errors) {
// return 500 error
console.log("500 error")
console.log(errors)
return res.status(500).send({ errors });
}
});
router.post("/api/login", (req, res, next) => {
return authMethods.login(req, res, next);
});
router.post("/api/logout", (req, res) => {
console.log("inside logout");
req.logout();
req.flash("success_msg", "Logout successful");
return res.send({});
});
module.exports = router;
This is the code snippet from Knex's docs I'm trying to emulate:
// Does not start a transaction yet
const trxProvider = knex.transactionProvider();
const books = [
{title: 'Canterbury Tales'},
{title: 'Moby Dick'},
{title: 'Hamlet'}
];
// Starts a transaction
const trx = await trxProvider();
const ids = await trx('catalogues')
.insert({name: 'Old Books'}, 'id')
books.forEach((book) => book.catalogue_id = ids[0]);
await trx('books').insert(books);
// Reuses same transaction
const sameTrx = await trxProvider();
const ids2 = await sameTrx('catalogues')
.insert({name: 'New Books'}, 'id')
books.forEach((book) => book.catalogue_id = ids2[0]);
await sameTrx('books').insert(books);
What's wrong with my code?
Upvotes: 0
Views: 3035
Reputation: 19718
You need to trx.commit()
or trx.rollback(new Error('fail'))
when you don't need the transaction anymore. Now it is left open and the connection is never released.
Also there is no reason why you would need to use transaction provider in your case. You could just do the transaction like this:
async function createUser(req, res) {
console.log("we're in createUser");
try {
const errors = validateData(req);
if (errors.length > 0) throw errors;
const salt = bcrypt.genSaltSync();
const hash = bcrypt.hashSync(req.body.password, salt);
console.log("trying to insert user with knex");
await knex.transcation(trx => {
return trx("users").insert({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: hash,
});
});
} catch (errors) {
console.log("error when inserting user with knex");
// return 400 error
return errors;
}
}
and actually since you are doing just single insert query you don't even need a transaction for that. This is just as safe:
await knex("users").insert({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: hash,
});
Upvotes: 2