1

I am Using Vercel Serverless funtions to make API Endpoints for Next.js application. I am Using MongoDB to store an Array of Objects.

I have this serverless function

// api/global/addlink.js

const MongoClient = require("mongodb").MongoClient;
const { ObjectId } = require("mongodb");

const client = new MongoClient(process.env.DB_URI, {});

export default async (req, res) => {
  await client.connect();

  const link = JSON.parse(req.body)["link"];
  const query = { _id: ObjectId(JSON.parse(req.body)["_id"]) };

  const result = await client
    .db("myio_guests")
    .collection("link_groups")
    .findOneAndUpdate(query, {
      $push: {
        links: link,
      },
    });

  res.json({
    message: "link added successfully",
    result: result,
  });

  await client.close();
};

This function Gives error every 2nd time it runs.

I tried 4 invocations, of the function using fetch post request from my frontend, but only 2 entries are saved those are 1st and 3rd entry. In case of 2nd and 4th invocation this error occured:

2021-01-26T14:14:18.964Z    cae61486-ecb3-4726-aa8c-f8e95d9326ad    WARN    the options [servers] is not supported
2021-01-26T14:14:18.964Z    cae61486-ecb3-4726-aa8c-f8e95d9326ad    WARN    the options [caseTranslate] is not supported
2021-01-26T14:14:18.964Z    cae61486-ecb3-4726-aa8c-f8e95d9326ad    WARN    the options [dbName] is not supported
2021-01-26T14:14:18.964Z    cae61486-ecb3-4726-aa8c-f8e95d9326ad    WARN    the options [srvHost] is not supported
2021-01-26T14:14:18.964Z    cae61486-ecb3-4726-aa8c-f8e95d9326ad    WARN    the options [credentials] is not supported
2021-01-26T14:14:20.203Z    cae61486-ecb3-4726-aa8c-f8e95d9326ad    ERROR   MongoError: topology was destroyed
    at executeCommand (/var/task/node_modules/mongodb/lib/operations/db_ops.js:222:21)
    at FindOneAndUpdateOperation.execute (/var/task/node_modules/mongodb/lib/operations/find_and_modify.js:107:5)
    at executeOperation (/var/task/node_modules/mongodb/lib/operations/execute_operation.js:77:17)
    at Collection.findOneAndUpdate (/var/task/node_modules/mongodb/lib/collection.js:1737:10)
    at module.exports.lt9b.__webpack_exports__.default (/var/task/.next/serverless/pages/api/global/addlink.js:296:75)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:8:1)
    at async /var/task/.next/serverless/pages/api/global/addlink.js:137:387
2021-01-26T14:14:20.203Z    cae61486-ecb3-4726-aa8c-f8e95d9326ad    ERROR   MongoError: topology was destroyed
    at executeCommand (/var/task/node_modules/mongodb/lib/operations/db_ops.js:222:21)
    at FindOneAndUpdateOperation.execute (/var/task/node_modules/mongodb/lib/operations/find_and_modify.js:107:5)
    at executeOperation (/var/task/node_modules/mongodb/lib/operations/execute_operation.js:77:17)
    at Collection.findOneAndUpdate (/var/task/node_modules/mongodb/lib/collection.js:1737:10)
    at module.exports.lt9b.__webpack_exports__.default (/var/task/.next/serverless/pages/api/global/addlink.js:296:75)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:8:1)
    at async /var/task/.next/serverless/pages/api/global/addlink.js:137:387
2021-01-26T14:14:20.204Z    cae61486-ecb3-4726-aa8c-f8e95d9326ad    ERROR   Unhandled Promise Rejection     {"errorType":"Runtime.UnhandledPromiseRejection","errorMessage":"MongoError: topology was destroyed","reason":{"errorType":"MongoError","errorMessage":"topology was destroyed","name":"MongoError","stack":["MongoError: topology was destroyed","    at executeCommand (/var/task/node_modules/mongodb/lib/operations/db_ops.js:222:21)","    at FindOneAndUpdateOperation.execute (/var/task/node_modules/mongodb/lib/operations/find_and_modify.js:107:5)","    at executeOperation (/var/task/node_modules/mongodb/lib/operations/execute_operation.js:77:17)","    at Collection.findOneAndUpdate (/var/task/node_modules/mongodb/lib/collection.js:1737:10)","    at module.exports.lt9b.__webpack_exports__.default (/var/task/.next/serverless/pages/api/global/addlink.js:296:75)","    at processTicksAndRejections (internal/process/task_queues.js:97:5)","    at async apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:8:1)","    at async /var/task/.next/serverless/pages/api/global/addlink.js:137:387"]},"promise":{},"stack":["Runtime.UnhandledPromiseRejection: MongoError: topology was destroyed","    at process.<anonymous> (/var/runtime/index.js:35:15)","    at process.emit (events.js:326:22)","    at processPromiseRejections (internal/process/promises.js:209:33)","    at processTicksAndRejections (internal/process/task_queues.js:98:32)"]}
Unknown application error occurred

How Can I solve this Issue on serverless case like this?

Rupam Kairi
  • 101
  • 2
  • 9
  • If you run this 4 times but with a 10sec stop between each does it work? my assumption that you're just closing the connection while the function runs – Tom Slabbaert Jan 26 '21 at 15:36
  • I have tried using logs to see where the app stops it seems somewhere after second connect. [See](https://i.imgur.com/lU4oO32.png). I know it is a asynchronous but it gone upto that part comparing to the previous or 1st call. – Rupam Kairi Jan 26 '21 at 15:52
  • But Somehow waiting for about 10 min between each request, that thing worked. Can You explain or help me with that case. – Rupam Kairi Jan 26 '21 at 15:55

1 Answers1

0

There issue you're having comes from this line:

  await client.close();

From the docs:

MongoClient.close() - closes the underlying connector, which in turn closes all open connections. Once called, this Mongo instance can no longer be used.

Meaning when you close the client, any other open connections close aswell. In your case #2 and #4 start running shortly after #1 and #3 (but before they are done), and you close the connection on them while they are still running.

Also from the docs:

The MongoClient class is designed to be thread safe and shared among threads. Typically you create only 1 instance for a given database cluster and use it across your application.

It would be a best practice to have some state management for your stateless functions (I know), have a connection service that just gives your stateless functions the connection, it would also be easier to manage errors this way.

Read more about this here and in the nodejs driver docs

A quick (and very suboptimal) solution to your problem would be to define the connection within the function like so:

// remove the definition from here

export default async (req, res) => {
     // and move it here
     const client = new MongoClient(process.env.DB_URI, {});
     await client.connect();

      .....

     await client.close();
};
Tom Slabbaert
  • 21,288
  • 10
  • 30
  • 43
  • This example did solved the issue. Thanks. So also in this case i am closing all the connections. So do i need to cache the connections for better results or that won't be needed? Just asking as suggestion. – Rupam Kairi Jan 26 '21 at 16:50
  • Caching the connection is a first step in the right direction. When you start thinking of monitoring and alerting connection issues you should build a proper service for that. – Tom Slabbaert Jan 26 '21 at 17:38