#begin

Temporal Coupling

The next major section of this chapter is about temporal coupling. I think we have all heard this term before but what is it exactly. It’s about time being a design element of the software itself. Time is an often ignored aspect of software architecture. There are two things that are important about time and those are concurrency and ordering.

Who hasn’t run into the issue where the changing order of invoking methods fixed a bug. I can give you a perfect example of this, like, calling close on a stream before open. Or maybe sending an email before setting the subject. Concurrency issues often show themselves as race conditions. These kinds of bugs are hard to figure out and solve. Additionally, it’s hard to write proper unit or integration tests that cover concurrency aspects.

Engineers or architects often design software without the notion of time. So, they end up with a linear system. However this will always lead to temporal coupling since the software is coupled to time itself. This approach is not very flexible or even realistic, especially in modern software and hardware capabilities. We need to think about concurrency, about decoupling any time and order dependencies.

And I fully agree! I think the software industry really has embraced the fact that almost everything is a distributed system nowadays. Look at the micro-service trend that’s still going strong. This is taking the battle against temporal coupling to an extreme. We often talk about the term ‘eventual consistency’ when it comes to distributed systems. If we allow async operations we need to accept the fact that not all data, is available, at any given time. In a Unity3D sense this might not be much of an impact. We can still communicate between tasks or even coroutines since it all lives in the same process. But once things start to become really distributed like with micro-services, eventual consistency take a more prevalent role.

 

Workflow

In most, if not all software, there are certain workflows. Many of such workflows might seem linear at the surface but when you really analyze them you notice that some things could be done in parallel. David and Andrew give the advice to analyze workflows using UML activity diagrams. These diagrams are perfect for documenting such things, indeed. The interesting bit about activity diagrams is that there is the concept of synchronization. This models a point in a workflows where multiple paths join and once all paths are complete, the workflow can continue.

So by modeling your workflows using activity diagrams you are forced to think about these concepts and this might give you the insight to remove temporal coupling from your system design.

A provided example in the book entails a workflow for making a pina colada. At first sight this workflow seems linear, however… There are certain steps that can be done in parallel, like turning on the blender while getting the glasses for example.

Andrew and David then discuss an example of how to break temporal coupling in an on-line transaction processing system they wrote. They describe it as: “At its simplest, all the system had to do was read a request and process the transaction against the database.” And, they wrote a three tier, multiprocessing distributed application. Each of these components was independent and this way they could take advantage of temporal coupling. When requests would come in asynchronously they would be queued and then the application could process the messages in this queue in a concurrent manner.

This sounds a lot like modern event driven systems, or well, modern software architecture. A lot of current architecture is event based. Queuing mechanisms are very, very popular.

They also mention a pattern they used in their architecture which they call the “hungry consumer model”. They describe it as the following: “In a hungry consumer model, you replace the central scheduler with a number of independent consumer tasks and a centralized work queue.” So a consumer can take whatever is on the queue and start process it independently. So this indeed breaks temporal coupling. Notice also how it is really easy to scale up processing power by simply adding more consumers. Yet, this might put the bottleneck on some other component in the architecture, like the database.

 

Design for concurrency

Next they mention something I think is funny, so I’ll just quote it: “The rising acceptance of Java as a platform has exposed more developers to multi-threaded programming. But programming with threads imposes some design constraints – that’s a good thing.” I think its funny how they describe Java rising as a platform. I think they were right. Java has indeed penetrated much of software industry and is still going strong. People might say it’s dead but, yeah, they are full of shit. Remember how big the Log4J problem was? Well that’s a Java thing. Everyone that said Java was dead was reminded back then how much of software still runs on Java, and probably will be for a long time.

Andrew and David say that with linear code it is easy to make assumptions that lead to sloppy programming, however with concurrency, you are forced to think through things a bit more carefully. I think this is such a powerful concept to keep in your mind. I think I’ve expressed my enthusiasm for concurrency and parallelism multiple times. So this really resonates with me.

They say that global or static variables should not be accessed since they will often cause race conditions, including adding temporal coupling in a concurrent system. If you lock some global variable, you have effectively introduced temporal coupling. So this will force you to ask the question, why do you need this global variable anyway. In any software design class, globals are said to be a code smell. I think so too. With proper design and dependency injections, globals are not necessary.

Concurrency also forces you to keep objects in a valid state from initialization until not longer used or garbage collected. Since objects can be referenced by multiple threads you don’t know exactly when or what thread will access the object. Andrew and David describe that you can use the design by contract approach to fight against this problem.

 

Cleaner interfaces.

All this thinking about concurrency and time-ordered dependencies will most likely inspire your to design cleaner interfaces. I wish they made unity3d more thread-safe. Things like Application.PersistentDataPath have no reason, not to be thread-safe. There’s probably a million other things that could have been made thread-safe but it is what it is.

For the code you write and own; keep concurrency in your mind at all times. Even if you are not accustomed to writing task based systems, threads or maybe simply using coroutines. At some point you might hit performance barriers, and the only thing you can do to speed things up is to introduce concurrency into the mix. If you system is built without assumptions of temporal coupling you will find it far more easy to adapt to a concurrent model.

 

Deployment

Andrew and David then mention something nice which is that; if you designed an architecture with an element of concurrency, it becomes easier to think about handling many concurrent entities. Once you ‘get’ the model, it becomes pervasive.

You will be able to introduce concurrency in many levels of the architecture, and you can take advantage of it to reach performance and scalability goals. The introduction of ECS and the job system in Unity3D showcases this in a very dramatic way. You can move up from managing let’s say 5000 GameObjects to managing 5000000 entities. Which is a very significant improvement.

 

#end

01010010 01110101 01100010 01100101 01101110

Hey, sorry to bother you but you can subscribe to my blog here.

Never miss a blog post!

You have Successfully Subscribed!