eCommerce Application
6 min read

How to Handle Failed Transactions in Microservices

By Ranju RDec. 9, 2022, 11:38 a.m. Application development company
Share This Article
Global Software Development Rates: An Overview

Modern technologies allow you to outsource software development to overseas programmers who will work remotely and help you to save costs. The other major advantage is that you will have access to large talent pools.


Download Ebook

Many challenges occur when you migrate to a microservices architecture from a monolithic application. These are related to the complexities of a distributed system.


Table of Contents

Subscribe to
Our Blog

We're committed to your privacy. SayOne uses the information you provide to us to contact you about our relevant content, products, and services. check out our privacy policy.

How to Handle Failed Transactions in Microservices

A normal database transaction done in a monolithic application using local transactions is a complicated distributed transaction problem when you move to microservices architecture. In this article, we investigate what causes this, look at some possible solutions, and some best practices to be followed.

In monolithic applications, ACID transactions as found in RDBMS are mainly used. 

Atomicity: All operations are either executed successfully or all of them fail together.

Consistency: The data in the database is kept in a valid state by referential integrity.

Isolation: Separated transactions running concurrently do not interfere with one another. Each transaction should run in its isolated environment and other transactions cannot see any of these changes in that duration.

Durability: Once a transaction is committed, the changes are stored in a durable medium such as a disk, so that any temporary crash of the database server will not cause data loss.

In a monolithic system, these ACID transactions help to maintain the integrity and correctness of the data. In most typical monolithic systems, these data and processing requirements reside on a single database server. However, when you need to scale up the system because of growing requirements for data access, read/write scaling, or storage capacity this architecture model falls apart. 

Data modeling in Microservices architecture

A microservices architecture model has to be both loosely coupled and cohesive. Therefore, microservices should not share databases in a strict sense. If a microservice cannot have a database of its own, then this microservice should be merged with another one. 

For data consistency in microservices, 2 PC based solutions are used. However, these are used with caution because typical 2 PC transactions cause increased lock times in the backend databases. The time gaps increase when there are extra communication hops between networks. However, a majority of real-life workflows do not need ACID guarantees. Here, incorrect actions may be reversed using the opposite of the action that took place (crediting back a payment to a credit card, adding product count back to the product inventory, etc.).

https://miro.medium.com/max/700/1*LR77QWF31dDR0DtnurusxA.png

What can go wrong

There are several moving components in a microservices architecture, and therefore, has more points of failure. Failures can be caused by many reasons such as the release of new code, errors and exceptions in code, bad deployments, data center failure, hardware failures, poor architecture, communication over an unreliable network, lack of unit tests, and dependent services, among others.

Read our blog “Transaction Boundaries in Monolithic and Microservices Systems”.

Making services resilient

Distributed applications have an inherent problem in that they communicate over a network, which by nature may be unreliable. Hence, it is vital to design your microservices so that they are fault-tolerant and handle failures gracefully. In a microservices architecture model, there may be several services talking with one another and hence you have to ensure that a failed service does not bring down the entire microservices system.

Other ways of Handling Partial Failures

  • Using asynchronous communication 

As an example, you could use message-based communication across internal microservices. One mistake is creating long chains of synchronous HTTP calls across internal microservices. The incorrect design will eventually cause bad outages. However, other than for the front-end communications between the client applications and the first level of microservices/fine-grained API Gateways, it is always recommended to use only asynchronous (message-based) communication across the internal microservices (i.e., once past the initial request/response cycle). Designing for eventual consistency and event-driven architectures can help to minimize ripple effects. These approaches enforce a better level of autonomy for the microservices and prevent the above mentioned problem.

Download and read our eBook “Porting from Monolith to Microservices – Is the Shift Worth It”.

Are you thinking of shifting to microservices to help your business grow? Call SayOne today!

  • Using retries with exponential backoff

This technique is used to avoid short/intermittent failures by performing call retries a specific number of times. This is resorted to if the service was not available only for a short period. This break could have occurred because of intermittent network issues or when a service/container is moved to a different node (in a cluster). In case these retries have not been appropriately designed using circuit breakers, it can aggravate ripple effects, and may even cause an eventual Denial of Service (DoS).

  • Working around network timeouts

In general, clients should be designed to always use timeouts when waiting for a response and not to block indefinitely. Using timeouts makes sure that resources are not tied up indefinitely.

  • Using the Circuit Breaker pattern

This would help to make your service more resilient. You can wrap a protected function call inside a circuit breaker object that looks out for failures. When the failures reach a specific threshold, the circuit breaker trips and any further call to the circuit breaker returns an error/alternative service/a default message, without the protected function call being made in the first place. This makes the system responsive enough. The circuit breaker is designed to have three distinct states, closed, open, and half-open.

  • Closed 

The circuit breaker remains ‘closed’ when all is normal and all calls pass through to the services. Once the number of failures exceeds a threshold (predetermined), then the breaker trips, and moves to the ‘open’ state.

  • Open

The circuit breaker returns an error for function calls without executing the function.

  • Half-Open

After a specified timeout period, the circuit switches to ‘half-open’ to test if the underlying problem persists. Even if a single call fails in this state, the circuit breaker trips once again. If the call succeeds, the circuit breaker resets back to normal and is in the ‘closed’ state.

You can implement latency and fault tolerance libraries that are designed to isolate services, points of access to remote systems, and  3rd-party libraries in a distributed environment.

  • Providing fallbacks

Here, the client process is designed to perform fallback logic when a request fails, including returning cached data/a default value. This is an approach that suits when a response has to be given for queries and gets more complex for updates/commands.

  • Limiting the number of queued requests

Clients should also be designed to impose an upper limit on the number of outstanding requests that a client microservice can send to another particular service. When this limit has been reached, the system should not able to make additional requests, and such attempts should automatically fail. The Polly Bulkhead Isolation policy can be used to implement this requirement. This is essentially a parallelization throttle which permits a "queue" outside the bulkhead. You can proactively cut off excess load even before execution. This can work faster than a circuit breaker in response to certain failure scenarios because the circuit breaker waits for failures to happen. 

Conclusion

Transactions in distributed systems surely bring their challenges, and even while solving them, a new set of problems can simultaneously evolve.

Are you looking for a microservices vendor? Call SayOne or drop us a note!

How SayOne can help in microservices development

At Sayone, we design and implement microservices systems that do not have complex architectural layers, and this enables the services to deliver exceptionally fast performance. Moreover, we provide services that are significantly decoupled, allowing you to launch independent services and not end up with the usual inter-dependent microservices that work more or less like a monolith.

We design the microservices keeping in mind the margin required to allow for the transitioning into the new system of your organization’s legacy architecture, as well as expanding into the cloud system. Our microservices comprise lightweight code and we provide competitive pricing options for our clients.

Our microservices are built according to the latest international security guidelines that ensure the complete safety of all the data. We also ensure that we deliver the services within stipulated deadlines and we always assure a quick turnaround time for our clients. Equipped with the best infrastructure and the latest tools and technologies, our expert developers will provide you with the best microservices that are easily scalable, enabling a good ROI in the shortest period.

Share This Article

Subscribe to
Our Blog

We're committed to your privacy. SayOne uses the information you provide to us to contact you about our relevant content, products, and services. check out our privacy policy.