As a total noob in system administration, it took me a while to realize that my machine’s services might have been stopped/restarted after Digital Ocean reached out about performing an emergency droplet migration after detecting problems on the physical server where my load balancing droplet/VM was running.
This was confirmed by running the following command and noting that the last time HaProxy had been active was on Digital Ocean’s maintenance day. (Please note that I am running these commands on Ubuntu.)
systemctl status haproxy.service
With this newfound knowledge, I attempted to restart HaProxy as sudo but ran into an error. After checking the HaProxy log (located in /var/log/), I found the following error message.
Something was already occupying/listening on port 80! After using the the netstat command and feeding its output to grep we identified the culprit…Apache! Of course…my droplet was restarted and the apache2 service is probably set to start on boot.
sudo netstat -ltnp | grep -i ":80"
Our site was up and running after stopping the Apache service and starting HaProxy.
sudo service apache2 stop sudo service haproxy start
I was able to confirm the issue arises on booting by checking our boot log with the following command.
I once came across a .NET C# process meant to manage Active Directory accounts for a few thousand employees. The code was probably over 10 years old and in shambles. You may call it a code nightmare. However, due to time and priority constraints the team never received the time to think of a better solution.
There was a number of bad programming practices in the process that were contributing to severe performance issues. The construction of bad conditional statements illustrated below was possibly the least guilty culprit but it peaked my curiosity: just how much do repetitive/bad conditional statements impact our code? The statements I’m referring to followed the format below:
if not x:
With the exception of the process’s first run, the process was always executing double the amount of conditional statements it needed! The GIF below illustrates what’s going on under the hood when statements are formatted like above. Note the additional step in the first block when x is true. This is a completely unnecessary stop. If x is true, there is no need to check whether x is not true. The execution of all three blocks is similar when x is false, except the first and second block still have to check the condition contained in the statement increasing our execution time. Remember that else-if and else statements are similar but not always interchangeable. Else catches all statements that do not fall under if, use else-if to further refine your conditional logic. Lastly, the order of your statements matters, our GIF would look slightly different if our statements began with “if not x” rather than “if x”. We have a simple example but if your logic is complex, incorrect order of your conditional statements can lead to logical errors that can be hard to identify. A giant yikes when number crunching!
In order to calculate how much the formation of these statements impacts our execution time I wrote a short program in C++. Our program populates a vector of variable size with random numbers from 1 to 100. It then iterates through each item in the vector to check if the item is even or odd. Please note in this case the if/else-if case is rather pointless (a number is either even or odd) but demonstrates the impact when having to check an additional condition. The program was run with vector sizes of 100,000, 1 million, 5 million and 10 million. We ran the test 10 times for each of our four scenarios. Below are our results; 1) The first graph illustrates our average execution time, 2) the second shows minimum execution time and 3) the third shows maximum execution time.
The tables below demonstrate the values in milliseconds present in our graphs. I have also included the percentage increase in average execution time from if only statements to the if/else. It is interesting to observe that after 1 million items the increase in average execution time remained steady. I wonder if this execution time would hold steady in additional experimental runs and if further increasing our vector size would impact it.
100,000 Items (~42% Increase in Average Execution Time)
1 Million Items (~36% Increase in Average Execution Time)
5 Million Items (~36% Increase in Average Execution Time)
10 Million Items (~35% Increase in Average Execution Time)
So how much do badly formatted conditional statements impact our code? In terms of execution time, significantly so. These increases may hardly be noticed in most day to day business applications but they are essential in a computation heavy process working with millions of items. Always take an additional minute to think and write proper conditional logic. Aside, from decreasing overall execution time, it will also greatly reduce the chances of introducing a logical error. In fact, it might be a great idea to follow your logic a couple of times through manually to make sure everything is working as expected (or maybe you can even write automated tests!).