I am trying to query mongodb simple findOne in using mongodb. Cloudflare worker is giving 10ms CPU time but during preview/publish throwing error
I have tried installing these npm modules
npm i mongodb, mongodb-core, dgram, fs
var MongoClient = require('mongodb').MongoClient;
try{
var db = await MongoClient.connect('mongodb+srv://mongoURL',{ useNewUrlParser: true,useUnifiedTopology: true });
var dbo = db.db("test");
var result = await dbo.collection("testcollection").findOne()
const init = {
status: 200,
headers: { "Access-Control-Allow-Origin": "*", 'content-type': 'application/json' },
}
return new Response(JSON.stringify(result), init)
} catch(e) { console.log(e); return new Response(JSON.stringify(result), init) }
Error thrown is here - https://pastebin.com/xMKKjdZF
Currently, Cloudflare Workers does not support raw TCP/UDP, only HTTP/HTTPS. Hence you can only connect to databases that offer HTTP(S) interfaces. MongoDB's protocol is not HTTP-based, so you'll need to find some sort of HTTP API proxy you can put in front of it. (Also note that Cloudflare Workers is not based on Node.js, so in general Node modules that use Node's system APIs will not work.)
I'm just trying to write a simple Lambda function to insert data into my MongoDB Atlas cluster. I've set the cluster to accept all incoming traffic (0.0.0.0/0) and confirmed that I can connect locally.
For AWS Lambda, I set up a VPC using the VPC wizard, and I gave my Lambda function a security role with full admin access. I set the timeout to 12 seconds, but I'm still getting the following error:
Response:
{
"errorMessage": "2018-11-19T15:17:23.200Z 3048e1fd-ec0e-11e8-a03d-fb79584484c5 Task timed out after 11.01 seconds"
}
Request ID:
"3048e1fd-ec0e-11e8-a03d-fb79584484c5"
Function Logs:
START RequestId: 3048e1fd-ec0e-11e8-a03d-fb79584484c5 Version: $LATEST
2018-11-19T15:17:12.191Z 3048e1fd-ec0e-11e8-a03d-fb79584484c5 Calling MongoDB Atlas from AWS Lambda with event: {"address":{"street":"2 Avenue","zipcode":"10075","building":"1480","coord":[-73.9557413,40.7720266]},"borough":"Manhattan","cuisine":"Italian","grades":[{"date":"2014-10-01T00:00:00Z","grade":"A","score":11},{"date":"2014-01-16T00:00:00Z","grade":"B","score":17}],"name":"Vella","restaurant_id":"41704620"}
2018-11-19T15:17:12.208Z 3048e1fd-ec0e-11e8-a03d-fb79584484c5 => connecting to database
2018-11-19T15:17:12.248Z 3048e1fd-ec0e-11e8-a03d-fb79584484c5 (node:1) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.
END RequestId: 3048e1fd-ec0e-11e8-a03d-fb79584484c5
REPORT RequestId: 3048e1fd-ec0e-11e8-a03d-fb79584484c5 Duration: 11011.08 ms Billed Duration: 11000 ms Memory Size: 128 MB Max Memory Used: 29 MB
2018-11-19T15:17:23.200Z 3048e1fd-ec0e-11e8-a03d-fb79584484c5 Task timed out after 11.01 seconds
The relevant part of my code for connecting is (with user and pass being the appropriate values):
const MongoClient = require('mongodb').MongoClient;
let atlas_connection_uri = "mongodb+srv://<user>:<pass>#restaurantcluster-2ylyf.gcp.mongodb.net/testdb"
let cachedDb = null;
exports.handler = (event, context, callback) => {
var uri = atlas_connection_uri
if (atlas_connection_uri != null) {
processEvent(event, context, callback);
}
else {
atlas_connection_uri = uri;
console.log('the Atlas connection string is ' + atlas_connection_uri);
processEvent(event, context, callback);
}
};
function processEvent(event, context, callback) {
console.log('Calling MongoDB Atlas from AWS Lambda with event: ' + JSON.stringify(event));
var jsonContents = JSON.parse(JSON.stringify(event));
//date conversion for grades array
if(jsonContents.grades != null) {
for(var i = 0, len=jsonContents.grades.length; i < len; i++) {
jsonContents.grades[i].date = new Date();
}
}
context.callbackWaitsForEmptyEventLoop = false;
try {
if (cachedDb == null) {
console.log('=> connecting to database');
MongoClient.connect(atlas_connection_uri, function (err, client) {
cachedDb = client.db('testdb');
return createDoc(cachedDb, jsonContents, callback);
});
}
else {
createDoc(cachedDb, jsonContents, callback);
}
}
catch (err) {
console.error('an error occurred', err);
}
}
I suspect that something is going on with my VPC firewall/permissions/security group considering the fact that I can connect from my local machine, but I have no idea how that could be the case when I'm granting full admins privileges in my security role and I've set all outgoing VPC traffic to my public subnet.
I would appreciate any advice/help in solving this!
edit to provide more info:
The function console.logs '=> connecting to database' and then immediately times out at MongoClient.connect (confirmed by attempting to console.log directly after that).
I have been using MLab MongoDB and mongoose library to create a db connection inside a serverless (Lambda) handler. It works smoothly on local machine. But sometimes it doesn't work after deployment.The request returns an Internal server error. The weird thing is sometimes it works. But If I remove the database connection code, the handler works. The serverless log just says Process exited before completing request. No real errors so no idea what to do.
The db connection looks like this:
handler.js
// Connect to database
mongoose.connect(process.env.DATABASE_URL, {
useMongoClient: false
}).then((ee) => {
console.log('------------------------invoke db ', ee);
})
.catch(err => console.error('-----------error db ', err));
No error in here too. Any idea what's happening?
When you get Process exited before completing request, it means that the node process has crashed before Lambda was able to call callback. If you go to Cloudwatch logs, there would be an error and stack trace of what happened.
You should connect to the MongoDB instance inside your handler and before you call callback(), disconnect first.
It would be like this...
exports.handler = (event, context, callback) => {
let response;
return mongoose.connect(process.env.DATABASE_URL, {
useMongoClient: false
}).then((ee) => {
// prepare your response
response = { hello: 'world' }
}).then(() => {
mongoose.disconnect()
}).then(() => {
// Success
callback(null, response)
}).catch((err) => {
console.error(err);
callback(err);
})
};
Here is an article explaining with details how lambda work with node and an example of how to implement DB connection.
Differently of #dashmug suggested, you should NOT disconnect your DB since connecting every time will decrease your performance.
UPDATE: I am using the 2.1 version on the driver, against 3.2
I have a node application that uses MongoDB. The problem I have is that if the MongoDB server goes down for any reason, the application doesn't reconnect.
To get this right, I based my tests on the code in this official tutorial.
var MongoClient = require('mongodb').MongoClient
, f = require('util').format;
MongoClient.connect('mongodb://localhost:27017/test',
// Optional: uncomment if necessary
// { db: { bufferMaxEntries: 3 } },
function(err, db) {
var col = db.collection('t');
setInterval(function() {
col.insert({a:1}, function(err, r) {
console.log("insert")
console.log(err)
col.findOne({}, function(err, doc) {
console.log("findOne")
console.log(err)
});
})
}, 1000)
});
The idea is to run this script, and then stop mongod, and then restart it.
So, here we go:
TEST 1: stopping mongod for 10 seconds
Stopping MongoDb for 10 seconds does the desired result: it will stop running the queries for those 10 seconds, and then will run all of them once the server is back ip
TEST 2: stopping mongod for 30 seconds
After exactly 30 seconds, I start getting:
{ [MongoError: topology was destroyed] name: 'MongoError', message: 'topology was destroyed' }
insert
{ [MongoError: topology was destroyed] name: 'MongoError', message: 'topology was destroyed' }
The trouble is that from this on, when I restart mongod, the connection is not re-establised.
Solutions?
Does this problem have a solution? If so, do you know what it is?
Once my app starts puking "topology was destroyed", the only way to get everything to work again is by restarting the whole app...
There are 2 connection options that control how mongo nodejs driver reconnects after connection fails
reconnectTries: attempt to reconnect #times (default 30 times)
reconnectInterval: Server will wait # milliseconds between retries
(default 1000 ms)
reference on mongo driver docs
Which means that mongo will keep trying to connect 30 times by default and wait 1 second before every retry. Which is why you start seeing errors after 30 seconds.
You should tweak these 2 parameters based on you needs like this sample.
var MongoClient = require('mongodb').MongoClient,
f = require('util').format;
MongoClient.connect('mongodb://localhost:27017/test',
{
// retry to connect for 60 times
reconnectTries: 60,
// wait 1 second before retrying
reconnectInterval: 1000
},
function(err, db) {
var col = db.collection('t');
setInterval(function() {
col.insert({
a: 1
}, function(err, r) {
console.log("insert")
console.log(err)
col.findOne({}, function(err, doc) {
console.log("findOne")
console.log(err)
});
})
}, 1000)
});
This will try 60 times instead of the default 30, which means that you'll start seeing errors after 60 seconds when it stops trying to reconnect.
Sidenote: if you want to prevent the app/request from waiting until the expiration of the reconnection period you have to pass the option bufferMaxEntries: 0. The price for this is that requests are also aborted during short network interruptions.
package.json: "mongodb": "3.1.3"
Reconnect existing connections
To fine-tune the reconnect configuration for pre-established connections, you can modify the reconnectTries/reconnectInterval options (default values and further documentation here).
Reconnect initial connection
For the initial connection, the mongo client does not reconnect if it encounters an error (see below). I believe it should, but in the meantime, I've created the following workaround using the promise-retry library (which uses an exponential backoff strategy).
const promiseRetry = require('promise-retry')
const MongoClient = require('mongodb').MongoClient
const options = {
useNewUrlParser: true,
reconnectTries: 60,
reconnectInterval: 1000,
poolSize: 10,
bufferMaxEntries: 0
}
const promiseRetryOptions = {
retries: options.reconnectTries,
factor: 1.5,
minTimeout: options.reconnectInterval,
maxTimeout: 5000
}
const connect = (url) => {
return promiseRetry((retry, number) => {
console.log(`MongoClient connecting to ${url} - retry number: ${number}`)
return MongoClient.connect(url, options).catch(retry)
}, promiseRetryOptions)
}
module.exports = { connect }
Mongo Initial Connect Error: failed to connect to server [db:27017] on first connect
By default the Mongo driver will try to reconnect 30 times, one every second. After that it will not try to reconnect again.
You can set the number of retries to Number.MAX_VALUE to keep it reconnecting "almost forever":
var connection = "mongodb://127.0.0.1:27017/db";
MongoClient.connect(connection, {
server : {
reconnectTries : Number.MAX_VALUE,
autoReconnect : true
}
}, function (err, db) {
});
With mongodb driver 3.1.10, you can set up your connection as
MongoClient.connect(connectionUrl, {
reconnectInterval: 10000, // wait for 10 seconds before retry
reconnectTries: Number.MAX_VALUE, // retry forever
}, function(err, res) {
console.log('connected')
})
You do not have to specify autoReconnect: true as that's the default.
It's happening because it might have crossed the retry connection limit. After number of retries it destroy the TCP connection and become idle. So for it increase the number of retries and it would be better if you increase the gap between connection retry.
Use below options:
retryMiliSeconds {Number, default:5000}, number of milliseconds between retries.
numberOfRetries {Number, default:5}, number of retries off connection.
For more details refer to this link https://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html
Solution:
MongoClient.connect("mongodb://localhost:27017/integration_test_?", {
db: {
native_parser: false,
retryMiliSeconds: 100000,
numberOfRetries: 100
},
server: {
socketOptions: {
connectTimeoutMS: 500
}
}
}, callback)
Behavior may differ with different versions of driver. You should mention your driver version.
driver version : 2.2.10 (latest)
mongo db version : 3.0.7
Below code will extend the time mongod can take to come back up.
var MongoClient = require('mongodb').MongoClient
, f = require('util').format;
function connectCallback(err, db) {
var col = db.collection('t');
setInterval(function() {
col.insert({a:1}, function(err, r) {
console.log("insert")
console.log(err)
col.findOne({}, function(err, doc) {
console.log("findOne")
console.log(err)
});
})
}, 1000)
}
var options = { server: { reconnectTries: 2000,reconnectInterval: 1000 }}
MongoClient.connect('mongodb://localhost:27017/test',options,connectCallback);
2nd argument can be used to pass server options.
If you was using Mongoose for your Schemas, it would be worth considering my option below since mongoose was never retrying to reconnect to mongoDB implicitly after first attempt failed.
Kindly note I am connecting to Azure CosmosDB for MongoDB API. On yours maybe on the local machine.
Below is my code.
const mongoose = require('mongoose');
// set the global useNewUrlParser option to turn on useNewUrlParser for every connection by default.
mongoose.set('useNewUrlParser', true);
// In order to use `findOneAndUpdate()` and `findOneAndDelete()`
mongoose.set('useFindAndModify', false);
async function mongoDbPool() {
// Closure.
return function connectWithRetry() {
// All the variables and functions in here will Persist in Scope.
const COSMODDBUSER = process.env.COSMODDBUSER;
const COSMOSDBPASSWORD = process.env.COSMOSDBPASSWORD;
const COSMOSDBCONNSTR = process.env.COSMOSDBCONNSTR;
var dbAuth = {
auth: {
user: COSMODDBUSER,
password: COSMOSDBPASSWORD
}
};
const mongoUrl = COSMOSDBCONNSTR + '?ssl=true&replicaSet=globaldb';
return mongoose.connect(mongoUrl, dbAuth, (err) => {
if (err) {
console.error('Failed to connect to mongo - retrying in 5 sec');
console.error(err);
setTimeout(connectWithRetry, 5000);
} else {
console.log(`Connected to Azure CosmosDB for MongoDB API.`);
}
});
};}
You may decide to export and reuse this module everywhere you need to connect to db via Dependency Injection. But instead I will only show how to access the database connection for now.
(async () => {
var dbPools = await Promise.all([mongoDbPool()]);
var mongoDbInstance = await dbPools[0]();
// Now use "mongoDbInstance" to do what you need.
})();
I'm trying to store users on my own mongo database not the default (auth0 server).
Below is the script:
function create (user, callback) {
mongo('mongodb://admin:pass#localhost:27017/mydb', function (db) {
var users = db.collection('subscribers');
users.findOne({ email: user.email },
function (err, withSameMail) {
if (err) return callback(err);
if (withSameMail) return callback(new Error('the user already exists'));
user.password = bcrypt.hashSync(user.password, 10);
users.insert(user, function (err, inserted) {
if (err) return callback(err);
callback(null);
});
});
});
}
This is the error I'm getting when I try to create a user:
[Error] Error: socket hang up
at createHangUpError (_http_client.js:200:15)
at Socket.socketOnEnd (_http_client.js:285:23)
at emitNone (events.js:72:20)
at Socket.emit (events.js:166:7)
at endReadableNT (_stream_readable.js:905:12)
at nextTickCallbackWith2Args (node.js:437:9)
at process._tickDomainCallback (node.js:392:17)
Your mongodb is in localhost (see your connection string). The create script runs in Auth0 servers, so localhost (your machine) is not reachable.
Normally your instance would run on a server that is reachable from Auth0 (e.g. mongolabs, a server in AWS, etc). If you are testing, then you might want to check out ngrok
Blakes suggestion of caching the connection is a good one, but it is an optimization, not the reason it is not working.