boolean detectAndHandleBackupTimeout(long timeoutMillis) {
        // if the backups have completed, we are done; this also filters out all non backup-aware operations
        // since the backupsAcksExpected will always be equal to the backupsAcksReceived
        boolean backupsCompleted = backupsAcksExpected == backupsAcksReceived;
        long responseReceivedMillis = pendingResponseReceivedMillis;

        // if this has not yet expired (so has not been in the system for a too long period) we ignore it
        long expirationTime = responseReceivedMillis + timeoutMillis;
        boolean timeout = expirationTime > 0 && expirationTime < Clock.currentTimeMillis();

        // if no response has yet been received, we we are done; we are only going to re-invoke an operation
        // if the response of the primary has been received, but the backups have not replied
        boolean responseReceived = pendingResponse != VOID;

        if (backupsCompleted || !responseReceived || !timeout) {
            return false;
        }

        if (shouldFailOnIndeterminateOperationState()) {
            complete(new IndeterminateOperationStateException(this + " failed because backup acks missed."));
            return true;
        }

        boolean targetDead = context.clusterService.getMember(targetAddress) == null;
        if (targetDead) {
            // the target doesn't exist, so we are going to re-invoke this invocation;
            // the reason for the re-invocation is that otherwise it's possible to lose data,
            // e.g. when a map.put() was done and the primary node has returned the response,
            // but has not yet completed the backups and then the primary node fails;
            // the consequence would be that the backups are never be made and the effects of the map.put() will never be visible,
            // even though the future returned a value;
            // so if we would complete the future, a response is sent even though its changes never made it into the system
            resetAndReInvoke();
            return false;
        }

        // the backups have not yet completed, but we are going to release the future anyway if a pendingResponse has been set
        complete(pendingResponse);
        return true;
    }
boolean detectAndHandleBackupTimeout ( long timeoutMillis ) { boolean backupsCompleted = backupsAcksExpected == backupsAcksReceived ; long responseReceivedMillis = pendingResponseReceivedMillis ; long expirationTime = responseReceivedMillis + timeoutMillis ; boolean timeout = expirationTime > 0 && expirationTime < Clock . currentTimeMillis ( ) ; boolean responseReceived = pendingResponse != VOID ; if ( backupsCompleted || ! responseReceived || ! timeout ) { return false ; } if ( shouldFailOnIndeterminateOperationState ( ) ) { complete ( new IndeterminateOperationStateException ( this + " failed because backup acks missed." ) ) ; return true ; } boolean targetDead = context . clusterService . getMember ( targetAddress ) == null ; if ( targetDead ) { resetAndReInvoke ( ) ; return false ; } complete ( pendingResponse ) ; return true ; }
gets called from the monitor-thread