// https://issues.hibernatingrhinos.com/issue/RavenDB-12525 // The problem was when sync operates while there is no data to sync // the sync operation continued with default properties // and at the end, it updated the state with those properties public void Sync_WhenThereIsNoJournalToSync_ShouldNotUpdateHeaderToDefault() { for (var i = 0; i < 100; i++) { using (var tx = Env.WriteTransaction()) { var tree = tx.CreateTree("foo"); tree.Add("items/" + i, StreamFor("values/" + i)); tx.Commit(); } } Env.FlushLogToDataFile(); using (var operation = new SyncOperation(Env.Journal.Applicator)) { operation.SyncDataFile(); } using (var operation = new SyncOperation(Env.Journal.Applicator)) { operation.SyncDataFile(); } var journalInfo = Env.Journal.GetCurrentJournalInfo(); Assert.NotEqual(-1, journalInfo.LastSyncedJournal); Assert.NotEqual(-1, journalInfo.LastSyncedTransactionId); }
// As part of the sync operation, there are stages where the sync operation needs the flush lock // and as part of the flush operation, there are stages the flush operation needs transaction write lock // this can lead to a situation where the sync is waiting to flush waiting to write transaction // so the sync pass his work that needs the flush lock to the flush operation if the lock is occupied // and if the flush operation can do it while it waits to write transaction lock //In this test, the sync is called while the flush is running and waiting to write transaction so the sync should not be blocked public void CanSyncWhileFlushWaiteToWriteTransaction() { var syncMayFinishedEvent = new AutoResetEvent(false); //Adding unsynced bytes so the sync thread will has work to do for (var i = 0; i < 100; i++) { using (var tx = Env.WriteTransaction()) { var tree = tx.CreateTree("foo"); tree.Add("items/" + i, StreamFor("values/" + i)); tx.Commit(); } } Env.FlushLogToDataFile(); //Adding unflushed bytes so the flush thread will has work to do for (var i = 0; i < 100; i++) { using (var tx = Env.WriteTransaction()) { var tree = tx.CreateTree("foo"); tree.Add("items/" + i, StreamFor("values/" + i)); tx.Commit(); } } var syncResult = false; void Sync() { try { using (var operation = new SyncOperation(Env.Journal.Applicator)) { syncResult = operation.SyncDataFile(); } } finally { syncMayFinishedEvent.Set(); } } void Flush() { try { using (Env.Journal.Applicator.TakeFlushingLock()) { Task.Run((Action)Sync); Env.FlushLogToDataFile(); } } catch (Exception) { syncMayFinishedEvent.Set(); } } // Write transaction lock is taken to block the flush using (var tx = Env.WriteTransaction()) { Task.Run((Action)Flush); syncMayFinishedEvent.WaitOne(TimeSpan.FromSeconds(10)); Assert.True(syncResult); var totalWrittenButUnsyncedBytes = Env.Journal.Applicator.TotalWrittenButUnsyncedBytes; Assert.Equal(0, totalWrittenButUnsyncedBytes); } }