/**
   * Managing timeouts is trickier than putting all of them in a long list, like regular message
   * delivery. Timeouts are ordered and can be cancelled, so they need special treatment. Hence a
   * separate method for managing timeouts triggering.
   */
  private Pair<ClusterAction, ClusterState> performNextTimeoutFrom(ClusterInstance instance)
      throws Exception {
    ClusterState newState = snapshot();
    ClusterAction clusterAction = newState.instance(instance.uri().toASCIIString()).popTimeout();
    clusterAction.perform(newState);

    return Pair.of(clusterAction, newState);
  }
  /**
   * Clone the state and perform the action with the provided index. Returns the new state and the
   * action.
   */
  ClusterState performAction(ClusterAction action) throws Exception {
    ClusterState newState = snapshot();

    // Remove the action from the list of things that can happen in the snapshot
    newState.pendingActions.remove(action);

    // Perform the action on the cloned state
    Iterable<ClusterAction> newActions = action.perform(newState);

    // Include any outcome actions into the new state snapshot
    newState.pendingActions.addAll(asCollection(newActions));

    return newState;
  }