<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nikita Vetoshkin</title>
    <description>The latest articles on DEV Community by Nikita Vetoshkin (@nekto0n).</description>
    <link>https://dev.to/nekto0n</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1186686%2Ffb7a20a1-93f9-4f7f-8b27-507d785c5742.png</url>
      <title>DEV Community: Nikita Vetoshkin</title>
      <link>https://dev.to/nekto0n</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nekto0n"/>
    <language>en</language>
    <item>
      <title>Software automation done right</title>
      <dc:creator>Nikita Vetoshkin</dc:creator>
      <pubDate>Mon, 23 Oct 2023 23:01:09 +0000</pubDate>
      <link>https://dev.to/nekto0n/software-automation-done-right-528l</link>
      <guid>https://dev.to/nekto0n/software-automation-done-right-528l</guid>
      <description>&lt;p&gt;Software is eating the world. For better or worse. Among many applications we try to make computers perform is automation - the most tedious, repetitive, and thus error-prone for humans type of task. A single mistake in an everyday routine operation may take down Internet-scale businesses for minutes and &lt;a href="https://en.wikipedia.org/wiki/2021_Facebook_outage"&gt;hours&lt;/a&gt;, taking more than 24 hours to &lt;a href="https://github.blog/2018-10-30-oct21-post-incident-analysis/"&gt;fully recover&lt;/a&gt; and incur millions of dollars in losses. Automating away most operations is thus a legitimate goal that saves operating expenses and improves business robustness.&lt;/p&gt;

&lt;p&gt;On the other hand automation is not free. It is an expensive and long process and should be properly evaluated from a business and engineering perspective. Overengineering and building low business value Rube Goldberg machines is often can be as bad as under-engineering and leaving crucial parts manual. Here I would like to describe a reasoning approach to such software engineering tasks. This approach is not a law of nature, but a useful abstraction that I came up with to guide decisions for me, my team, and the business I work for. Let’s start with a task that sounds like a good junior DevOps assignment:&lt;br&gt;
    &lt;em&gt;Convert from RAW to MP3 and backup all call records files on a call center server&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Do it somehow
&lt;/h2&gt;

&lt;p&gt;How do we start things? Manually - employing the power of our brain, its experiences, and modern Internet search. Our brain is good at decomposing tasks and addressing them one by one. We also constantly run a cost function, checking the efforts and time spent against current progress and potential profits. After some time and trial and error, we arrived at one of the possible results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first one&lt;/strong&gt;: the task is impossible, there’s no currently known workaround for a fundamental limitation. This is a good (alas disappointing) result. If we were planning to bet our business on that - we have data against and can plan and act accordingly.&lt;br&gt;
&lt;strong&gt;Another similar conclusion&lt;/strong&gt;: the task is possible, but prohibitively slow and/or expensive. Again, armed with this data we can make better business decisions.&lt;br&gt;
&lt;strong&gt;Best case&lt;/strong&gt;: the task is possible after a series of manual steps, here’s the result. That is a great achievement. At this point it is worth to ask yourself a question:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do we ever need to repeat that?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Or, even better, what is the probability we can assign to the positive response. If the answer is “no” or “&lt;strong&gt;probably&lt;/strong&gt;, &lt;strong&gt;no&lt;/strong&gt;”, then we’re done. No need to spend time and resources on this, no need to move to the next step of automation. Usually software engineering projects have lots of other things to work on.&lt;br&gt;
If we’d like to persist and make this task easily repeatable, then it is time to move to the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write it down
&lt;/h2&gt;

&lt;p&gt;According to the “Software Engineering at Google” book, software development is a team effort integrated over time and one of many aspects of it is preserving and sharing the knowledge among org members. Right now there is only one person who knows how to solve the task (or even if it solvable at all) and can repeat it - you. The &lt;a href="https://en.wikipedia.org/wiki/Bus_factor"&gt;bus factor&lt;/a&gt; is 1 and that is not the state we want our team to be in.&lt;/p&gt;

&lt;p&gt;So let us put a HOWTO.txt in our project’s repo or write the exact steps on a wiki page called “136 easy steps to …”. Yes, as simple as that. What’s the profit?&lt;/p&gt;

&lt;p&gt;We &lt;strong&gt;serialized our experience&lt;/strong&gt; (probably double-checking it in the process) and put it on persistent storage, that is more reliable than a human brain in the long run. In a month, in a year or 5 we can read and repeat. In software we serialize data when we need to pass it around, to share. The same directly applies here, because we’ve just &lt;strong&gt;shared the knowledge&lt;/strong&gt; and someone else can carry out the task. Again, software engineering is a team game.&lt;/p&gt;

&lt;p&gt;Another useful property to stress out is the low requirements for preciseness of the document - it is targeted to be interpreted by a human being, which handles inaccuracies and errors much more gracefully than, say, Python interpreter. Many details can be omitted, meaning the task of writing the doc can be accomplished fast and thus cheap.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are we done? It is imperative to ask following questions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Is the task tedious enough to invest in it further?&lt;br&gt;
If the task is a sequence of a couple of bash commands with clear path on how to handle errors and deal with changes - probably we’re done. If we need to wait for hours for something to be downloaded or juggle and copy-paste tens of variables, certificates and unreadable long hash strings (remember, humans are bad at that) - maybe it is worth moving to the next step.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How long does it take to execute the task?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If it takes hours to execute it (i.e. longer than a typical human attention span) - it is a good indicator, than we (i.e. our business) will benefit from further automation.&lt;br&gt;
    - &lt;em&gt;Are the readers of our doc capable of following it?&lt;/em&gt; &lt;br&gt;
What if a manager or a customer might need to follow the steps - can they accomplish and confidently deal with imperfections, handle errors, and recover from missed steps?&lt;/p&gt;

&lt;p&gt;Depending on the answers we might decide to move on to the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Translate into a script
&lt;/h2&gt;

&lt;p&gt;Usually all it takes is follow the existing doc and translate from a natural into a more strict computer language. It can be as simple as a page long bash script or an elaborate Python script, employing rich ecosystem of freely or locally available libraries. Today (in 2023) some may even consider using Go for this task.&lt;br&gt;
Let’s look at the profit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A script can accept parameters like a version to work with, where to put resulting artifacts, etc.&lt;/li&gt;
&lt;li&gt;We lowered the bar, executing the task is even easier - just checkout/download and run.&lt;/li&gt;
&lt;li&gt;Thus it can be integrate into other automation pipelines like CI/CD&lt;/li&gt;
&lt;li&gt;A script can log usage stats to allow data driven assessment of its usefulness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is no small feat. We improved robustness and now our team/org can execute the same task only in a couple of seconds of human attention during execution: to start and check the results.&lt;br&gt;
The script approach often works great for periodic, cron-style jobs. If we want to run the task daily or hourly we put install the script on the server (package it along with other server components) and configure local cron daemon (or systemd.timer accordingly). Done. Simple and stateless automation is easy to manage and debug.&lt;/p&gt;

&lt;p&gt;On the flip side the script approach comes short when we need to react to some external event: RPC call, disk size reaching some limit, etc. In other words, a script is not well suited for interactive or reactive tasks. Next shortcoming is lack of state: if we need to keep TCP connections or load some lookup data from a remote storage during each execution - script might be too resource-hungry and slow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let it run the background
&lt;/h2&gt;

&lt;p&gt;If we make the next logical step and make our script stateful and running in the background - now it is called a daemon. With a daemon we can pro-actively react to local changes, timers, disk space, etc. And do that efficiently - connections state and in-memory cache our at our disposal. Though state can be fragile to manage, we can keep the process single threaded to continue keeping things simple. Do not forget, in our case simple means “reliable”. The cost of having a daemon is highe: we need to carefully work with state and possible memory leaks - we had no such issues with a script. That is why we need to carefully estimate if this costs its while in each case.&lt;/p&gt;

&lt;p&gt;Daemon/process approach in our Internet scale times has some shortcomings too. First, it cannot handle something that does not fit a single host. Modern servers can be super powerful and oftentimes it can be cheaper and faster to buy (or rent in your cloud) a beefier machine, while keeping the software simple. We might add multiple threads to handle the load and utilize additional cores. The upside here is that we can improve things &lt;strong&gt;gradually&lt;/strong&gt;, having something that works and gets things done on each step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provide a service
&lt;/h2&gt;

&lt;p&gt;If our task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fails to fit into a single machine&lt;/li&gt;
&lt;li&gt;needs to provide an external service (in our case: remote sound file encoding and storing)&lt;/li&gt;
&lt;li&gt;&lt;p&gt;needs to be single machine failure resistant&lt;br&gt;
we can solve it by promoting our daemon process into a service by adding an external API, making it discoverable and reachable via service mesh, DNS or other means. Upsides:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flexible: the task execution is not tight to a particular machine&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scalable: potentially we can more replicas and scale our service horizontally&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reliable: we can be resilient to multiple domain failures: server, rack, DC, etc.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Downsides are there and include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increased complexity: we have a distributed system on our hands with all its complexity and potential issue&lt;/li&gt;
&lt;li&gt;Increased cognitive load: we need to carefully &lt;a href="https://www.hyrumslaw.com/"&gt;design and evolve API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Increased operational costs: we need to monitor if our service is reachable over the network, protect against DoS attacks, potentially customer isolation, etc.
All these lead to increased costs, which must be assessed against the profits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Breaking the rules&lt;/strong&gt;&lt;br&gt;
All these steps are not a definitive recipe for success, but they do provide a framework to reason about engineering tasks at hand. Throughout my career I’ve seen many attempts to skip ahead, ignore a step or two to get to the final solution faster. Sometimes that does work, but oftentimes it doesn’t and we end up wasting a lot of time and efforts. E.g. given the same task I would like to try myself in API design and skip the boring manual and script parts. After days of intensive labour I arrive at a perfect set of API functions my encoding server will provide. Then I can decide to make a tiny prototype to do the encoding… Given there’s only one good ending to this story, there are many not so “happy ever after” ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network costs to send uncompressed files are too large given the refined requirements (which were not there from the start, of course).&lt;/li&gt;
&lt;li&gt;There’s no spare server/capacity on premises to run this service on.&lt;/li&gt;
&lt;li&gt;The script solution fits the needs and is needed yesterday&lt;/li&gt;
&lt;li&gt;The daemon solution we started off with is leaking memory and clients are not happy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list can go on and on, the message here is: be cautious, know the rules and justify breaking them if needed. And that’s all, thank you.&lt;/p&gt;

</description>
      <category>software</category>
      <category>automaton</category>
    </item>
    <item>
      <title>Fault Tolerance in Distributed Systems: Strategies and Case Studies</title>
      <dc:creator>Nikita Vetoshkin</dc:creator>
      <pubDate>Wed, 18 Oct 2023 11:31:56 +0000</pubDate>
      <link>https://dev.to/nekto0n/fault-tolerance-in-distributed-systems-strategies-and-case-studies-29d2</link>
      <guid>https://dev.to/nekto0n/fault-tolerance-in-distributed-systems-strategies-and-case-studies-29d2</guid>
      <description>&lt;p&gt;The complex technological web that supports our daily lives has grown into a vast network of distributed systems. It is especially visible in the present era when our world is more connected than ever. The smooth operation of these systems has evolved into more than just a convenience; rather, it has become essential for everything from streaming our favourite movies to managing crucial financial transactions.&lt;/p&gt;

&lt;p&gt;Imagine living in a society where a single system glitch could impair your ability to access essential services or even the world economy. Quoting Leslie Lamport: “A distributed system is one in which the failure of a computer you didn't even know existed can render your own computer unusable” &lt;a href="https://amturing.acm.org/award_winners/lamport_1205376.cfm#:~:text=%60%60A%20distributed%20system%20is,is%20concerned%20with%20fault%20tolerance."&gt;[1]&lt;/a&gt;. A situation like this emphasises the critical significance of fault tolerance, a concept at the core of these complex networks.&lt;/p&gt;

&lt;p&gt;This article is dedicated, therefore, to a more focused consideration of what is fault tolerance in distributed systems, what are the best approaches to achieving it and which of them are already implemented. &lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Fault Tolerance
&lt;/h2&gt;

&lt;p&gt;Fault tolerance, in the realm of distributed systems, refers to the ability of a system to continue operating without interruption despite encountering failures or faults in one or more of its components. It is a measure of the system's resilience against disruptions (ranging from a single server failure to a whole data centre outage due to power failure) and its capability to ensure consistent and reliable performance.&lt;/p&gt;

&lt;p&gt;Our reliance on online platforms for everything from business operations to personal communications means that even a minor system disruption can have far-ranging consequences. An outage can result in financial losses, hinder productivity, compromise security, or shatter trust among users.&lt;/p&gt;

&lt;p&gt;However, ensuring fault tolerance in distributed systems is not at all easy. These systems are complex, with multiple nodes or components working together. A failure in one node can cascade across the system if not addressed timely. Moreover, the inherently distributed nature of these systems can make it challenging to pinpoint the exact location and cause of fault - that is why modern systems rely heavily on distributed tracing solutions pioneered by &lt;a href="https://research.google/pubs/pub36356/"&gt;Google Dapper&lt;/a&gt; and widely available now in &lt;a href="https://www.jaegertracing.io/"&gt;Jaeger&lt;/a&gt; and &lt;a href="https://opentracing.io/"&gt;OpenTracing&lt;/a&gt;. But still, understanding and implementing fault tolerance becomes not just about addressing the failure but predicting and mitigating potential risks before they escalate.&lt;/p&gt;

&lt;p&gt;In essence, the journey to achieving fault tolerance is riddled with challenges, but its importance in ensuring seamless technological experiences makes it an indispensable pursuit. Therefore, it is important to observe the strategies for improving this resilience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategies for Fault Tolerance
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Redundancy&lt;/strong&gt; &lt;br&gt;
At its core, redundancy implies having backup systems or components that can take over if the primary ones fail (either manually or automatically) — this ensures that a single failure doesn’t compromise the entire system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sharding&lt;/strong&gt;&lt;br&gt;
A technique primarily used in databases, sharding involves dividing the data into smaller and independent chunks called shards. If one shard fails, only a subset of the data is affected. It allows the remaining shards to serve the unaffected parts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Replication&lt;/strong&gt;&lt;br&gt;
This strategy involves creating copies of data or services. In the situation of a failure, the system can switch to a replica, ensuring continuous service. Replication can be local, in the same data centre, or geographically distributed for even higher fault tolerance. Replicas can serve the same traffic, providing higher throughput to the system, e.g. in a search engine having 10 or more replicas is not uncommon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Load Balancing&lt;/strong&gt;&lt;br&gt;
By distributing incoming traffic across multiple servers or components, load balancers prevent any single component from becoming a bottleneck or point of failure. If one component fails, the load balancer redirects traffic to the operational ones. There is a multitude of &lt;a href="https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/load_balancers"&gt;concrete strategies&lt;/a&gt; and this is a rapidly evolving part of computer science.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure Detection and Recovery&lt;/strong&gt;&lt;br&gt;
It’s not enough to have backup systems. It’s also crucial to detect failures quickly. Modern systems employ monitoring tools and rely on distributed coordination systems such as &lt;a href="https://zookeeper.apache.org/"&gt;Zookeeper&lt;/a&gt; or &lt;a href="https://github.com/etcd-io/etcd"&gt;etcd&lt;/a&gt; to identify faults in real-time: once detected, recovery mechanisms are triggered to restore the service.&lt;/p&gt;

&lt;p&gt;In the journey towards achieving fault tolerance, the blend of these strategies ensures that systems are resilient, reliable, and consistently available, even in the face of startling challenges. Let us proceed to the practical cases to showcase the art of using fault tolerance approaches. &lt;/p&gt;

&lt;h2&gt;
  
  
  Case Study 1: Google's Infrastructure
&lt;/h2&gt;

&lt;p&gt;Google's colossal distributed infrastructure is symbolic of a robust fault-tolerant system. A central strategy they employ is replication, the one which we’ve already discussed. By replicating &lt;a href="https://research.google/pubs/pub48190/"&gt;Zanzibar&lt;/a&gt; data across the globe, not only is latency diminished, but data resilience is enhanced. Specifically, replicas are in various locations worldwide, with multiple replicas within each region. &lt;/p&gt;

&lt;p&gt;Another crucial aspect of Google's fault-tolerance approach is the focus on &lt;strong&gt;performance isolation&lt;/strong&gt;. This strategy is indispensable for shared services aiming for low latency and high uptime. In situations where Zanzibar or its clients might not provide sufficient resources due to unpredictable usage patterns, performance isolation mechanisms help. These mechanisms determine that performance issues are contained within the problematic area, ensuring no adverse effects on other clients.&lt;/p&gt;

&lt;p&gt;Furthermore, Google's large-scale cluster management, exemplified by &lt;a href="https://research.google/pubs/pub43438/"&gt;Borg&lt;/a&gt;, showcases its commitment to reliability and availability, even as challenges arise from scale and complexity. In essence, Borg manages vast clusters by combining optimised task distribution, performance isolation, and fault-recovery features while simplifying user experience with a declarative job specification and integrated monitoring tools. This fusion of technology and strategy underscores Google's dedication to real-world benefits while managing inherent challenges in its vast infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case Study 2: AWS Route 53
&lt;/h2&gt;

&lt;p&gt;Amazon Web Services (AWS) exemplify high availability and fault tolerance, particularly in Route 53. This service employs a widespread network of health checkers across multiple AWS regions that continuously monitor targets. Through smart aggregation logic, isolated failures don't destabilise the system: a target is only deemed unhealthy if multiple checks fail, and this can be customised based on user preferences. &lt;/p&gt;

&lt;p&gt;Regardless of the target's health status, the system maintains a constant workload &lt;a href="https://aws.amazon.com/builders-library/reliability-and-constant-work/"&gt;[2]&lt;/a&gt;, which ensures operational predictability during high-demand periods. The &lt;strong&gt;cellular design&lt;/strong&gt; of health checkers and aggregators allows for scalability. As needs grow, new cells can be introduced without compromising the system's capacity. &lt;/p&gt;

&lt;p&gt;Even in the face of large-scale failures, such as numerous targets failing simultaneously, the system remains resilient, with potential reductions in workload due to aligned system redundancies. Instead of making numerous DNS adjustments, Route 53 efficiently updates its DNS servers with fixed-size health status tables. By proactively pushing data, workload distribution remains balanced. In essence, Route 53's design ensures total resilience and adaptability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges and Future Trends
&lt;/h2&gt;

&lt;p&gt;Since a growing number of projects are transitioning into distributed systems, the imperative for fault tolerance is greater than ever. The complexity and interconnectedness of these systems mean that early error detection, often referred to as "shifting left" error discoveries, is vital.&lt;/p&gt;

&lt;p&gt;Emerging strategies include a deep focus on static analysis. &lt;a href="https://en.wikipedia.org/wiki/TLA%2B"&gt;TLA+&lt;/a&gt; models and modern programming languages like Rust are at the leading edge of this movement, aiming to identify and address issues even before runtime. However, while preventive measures are important, it's equally crucial to have runtime safeguards: machine learning algorithms can predict potential system failures, allowing for timely interventions; additionally, robotics research, branching into automated testing and maintenance, offers promising avenues to ensure system robustness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Implementing Fault Tolerance
&lt;/h2&gt;

&lt;p&gt;To make the presented case studies more practical and useful, I’d prefer to present a checklist for designing fault-tolerant systems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Replication&lt;/strong&gt;: Implement data replication across multiple regions and ensure multiple replicas within each region as well.&lt;br&gt;
&lt;strong&gt;Isolate Performance&lt;/strong&gt;: Create barriers so that a fault in one area doesn't spread.&lt;br&gt;
&lt;strong&gt;Monitor Constantly&lt;/strong&gt;: Utilise integrated tools for constant system health checks.&lt;br&gt;
&lt;strong&gt;Stay Scalable&lt;/strong&gt;: Adopt designs that allow easy scalability in response to growing needs.&lt;br&gt;
&lt;strong&gt;Maintain Consistency&lt;/strong&gt;: Ensure that the system behaves predictably at all times, especially during peak loads or failures.&lt;br&gt;
Plan for Failures: Assume things will break and design recovery strategies in advance.&lt;/p&gt;

&lt;p&gt;By adhering to these principles and referencing this checklist, businesses can foster systems that stand resilient against the unpredictable nature of the digital realm.&lt;/p&gt;

&lt;p&gt;As technology continually evolves, the complexities and demands of these systems heighten. With such rapid advancements in this realm, it's highly important for professionals and enthusiasts alike to keep pace with the latest methodologies and strategies. This overview is hopefully a good squeeze of the latest strategies that will help developers and engineers make resilient systems. &lt;/p&gt;

</description>
      <category>faulttolerance</category>
      <category>distributedsystems</category>
      <category>strategies</category>
    </item>
  </channel>
</rss>
