Reactive Architecture. Theoretical Dive into Blocking Architecture

Reactive Architecture. Theoretical Dive into Blocking Architecture
Photo by SpaceX / Unsplash

Business needs first

Goals

The primary objective for every software engineer is not to provide solutions with the latest modern library or architecture, the main goal is to help businesses to solve their problems. To achieve this software engineers need to provide solutions that will efficiently help to solve business problems with minimum errors and optimum costs that will help businesses be profitable in the end. This goal is critical and directly informs how we allocate our resources and design our systems.

With current globalization and business orientation to the whole world no one is surprised these days with 10k requests from geo-distributed customers and our common challenges now are:

  1. Scaling to handle a substantial number of simultaneous requests efficiently.
  2. Complex business workflows with robust backend systems and managing inter-service dependencies effectively.
  3. Global user base with varying network conditions, while maintaining consistent performance across regions. Given the current challenges, the solution must be designed to be easily scaled, resilient, and have low-latency performance, geo-distributed

Modern Architectural Best Practices

Modern architecture should be leveraging the following principles (*1):

  1. Loosely Coupled Components. To reduce dependencies to enable independent evolution, testing, and scaling.
  2. Microservices based Design. To enable horizontal scalability, allowing individual services to scale independently based on demand.
  3. Geo-Distributed Deployments. To deploy services closer to customers in several geographical regions to reduce latency and improve availability.
  4. Fault Tolerance. To remain service operational even if one of the component failures, such as server outages or data center failures. To solve these challenges our architecture must be robust, adaptive, scalable, fault-tolerant, and geo-distributed.

Architecture patterns

In this article, we will not cover all architectures and principles to build good and solid solutions or dig them very deep. I wanted to focus on the service communication aspect, but we need to go to the high level over the most popular architectural patterns. If you want you could follow the links to read more details about it.

We could concentrate on two core architectural patterns (*2)

  • Monolithic Architecture
  • Microservice Architecture

Monolithic Architecture

The application is built as a single project with all services/components. All components such as business logic, data access, and user interface are connected and dependent and run in a single process and share memory. (*3)

Monolithic Architecture: All components are integrated within a single application.

Pros: Simple to develop and deploy, and test.

Cons: Difficult to scale and maintain because highly coupled components make it difficult to update or modify parts of the system.

Microservices Architecture

The application is split into smaller independent applications grouped by business components/services. Each service is focused on a specific business functionality and communicates with other services via remote APIs. (*4)

Microservice Architecture: Applications are structured and segmented into domain-specific components.

Pros: Scalability, and ease to maintenance because each service can be developed, deployed, and scaled independently.

Cons: Increased complexity in service management and service-service communication can become a bottleneck and require a sophisticated deployment setup.

Service Communication Patterns

Service communication patterns define how services/applications interact with each other and there are two basic patterns (*5)

Synchronous Pattern

In this pattern, a service calls an API that another service exposes, using a protocol such as HTTP or gRPC, and waits for a response from the remote service.

Synchronous Pattern

Asynchronous Pattern

In this pattern, a service sends a request without waiting for a response, and one or more services process the message asynchronously.

Asynchronous Pattern

Summary

These communication patterns provide base strategies for systems. Using a specific pattern depends on the system’s requirements, such as scalability, responsiveness, and resource management, as well as its constraints and domain-specific needs.

Practice

Artificial Challenge. Design an Architecture for Global Temperature Monitoring System

Background:

We are deploying temperature monitoring devices worldwide. Each device comprises a standard PC connected to the internet and equipped with:

  • Temperature Sensor: Provides real-time temperature readings.
  • GPS Tracker: Offers current geographic location data.

Note: Internet connectivity and speed are unstable and can fluctuate based on location and time.

Requirements:

Data Collection:

  • Collect temperature readings from all devices globally.
  • Ensure data is accurately associated with each device’s location.

Data Visualization:

  • Display real-time temperature data on a global map based on device locations.

Data History:

  • Maintain a historical log of temperature readings for each individual device.
  • Enable retrieval and analysis of historical data per device.

System Reliability:

  • Design the service to be fault-tolerant.
  • Guarantee data delivery despite unreliable internet connections.
  • Implement mechanisms to handle data buffering and retransmission as needed.

Objective:

Develop a comprehensive architecture that addresses the above requirements. The solution should consider scalability, reliability, and efficiency, ensuring seamless operation across diverse geographic locations with varying network conditions.

Solution

In most cases, if you ask someone to sketch the architectural design of an application, you’re likely to get a similar diagram.

In this example, we will not cover solutions with Monolithic architecture

Architecture example

Problems

Microservice architecture became more popular and replaced monolithic architecture due to its scalability, flexibility, and alignment with modern software development practices. However, it also has a lot of problems. (*6), (*7)

Complexity of Development and Deployment

Managing multiple independent services that can have their own technology stack, development lifecycle, and deployment pipeline could lead to problems with coordination and consistency in development and deployment.

Data Management Challenges

In a microservices architecture, each service typically manages its database (not always a separate instance). This leads to problems in maintaining data consistency, especially across distributed transactions.

Distributed System Complexity

Microservices architectures are distributed systems, which means that such systems will have issues such as network latency, partial failures, and problems in service communication.

Service Dependency Management

Microservices are dependent on each other, and in most cases it is not Service A that relies on Service B, The number of services could be hundreds, and they could be represented in Cyclic Graph. Managing such systems/dependencies during deployment and updates can be challenging.

Communication Overhead

Microservices communicate over a network, typically using HTTP or messaging queues. This adds significant communication overhead and can lead to performance bottlenecks.

Testing Complexity

Testing a microservices-based application is more complicated than testing a monolithic application.

Increased Resource Consumption

Each microservice typically runs in its process with its environment, often in its own container or virtual machine. This can lead to increased resource consumption compared to a monolithic application, which is the root cause of higher infrastructure costs.

Complexity in Managing Failures

In a distributed system like microservices, mindful handling failures becomes more complex.

In this article, we’ll concentrate on communication problems.

Understanding Blocking

Blocking Operation

A blocking operation/call is where the underlying system “stop” or “block" execution until the operation completes (*8). During this period, the thread cannot perform any other compute tasks.

Common examples of blocking operations:

  • I/O Operations. Reading from or writing to a disk, database, or network socket. (*9)
  • Waiting for a Lock. When a thread is trying to acquire a lock on a resource that is currently held by another thread. (*10)

How Blocking Operations Affect Performance

Blocking operations can significantly impact performance, especially in applications that have high concurrency. Let’s focus on the main problems.

Resource Underutilization

When a thread is blocked, it does nothing until the operation completes. If multiple threads are blocked simultaneously, it can lead to underutilization of the system CPU and memory resources.

Scalability Issues

For applications that need to handle many concurrent operations (like web servers to handle multiple client requests), blocking operations can quickly become a bottleneck. If each request blocks the thread handling it, the server can run out of available threads, leading to slow response times or even application failure with Gateway Timeout etc.

Increased Latency

Blocking operations/calls can increase the time to complete a main task, particularly if the blocked operation is slow (like a network request to another service or DB). This latency can degrade the user experience in real-time applications.

Summary

Blocking operations could impact the performance, scalability, and latency of an application. With an understanding of where and how blocking operations occur, software engineers can avoid main problems by using non-blocking alternatives. Better architecture leads to better resource utilization, reduced latency, improved overall system performance, and also decreased resource costs.

What is Network Blocking?

Network blocking operations/calls are the same common blocking operation/call that we discussed in the previous section but they are related to network IO operations. Network blocking occurs when a thread is paused while waiting for a network communication to complete. This situation typically could happen on network requests (for example HTTP request, DB query or any remote communication) and waits for the response before continuing its execution. (*11)

Network Blocking Operation

Network blocking has a higher impact on the application performance due to overhead on network communication. I will not cover all performance issues and benchmark comparisons but if you are interested you could check these articles:

https://www.geeksforgeeks.org/performance-of-a-network/

https://www.baeldung.com/linux/ipc-performance-comparison

https://3tilley.github.io/posts/simple-ipc-ping-pong/

Conclusion

Designing a modern architecture requires efficiency, scalability, and reliability. By understanding the impact of blocking operations and utilizing the best modern architectural practices such as microservices, asynchronous communications, and fault-tolerant designs, we can design systems that can address high-load, complex business workflows.

Modern architecture, with its use of non-blocking operations and asynchronous communications, can offer a more powerful solution than traditional blocking systems. By adhering to these principles, you can ensure resiliency, agility, and the ability to deliver services to customers even in the face of a variable network and high demand.

  1. https://www.lucidchart.com/blog/how-to-design-software-architecture
  2. https://www.simform.com/blog/software-architecture-patterns
  3. https://en.wikipedia.org/wiki/Monolithic_application
  4. https://en.wikipedia.org/wiki/Microservices
  5. https://learn.microsoft.com/en-us/azure/architecture/microservices/design/interservice-communication
  6. https://developer.ibm.com/articles/challenges-and-benefits-of-the-microservice-architectural-style-part-1/
  7. https://dev.to/somadevtoo/10-microservices-architecture-challenges-for-system-design-interviews-6g0
  8. https://en.wikipedia.org/wiki/Blocking_(computing)
  9. https://www.geeksforgeeks.org/blocking-and-nonblocking-io-in-operating-system/
  10. https://en.wikipedia.org/wiki/Lock_(computer_science)
  11. https://dev.to/vivekyadav200988/understanding-blocking-and-non-blocking-sockets-in-c-programming-a-comprehensive-guide-2ien