0.0.1 • Published 4 years ago

retry-promise-mechanism v0.0.1

Weekly downloads
-
License
ISC
Repository
-
Last release
4 years ago

There may be more I may not be considering. I will try to update this documentation periodically.

Install

npm i @toorieaa/retry-promise-mechanism

1. Official Documentation

The primary committer to node-mongodb-native says:

You open do MongoClient.connect once when your app boots up and reuse the db object. It's not a singleton connection pool each .connect creates a new connection pool. (reference : https://stackoverflow.com/questions/10656574/how-do-i-manage-mongodb-connections-in-a-node-js-web-application)

Official mongo db documentation https://mongodb.github.io/node-mongodb-native/1.4/driver-articles/mongoclient.html https://docs.mongodb.com/manual/administration/install-community/

2. Things that they have said that don't work for me

  • If you can run the app after the mongo service is running, it means mongo is taking longer to start up and be connection-ready than when your app tries to connect. If your app doesn't ever connect it may not be restarting properly - you probably want it to exit if it doesn't connect and then make sure you have your Docker restart policy set accordingly ("always", "on-failure", etc.) see docs. Assuming mongo eventually starts up, your app will eventually connect.
  • You can also look into using healthchecks in which case you could see if the database is actually connecting rather than starting up. The depends_on flag will only check to see that the container is up, it doesn't check to see if the apps in the container are running much less if they are working properly.

3. Usage

This module provides a method to retry any promise, and provide a callback which expects a result (the result of the mongo db connect promise). You should create an empty object to store your connection objects, and use the callback to reference this object and change the value of the connections key to the result. The callback will be called if the promise successfully resolves before the numberOfAttempts. Also note you can specify an exponential back of input as a true or valse value which will double the sleeping time on every failed attempt. You can also set numberOfAttempts to -1 so that the number of retries will be essentially infinite, and use it in combination with the exponential parameter flag. Was created for mongodb but can be used for other databases if the setup and problem is similar

const databasePoolStorage = {}; //use this in your requests, create object, call on method with param, export and import in the module you need
retryPromiseNTimes(
  testPromise, //promise that returns a connection (pool) object
  (result) => (databasePoolStorage.connection = result) //export databasePoolStorage, and use the value at property connection as the connection database (pool) connection object.
);

4. Motivation

Database pooling allows multithreaded/pseudo-multithreaded APIs efficient ways to use database connections. Rather than all tasks sharing a single database connection, or having the endpoints teardown and rebuild connections on every request, the session pool allows multiple session connections to a database to exist, and allow your express api / tasks to randomly access one of these session connections. This provides your application with a huge performance boost, because if each database connection is synchronized, each task will be bottle-necked by a single database connection. ATOMIC implementations, while more efficient than synchronized blocking, still impose a cost for synchronization. A volatile implementation would mean the database would have problems with consistency.

5. Simple Explanation

  • The connect function promise for mongodb will return the database pool when provided correct option configurations to the API and is able to successfully connect once. The issue may become apparent when attempting to add reconnect parameters to the options object, only to realize that they rely on a successful first connect to a mongo database.

6. Longer Explanation

By default, mongoose throws an Error if the first connect fails, which crashes node.

So to reproduce this bug, you will need the following code in your app, to catch the error and prevent the crash:

db.on('error', console.error.bind(console, 'connection error:'));

Now we can reproduce this bug as follows:

  • Shut down your MongoDB
  • Start up your node app that uses mongoose
  • Your app will log: [MongoError: failed to connect to server localhost:27017 on first connect MongoError: connect ECONNREFUSED 127.0.0.1:27017]
  • Start up your MongoDB again
  • Observe that mongoose does not now connect to the working MongoDB. The only way to get reconnected is to restart your app, or to use a manual workaround.
  • Expected behaviour: Since autoreconnect defaults to true, I would expect mongoose to establish a connection soon after the MongoDB is accessible again.

Note: If the first connect succeeds, but the connection to MongoDB is lost during runtime, then autoreconnect works fine, as expected. The problem is the inconsistency if MongoDB is not available when the app starts up.

(If this is the desired behaviour, and developers are recommended to handle this situation by not catching the error, and letting node crash, then I can accept that, but it is worth making it clear.)

Users like fdmxfarhan who commented on Jan 3,2020 still requesting an easy to way to get around this issue.

7. Potential Solutions

  • Potential Fix? Did not go deeper than looking at the solution. https://gist.github.com/asalant/4092454
  • Import an async library to handle this like BlueBird. This can be overkill if all you need to provide is this specific functionality to get the pool configured and setup correctly.
  • Read logs from mongo db periodically until you sniff logs that look like the database is up. This becomes difficult in a containerized environment, and the solution can tend to be clunky

8. Issue

If you start an express server and your mongo database is not ready, your first connect to mongo db will fail. Despite adding retry configuration parameters to the options object, you will realize that mongo will not auto retry to connect until it has successfully connected at least once.

8.1. Docker Issues

Despite specifying depends on on docker compose, the express service will start but will fail to connect due.

8.2. Mongo Specific

Across different database drivers, retry options work differently. This can make it hard to come up with a universal solution

9. Links describing the issue

10. Useful links

11. Extra things to check

Application and Network Firewall, and anything strange in app preferences access. Check if you are on VPN or if you are not corrected to the proper wifi

Random command. May or may not help on mac

xattr -d com.apple.quarantine <exposed-alias-or-shellVisible>

12. Sample Run

> node index.js

🔄: 1
🙈RetryMechanism Waiting 3000 ms
🙈RetryMechanism Error: did not go well boss
    at testPromise (file:///Users/lorainetoorie/Desktop/public%20npm%20modules%20by%20Anthony%20Toorie/Retry/index.js:13:9)
    at async retryPromiseNTimes (file:///Users/lorainetoorie/Desktop/public%20npm%20modules%20by%20Anthony%20Toorie/Retry/index.js:84:22)
🔄: 2
🙈RetryMechanism Waiting 3000 ms
🙈RetryMechanism Error: did not go well boss
    at testPromise (file:///Users/lorainetoorie/Desktop/public%20npm%20modules%20by%20Anthony%20Toorie/Retry/index.js:13:9)
    at async retryPromiseNTimes (file:///Users/lorainetoorie/Desktop/public%20npm%20modules%20by%20Anthony%20Toorie/Retry/index.js:84:22)
🔄: 3
🙈RetryMechanism Waiting 3000 ms
🙈RetryMechanism Error: did not go well boss
    at testPromise (file:///Users/lorainetoorie/Desktop/public%20npm%20modules%20by%20Anthony%20Toorie/Retry/index.js:13:9)
    at async retryPromiseNTimes (file:///Users/lorainetoorie/Desktop/public%20npm%20modules%20by%20Anthony%20Toorie/Retry/index.js:84:22)
🔄: 4
🙈RetryMechanism Waiting 3000 ms
🙈RetryMechanism Error: did not go well boss
    at testPromise (file:///Users/lorainetoorie/Desktop/public%20npm%20modules%20by%20Anthony%20Toorie/Retry/index.js:13:9)
    at async retryPromiseNTimes (file:///Users/lorainetoorie/Desktop/public%20npm%20modules%20by%20Anthony%20Toorie/Retry/index.js:84:22)
🔄: 5
🙈RetryMechanism Waiting 3000 ms
🙈RetryMechanism Error: did not go well boss
    at testPromise (file:///Users/lorainetoorie/Desktop/public%20npm%20modules%20by%20Anthony%20Toorie/Retry/index.js:13:9)
    at async retryPromiseNTimes (file:///Users/lorainetoorie/Desktop/public%20npm%20modules%20by%20Anthony%20Toorie/Retry/index.js:84:22)
🔄: 6
🙈RetryMechanism Waiting 3000 ms
🙈RetryMechanism success
⏲: 18.026s