Source:
<code><START> public void testAckBeforeSync() throws Exception { <END> File journalDir = tempDir.newFolder(); Bookie.checkDirectoryStructure(Bookie.getCurrentDirectory(journalDir)); ServerConfiguration conf = TestBKConfiguration.newServerConfiguration(); conf.setJournalDirName(journalDir.getPath()) .setZkServers(null); LedgerDirsManager ledgerDirsManager = mock(LedgerDirsManager.class); Journal journal = new Journal(journalDir, conf, ledgerDirsManager); journal.start(); CountDownLatch latch = new CountDownLatch(1); AtomicInteger countForceWriteCallsBeforeAck = new AtomicInteger(); replace(method(JournalChannel.class, "forceWrite")).with(new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log.info("{} called with arguments ", method.getName(), Arrays.toString(args)); if (latch.getCount() == 1) { countForceWriteCallsBeforeAck.incrementAndGet(); } try { method.invoke(proxy, args); } catch (InvocationTargetException err) { throw err.getCause(); } return null; } }); while (countForceWriteCallsBeforeAck.get() == 0) { Thread.sleep(100); } long ledgerId = 1; long entryId = 0; journal.logAddEntry(ledgerId, entryId, DATA, true , new WriteCallback() { @Override public void writeComplete(int rc, long ledgerId, long entryId, BookieSocketAddress addr, Object ctx) { latch.countDown(); } }, null); assertTrue(latch.await(20, TimeUnit.SECONDS)); assertEquals(1, countForceWriteCallsBeforeAck.get()); journal.shutdown(); }</code><technical_language>logic in testAckBeforeSync testAckAfterSync sounds good me. I for: - for testAckBeforeSync, need ensure callback sync eventually sync advance file log marker - for testAckAfterSync, need ensure callback sync log markers advanced that. solution for this: - need able control force write thread, e.g. suspend forcewrite thread callbacks trigger forcewrite thread is suspended verify forcewrite request enqueued forcewrite queue contains null items</technical_language>

Target:
public void testAckBeforeSync() throws Exception { File journalDir = tempDir.newFolder(); Bookie.checkDirectoryStructure(Bookie.getCurrentDirectory(journalDir)); ServerConfiguration conf = TestBKConfiguration.newServerConfiguration(); conf.setJournalDirName(journalDir.getPath()) .setZkServers(null); JournalChannel jc = spy(new JournalChannel(journalDir, 1)); whenNew(JournalChannel.class).withAnyArguments().thenReturn(jc); LedgerDirsManager ledgerDirsManager = mock(LedgerDirsManager.class); Journal journal = new Journal(journalDir, conf, ledgerDirsManager); CountDownLatch forceWriteThreadSuspendedLatch = new CountDownLatch(1); enableForceWriteThreadSuspension(forceWriteThreadSuspendedLatch, journal); journal.start(); LogMark lastLogMarkBeforeWrite = journal.getLastLogMark().markLog().getCurMark(); CountDownLatch latch = new CountDownLatch(1); long ledgerId = 1; long entryId = 0; journal.logAddEntry(ledgerId, entryId, DATA, true , new WriteCallback() { @Override public void writeComplete(int rc, long ledgerId, long entryId, BookieSocketAddress addr, Object ctx) { latch.countDown(); } }, null); latch.await(20, TimeUnit.SECONDS); verify(jc, times(0)).forceWrite(true); verify(jc, times(0)).forceWrite(false); LastLogMark lastLogMarkAfterForceWrite = journal.getLastLogMark(); assertEquals(0, lastLogMarkAfterForceWrite.getCurMark().compare(lastLogMarkBeforeWrite)); forceWriteThreadSuspendedLatch.countDown(); journal.shutdown(); }