@Test
    public void testSnapshotSync() throws Exception {
        final Endpoint addr = new Endpoint(TestUtils.getMyIp(), TestUtils.INIT_PORT);
        NodeManager.getInstance().addAddress(addr);
        final Node node = new NodeImpl("unittest", new PeerId(addr, 0));
        final NodeOptions nodeOptions = createNodeOptionsWithSharedTimer();
        nodeOptions.setSnapshotIntervalSecs(3600);
        final List<LogId> logs = Collections.synchronizedList(new ArrayList<LogId>());
        final List<LogId> snapshotsExpected = Collections.synchronizedList(new ArrayList<LogId>());
        final List<LogId> snapshots = Collections.synchronizedList(new ArrayList<LogId>());
        final CountDownEvent running = new CountDownEvent();
        final StateMachine fsm = new StateMachineAdapter() {

            @Override
            public void onApply(Iterator iter) {
                while (iter.hasNext()) {
                    final LogId logId = new LogId(iter.getIndex(), iter.getTerm());
                    logs.add(logId);
                    //random snapshot
                    if (ThreadLocalRandom.current().nextInt(10) < 5) {
                        // commit before do snapshot
                        iter.commit();
                        running.incrementAndGet();
                        node.snapshotSync(new Closure() {

                            @Override
                            public void run(Status status) {
                                if (status.isOk()) {
                                    snapshotsExpected.add(logId.copy());
                                } else {
                                    assertEquals(status.getCode(), RaftError.EBUSY.getNumber());
                                }
                                running.countDown();

                            }
                        });
                    }
                    iter.done().run(Status.OK());
                    iter.next();
                }

            }

            @Override
            public void onSnapshotSave(SnapshotWriter writer, Closure done) {
                snapshots.add(new LogId(writer.getCurrentMeta().getLastIncludedIndex(), writer.getCurrentMeta()
                    .getLastIncludedTerm()));
                done.run(Status.OK());
            }

        };
        nodeOptions.setFsm(fsm);
        nodeOptions.setLogUri(this.dataPath + File.separator + "log");
        nodeOptions.setSnapshotUri(this.dataPath + File.separator + "snapshot");
        nodeOptions.setRaftMetaUri(this.dataPath + File.separator + "meta");
        nodeOptions.setSnapshotIntervalSecs(10);
        nodeOptions.setInitialConf(new Configuration(Collections.singletonList(new PeerId(addr, 0))));

        assertTrue(node.init(nodeOptions));
        // wait node elect self as leader
        Thread.sleep(2000);

        CountDownLatch latch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            final ByteBuffer data = ByteBuffer.wrap(("hello" + i).getBytes());
            final Task task = new Task(data, new ExpectClosure(0, null, latch));
            node.apply(task);
            if (i % 10 == 0) {
                try {
                    node.snapshotSync(null);
                    fail();
                } catch (IllegalStateException e) {

                }
            }
        }
        waitLatch(latch);

        running.await();

        assertEquals(1000, logs.size());
        assertTrue(snapshotsExpected.size() > 1);
        //The metadata should be the same.
        assertEquals(snapshotsExpected, snapshots);

        latch = new CountDownLatch(1);
        node.shutdown(new ExpectClosure(latch));
        node.join();
        waitLatch(latch);
    }
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