@Test
    public void testRollbackStateMachineWithReadIndex_Issue317() throws Exception {
        final Endpoint addr = new Endpoint(TestUtils.getMyIp(), TestUtils.INIT_PORT);
        final PeerId peer = new PeerId(addr, 0);

        NodeManager.getInstance().addAddress(addr);
        final NodeOptions nodeOptions = createNodeOptionsWithSharedTimer();
        final CountDownLatch applyCompleteLatch = new CountDownLatch(1);
        final CountDownLatch applyLatch = new CountDownLatch(1);
        final CountDownLatch readIndexLatch = new CountDownLatch(1);
        final AtomicInteger currentValue = new AtomicInteger(-1);
        final String errorMsg = this.testName.getMethodName();
        final StateMachine fsm = new StateMachineAdapter() {

            @Override
            public void onApply(final Iterator iter) {
                // Notify that the #onApply is preparing to go.
                readIndexLatch.countDown();
                // Wait for submitting a read-index request
                try {
                    applyLatch.await();
                } catch (InterruptedException e) {
                    fail();
                }
                int i = 0;
                while (iter.hasNext()) {
                    byte[] data = iter.next().array();
                    int v = Bits.getInt(data, 0);
                    assertEquals(i++, v);
                    currentValue.set(v);
                }
                if (i > 0) {
                    // rollback
                    currentValue.set(i - 1);
                    iter.setErrorAndRollback(1, new Status(-1, errorMsg));
                    applyCompleteLatch.countDown();
                }
            }
        };
        nodeOptions.setFsm(fsm);
        nodeOptions.setLogUri(this.dataPath + File.separator + "log");
        nodeOptions.setRaftMetaUri(this.dataPath + File.separator + "meta");
        nodeOptions.setSnapshotUri(this.dataPath + File.separator + "snapshot");
        nodeOptions.setInitialConf(new Configuration(Collections.singletonList(peer)));
        final Node node = new NodeImpl("unittest", peer);
        assertTrue(node.init(nodeOptions));

        assertEquals(1, node.listPeers().size());
        assertTrue(node.listPeers().contains(peer));

        while (!node.isLeader()) {
            ;
        }

        int n = 5;
        {
            // apply tasks
            for (int i = 0; i < n; i++) {
                byte[] b = new byte[4];
                Bits.putInt(b, 0, i);
                node.apply(new Task(ByteBuffer.wrap(b), null));
            }
        }

        final AtomicInteger readIndexSuccesses = new AtomicInteger(0);
        {
            // Submit a read-index, wait for #onApply
            readIndexLatch.await();
            final CountDownLatch latch = new CountDownLatch(1);
            node.readIndex(null, new ReadIndexClosure() {

                @Override
                public void run(final Status status, final long index, final byte[] reqCtx) {
                    try {
                        if (status.isOk()) {
                            readIndexSuccesses.incrementAndGet();
                        } else {
                            assertTrue("Unexpected status: " + status,
                                status.getErrorMsg().contains(errorMsg) || status.getRaftError() == RaftError.ETIMEDOUT
                                        || status.getErrorMsg().contains("Invalid state for readIndex: STATE_ERROR"));
                        }
                    } finally {
                        latch.countDown();
                    }
                }
            });
            // We have already submit a read-index request,
            // notify #onApply can go right now
            applyLatch.countDown();

            // The state machine is in error state, the node should step down.
            while (node.isLeader()) {
                Thread.sleep(10);
            }
            latch.await();
            applyCompleteLatch.await();
        }
        // No read-index request succeed.
        assertEquals(0, readIndexSuccesses.get());
        assertTrue(n - 1 >= currentValue.get());

        node.shutdown();
        node.join();
    }
Test rollback stateMachine with readIndex for issue 317:
https://github.com/sofastack/sofa-jraft/issues/317
jraft-core/src/test/java/com/alipay/sofa/jraft/core/NodeTest.java