anchor.diff:  onSessionOpen(ClientSession{id=2, responseStreamId=102, responseChannel='aeron:udp?endpoint=localhost:19132', encodedPrincipal=[], clusteredServiceAgent=io.aeron.cluster.service.ClusteredServiceAgent@19a05123, responsePublication=null, isClosing=false})
   ----
   
  -You can see that the new node looks like it is re-executing application messages.  That is because it is.  It is
  +You can see that the new node looks like it is re-executing application messages. That is because it is. It is
   replaying the log of the messages that have been sent into the cluster up to this point in order to restore its
  -in-memory to what it was before the failure.
  +in-memory state to what it was before the failure.
   
   === Snapshots
   
   Earlier in the tutorial we looked at how a node can store a snapshot of its current state so that it would not need to
  -replay every single application message since the beginning of time.  We can look at the recording log for a node to see
  -the logs and the snapshots that are stored for a node in the cluster.  We can also use the tools to trigger a snapshot.
  +replay every single application message since the beginning of time. We can look at the recording log for a node to see
  +the logs, and snapshots, that are stored for a node in the cluster. We can also use the tool to trigger a snapshot.
   
   Firstly lets look at the recording log.
   
  
positive.code: ----
  
  IMPORTANT: If you look carefully at the `correlationId` values you will notice there are no `SessionMessage` log entries
  for a number of the messages that were sent.  This is because when the leader fails some of the messages being sent into
  the cluster can be lost.  For this reason it is important the client needs to track responses to it's messages and
  provide timeouts to the caller if required.
  
  We can restore the failed node back into service:
  
  
negative.code: 
  <1> The method `Image::isEndOfStream` can be used to determine if there is going to be any more input.
  
  <2> Because our snapshot is a stream of messages written to a publication, we use the `Image`'s poll method for
  extracting data from the snapshot.
  
  <3> Our total snapshot length (16 bytes) is going to be smaller than any reasonable MTU therefore we can assume that all
  the data will come in a single message.
  
  <4> Once all the data is loaded, we can initialise the application logic state from the snapshot.
  
  <5> Once we've loaded all the data for the application we can break out of the snapshot loading loop.
  
  <6> Again make sure we use the Cluster's `IdleStrategy::idle` in the tight loops.  It could take time for the snapshot
  to be propagated to the service, so the number of fragments can be zero on any given invocation.
  
  <7> It is also worthwhile having some sanity checks, these ensure that the snapshot store/load code and Aeron agree on
  where the end of the input data is. We've used asserts, but other mechanisms, (e.g. log message, exceptions) could
  also be used to indicate an issue.
  
  === Other events
  
  There a number other events received by the `ClusteredService` interface, such as timer events, clients connecting and
  disconnecting from the cluster, and leadership and role changes. We won't look at these in this tutorial.
  
  == Configuring a Cluster
  
  Now we have our application implemented we can move onto running it in a cluster. There are a number of moving parts to
  setting up a cluster node, one of the trickiest parts of using Aeron Cluster is getting the configuration correct. We
  are going to start with a static three-node cluster with a simplified configuration where all the components
  (MediaDriver, Archive, ConsensusModule and ClusteredServiceContainer) for a single node are hosted in a
  single process. Later we will then start the cluster as three separate processes.
  
  === Running Multiple Nodes on the Same Host
  
  In a production deployment, you will want to run the 3 instances on three separate servers, however for our example we
  want to run the services on a single machine. This does make port allocation a concern as each node within the cluster
  needs to bind to a number of ports, so we need to make sure are no clashes.  We could do this with VMs or containers,
  but in the interest of simplicity we are going to specify a port range for each node.  For example:
  
  - Node 0, ports: 9000-9099
  - Node 1, ports: 9100-9199
  