Over-Engineering, the Evil of the Century
By: Rafael Faita
Tags: Clean Architecture, Programming, Software Development, Clean Code, Software Architecture

Why my system is slow

Before deciding on creating a whole new system to solve the performance problem, I want to show you some ideas on how to try to solve this. In most cases, using the basics of computer engineering we will end with a satisfactory solution, with less time consumption ($$$) and probably better results.

Let’s define a (web)request

Generally speaking, web or not requests can be represented by the below picture:

In resume, we could represent any request with the following steps:

1 — a client sends a request to a server

2 — the server process this request and, in the most general cases, send a request to a database to retrieve or persist some information

3 — the database executes the query and returns the requested data

4 — the server creates a response and sends it back to the client

The time between the first and the last step is called with response time, how much lower is the response time, faster and more clients(requests) your server will handle, the name of this is throughput. But for each new client(request) in your server, a little greater the response time will be, until the moment the server reaches the roof of the resources(maximum throughput), and then all the next requests will get an error.

With that in mind, it’s important to note, to be fast, our system must be able to respond as fast as possible and support as much as possible of incoming requests.

First things first

But how to know if our system is presenting slow requests?

Well, the simple answer is: you have to collect METRICS, collect the response time and the amount of each request, and then use the famous 80/20 graph and focus on the slower problems first.

You could use some tools like Dynatrace, New Relic, or another APM (Application performance management) to help you find these issues.

With these metrics collected now, you can focus on how to solve the slower problems.

Optimization of the current application

Now we could start talking about how to improve the performance of our application. I’m suggesting a separating of the optimization process into four steps.

1 — Clean Code and Clean Architecture

Inside of our server, we are running an application, this application has been constructed and executed on top of a source code, whether this source code isn’t optimized and organized the result of response time and throughput will be negatively impacted.

If you didn’t read these two books (Clean Code and Clean Architecture), I really recommend that you read this as fast as possible, and after reading the books start using some important concepts described in your systems.

Photo by Ragul Ebinezar on Unsplash

At minimum our application must have an layer structured development, using the three basic layers:

presentation

business

data access

With our code organized in layers and with the flow of data clearly defined in our mind, it will be easier to find which layer will have more problems and were to act to bring more results to our application(remember about the 80/20).

Probably the layer with more problems will be the data access layer, because this layer is one of the most common, in the development cycle, where the developers don’t care about or forget optimization.

2— Query Optimization

When our queries are slow, our requests will be slow, start focusing on fixing this problem. With the metrics result, find the slower queries and the most repeated ones.

The most important aspect to focus on solving queries problem are:

if you are inserting/updating/deleting too many registers in the database, try to use a batch routine

if you don’t need a field from a query, don’t bring it on the result, a:

select name, age, email from user where id in (????)

is better than a :

select * from user where id in (????)

if you are using an ORM framework , like Hibernate or Spring Data, never forget about the N+1 queries problem.

, like Hibernate or Spring Data, never forget about the never forget about the DBA role, he really can help you with hints and tips to reach the needed performance on a query

One last advice: generally external connections (like databases) are an expensive resource, creating a pool of connections to handle the opening and closing of connections is a MUST have.

3 — Caching

Even with the best queries on the database, we could have problems with peak moments of requests. We should have some mechanism to help us when these moments happen. A good idea is to use a distributed cache mechanism to keep the most expensive queries cached. This way, we give some breath to the database executes other queries and keep a better value of response time and throughput.

But always remember, there is no free lunch:

There are only two hard things in Computer Science: cache invalidation and naming things. — Phil Karlton

When we start using the cache, we have to create good policies to cache invalidation, and creating these policies could be hard because it’s important to define the correct moment of invalidation. Use this feature carefully.

4 — Heavy and/or frequent processes

Some processes of our application will be heavy and/or frequently executed and even with the best code design and query optimization, these routines will demand a lot of time to execute.

The most common solution to this problem is: use a QUEUE.

Photo by Adrien Delforge on Unsplash

To create an effective queue is a good idea, choose a message broker, here RabbitMQ, Apache Kafka, ActiveMQ could help you.

Today any framework/language has clients and wide support to these brokers.

The big trade-off here is the need to adjust our business to work correctly with asynchronous processing, because when you begin using a queue, the enqueue process will end very fast, but the dequeue process could suffer from the same problems of time consumption and if you don’t scale correctly your consumers.

Optimization of the current environment

Whether you reach this point, probably you applied the best practices in your application and your code is at a good level of quality. Now the obvious choice, to reach better response time and throughput, is to focus on optimization of the environment.

If your application isn’t running on docker containers, now is a good time to start thinking about how to encapsulate your application inside a docker image, because the easiest way to scale your application will be on top of docker. Even an old application, with a heavy application server (like JBoss, WebLogic, etc) could run on top of a docker container, the docker image will be very (in some cases, very, very, very….) fat, but my opinion is: Start with big images and then try to migrate to smaller images, migrating the app server and trying to remove unnecessary resources.

Vertical Scaling

Vertically scaled application

The first step to scaling our application vertically is adding more resources (memory and CPU).

But adding more resource result in some problems:

when we reach a specific value of resources, adding more resource have a high price ($$$)

and in a specific moment, we will reach the top hardware and we couldn’t scale to better hardware, because we already are using the better one =P

The important aspect I want you to pay attention to is the correct usage of these resources. For instance, if we are using Java in our application, then the application is running on top of a JVM. Here is exactly where we could have problems with the correct configuration, if our machine has 4 gigabytes of memory it is a good practice to configure the minimum and maximum of the memory usage, for example setting the size of Heap to 2 gigabytes and configure a more 256 megabytes to PermSize. What I want you to learn here is, using Java or not: configure your application to use the machine (VM or not) resources in the best way possible.

Horizontal Scaling

Horizontally scaled application

You will prefer to scale your application horizontally before reaching the top hardware. The idea behind Horizontal Scaling is: divide to conquer, instead of adding more resources to a single instance/machine, we could start new replicas of the same application.

This way, our application could handle more requests and keep throughput as high as possible.

That is because I told you to dockerize your application as fast as possible because when you do that, you can easily scale horizontally your application.

But remember there is no free lunch, when you start using replicas of the same application some new problems will appear, like:

distributed sessions

load-balance

log aggregation

etc…

Final considerations

The idea behind of article is to show new approaches to help you before deciding to recreate a whole application, but don’t forget if you are going to migrate to (CHOSE A MAGICAL ARCHITECTURE TYPE HERE), apply the concepts of this article and don’t end again with an application with lots of problems.

See you soon.