class RaftLogShipper {

    public synchronized void onMatch( long newMatchIndex, LeaderContext leaderContext )
    {
        boolean progress = newMatchIndex > matchIndex;
        if ( newMatchIndex > matchIndex )
        {
            matchIndex = newMatchIndex;
        }
        else
        {
            log.warn( "%s: match index not progressing. This should be transient.", statusAsString() );
        }

        switch ( mode )
        {
            case MISMATCH:
                if ( sendNextBatchAfterMatch( leaderContext ) )
                {
                    log.info( "%s: caught up after mismatch, moving to PIPELINE mode", statusAsString() );
                    mode = PIPELINE;
                }
                else
                {
                    log.info( "%s: starting catch up after mismatch, moving to CATCHUP mode", statusAsString() );
                    mode = Mode.CATCHUP;
                }
                break;
            case CATCHUP:
                if ( matchIndex >= lastSentIndex )
                {
                    if ( sendNextBatchAfterMatch( leaderContext ) )
                    {
                        log.info( "%s: caught up, moving to PIPELINE mode", statusAsString() );
                        mode = PIPELINE;
                    }
                }
                break;
            case PIPELINE:
                if ( matchIndex == lastSentIndex )
                {
                    abortTimeout();
                }
                else if ( progress )
                {
                    scheduleTimeout( retryTimeMillis );
                }
                break;

            default:
                throw new IllegalStateException( "Unknown mode: " + mode );
        }

        lastLeaderContext = leaderContext;
    }

      RaftLogShipper( Outbound<MemberId, RaftMessages.RaftMessage> outbound, LogProvider logProvider,
                    ReadableRaftLog raftLog, Clock clock, TimerService timerService,
                    MemberId leader, MemberId follower, long leaderTerm, long leaderCommit, long retryTimeMillis,
                    int catchupBatchSize, int maxAllowedShippingLag, InFlightCache inFlightCache );

}