I am trying to find and update a document from a mongodb database but I am facing this error over and over again
The error
MongooseError: Operation `users.findOneAndUpdate()` buffering timed out after 10000ms
at Timeout.<anonymous> (C:\Development\discord-bot\api\node_modules\mongoose\lib\drivers\node-mongodb-native\collection.js:148:23)
What I haved tried ?
I have used using async/await for the mongodb connection and still it doesnt work
Code
index.js
client.once('ready', async () => {
console.log('Ready!');
client.user.setActivity(`Mind Meld`, {
type: "PLAYING",
});
await mongo().then(async mongoose => {
try {
console.log('Connected to mongo!!');
}
finally {
mongoose.connection.close();
}
});
});
database/mongo.js
const mongoose = require('mongoose');
require('dotenv').config();
module.exports = async () => {
await mongoose.connect(process.env.MONGODB_URL, {
keepAlive: true,
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(x => {
console.log(
`Connected to Mongo! Database name: "${x.connections[0].name}"`,
);
})
.catch(err => {
console.error('Error connecting to mongo', err);
});
return mongoose;
};
The command
const Discord = require('discord.js')
const User = require('../../../api/database/schemas/user')
const { error, success } = require('../../utils/colors')
module.exports = {
name: 'lobby',
aliases: ['lobby'],
execute(message, args) {
User.findOneAndUpdate({ discordId: message.author.id }, { $set: { playing: true } }, { new: true }, (err, doc) => {
if (err) {
console.log(err)
}
else {
message.reply("done!")
}
})
},
};
Related
require('dotenv').config()
const mongoose = require("mongoose");
const logger = require("./logger");
module.exports = {
init: async () => {
mongoose.Promise = global.Promise;
mongoose.connection.on("err", (err) => {
logger.error(`Mongoose connection error: ${err.stack}`, {
label: "Database",
});
});
mongoose.connection.on("disconnected", () => {
logger.error(`Mongoose connection lost`, { label: "Database" });
});
mongoose.connection.on("connected", () => {
logger.info(`Mongoose connection connected`, { label: "Database" });
});
mongoose.set("useNewUrlParser", true);
mongoose.set("useFindAndModify", false);
mongoose.set("useCreateIndex", true);
await mongoose
.connect(process.env.MONGO)
.catch((e) => {
logger.error(e.message, { label: "Database" });
process.exit(1);
});
return true;
},
};
Error:
[unhandledRejection] Query was already executed: guild.findOne({ guildId: 'idnumber' })
I am trying to fix it and still having the query error as shown in the image. I am using latest mongoose.
I have an Express based CRUD application which uses MongoDB as its DB. I have noticed that some concurrent writes fail if they use bulkWrite and session.
A simplified example looks like this:
import express from 'express';
import { v4 } from 'uuid';
import mongoose from 'mongoose';
const router = express.Router();
const mongoString = 'mongodb://127.0.0.1:27017/testMongo?directConnection=true';
const port = 3288;
const testId = mongoose.Types.ObjectId();
const Model = mongoose.model('Data', new mongoose.Schema({
whateverString: {
required: true,
type: String,
},
}));
mongoose.connect(mongoString);
const database = mongoose.connection;
database.on('error', (error) => {
console.log(error);
});
database.once('connected', async () => {
console.log('Database connected');
// Add test data if not exists
if (!await Model.exists({ _id: testId })) {
const data = new Model({
_id: testId,
whateverString: v4(),
});
await data.save();
}
});
const app = express();
app.use(express.json());
router.post('/', async (req, res) => {
const session = await mongoose.startSession();
session.startTransaction();
try {
await Model.bulkWrite([
{
updateOne: {
filter: {
_id: testId,
},
update: {
whateverString: v4(),
},
},
},
], { session });
await session.commitTransaction();
res.status(200).json({ allRight: true });
} catch (error) {
await session.abortTransaction();
console.log(error.message);
res.status(400).json({ message: error.message });
} finally {
session.endSession();
}
});
app.use('/', router);
app.listen(port, async () => {
console.log(`Server started at ${port}`);
});
What this does is:
connecting to Mongo
creating a test document
creating a web server and one post route
if the post route is called, the test document is updated with a random string in a bulkWrite and a session
Now take a simple client script which does three requests in parallel:
import fetch from 'node-fetch';
function doFetch() {
return fetch('http://localhost:3288', { method: 'post' });
}
async function myMain() {
try {
const promises = [doFetch(), doFetch(), doFetch()];
const response = await Promise.all(promises);
console.log('response', response.map(resp => ({ status: resp.status, text: resp.statusText })));
} catch (error) {
console.log(error);
}
}
myMain();
The result is: Only one DB query works, whereas the others fail with the error WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.
I am rather new to MongoDB and Mongoose but in my understanding of databases, such a use-case should be fine. (In this example, the requests would overwrite each other and create chaos, but in the real-life use case that should not be a problem at all.)
Some observations I've made:
Without passing session to bulkWrite, everything works fine.
It is obviously some kind of race condition: sometimes two queries go through, sometimes only one.
Setting maxTransactionLockRequestTimeoutMillis to 20000 did not help.
If I include the fetching in the server process itself (after console.log('Server started ...), then everything works fine. I cannot explain that to be honest.
What am I doing wrong? How can I solve that problem?
Appendix: The package.json file of that example looks like this:
{
"name": "rest-api-express-mongo",
"dependencies": {
"express": "^4.17.3",
"mongoose": "^6.2.2",
"node-fetch": "^3.2.10",
"uuid": "^9.0.0"
},
"type": "module"
}
``
Thanks to the comment provided by Marco Luzzara I was able to refactor and solve the issue via callbacks.
The code being now:
let retry = 0;
await database.getClient().withSession(async (session) => {
try {
await session.withTransaction(async () => {
await Model.bulkWrite([
{
updateOne: {
filter: {
_id: testId,
},
update: {
whateverString: v4(),
},
},
},
], { session });
await session.commitTransaction();
res.status(200).json({ allRight: true });
});
} catch (error) {
console.log(error.message);
res.status(400).json({ message: error.message });
retry += 1;
if (retry > 5) {
session.endSession();
}
}
});
Just for reference - the whole file looks now like:
import express from 'express';
import { v4 } from 'uuid';
import mongoose from 'mongoose';
const router = express.Router();
const mongoString = 'mongodb://127.0.0.1:27017/testMongo?directConnection=true';
const port = 3288;
const testId = mongoose.Types.ObjectId();
const Model = mongoose.model('Data', new mongoose.Schema({
whateverString: {
required: true,
type: String,
},
}));
mongoose.connect(mongoString);
const database = mongoose.connection;
database.on('error', (error) => {
console.log(error);
});
database.once('connected', async () => {
console.log('Database connected');
// Add test data if not exists
if (!await Model.exists({ _id: testId })) {
const data = new Model({
_id: testId,
whateverString: v4(),
});
await data.save();
}
});
const app = express();
app.use(express.json());
router.post('/', async (req, res) => {
let retry = 0;
await database.getClient().withSession(async (session) => {
try {
await session.withTransaction(async () => {
await Model.bulkWrite([
{
updateOne: {
filter: {
_id: testId,
},
update: {
whateverString: v4(),
},
},
},
], { session });
await session.commitTransaction();
res.status(200).json({ allRight: true });
});
} catch (error) {
console.log(error.message);
res.status(400).json({ message: error.message });
retry += 1;
if (retry > 5) {
session.endSession();
}
}
});
});
app.use('/', router);
app.listen(port, async () => {
console.log(`Server started at ${port}`);
});
I have wrote a simple Update function. Its working fine for some minutes and then again its not working. Where I am going wrong? Please help me. I use PUT as my method.
code
accept = (req, res) => {
this._model.update({
user: new mongoose.Types.ObjectId(req.params.uid)
}, {
$set: {
status: 'active'
}
}, (err, obj) => {
if (err || !obj) {
res.send(err);
} else {
res.send(obj);
}
});
}
Model
{
"_id":"5d3189a00789e24a23438a0d",
"status":"pending",
"user":ObjectId("5d3189a00789e24a23438a0d"),
"code":"CT-123-345-234-233-423344",
"created_Date":"2019-07-19T09:13:04.297Z",
"updated_Date":"2019-07-19T09:13:04.297Z",
"__v":0
}
Request
api.abc.com/api/accept/5d3189a00789e24a23438a0d
Sometime it is returing values and sometime null.
You can use the following code to ensure the model is tied to a connection. This could be an issue of connection to the database.
const config = require('./config');
console.log('config.database.url', config.database.url);
return mongoose.createConnection(config.database.url, {
useMongoClient: true
})
.then((connection) => {
// associate model with connection
User = connection.model('User', UserSchema);
const user = new User({
email: 'someuser#somedomain.com',
password: 'xxxxx'
});
const prom = user.update();
// Displays: 'promise: Promise { <pending> }'
console.log('promise:', prom);
return prom
.then((result) => {
// Don't see this output
console.log('result:', result);
})
.catch((error) => {
// Don't see this output either
console.log('error:', error);
});
})
.catch((error) => {
console.log(error);
});
I think you need to use promise or async/await, try this
accept = async (req, res) => {
try {
const result = await this._model.update({
user: new mongoose.Types.ObjectId(req.params.uid)
}, {
$set: {
status: 'active'
}
});
return res.send(result);
} catch (e) {
return res.send(e);
}
};
I have a simple app with 2 JavaScript files. How do I throw my own safe error (from the insertorupdate of service.js) so the call stack isn't revealed to user?
console.js
const contracts = require('./contractService');
(async () => {
let contractToUpdate = {
idContract: 102,
AccountNo_Lender: 'Back to resolve'
};
try {
var results2 = await contracts.update(contractToUpdate);
console.log(results2);
} catch (error) {
console.log(error);
}
})();
contractService.js
require('dotenv/config');
const Sequelize = require('sequelize');
const sequelize = new Sequelize(process.env.DATABASE, process.env.USER, process.env.PASSWORD, {
host: process.env.HOST,
dialect: 'mysql',
operatorsAliases: false,
define: {
timestamps: false,
freezeTableName: true
},
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
});
sequelize
.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
})
.catch(err => {
console.error('Unable to connect to the database:', err);
});
/**
* Model for the contract entity.
*/
const Contract = sequelize.define('Contract', {
idContract: {
type: Sequelize.INTEGER,
primaryKey: true
},
AccountNo_Lender: {
type: Sequelize.STRING,
allowNull: false
}
});
exports.update = function (contract) {
return new Promise(function (resolve, reject) {
Contract.insertOrUpdate(contract) <====== Right here don't throw call stack
.then(c => {
resolve(c);
});
});
}
To avoid showing the callstack in general, just do:
function someFunction()
{
throw new Error("Something bad happened");
}
try
{
someFunction();
}
catch (err)
{
console.error("Error message: " + err.message);
}
I am following this graphql tutorial, everything was going ok until I try to use dataloaders.
My server.js is:
const start = async () => {
const mongo = await connectMongo();
const buildOptions = async req => {
const user = await authenticate(req, mongo.Users);
return {
context: {
dataloaders: buildDataloaders(mongo),
mongo,
user
},
schema
};
};
app.use('/graphql', bodyParser.json(), graphqlExpress(buildOptions));
app.use(
'/graphiql',
graphiqlExpress({
endpointURL: '/graphql',
passHeader: `'Authorization': 'bearer token-name#email.com'`
})
);
app.use('/', expressStaticGzip('dist'));
app.use('/attendance', expressStaticGzip('dist'));
app.use('/login', expressStaticGzip('dist'));
spdy.createServer(sslOptions, app).listen(process.env.PORT || 8080, error => {
if (error) {
console.error(error);
return process.exit(1);
} else {
console.info(
`App available at https://localhost:${process.env.PORT || 3000}`
);
}
});
};
My copy and paste dataloaders.js:
const DataLoader = require('dataloader');
async function batchUsers(Users, keys) {
return await Users.find({ _id: { $in: keys } }).toArray();
}
module.exports = ({ Users }) => ({
userLoader: new DataLoader(keys => batchUsers(Users, keys), {
cacheKeyFn: key => key.toString()
})
});
And my resolvers.js:
export default {
Query: {
allLinks: async (root, data, { mongo: { Links } }) =>
Links.find({}).toArray()
},
Mutation: {
createLink: async (root, data, { mongo: { Links }, user }) => {
const newLink = Object.assign({ postedById: user && user._id }, data);
const response = await Links.insert(newLink);
return Object.assign({ id: response.insertedIds[0] }, newLink);
},
createUser: async (root, data, { mongo: { Users } }) => {
const newUser = {
name: data.name,
email: data.authProvider.email.email,
password: data.authProvider.email.password
};
const response = await Users.insert(newUser);
return Object.assign({ id: response.insertedIds[0] }, newUser);
},
signinUser: async (root, data, { mongo: { Users } }) => {
const user = await Users.findOne({ email: data.email.email });
if (data.email.password === user.password) {
return { token: `token-${user.email}`, user };
}
}
},
Link: {
id: root => root._id || root.id,
postedBy: async ({ postedById }, data, { dataloaders: { userLoader } }) => {
return await userLoader.load(postedById);
}
},
User: {
id: root => root._id || root.id
}
};
When I try get my allLinks I got the error:
TypeError: The loader.load() function must be called with a value,but
got: undefined.
Can anyone help me?
So I was able to reproduce the error by creating a link with a user, deleting the user from the Mongo database, and then querying for the postedBy attribute of the Link.
I would suggest dropping all your links and recreating your user (register + sign in), creating a new link, then querying for the postedBy field.