Writing concurrent programs is tricky. Languages tend to go down one of two routes: either threads are allowed to access the same data-structures directly, and it's left up to the programmer to decide how to manage locking; or the language presents a model whereby each thread has its own specific memory, and data is passed between threads through message passing. The former approach gives the programmer a slightly bigger gun to shoot themselves with: the ease of writing code that deadlocks is unrivalled. The latter is conceptually much simpler and more intuitive, but is relatively confined: languages such as Erlang or dedicated actor frameworks such as Akka are about as mainstream as it gets.
Writing distributed programs is trickier still. In addition to the issues of writing concurrent programs, you have further difficulties of dealing with the safe access or distribution of data across the system, combined with the increased potential for partial failures.
The mental model you should have when using AtomizeJS is as follows:
- Assume there is an object graph that gets distributed automatically to every browser looking at your site.
- To make safe changes to this object graph, you write functions which
change the objects as desired. These functions are then run by
AtomizeJS as transactions. AtomizeJS ensures that these transaction
functions are run:
- atomically: Transactions are atomic (all or nothing).
- consistently: Transactions preserve the object graph's consistency. That is, a transaction transforms a consistent state of the object graph into another consistent state, without necessarily preserving consistency at all intermediate points.
- in isolation: Transactions are isolated from one another. That is, even though in general there will be many transactions running concurrently, any given transaction's updates are concealed from all the rest, until that transaction commits. Another way of saying that same thing is that, for any two distinct transactions T1 and T2, T1 might see T2's updates (after T2 has committed) or T2 might see T1's updates (after T1 has committed), but certainly not both.
Transactions cannot deadlock: when a transaction function is run, AtomizeJS detects if other transactions have modified the same objects that are being altered by this function. If so, this function is automatically restarted with the updated current state-of-the-world. If a transaction is restarted then it will be because some other transaction has committed, thus the system as a whole has made progress.
Transactions allow programmers to safely manipulate data-structures
shared between many threads, or in the case of AtomizeJS, clients
(such as web browsers). You don't need to write distribution
mechanisms as you would with message-passing: all the code you write
assumes the data is local - you never write
send or have
Writing applications with transactions
Many applications do more and more work on the client-side - i.e. in the browser. But currently, if a client wants to modify some global state, the usual practice is to make some sort of request to the server, whether it's via XHR or WebSockets or some other means. The server receives the message and acts on it. This means you have to write both client-side code, and server-side code. But with AtomizeJS, you can just write client-side code, and rely on AtomizeJS to distribute the changes to all clients.
Indeed, I have written an entire multi-player game - bomberman that has no server-side code at all beyond the AtomizeJS server.
Beyond plain transactions
If a transaction allows you to safely modify global data, what about
waiting for someone else to modify a value? If you can wait for
someone else to modify a value then you can very easily implement the
observer pattern, which, across global distributed data gets you
broadcast and other general-purpose communication mechanisms, amongst
other things. Well, AtomizeJS supports both the
orElse functions of STM, which gives you the ability to force a
transaction to suspend and be restarted only when values which have
been read as part of the transaction have been changed.
AtomizeJS also supports nested transactions.