|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 3 of 7
The choice to break your code down into more atomic steps is all about flexibility and control. Whether your code is executing across multiple cores on multiple threads or processes, or across multiple servers in a distributed computing environment, you can gain more control over how it executes and make that execution more flexible if you break the code down into atomic steps.
For example, a general rule of thumb is that your code ought to be able to execute anywhere in a distributed computing environment
at any given time. If setRadius() in Listing 1 were called on an instance of the object NonAtomicCircle on one server, and then getDiameter() were called on a different instance of NonAtomicCircle on another server, then obviously any interdependency could not be easily satisfied without additional overhead and effort.
(You'll see an example of such overhead later in this article's discussion of statefulness versus statelessness.) Such overhead,
if not handled correctly, can inhibit scalability and availability.
In addition, some distributed computing environments plan for failure -- indeed, they expect failure to arise at any time
when code is executing. Consider the provisionLineItem() method of the OverAchiever class in Listing 3. If a power supply takes down a server when it's in the process of executing the provisionLineItem() method (which does multiple things), and your distributed environment allows for automatic retries of failed method calls,
what are the implications? How far did the provisionLineItem() method get in its work? The reserveInventory() step? Or the calculateWeight() call? Which steps were satisfied, and what was left undone?
Atomic steps allow more fine-grained levels of reliability and problem resolution. Typically, distributed environments use
compensating tasks to check failed calls and make corrections if needed. If reserveInventory() is called as an atomic method call and is interrupted, you know exactly where you were, and what needs to be checked and
corrected. However, when it is called within an aggregate, coarse-grained method like provisionLineItem(), then you're making failure resolution more difficult. Figure 1 shows a sample orchestration of atomic calls being handled
reliably during failure in a cloud application platform environment.
Long-running code only makes this situation worse. Perhaps the long-running code has a single purpose; but if it runs for a long time to achieve a single end, and the server running it dies ninety percent of the way through the job, do you retry it from the beginning? Starting over is fine for some purposes, but in time-critical work, it would be unacceptable. If a long-running method can be broken down into more atomic steps, then your distributed application can snapshot progress along the way, and retry from failure mid-process more efficiently.
In terms of parallelism and multicore, Daniel Spiewak wrote an excellent entry on this very topic on his blog, Code Commit. Daniel argues that
[t]here is actually a deeper question underlying concurrency: what operations do not depend upon each other in a sequential fashion? As soon as we identify these critical operations, we're one step closer to being able to effectively optimize a particular algorithm with respect to asynchronous processing.
Later in his post, he states:
This is truly the defining factor of atomic computations: it may be possible to reorder a series of atomic computations, but such a reordering cannot affect the internals of these computations. Within the "atom," the order is fixed.
So what does reordering have to do with concurrency? Everything, as it turns out. In order to implement an asynchronous algorithm, it is necessary to identify the parts of the algorithm which can be executed in parallel. In order for one computation to be executed concurrently with another, neither must rely upon the other being at any particular stage in its evaluation.
All of that should sound familiar at this point, and that serves as a good stopping point on this topic.