- Reference >
- Build a Resilient Application with MongoDB
Build a Resilient Application with MongoDB¶
On this page
To write application code that takes advantage of the capabilities of MongoDB and gracefully handles replica set elections, you should:
- Install the latest drivers.
- Use a connection string that specifies all hosts.
- Use retryable writes and retryable reads.
- Use a
majority
write concern and a read concern that makes sense for your application. - Handle errors in your application.
Install the Latest Drivers¶
First, install the latest drivers for your language from MongoDB Drivers. Drivers connect and relay queries from your application to your database. Using the latest drivers enables the latest MongoDB features.
Then, in your application, import the dependency:
Connection Strings¶
Use a connection string that specifies all the hosts in your deployment to connect your application to your database. If your deployment performs a replica set election and a new primary is elected, a connection string that specifies all hosts in your deployment discovers the new primary without application logic.
You can specify all the hosts in your deployment using either:
The connection string can also specify options, notably retryWrites and writeConcern.
See also
For help formatting your connection string, see Connect to a Deployment Using a MongoDB Driver.
Use your connection string to instantiate a MongoDB client in your application:
- Java (Sync)
- Node.js
Retryable Writes and Reads¶
Note
Starting in MongoDB version 3.6 and with 4.2-compatible drivers, MongoDB retries both writes and reads once by default.
Retryable Writes¶
Use retryable writes to retry certain write operations a single time if they fail.
Retrying writes exactly once is the best strategy for handling transient network errors and replica set elections in which the application temporarily can’t find a healthy primary node. If the retry succeeds, the operation as a whole succeeds and no error is returned. If the operation fails, it is likely due to:
- A lasting network error, or
- An invalid command.
See also
For more information on enabling retryable writes, see Enabling Retryable Writes.
When an operation fails, your application needs to handle the error itself.
Retryable Reads¶
Read operations are automatically retried a single time if they fail starting in MongoDB version 3.6 and with 4.2-compatible drivers. You don’t need to configure your application to retry reads.
Write and Read Concern¶
You can tune the consistency and availability of your application using write concerns and read concerns. Stricter concerns imply that database operations wait for stronger data consistency guarantees, whereas loosening consistency requirements provides higher availability.
Example
If your application handles monetary balances, consistency is
extremely important. You might use majority
write and read
concerns to ensure you never read from stale data or data that might
be rolled back.
Alternatively, if your application records temperature data from hundreds of sensors every second, you may not be concerned if you read data that does not include the most recent readouts. You can loosen consistency requirements to provide faster access to that data.
Write Concern¶
You can set the
write concern level
of your replica set through the connection string URI. Use a
majority
write concern to ensure your data is successfully written
to your database and persisted. This is the recommended default and
sufficient for most use cases.
When you use a write concern that requires acknowledgement, such as
majority
, you may also specify a maximum time limit for writes
to achieve that level of acknowledgement:
- The wtimeoutMS connection string parameter for all writes, or
- The wtimeout option for a single write operation.
Whether or not you use a time limit and the value you use depends on your application context.
See also
For more information on setting write concern levels, see Write Concern Options.
Important
If you do not specify a time limit for writes and the level of write concern is unachievable, the write operation will never complete.
Read Concern¶
You can set the read concern level of your replica set through the connection string URI. The ideal read concern depends on your application requirements, but the default is sufficient for most use cases. No connection string parameter is required to use default read concerns.
Specifying a read concern can improve guarantees around the data your application receives from your database.
See also
For more information on setting read concern levels, see Read Concern Options.
Note
The specific combination of write and read concern your application uses affects order-of-operation guarantees. This is called causal consistency. For more information on causal consistency guarantees, see Causal Consistency and Read and Write Concerns.
Error Handling¶
Invalid commands, network outages, and network errors that are not handled by retryable writes return errors. Refer to your driver’s API documentation for error details.
For example, if an application tries to insert a document with a
duplicate _id
, your driver returns an error that includes:
- Java (Sync)
- Node.js
Without proper error handling, an error might block your application from processing requests until it is restarted.
Your application should handle errors without crashing or side
effects. In the previous example of an application inserting a
duplicate _id
, that application could handle errors as follows:
- Java (Sync)
- Node.js
The insert operation in this example throws a “duplicate key”
error the second time it’s invoked because the _id
field must be
unique. The application catches the error, the client is notified, and the app
continues to run. The insert operation fails, however, and it is
up to you to decide whether to show the user a message, retry the
operation, or do something else.
You should always log errors. Common strategies for further processing errors include:
- Return the error to the client with an error message. This is a good strategy when you cannot resolve the error and need to inform a user that an action can’t be completed.
- Write to a backup database. This is a good strategy when you can’t resolve the error but don’t want to risk losing the request data.
- Retry the operation beyond the single default retry. This is a good strategy when you can solve the cause of an error programmatically, then retry it.
You must select the best strategies for your application context.
Example
In the example of a duplicate key error, you should log the error but not retry the operation because it will never succeed. Instead, you could write to a fallback database and review the contents of that database at a later time to ensure that no information is lost. The user doesn’t need to do anything else and the data is recorded, so you can choose not to send an error message to the client.
Planning for Network Errors¶
Returning an error can be desirable behavior when an operation would otherwise never complete and block your application from executing new operations. You can use the maxTimeMS method to place a time limit on individual operations, returning an error for your application to handle if that time limit is exceeded.
The time limit you place on each operation depends on the context of that operation.
Example
If your application reads and displays simple product information
from an inventory
collection, you can be reasonably confident
that those read operations only take a moment. An unusually
long-running query is a good indicator that there is a lasting
network problem. Setting maxTimeMS
on that operation to 5000, or
5 seconds, means that your application receives feedback as soon as
you are confident there is a network problem.
Resilient Example Application¶
The following example application brings together the recommendations for building resilient applications.
The application is a simple user records API that exposes two endpoints on http://localhost:3000:
Method | Endpoint | Description |
---|---|---|
GET |
/users |
Gets a list of user names from a users collection. |
POST |
/users |
Requires a name in the request body. Adds a new user to a
users collection. |