public async Task DontRevertToConflicted() { // put at 8:30 // conflicted at 8:50 // resolved at 9:10 // will revert to 9:00 using (var store1 = GetDocumentStore(options: new Options { ModifyDatabaseRecord = record => { record.ConflictSolverConfig = new ConflictSolver { ResolveToLatest = false, ResolveByCollection = new Dictionary <string, ScriptResolver>() }; } })) using (var store2 = GetDocumentStore()) { DateTime last = default; await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database); using (var session = store2.OpenAsyncSession()) { await session.StoreAsync(new Company(), "keep-conflicted-revision-insert-order"); await session.SaveChangesAsync(); var company = new Company { Name = "Name2" }; await session.StoreAsync(company, "foo/bar"); await session.SaveChangesAsync(); } using (var session = store1.OpenAsyncSession()) { var company = new Company { Name = "Name1" }; await session.StoreAsync(company, "foo/bar"); await session.SaveChangesAsync(); var companiesRevisions = await session.Advanced.Revisions.GetForAsync <Company>("foo/bar"); Assert.Equal(1, companiesRevisions.Count); } await SetupReplicationAsync(store2, store1); WaitUntilHasConflict(store1, "foo/bar"); last = DateTime.UtcNow; using (var session = store1.OpenAsyncSession()) { var company = new Company { Name = "Resolver" }; await session.StoreAsync(company, "foo/bar"); await session.SaveChangesAsync(); } var db = await GetDocumentDatabaseInstanceFor(store1); RevertResult result; using (var token = new OperationCancelToken(db.Configuration.Databases.OperationTimeout.AsTimeSpan, db.DatabaseShutdown)) { result = (RevertResult)await db.DocumentsStorage.RevisionsStorage.RevertRevisions(last.Add(TimeSpan.FromMinutes(1)), TimeSpan.FromMinutes(60), onProgress : null, token : token); } Assert.Equal(4, result.ScannedRevisions); Assert.Equal(2, result.ScannedDocuments); Assert.Equal(0, result.RevertedDocuments); using (var session = store1.OpenAsyncSession()) { var companiesRevisions = await session.Advanced.Revisions.GetForAsync <Company>("foo/bar"); Assert.Equal(3, companiesRevisions.Count); } } }
public async Task GetRevisionsBinEntries(bool useSession) { using (var store1 = GetDocumentStore()) using (var store2 = GetDocumentStore()) { var database = await GetDocumentDatabaseInstanceFor(store1); var database2 = await GetDocumentDatabaseInstanceFor(store2); database.TombstoneCleaner.Subscribe(this); database2.TombstoneCleaner.Subscribe(this); await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database, configuration => { configuration.Collections["Users"].PurgeOnDelete = false; }); await RevisionsHelper.SetupRevisions(Server.ServerStore, store2.Database, configuration => { configuration.Collections["Users"].PurgeOnDelete = false; }); await SetupReplicationAsync(store1, store2); var deletedRevisions = await store1.Commands().GetRevisionsBinEntriesAsync(long.MaxValue); Assert.Equal(0, deletedRevisions.Count()); var id = "users/1"; if (useSession) { var user = new User { Name = "Fitzchak" }; for (var i = 0; i < 2; i++) { using (var session = store1.OpenAsyncSession()) { await session.StoreAsync(user); await session.SaveChangesAsync(); } using (var session = store1.OpenAsyncSession()) { session.Delete(user.Id); await session.SaveChangesAsync(); } } id += "-A"; } else { await store1.Commands().PutAsync(id, null, new User { Name = "Fitzchak" }); await store1.Commands().DeleteAsync(id, null); await store1.Commands().PutAsync(id, null, new User { Name = "Fitzchak" }); await store1.Commands().DeleteAsync(id, null); } WaitForMarker(store1, store2); var statistics = store2.Maintenance.Send(new GetStatisticsOperation()); Assert.Equal(useSession ? 2 : 1, statistics.CountOfDocuments); Assert.Equal(4, statistics.CountOfRevisionDocuments); //sanity deletedRevisions = await store1.Commands().GetRevisionsBinEntriesAsync(long.MaxValue); Assert.Equal(1, deletedRevisions.Count()); deletedRevisions = await store2.Commands().GetRevisionsBinEntriesAsync(long.MaxValue); Assert.Equal(1, deletedRevisions.Count()); using (var session = store2.OpenAsyncSession()) { var users = await session.Advanced.Revisions.GetForAsync <User>(id); Assert.Equal(4, users.Count); Assert.Equal(null, users[0].Name); Assert.Equal("Fitzchak", users[1].Name); Assert.Equal(null, users[2].Name); Assert.Equal("Fitzchak", users[3].Name); // Can get metadata only var revisionsMetadata = await session.Advanced.Revisions.GetMetadataForAsync(id); Assert.Equal(4, revisionsMetadata.Count); Assert.Equal((DocumentFlags.DeleteRevision | DocumentFlags.HasRevisions | DocumentFlags.FromReplication).ToString(), revisionsMetadata[0].GetString(Constants.Documents.Metadata.Flags)); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.Revision | DocumentFlags.FromReplication).ToString(), revisionsMetadata[1].GetString(Constants.Documents.Metadata.Flags)); Assert.Equal((DocumentFlags.DeleteRevision | DocumentFlags.HasRevisions | DocumentFlags.FromReplication).ToString(), revisionsMetadata[2].GetString(Constants.Documents.Metadata.Flags)); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.Revision | DocumentFlags.FromReplication).ToString(), revisionsMetadata[3].GetString(Constants.Documents.Metadata.Flags)); } await store1.Maintenance.SendAsync(new RevisionsTests.DeleteRevisionsOperation(new AdminRevisionsHandler.Parameters { DocumentIds = new[] { id, "users/not/exists" } })); WaitForMarker(store1, store2); statistics = store2.Maintenance.Send(new GetStatisticsOperation()); Assert.Equal(useSession ? 3 : 2, statistics.CountOfDocuments); Assert.Equal(0, statistics.CountOfRevisionDocuments); } }
public async Task ReplicateExpiredAndDeletedRevisions() { var revisionsAgeLimit = TimeSpan.FromSeconds(10); Action <RevisionsConfiguration> modifyConfiguration = configuration => configuration.Collections["Users"] = new RevisionsCollectionConfiguration { Disabled = false, MinimumRevisionAgeToKeep = revisionsAgeLimit }; using (var store1 = GetDocumentStore()) using (var store2 = GetDocumentStore()) { //setup revisions on both stores and setup replication await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database, modifyConfiguration); await RevisionsHelper.SetupRevisions(Server.ServerStore, store2.Database, modifyConfiguration); await SetupReplicationAsync(store1, store2); // create some revisions on store1 using (var session = store1.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Aviv" }, "users/1-A"); await session.SaveChangesAsync(); } for (int i = 2; i <= 10; i++) { using (var session = store1.OpenAsyncSession()) { var user = await session.LoadAsync <User>("users/1-A"); user.Name = "Aviv" + i; await session.SaveChangesAsync(); } } // wait for replication WaitForMarker(store1, store2); using (var session = store2.OpenAsyncSession()) { var doc = await session.LoadAsync <User>("users/1-A"); Assert.Equal("Aviv10", doc.Name); } // wait until revisions are expired await Task.Delay(revisionsAgeLimit); // modify document using (var session = store1.OpenAsyncSession()) { var user = await session.LoadAsync <User>("users/1-A"); user.Name = "Grisha"; await session.SaveChangesAsync(); } WaitForMarker(store1, store2); // expired revisions should be deleted // assert that both stores have just one revision now using (var session = store1.OpenAsyncSession()) { var revisions = await session.Advanced.Revisions.GetForAsync <User>("users/1-A"); Assert.Equal(1, revisions.Count); Assert.Equal("Grisha", revisions[0].Name); } using (var session = store2.OpenAsyncSession()) { var revisions = await session.Advanced.Revisions.GetForAsync <User>("users/1-A"); Assert.Equal(1, revisions.Count); Assert.Equal("Grisha", revisions[0].Name); } } }
public async Task limitRevisionDeletion() { using (var store = GetDocumentStore()) { var configuration = new RevisionsConfiguration { Default = new RevisionsCollectionConfiguration { Disabled = false, MinimumRevisionsToKeep = 1000000 }, }; await RevisionsHelper.SetupRevisions(store, Server.ServerStore, configuration); using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Toli" }, "users/1"); await session.StoreAsync(new User { Name = "Mitzi" }, "users/2"); await session.StoreAsync(new Order { Employee = "Daniel" }, "orders/1"); await session.SaveChangesAsync(); } for (int i = 0; i < 500; i++) { using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Toli" + i }, "users/1"); await session.StoreAsync(new User { Name = "Mitzi" + i }, "users/2"); await session.StoreAsync(new Order { Employee = "Daniel" + i }, "orders/1"); await session.SaveChangesAsync(); } } for (int i = 500; i < 630; i++) { using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Mitzi" + i }, "users/2"); await session.SaveChangesAsync(); } } using (var session = store.OpenAsyncSession()) { var revisionCount = session.Advanced.Revisions.GetForAsync <User>("users/1", 0, 1000).Result.Count; Assert.Equal(501, revisionCount); revisionCount = session.Advanced.Revisions.GetForAsync <User>("users/2", 0, 1000).Result.Count; Assert.Equal(631, revisionCount); revisionCount = session.Advanced.Revisions.GetForAsync <User>("orders/1", 0, 1000).Result.Count; Assert.Equal(501, revisionCount); } configuration = new RevisionsConfiguration { Default = new RevisionsCollectionConfiguration { Disabled = false, MinimumRevisionsToKeep = 10, MaximumRevisionsToDeleteUponDocumentUpdate = 100 }, Collections = new Dictionary <string, RevisionsCollectionConfiguration> { ["Orders"] = new RevisionsCollectionConfiguration { Disabled = false, MinimumRevisionsToKeep = 33, MaximumRevisionsToDeleteUponDocumentUpdate = 35 } } }; await RevisionsHelper.SetupRevisions(store, Server.ServerStore, configuration); using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Toli" }, "users/1"); await session.StoreAsync(new User { Name = "Toli" }, "users/2"); await session.StoreAsync(new Order { Employee = "Toli" }, "orders/1"); await session.SaveChangesAsync(); var revisionCount = session.Advanced.Revisions.GetForAsync <User>("users/1", 0, 1000).Result.Count; Assert.Equal(402, revisionCount); revisionCount = session.Advanced.Revisions.GetForAsync <User>("users/2", 0, 1000).Result.Count; Assert.Equal(532, revisionCount); revisionCount = session.Advanced.Revisions.GetForAsync <Order>("orders/1", 0, 1000).Result.Count; Assert.Equal(467, revisionCount); } for (int i = 0; i < 4; i++) { using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "A" + i }, "users/1"); await session.StoreAsync(new User { Name = "B" + i }, "users/2"); await session.StoreAsync(new Order { Employee = "C" + i }, "orders/1"); await session.SaveChangesAsync(); } } using (var session = store.OpenAsyncSession()) { var revisionCount = session.Advanced.Revisions.GetForAsync <User>("users/1", 0, 1000).Result.Count; Assert.Equal(10, revisionCount); revisionCount = session.Advanced.Revisions.GetForAsync <User>("users/2", 0, 1000).Result.Count; Assert.Equal(136, revisionCount); revisionCount = session.Advanced.Revisions.GetForAsync <Order>("orders/1", 0, 1000).Result.Count; Assert.Equal(331, revisionCount); } for (int i = 4; i < 13; i++) { using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "A" + i }, "users/1"); await session.StoreAsync(new User { Name = "B" + i }, "users/2"); await session.StoreAsync(new Order { Employee = "C" + i }, "orders/1"); await session.SaveChangesAsync(); } } using (var session = store.OpenAsyncSession()) { var revisionCount = session.Advanced.Revisions.GetForAsync <User>("users/1", 0, 1000).Result.Count; Assert.Equal(10, revisionCount); revisionCount = session.Advanced.Revisions.GetForAsync <User>("users/2", 0, 1000).Result.Count; Assert.Equal(10, revisionCount); revisionCount = session.Advanced.Revisions.GetForAsync <Order>("orders/1", 0, 1000).Result.Count; Assert.Equal(33, revisionCount); } WaitForUserToContinueTheTest(store); } }
public async Task ImportRevisionDocumentsWithoutDocuments() { var file = Path.Combine(NewDataPath(forceCreateDir: true), Guid.NewGuid().ToString()); try { using (var store1 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store1" })) { using (var session = store1.OpenAsyncSession()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database); await session.StoreAsync(new Person { Name = "Name1" }); await session.StoreAsync(new Person { Name = "Name2" }); await session.StoreAsync(new Company { Name = "Hibernating Rhinos " }); await session.SaveChangesAsync(); } for (int i = 0; i < 2; i++) { using (var session = store1.OpenAsyncSession()) { var company = await session.LoadAsync <Company>("companies/1-A"); var person = await session.LoadAsync <Person>("people/1-A"); company.Name += " update " + i; person.Name += " update " + i; await session.StoreAsync(company); await session.StoreAsync(person); await session.SaveChangesAsync(); } } using (var session = store1.OpenAsyncSession()) { var person = await session.LoadAsync <Person>("people/2-A"); Assert.NotNull(person); session.Delete(person); await session.SaveChangesAsync(); } var operation = await store1.Smuggler.ExportAsync(new DatabaseSmugglerExportOptions { OperateOnTypes = DatabaseItemType.RevisionDocuments | DatabaseItemType.DatabaseRecord }, file); await operation.WaitForCompletionAsync(TimeSpan.FromMinutes(1)); var stats = await store1.Maintenance.SendAsync(new GetStatisticsOperation()); Assert.Equal(4, stats.CountOfDocuments); Assert.Equal(8, stats.CountOfRevisionDocuments); } using (var store2 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store2" })) { var operation = await store2.Smuggler.ImportAsync(new DatabaseSmugglerImportOptions(), file); await operation.WaitForCompletionAsync(TimeSpan.FromMinutes(1)); var stats = await store2.Maintenance.SendAsync(new GetStatisticsOperation()); Assert.Equal(0, stats.CountOfDocuments); Assert.Equal(10, stats.CountOfRevisionDocuments); using (Server.ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context)) { var command = new GetRevisionsBinEntryCommand(long.MaxValue, 5); await store2.GetRequestExecutor().ExecuteAsync(command, context); Assert.Equal(3, command.Result.Results.Length); } } } finally { File.Delete(file); } }
public async Task PutAttachments() { using (var store = GetDocumentStore()) { await SetDatabaseId(store, dbId); await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database, configuration => { configuration.Collections["Users"].PurgeOnDelete = false; configuration.Collections["Users"].MinimumRevisionsToKeep = 4; }); var names = CreateDocumentWithAttachments(store); AssertRevisions(store, names, (session, revisions) => { AssertRevisionAttachments(names, 3, revisions[0], session); AssertRevisionAttachments(names, 2, revisions[1], session); AssertRevisionAttachments(names, 1, revisions[2], session); AssertNoRevisionAttachment(revisions[3], session); }, 9); // Delete document should delete all the attachments store.Commands().Delete("users/1", null); AssertRevisions(store, names, (session, revisions) => { AssertNoRevisionAttachment(revisions[0], session, true); AssertRevisionAttachments(names, 3, revisions[1], session); AssertRevisionAttachments(names, 2, revisions[2], session); AssertRevisionAttachments(names, 1, revisions[3], session); }, 6, expectedCountOfDocuments: 0); // Create another revision which should delete old revision using (var session = store.OpenSession()) // This will delete the revision #1 which is without attachment { session.Store(new User { Name = "Fitzchak 2" }, "users/1"); session.SaveChanges(); } AssertRevisions(store, names, (session, revisions) => { AssertNoRevisionAttachment(revisions[0], session); AssertNoRevisionAttachment(revisions[1], session, true); AssertRevisionAttachments(names, 3, revisions[2], session); AssertRevisionAttachments(names, 2, revisions[3], session); }, 5); using (var session = store.OpenSession()) // This will delete the revision #2 which is with attachment { session.Store(new User { Name = "Fitzchak 3" }, "users/1"); session.SaveChanges(); } AssertRevisions(store, names, (session, revisions) => { AssertNoRevisionAttachment(revisions[0], session); AssertNoRevisionAttachment(revisions[1], session); AssertNoRevisionAttachment(revisions[2], session, true); AssertRevisionAttachments(names, 3, revisions[3], session); }, 3); using (var session = store.OpenSession()) // This will delete the revision #3 which is with attachment { session.Store(new User { Name = "Fitzchak 4" }, "users/1"); session.SaveChanges(); } AssertRevisions(store, names, (session, revisions) => { AssertNoRevisionAttachment(revisions[0], session); AssertNoRevisionAttachment(revisions[1], session); AssertNoRevisionAttachment(revisions[2], session); AssertNoRevisionAttachment(revisions[3], session, true); }, 0, expectedCountOfUniqueAttachments: 0); using (var session = store.OpenSession()) // This will delete the revision #4 which is with attachment { session.Store(new User { Name = "Fitzchak 5" }, "users/1"); session.SaveChanges(); } AssertRevisions(store, names, (session, revisions) => { AssertNoRevisionAttachment(revisions[0], session); AssertNoRevisionAttachment(revisions[1], session); AssertNoRevisionAttachment(revisions[2], session); AssertNoRevisionAttachment(revisions[3], session); }, 0, expectedCountOfUniqueAttachments: 0); } }
public async Task CanExportAndImportAttachmentsAndRevisionAttachments() { var file = GetTempFileName(); try { using (var store1 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store1" })) { await SetDatabaseId(store1, new Guid("00000000-48c4-421e-9466-000000000000")); await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database, configuration => { configuration.Collections["Users"].PurgeOnDelete = false; configuration.Collections["Users"].MinimumRevisionsToKeep = 4; }); AttachmentsRevisions.CreateDocumentWithAttachments(store1); using (var bigStream = new MemoryStream(Enumerable.Range(1, 999 * 1024).Select(x => (byte)x).ToArray())) store1.Operations.Send(new PutAttachmentOperation("users/1", "big-file", bigStream, "image/png")); var exportOperation = await store1.Smuggler.ExportAsync(new DatabaseSmugglerExportOptions(), file); var exportResult = (SmugglerResult)exportOperation.WaitForCompletion(); Assert.Equal(1, exportResult.Documents.ReadCount); Assert.Equal(4, exportResult.RevisionDocuments.ReadCount); Assert.Equal(4, exportResult.Documents.Attachments.ReadCount); Assert.Equal(10, exportResult.RevisionDocuments.Attachments.ReadCount); var stats = await store1.Maintenance.SendAsync(new GetStatisticsOperation()); Assert.Equal(1, stats.CountOfDocuments); Assert.Equal(4, stats.CountOfRevisionDocuments); Assert.Equal(14, stats.CountOfAttachments); Assert.Equal(4, stats.CountOfUniqueAttachments); } using (var store2 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store2" })) { var dbId = new Guid("00000000-48c4-421e-9466-000000000000"); await SetDatabaseId(store2, dbId); await RevisionsHelper.SetupRevisions(Server.ServerStore, store2.Database); for (var i = 0; i < 2; i++) // Make sure that we can import attachments twice and it will overwrite { var importOperation = await store2.Smuggler.ImportAsync(new DatabaseSmugglerImportOptions(), file); var importResult = (SmugglerResult)importOperation.WaitForCompletion(); Assert.Equal(1, importResult.Documents.ReadCount); Assert.Equal(4, importResult.RevisionDocuments.ReadCount); Assert.Equal(4, importResult.Documents.Attachments.ReadCount); Assert.Equal(4, importResult.RevisionDocuments.Attachments.ReadCount); var stats = await store2.Maintenance.SendAsync(new GetStatisticsOperation()); Assert.Equal(1, stats.CountOfDocuments); Assert.Equal(5, stats.CountOfRevisionDocuments); Assert.Equal(14 + 4, stats.CountOfAttachments); // the imported document will create 1 additional revision with 4 attachments Assert.Equal(4, stats.CountOfUniqueAttachments); using (var session = store2.OpenSession()) { var readBuffer = new byte[1024 * 1024]; using (var attachmentStream = new MemoryStream(readBuffer)) using (var attachment = session.Advanced.Attachments.Get("users/1", "big-file")) { attachment.Stream.CopyTo(attachmentStream); Assert.Equal("big-file", attachment.Details.Name); Assert.Equal("zKHiLyLNRBZti9DYbzuqZ/EDWAFMgOXB+SwKvjPAINk=", attachment.Details.Hash); Assert.Equal(999 * 1024, attachmentStream.Position); Assert.Equal(Enumerable.Range(1, 999 * 1024).Select(x => (byte)x), readBuffer.Take((int)attachmentStream.Position)); } } } } } finally { File.Delete(file); } }
public async Task ExternalReplicationWithRevisionsBin4() { using (var store1 = GetDocumentStore()) using (var store2 = GetDocumentStore()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store2.Database, modifyConfiguration : configuration => configuration.Collections["Users"].PurgeOnDelete = true); var externalTask = new ExternalReplication(store2.Database, "ExternalReplication"); await AddWatcherToReplicationTopology(store1, externalTask); using (var s1 = store1.OpenSession()) { s1.Store(new User() { Name = "Toli" }, "foo/bar"); s1.SaveChanges(); } Assert.True(WaitForDocument(store2, "foo/bar")); for (int i = 0; i < 4; i++) { var name = "Toli" + i; using (var s1 = store1.OpenSession()) { s1.Store(new User() { Name = name }, "foo/bar"); s1.SaveChanges(); } await WaitForValueAsync(async() => { using (var s2 = store2.OpenAsyncSession()) { var user = await s2.LoadAsync <User>("foo/bar"); return(name.Equals(user.Name, StringComparison.InvariantCultureIgnoreCase)); } }, true); } using (var s1 = store1.OpenSession()) { s1.Delete("foo/bar"); s1.SaveChanges(); } await WaitForValueAsync(async() => { using (var s2 = store2.OpenAsyncSession()) { var user = await s2.LoadAsync <User>("foo/bar"); return(user == null); } }, true); var database = await Server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store2.Database); var revisionsStorage = database.DocumentsStorage.RevisionsStorage; using (database.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context)) using (context.OpenReadTransaction()) { var revisions = revisionsStorage.GetRevisionsBinEntries(context, long.MaxValue, 6).Count(); Assert.Equal(0, revisions); } database = await Server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store1.Database); revisionsStorage = database.DocumentsStorage.RevisionsStorage; using (database.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context)) using (context.OpenReadTransaction()) { var revisions = revisionsStorage.GetRevisionsBinEntries(context, long.MaxValue, 6).Count(); Assert.Equal(0, revisions); } } }
public async Task CanMigrateFromRavenDb() { var file = Path.Combine(NewDataPath(forceCreateDir: true), "export.ravendbdump"); var id = "users/1"; using (var store1 = GetDocumentStore()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database); using (var session = store1.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Egor" }, id); { session.CountersFor(id).Increment("Downloads", int.MaxValue); session.CountersFor(id).Increment("ShouldBePositiveValueAfterSmuggler", long.MaxValue); session.CountersFor(id).Increment("LittleCounter", 500); } await session.SaveChangesAsync(); } for (int i = 0; i < 10; i++) { using (var session = store1.OpenAsyncSession()) { var user = await session.LoadAsync <Company>(id); user.Name = "Egor " + i; await session.SaveChangesAsync(); } } using (var session = store1.OpenAsyncSession()) { var revisionsMetadata = await session.Advanced.Revisions.GetMetadataForAsync(id); Assert.Equal(12, revisionsMetadata.Count); } var operation = await store1.Smuggler.ExportAsync(new DatabaseSmugglerExportOptions(), file); await operation.WaitForCompletionAsync(TimeSpan.FromMinutes(1)); } // all data import using (var store2 = GetDocumentStore()) { var operation = await store2.Smuggler.ImportAsync(new DatabaseSmugglerImportOptions(), file); await operation.WaitForCompletionAsync(TimeSpan.FromMinutes(1)); using (var session = store2.OpenAsyncSession()) { var metadata = session.Advanced.GetMetadataFor(await session.LoadAsync <User>(id)); Assert.Equal("HasRevisions, HasCounters", metadata.GetString("@flags")); var revisionsMetadata = await session.Advanced.Revisions.GetMetadataForAsync(id); Assert.Equal(13, revisionsMetadata.Count); // +1 revision added when importing var dic = await session.CountersFor(id).GetAllAsync(); Assert.Equal(3, dic.Count); Assert.Equal(int.MaxValue, dic["Downloads"]); Assert.Equal(long.MaxValue, dic["ShouldBePositiveValueAfterSmuggler"]); Assert.Equal(500, dic["LittleCounter"]); } } // no DatabaseRecord, no RevisionDocuments, no Counters using (var store3 = GetDocumentStore()) { var importOptions = new DatabaseSmugglerImportOptions(); importOptions.OperateOnTypes -= DatabaseItemType.DatabaseRecord; importOptions.OperateOnTypes -= DatabaseItemType.RevisionDocuments; importOptions.OperateOnTypes -= DatabaseItemType.Counters; var operation = await store3.Smuggler.ImportAsync(importOptions, file); await operation.WaitForCompletionAsync(TimeSpan.FromMinutes(1)); using (var session = store3.OpenAsyncSession()) { var metadata = session.Advanced.GetMetadataFor(await session.LoadAsync <User>(id)); Assert.False(metadata.ContainsKey("@flags")); Assert.False(metadata.ContainsKey("@counters")); var revisionsMetadata = await session.Advanced.Revisions.GetMetadataForAsync(id); Assert.Equal(0, revisionsMetadata.Count); // +1 revision added when importing var dic = await session.CountersFor(id).GetAllAsync(); Assert.Equal(0, dic.Count); } } // if doc has counters AND revisions => they must be kept. using (var store4 = GetDocumentStore()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store4.Database); using (var session = store4.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Egor" }, id); { session.CountersFor(id).Increment("ShouldBeKeptAfterSmugglerImport", 322); } await session.SaveChangesAsync(); } var importOptions = new DatabaseSmugglerImportOptions(); importOptions.OperateOnTypes -= DatabaseItemType.DatabaseRecord; importOptions.OperateOnTypes -= DatabaseItemType.RevisionDocuments; importOptions.OperateOnTypes -= DatabaseItemType.Counters; var operation = await store4.Smuggler.ImportAsync(importOptions, file); await operation.WaitForCompletionAsync(TimeSpan.FromMinutes(1)); using (var session = store4.OpenAsyncSession()) { var user = await session.LoadAsync <User>(id); Assert.Equal("Egor 9", user.Name); // check if document changed. var metadata = session.Advanced.GetMetadataFor(user); Assert.Equal("HasRevisions, HasCounters", metadata.GetString("@flags")); var revisionsMetadata = await session.Advanced.Revisions.GetMetadataForAsync(id); Assert.Equal(3, revisionsMetadata.Count); // +1 revision added when importing var dic = await session.CountersFor(id).GetAllAsync(); Assert.Equal(1, dic.Count); Assert.Equal(322, dic["ShouldBeKeptAfterSmugglerImport"]); } } }
public async Task Can_handle_delete_revision_of_doc_that_changed_collection() { using (var store = GetDocumentStore()) { // setup revision with PurgeOnDelete = false var index = await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database, new RevisionsConfiguration { Default = new RevisionsCollectionConfiguration() }); var documentDatabase = await Server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database); await documentDatabase.RachisLogIndexNotifications.WaitForIndexNotification(index, Server.ServerStore.Engine.OperationTimeout); // store a document "users/1" under 'Users' collection // and create some revisions for it using (var session = store.OpenAsyncSession()) { var user = new User { Name = "Aviv " }; await session.StoreAsync(user, "users/1"); await session.SaveChangesAsync(); } for (int i = 0; i < 9; i++) { using (var session = store.OpenAsyncSession()) { var user = await session.LoadAsync <User>("users/1"); user.Name += i; await session.StoreAsync(user); await session.SaveChangesAsync(); } } using (var session = store.OpenAsyncSession()) { var revisions = await session.Advanced.Revisions.GetForAsync <User>("users/1"); Assert.Equal(10, revisions.Count); } // delete document "users/1" using (var session = store.OpenSession()) { session.Delete("users/1"); session.SaveChanges(); } using (var session = store.OpenAsyncSession()) { var revisions = await session.Advanced.Revisions.GetForAsync <User>("users/1"); Assert.Equal(11, revisions.Count); } // store a new document with the same id - "users/1" // but under 'Companies' collection using (var session = store.OpenAsyncSession()) { var company = new Company { Name = "HR " }; await session.StoreAsync(company, "users/1"); await session.SaveChangesAsync(); } using (var session = store.OpenAsyncSession()) { var revisions = await session.Advanced.Revisions.GetForAsync <object>("users/1"); Assert.Equal(12, revisions.Count); } // enable PurgeOnDelete var configuration = new RevisionsConfiguration { Default = new RevisionsCollectionConfiguration { PurgeOnDelete = true } }; index = await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database, configuration); await documentDatabase.RachisLogIndexNotifications.WaitForIndexNotification(index, Server.ServerStore.Engine.OperationTimeout); // make sure we don't have a tombstone for "users/1" await documentDatabase.TombstoneCleaner.ExecuteCleanup(); // delete document "users/1" using (var session = store.OpenSession()) { session.Delete("users/1"); session.SaveChanges(); } // all the revisions for "users/1" should be deleted - // the old ones ('Users' collection) as well as the new ones ('Companies' collection) using (var session = store.OpenAsyncSession()) { var revisions = await session.Advanced.Revisions.GetForAsync <object>("users/1"); Assert.Empty(revisions); } } }
public async Task ShouldPreserveTombstoneFlagsAfterRestore() { using (var store = GetDocumentStore()) { var configuration = new RevisionsConfiguration { Default = new RevisionsCollectionConfiguration { Disabled = false, MinimumRevisionsToKeep = 10 }, Collections = new Dictionary <string, RevisionsCollectionConfiguration> { ["Users"] = new RevisionsCollectionConfiguration { Disabled = false, MinimumRevisionsToKeep = 5 } } }; await RevisionsHelper.SetupRevisions(store, Server.ServerStore, configuration); using (var session = store.OpenSession()) { session.Store(new User() { Name = "Arek" }, "users/arek"); session.SaveChanges(); } using (var session = store.OpenSession()) { session.Delete("users/arek"); // this will create tombstone with HasRevisions flags session.SaveChanges(); } var backupPath = NewDataPath(suffix: "BackupFolder"); var config = Backup.CreateBackupConfiguration(backupPath); var backupTaskId = await Backup.UpdateConfigAndRunBackupAsync(Server, config, store); // restore var backupDirectory = Directory.GetDirectories(backupPath).First(); var databaseName = GetDatabaseName() + "restore"; var files = Directory.GetFiles(backupDirectory) .Where(BackupUtils.IsBackupFile) .OrderBackups() .ToArray(); RestoreBackupConfiguration config2 = new RestoreBackupConfiguration() { BackupLocation = backupDirectory, DatabaseName = databaseName, LastFileNameToRestore = files.Last() }; RestoreBackupOperation restoreOperation = new RestoreBackupOperation(config2); await store.Maintenance.Server.Send(restoreOperation) .WaitForCompletionAsync(TimeSpan.FromSeconds(30)); using (var storeOfRestoredDb = GetDocumentStore(new Options() { CreateDatabase = false, ModifyDatabaseName = s => databaseName })) { var db = await GetDatabase(storeOfRestoredDb.Database); using (db.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext ctx)) using (ctx.OpenReadTransaction()) { var tombstones = db.DocumentsStorage.GetTombstonesFrom(ctx, 0, 0, int.MaxValue).ToList(); Assert.Equal(1, tombstones.Count); Assert.Equal(DocumentFlags.HasRevisions, tombstones[0].Flags); } } } }
public async Task CanExportAndImportAttachmentsAndRevisionAttachments() { var file = Path.GetTempFileName(); try { using (var store1 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store1" })) { await SetDatabaseId(store1, new Guid("00000000-48c4-421e-9466-000000000000")); await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database, false, 4); AttachmentsRevisions.CreateDocumentWithAttachments(store1); using (var bigStream = new MemoryStream(Enumerable.Range(1, 999 * 1024).Select(x => (byte)x).ToArray())) store1.Operations.Send(new PutAttachmentOperation("users/1", "big-file", bigStream, "image/png")); /*var result = */ await store1.Smuggler.ExportAsync(new DatabaseSmugglerExportOptions(), file); // TODO: RavenDB-6936 store.Smuggler.Export and Import method should return the SmugglerResult var stats = await store1.Admin.SendAsync(new GetStatisticsOperation()); Assert.Equal(1, stats.CountOfDocuments); Assert.Equal(4, stats.CountOfRevisionDocuments); Assert.Equal(14, stats.CountOfAttachments); Assert.Equal(4, stats.CountOfUniqueAttachments); } using (var store2 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store2" })) { var dbId = new Guid("00000000-48c4-421e-9466-000000000000"); await SetDatabaseId(store2, dbId); await RevisionsHelper.SetupRevisions(Server.ServerStore, store2.Database); for (var i = 0; i < 2; i++) // Make sure that we can import attachments twice and it will overwrite { await store2.Smuggler.ImportAsync(new DatabaseSmugglerImportOptions(), file); var stats = await store2.Admin.SendAsync(new GetStatisticsOperation()); Assert.Equal(1, stats.CountOfDocuments); Assert.Equal(5, stats.CountOfRevisionDocuments); Assert.Equal(14, stats.CountOfAttachments); Assert.Equal(4, stats.CountOfUniqueAttachments); using (var session = store2.OpenSession()) { var readBuffer = new byte[1024 * 1024]; using (var attachmentStream = new MemoryStream(readBuffer)) using (var attachment = session.Advanced.GetAttachment("users/1", "big-file")) { attachment.Stream.CopyTo(attachmentStream); Assert.Contains("A:" + (2 + 20 * i), attachment.Details.ChangeVector); Assert.Equal("big-file", attachment.Details.Name); Assert.Equal("zKHiLyLNRBZti9DYbzuqZ/EDWAFMgOXB+SwKvjPAINk=", attachment.Details.Hash); Assert.Equal(999 * 1024, attachmentStream.Position); Assert.Equal(Enumerable.Range(1, 999 * 1024).Select(x => (byte)x), readBuffer.Take((int)attachmentStream.Position)); } } } } } finally { File.Delete(file); } }
public async Task ExportEmptyStream() { var file = Path.GetTempFileName(); try { var dbId2 = new Guid("99999999-48c4-421e-9466-999999999999"); var dbId = new Guid("00000000-48c4-421e-9466-000000000000"); using (var store1 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store1" })) { await SetDatabaseId(store1, dbId); await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database, false, 4); using (var session = store1.OpenSession()) { session.Store(new User { Name = "Fitzchak" }, "users/1"); session.SaveChanges(); } using (var emptyStream = new MemoryStream(new byte[0])) { var result = store1.Operations.Send(new PutAttachmentOperation("users/1", "empty-file", emptyStream, "image/png")); Assert.Equal("A:3", result.ChangeVector.Substring(0, 3)); Assert.Equal("empty-file", result.Name); Assert.Equal("users/1", result.DocumentId); Assert.Equal("image/png", result.ContentType); Assert.Equal("DldRwCblQ7Loqy6wYJnaodHl30d3j3eH+qtFzfEv46g=", result.Hash); Assert.Equal(0, result.Size); } await store1.Smuggler.ExportAsync(new DatabaseSmugglerExportOptions(), file); var stats = await store1.Admin.SendAsync(new GetStatisticsOperation()); Assert.Equal(1, stats.CountOfDocuments); Assert.Equal(2, stats.CountOfRevisionDocuments); Assert.Equal(2, stats.CountOfAttachments); Assert.Equal(1, stats.CountOfUniqueAttachments); } using (var store2 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store2" })) { await SetDatabaseId(store2, dbId2); await RevisionsHelper.SetupRevisions(Server.ServerStore, store2.Database); await store2.Smuggler.ImportAsync(new DatabaseSmugglerImportOptions(), file); var stats = await store2.Admin.SendAsync(new GetStatisticsOperation()); Assert.Equal(1, stats.CountOfDocuments); Assert.Equal(3, stats.CountOfRevisionDocuments); Assert.Equal(2, stats.CountOfAttachments); Assert.Equal(1, stats.CountOfUniqueAttachments); using (var session = store2.OpenSession()) { var readBuffer = new byte[1024 * 1024]; using (var attachmentStream = new MemoryStream(readBuffer)) using (var attachment = session.Advanced.GetAttachment("users/1", "empty-file")) { attachment.Stream.CopyTo(attachmentStream); Assert.Contains("A:1", attachment.Details.ChangeVector); Assert.Equal("empty-file", attachment.Details.Name); Assert.Equal(0, attachment.Details.Size); Assert.Equal("DldRwCblQ7Loqy6wYJnaodHl30d3j3eH+qtFzfEv46g=", attachment.Details.Hash); Assert.Equal(0, attachmentStream.Position); Assert.Equal(new byte[0], readBuffer.Take((int)attachmentStream.Position)); } } } } finally { File.Delete(file); } }
public async Task RevertResolvedConflictByLocalToDeleted() { // deleted was at 8:50 // conflict at 9:10 // resolved at 9:15 // will revert to 9:00 using (var store1 = GetDocumentStore()) using (var store2 = GetDocumentStore()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database); DateTime last = default; using (var session = store2.OpenAsyncSession()) { var company = new Person { Name = "Name2" }; await session.StoreAsync(company, "foo/bar"); await session.SaveChangesAsync(); await session.StoreAsync(new Company(), "marker"); await session.SaveChangesAsync(); } using (var session = store1.OpenAsyncSession()) { var company = new Person { Name = "Name1" }; await session.StoreAsync(company, "foo/bar"); await session.SaveChangesAsync(); session.Delete("foo/bar"); await session.SaveChangesAsync(); var companiesRevisions = await session.Advanced.Revisions.GetForAsync <Person>("foo/bar"); Assert.Equal(2, companiesRevisions.Count); last = DateTime.UtcNow; } await SetupReplicationAsync(store2, store1); WaitForDocument(store1, "marker"); using (var session = store1.OpenAsyncSession()) { var companiesRevisions = await session.Advanced.Revisions.GetForAsync <Person>("foo/bar"); Assert.Equal(4, companiesRevisions.Count); } var db = await GetDocumentDatabaseInstanceFor(store1); RevertResult result; using (var token = new OperationCancelToken(db.Configuration.Databases.OperationTimeout.AsTimeSpan, db.DatabaseShutdown)) { result = (RevertResult)await db.DocumentsStorage.RevisionsStorage.RevertRevisions(last, TimeSpan.FromMinutes(60), onProgress : null, token : token); } Assert.Equal(5, result.ScannedRevisions); Assert.Equal(2, result.ScannedDocuments); Assert.Equal(1, result.RevertedDocuments); using (var session = store1.OpenAsyncSession()) { var persons = await session.Advanced.Revisions.GetForAsync <Person>("foo/bar"); Assert.Equal(5, persons.Count); Assert.Equal(null, persons[0].Name); var metadata = session.Advanced.GetMetadataFor(persons[0]); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.DeleteRevision | DocumentFlags.Reverted).ToString(), metadata.GetString(Constants.Documents.Metadata.Flags)); Assert.Equal(null, persons[1].Name); metadata = session.Advanced.GetMetadataFor(persons[1]); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.DeleteRevision | DocumentFlags.Resolved).ToString(), metadata.GetString(Constants.Documents.Metadata.Flags)); Assert.Equal(null, persons[2].Name); metadata = session.Advanced.GetMetadataFor(persons[2]); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.DeleteRevision | DocumentFlags.Conflicted).ToString(), metadata.GetString(Constants.Documents.Metadata.Flags)); Assert.Equal("Name2", persons[3].Name); metadata = session.Advanced.GetMetadataFor(persons[3]); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.Revision | DocumentFlags.FromReplication | DocumentFlags.Conflicted).ToString(), metadata.GetString(Constants.Documents.Metadata.Flags)); Assert.Equal("Name1", persons[4].Name); metadata = session.Advanced.GetMetadataFor(persons[4]); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.Revision).ToString(), metadata.GetString(Constants.Documents.Metadata.Flags)); } } }
public async Task ClusterTransactionRequestWithRevisions() { var leader = await CreateRaftClusterAndGetLeader(5, shouldRunInMemory : false, leaderIndex : 0); using (var leaderStore = GetDocumentStore(new Options { DeleteDatabaseOnDispose = false, Server = leader, ReplicationFactor = 5, ModifyDocumentStore = (store) => store.Conventions.DisableTopologyUpdates = true })) { var user1 = new User() { Name = "Karmel" }; var user3 = new User() { Name = "Indych" }; var index = await RevisionsHelper.SetupRevisions(leader.ServerStore, leaderStore.Database, configuration => configuration.Collections["Users"].PurgeOnDelete = false); await WaitForRaftIndexToBeAppliedInCluster(index, TimeSpan.FromSeconds(15)); // bring our SUT node down, but we still have a cluster and can execute cluster transaction. var server = Servers[1]; var url = server.WebUrl; var dataDir = Servers[1].Configuration.Core.DataDirectory.FullPath.Split('/').Last(); await DisposeServerAndWaitForFinishOfDisposalAsync(server); using (var session = leaderStore.OpenAsyncSession(new SessionOptions { TransactionMode = TransactionMode.ClusterWide })) { Assert.Equal(1, session.Advanced.RequestExecutor.TopologyNodes.Count); Assert.Equal(leader.WebUrl, session.Advanced.RequestExecutor.Url); session.Advanced.ClusterTransaction.CreateCompareExchangeValue("usernames/ayende", user1); await session.StoreAsync(user3, "foo/bar"); await session.SaveChangesAsync(); var user = (await session.Advanced.ClusterTransaction.GetCompareExchangeValueAsync <User>("usernames/ayende")).Value; Assert.Equal(user1.Name, user.Name); user = await session.LoadAsync <User>("foo/bar"); Assert.Equal(user3.Name, user.Name); var list = await session.Advanced.Revisions.GetForAsync <User>(user.Id); Assert.Equal(1, list.Count); var changeVector = session.Advanced.GetChangeVectorFor(user); Assert.NotNull(await session.Advanced.Revisions.GetAsync <User>(changeVector)); } // bring more nodes down, so only one node is left var dataDir2 = Servers[2].Configuration.Core.DataDirectory.FullPath.Split('/').Last(); var url2 = Servers[2].WebUrl; var task1 = DisposeServerAndWaitForFinishOfDisposalAsync(Servers[2]); var task2 = DisposeServerAndWaitForFinishOfDisposalAsync(Servers[3]); var task3 = DisposeServerAndWaitForFinishOfDisposalAsync(Servers[4]); await Task.WhenAll(task1, task2, task3); using (var session = leaderStore.OpenAsyncSession()) { Assert.Equal(leader.WebUrl, session.Advanced.RequestExecutor.Url); await session.StoreAsync(user1, "foo/bar"); await session.SaveChangesAsync(); var list = await session.Advanced.Revisions.GetForAsync <User>(user1.Id); Assert.Equal(2, list.Count); } long lastRaftIndex; using (leader.ServerStore.Engine.ContextPool.AllocateOperationContext(out TransactionOperationContext ctx)) using (ctx.OpenReadTransaction()) { lastRaftIndex = leader.ServerStore.Engine.GetLastCommitIndex(ctx); } // revive the SUT node var revived = Servers[1] = GetNewServer(new Dictionary <string, string> { [RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = url, [RavenConfiguration.GetKey(x => x.Cluster.ElectionTimeout)] = "400" }, runInMemory: false, deletePrevious: false, partialPath: dataDir); using (var revivedStore = new DocumentStore() { Urls = new[] { revived.WebUrl }, Database = leaderStore.Database, Conventions = new DocumentConventions { DisableTopologyUpdates = true } }.Initialize()) { // let the document with the revision to replicate Assert.True(WaitForDocument(revivedStore, "foo/bar")); using (var session = revivedStore.OpenAsyncSession()) { var user = await session.LoadAsync <User>("foo/bar"); var changeVector = session.Advanced.GetChangeVectorFor(user); Assert.NotNull(await session.Advanced.Revisions.GetAsync <User>(changeVector)); var list = await session.Advanced.Revisions.GetForAsync <User>("foo/bar"); Assert.Equal(2, list.Count); // revive another node so we should have a functional cluster now Servers[2] = GetNewServer(new Dictionary <string, string> { [RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = url2, [RavenConfiguration.GetKey(x => x.Cluster.ElectionTimeout)] = "400" }, runInMemory: false, deletePrevious: false, partialPath: dataDir2); // wait for the log to apply on the SUT node using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15))) { await leader.ServerStore.Engine.WaitForLeaveState(RachisState.Candidate, cts.Token); } var database = await Servers[1].ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(leaderStore.Database); await database.RachisLogIndexNotifications.WaitForIndexNotification(lastRaftIndex, TimeSpan.FromSeconds(15)); list = await session.Advanced.Revisions.GetForAsync <User>("foo/bar"); Assert.Equal(2, list.Count); } } } }
public async Task CanGetTimeSeriesSnapshotInRevisions() { using (var store = GetDocumentStore()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database); using (var session = store.OpenAsyncSession()) { // revision 1 await session.StoreAsync(new Company(), "companies/1-A"); await session.SaveChangesAsync(); } var baseline = DateTime.Today; using (var session = store.OpenAsyncSession()) { var company = await session.LoadAsync <Company>("companies/1-A"); // revision 2 company.Name = "HR"; await session.SaveChangesAsync(); var tsf = session.TimeSeriesFor(company, "temperature"); // revision 3 tsf.Append(baseline.AddMinutes(10), 17.5); tsf.Append(baseline.AddMinutes(20), 17.4); await session.SaveChangesAsync(); // no revision for this one tsf.Append(baseline.AddMinutes(30), new[] { 17.2d }); await session.SaveChangesAsync(); } using (var session = store.OpenAsyncSession()) { var companiesRevisions = await session.Advanced.Revisions.GetForAsync <Company>("companies/1-A"); Assert.Equal(3, companiesRevisions.Count); var metadatas = companiesRevisions.Select(c => session.Advanced.GetMetadataFor(c)).ToList(); Assert.Equal("HR", companiesRevisions[0].Name); var tsRevisions = (IMetadataDictionary)metadatas[0][Constants.Documents.Metadata.RevisionTimeSeries]; Assert.Equal(1, tsRevisions.Count); var tsStats = (IMetadataDictionary)tsRevisions["temperature"]; Assert.Equal(2L, tsStats["Count"]); Assert.Equal(baseline.AddMinutes(10), DateTime.Parse((string)tsStats["Start"]), RavenTestHelper.DateTimeComparer.Instance); Assert.Equal(baseline.AddMinutes(20), DateTime.Parse((string)tsStats["End"]), RavenTestHelper.DateTimeComparer.Instance); Assert.Equal("HR", companiesRevisions[1].Name); Assert.False(metadatas[1].TryGetValue(Constants.Documents.Metadata.RevisionTimeSeries, out _)); Assert.Null(companiesRevisions[2].Name); Assert.False(metadatas[1].TryGetValue(Constants.Documents.Metadata.RevisionTimeSeries, out _)); } using (var session = store.OpenAsyncSession()) { var company = await session.LoadAsync <Company>("companies/1-A"); // revision 4 company.Name = "Hibernating Rhinos"; await session.SaveChangesAsync(); // revision 5 var tsf = session.TimeSeriesFor(company, "temperature"); tsf.Append(baseline.AddMonths(6), new[] { 27.6d }); // will create a new segment tsf = session.TimeSeriesFor(company, "heartrate"); tsf.Append(baseline.AddHours(1), new[] { 92.8d }); tsf.Append(baseline.AddHours(2), new[] { 89d }); await session.SaveChangesAsync(); } using (var session = store.OpenAsyncSession()) { var companiesRevisions = await session.Advanced.Revisions.GetForAsync <Company>("companies/1-A"); Assert.Equal(5, companiesRevisions.Count); var metadatas = companiesRevisions.Select(c => session.Advanced.GetMetadataFor(c)).ToList(); Assert.Equal("Hibernating Rhinos", companiesRevisions[0].Name); var tsRevisions = (IMetadataDictionary)metadatas[0][Constants.Documents.Metadata.RevisionTimeSeries]; Assert.Equal(2, tsRevisions.Count); var tsStats = (IMetadataDictionary)tsRevisions["temperature"]; Assert.Equal(4L, tsStats["Count"]); Assert.Equal(baseline.AddMinutes(10), DateTime.Parse((string)tsStats["Start"]), RavenTestHelper.DateTimeComparer.Instance); Assert.Equal(baseline.AddMonths(6), DateTime.Parse((string)tsStats["End"]), RavenTestHelper.DateTimeComparer.Instance); tsStats = (IMetadataDictionary)tsRevisions["heartrate"]; Assert.Equal(2L, tsStats["Count"]); Assert.Equal(baseline.AddHours(1), DateTime.Parse((string)tsStats["Start"]), RavenTestHelper.DateTimeComparer.Instance); Assert.Equal(baseline.AddHours(2), DateTime.Parse((string)tsStats["End"]), RavenTestHelper.DateTimeComparer.Instance); Assert.Equal("Hibernating Rhinos", companiesRevisions[1].Name); tsRevisions = (IMetadataDictionary)metadatas[1][Constants.Documents.Metadata.RevisionTimeSeries]; Assert.Equal(1, tsRevisions.Count); tsStats = (IMetadataDictionary)tsRevisions["temperature"]; Assert.Equal(3L, tsStats["Count"]); Assert.Equal(baseline.AddMinutes(10), DateTime.Parse((string)tsStats["Start"]), RavenTestHelper.DateTimeComparer.Instance); Assert.Equal(baseline.AddMinutes(30), DateTime.Parse((string)tsStats["End"]), RavenTestHelper.DateTimeComparer.Instance); Assert.Equal("HR", companiesRevisions[2].Name); tsRevisions = (IMetadataDictionary)metadatas[2][Constants.Documents.Metadata.RevisionTimeSeries]; Assert.Equal(1, tsRevisions.Count); tsStats = (IMetadataDictionary)tsRevisions["temperature"]; Assert.Equal(2L, tsStats["Count"]); Assert.Equal(baseline.AddMinutes(10), DateTime.Parse((string)tsStats["Start"]), RavenTestHelper.DateTimeComparer.Instance); Assert.Equal(baseline.AddMinutes(20), DateTime.Parse((string)tsStats["End"]), RavenTestHelper.DateTimeComparer.Instance); Assert.Equal("HR", companiesRevisions[3].Name); Assert.False(metadatas[3].TryGetValue(Constants.Documents.Metadata.RevisionCounters, out _)); Assert.Null(companiesRevisions[4].Name); Assert.False(metadatas[4].TryGetValue(Constants.Documents.Metadata.RevisionCounters, out _)); } } }
public async Task CanIncrementNumberOfRequests() { using (var store = GetDocumentStore()) { const string id = "users/Hibernating"; await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database); using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Hibernating", }, id); byte[] byteArray = Encoding.UTF8.GetBytes("Rhinos"); await session.SaveChangesAsync(); session.Advanced.Attachments.Store(id, "invoice.pdf", new MemoryStream(byteArray), "application/pdf"); await session.SaveChangesAsync(); } string changeVector; using (var session = store.OpenAsyncSession()) { var metadatas = await session.Advanced.Revisions.GetMetadataForAsync(id); Assert.Equal(2, metadatas.Count); changeVector = metadatas.First().GetString(Constants.Documents.Metadata.ChangeVector); } using (var session = store.OpenAsyncSession()) { var revision1 = await session.Advanced.Revisions.GetAsync <User>(id, DateTime.UtcNow); var revision2 = await session.Advanced.Revisions.GetAsync <User>(changeVector : changeVector); var revision3 = await session.Advanced.Revisions.GetAsync <User>(new[] { changeVector }); Assert.NotNull(revision1); Assert.NotNull(revision2); Assert.NotNull(revision3); using (var attachment = await session.Advanced.Attachments.GetAsync(id, "invoice.pdf")) { Assert.NotNull(attachment); } Assert.Equal(4, session.Advanced.NumberOfRequests); } using (var session = store.OpenSession()) { var revision1 = session.Advanced.Revisions.Get <User>(id, DateTime.UtcNow); var revision2 = session.Advanced.Revisions.Get <User>(changeVector: changeVector); var revision3 = session.Advanced.Revisions.Get <User>(new[] { changeVector }); Assert.NotNull(revision1); Assert.NotNull(revision2); Assert.NotNull(revision3); Assert.Equal(3, session.Advanced.NumberOfRequests); } } }
public async Task RealSupportForTransactionMarkerAcrossMultiUpdates() { using (var store1 = GetDocumentStore()) using (var store2 = GetDocumentStore()) { var database1 = await GetDocumentDatabaseInstanceFor(store1); database1.Configuration.Replication.MaxItemsCount = 1; database1.ReplicationLoader.DebugWaitAndRunReplicationOnce = new ManualResetEventSlim(); await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database, configuration => { configuration.Collections["Users"].PurgeOnDelete = false; }); await RevisionsHelper.SetupRevisions(Server.ServerStore, store2.Database, configuration => { configuration.Collections["Users"].PurgeOnDelete = false; }); using (var session = store1.OpenAsyncSession()) { await session.StoreAsync(new User { Id = "users/oren", Name = "Oren", Balance = 10 }); await session.StoreAsync(new User { Id = "users/fitzchak", Name = "Fitzchak", Balance = 10 }); await session.StoreAsync(new User { Id = "users/michael", Name = "Michael", Balance = 10 }); await session.SaveChangesAsync(); } using (var session = store1.OpenAsyncSession()) { var fitzchak = await session.LoadAsync <User>("users/fitzchak"); var michael = await session.LoadAsync <User>("users/michael"); michael.Balance -= 10; fitzchak.Balance += 10; await session.SaveChangesAsync(); } using (var session = store1.OpenAsyncSession()) { var fitzchak = await session.LoadAsync <User>("users/fitzchak"); var oren = await session.LoadAsync <User>("users/oren"); fitzchak.Balance -= 5; oren.Balance += 5; await session.SaveChangesAsync(); } using (var session = store1.OpenAsyncSession()) { var michael = await session.LoadAsync <User>("users/michael"); session.Delete(michael); var fitzchak = await session.LoadAsync <User>("users/fitzchak"); var oren = await session.LoadAsync <User>("users/oren"); fitzchak.Balance -= 5; oren.Balance += 5; await session.SaveChangesAsync(); } using (var session = store1.OpenAsyncSession()) { await session.StoreAsync(new User { Id = "users/michael", Name = "Michael", Balance = 10 }); var oren = await session.LoadAsync <User>("users/oren"); oren.Balance -= 10; await session.SaveChangesAsync(); } await SetupReplicationAsync(store1, store2); database1.ReplicationLoader.DebugWaitAndRunReplicationOnce.Set(); using (var session = store2.OpenAsyncSession()) { Assert.True(WaitForDocument <User>(store2, "users/michael", u => u.Balance == 10)); var fitzchak = await session.LoadAsync <User>("users/fitzchak"); var oren = await session.LoadAsync <User>("users/oren"); Assert.Equal(10, fitzchak.Balance); Assert.Equal(10, oren.Balance); } database1.ReplicationLoader.DebugWaitAndRunReplicationOnce.Set(); using (var session = store2.OpenAsyncSession()) { Assert.True(WaitForDocument <User>(store2, "users/michael", u => u.Balance == 0)); var fitzchak = await session.LoadAsync <User>("users/fitzchak"); var oren = await session.LoadAsync <User>("users/oren"); Assert.Equal(20, fitzchak.Balance); Assert.Equal(10, oren.Balance); } database1.ReplicationLoader.DebugWaitAndRunReplicationOnce.Set(); using (var session = store2.OpenAsyncSession()) { Assert.True(WaitForDocument <User>(store2, "users/oren", u => u.Balance == 15)); var fitzchak = await session.LoadAsync <User>("users/fitzchak"); var michael = await session.LoadAsync <User>("users/michael"); Assert.Equal(15, fitzchak.Balance); Assert.Equal(0, michael.Balance); } database1.ReplicationLoader.DebugWaitAndRunReplicationOnce.Set(); using (var session = store2.OpenAsyncSession()) { Assert.True(WaitForDocument <User>(store2, "users/oren", u => u.Balance == 20)); var fitzchak = await session.LoadAsync <User>("users/fitzchak"); var michael = await session.LoadAsync <User>("users/michael"); Assert.Equal(10, fitzchak.Balance); Assert.Null(michael); } database1.ReplicationLoader.DebugWaitAndRunReplicationOnce.Set(); using (var session = store2.OpenAsyncSession()) { Assert.True(WaitForDocument <User>(store2, "users/oren", u => u.Balance == 10)); var fitzchak = await session.LoadAsync <User>("users/fitzchak"); var michael = await session.LoadAsync <User>("users/michael"); Assert.Equal(10, fitzchak.Balance); Assert.Equal(10, michael.Balance); } } }
public async Task ExportEmptyStream() { var file = GetTempFileName(); try { var dbId2 = new Guid("99999999-48c4-421e-9466-999999999999"); var dbId = new Guid("00000000-48c4-421e-9466-000000000000"); const string documentId = "users/1"; const string attachmentName = "empty-file"; using (var store1 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store1" })) { await SetDatabaseId(store1, dbId); await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database, configuration => { configuration.Collections["Users"].PurgeOnDelete = false; configuration.Collections["Users"].MinimumRevisionsToKeep = 4; }); using (var session = store1.OpenSession()) { session.Store(new User { Name = "Fitzchak" }, documentId); session.SaveChanges(); } using (var emptyStream = new MemoryStream(new byte[0])) { var result = store1.Operations.Send(new PutAttachmentOperation(documentId, attachmentName, emptyStream, "image/png")); Assert.Equal("A:3", result.ChangeVector.Substring(0, 3)); Assert.Equal(attachmentName, result.Name); Assert.Equal(documentId, result.DocumentId); Assert.Equal("image/png", result.ContentType); Assert.Equal("DldRwCblQ7Loqy6wYJnaodHl30d3j3eH+qtFzfEv46g=", result.Hash); Assert.Equal(0, result.Size); } var exportOperation = await store1.Smuggler.ExportAsync(new DatabaseSmugglerExportOptions(), file); var exportResult = (SmugglerResult)exportOperation.WaitForCompletion(); Assert.Equal(1, exportResult.Documents.ReadCount); Assert.Equal(2, exportResult.RevisionDocuments.ReadCount); Assert.Equal(1, exportResult.Documents.Attachments.ReadCount); Assert.Equal(1, exportResult.RevisionDocuments.Attachments.ReadCount); var stats = await store1.Maintenance.SendAsync(new GetStatisticsOperation()); Assert.Equal(1, stats.CountOfDocuments); Assert.Equal(2, stats.CountOfRevisionDocuments); Assert.Equal(2, stats.CountOfAttachments); Assert.Equal(1, stats.CountOfUniqueAttachments); } using (var store2 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store2" })) { await SetDatabaseId(store2, dbId2); var importOperation = await store2.Smuggler.ImportAsync(new DatabaseSmugglerImportOptions(), file); var importResult = (SmugglerResult)importOperation.WaitForCompletion(); Assert.Equal(1, importResult.Documents.ReadCount); Assert.Equal(2, importResult.RevisionDocuments.ReadCount); Assert.Equal(1, importResult.Documents.Attachments.ReadCount); Assert.Equal(1, importResult.RevisionDocuments.Attachments.ReadCount); var stats = await store2.Maintenance.SendAsync(new GetStatisticsOperation()); Assert.Equal(1, stats.CountOfDocuments); Assert.Equal(3, stats.CountOfRevisionDocuments); Assert.Equal(2 + 1, stats.CountOfAttachments); // the imported document will create 1 additional revision with 1 attachment Assert.Equal(1, stats.CountOfUniqueAttachments); using (var session = store2.OpenSession()) { var readBuffer = new byte[1024 * 1024]; using (var attachmentStream = new MemoryStream(readBuffer)) using (var attachment = session.Advanced.Attachments.Get(documentId, attachmentName)) { attachment.Stream.CopyTo(attachmentStream); Assert.Equal(new byte[0], readBuffer.Take((int)attachmentStream.Position)); var attachmentCv = attachment.Details.ChangeVector; Assert.Contains("A:1", attachmentCv); Assert.Equal(attachmentName, attachment.Details.Name); Assert.Equal(0, attachment.Details.Size); Assert.Equal("DldRwCblQ7Loqy6wYJnaodHl30d3j3eH+qtFzfEv46g=", attachment.Details.Hash); Assert.Equal(0, attachmentStream.Position); var user = session.Load <User>(documentId); var documentCv = session.Advanced.GetChangeVectorFor(user); var conflictStatus = ChangeVectorUtils.GetConflictStatus(documentCv, attachmentCv); // document CV is larger than attachment CV Assert.Equal(ConflictStatus.Update, conflictStatus); } } } } finally { File.Delete(file); } }
public async Task RevertRevisionWithDeleteAttachments() { var names = new[] { "background-photo.jpg", "fileNAME_#$1^%_בעברית.txt", "profile.png", }; DateTime last = default; using (var store = GetDocumentStore()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database); using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Name1", LastName = "LastName1" }, "users/1"); await using (var backgroundStream = new MemoryStream(new byte[] { 10, 20, 30, 40, 50 })) { session.Advanced.Attachments.Store("users/1", names[0], backgroundStream, "ImGgE/jPeG"); await session.SaveChangesAsync(); } last = DateTime.UtcNow; } using (var session = store.OpenAsyncSession()) { session.Advanced.Attachments.Delete("users/1", names[0]); await session.SaveChangesAsync(); } var db = await Databases.GetDocumentDatabaseInstanceFor(store); RevertResult result; using (var token = new OperationCancelToken(db.Configuration.Databases.OperationTimeout.AsTimeSpan, db.DatabaseShutdown, CancellationToken.None)) { result = (RevertResult)await db.DocumentsStorage.RevisionsStorage.RevertRevisions(last, TimeSpan.FromMinutes(60), onProgress : null, token : token); } WaitForUserToContinueTheTest(store); using (var session = store.OpenAsyncSession()) { var rev = await session.Advanced.Revisions.GetForAsync <User>("users/1"); Assert.Equal(4, rev.Count); var cv = session.Advanced.GetChangeVectorFor(rev[0]); Assert.Equal("Name1", rev[0].Name); Assert.NotNull(await session.Advanced.Attachments.GetRevisionAsync("users/1", names[0], cv)); var user = await session.LoadAsync <User>("users/1"); var metadata = session.Advanced.GetMetadataFor(user); var flags = metadata.GetString(Constants.Documents.Metadata.Flags); Assert.Contains(DocumentFlags.HasAttachments.ToString(), flags); var att = session.Advanced.Attachments.GetNames(user); Assert.Equal(1, att.Length); Assert.Equal(names[0], att[0].Name); } } }
public async Task limitRevisionDeletionWithEnforceConfiguration() { using (var store = GetDocumentStore()) { var configuration = new RevisionsConfiguration { Default = new RevisionsCollectionConfiguration { Disabled = false, MinimumRevisionsToKeep = 1000000 }, }; await RevisionsHelper.SetupRevisions(store, Server.ServerStore, configuration); using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Toli" }, "users/1"); await session.StoreAsync(new User { Name = "Mitzi" }, "users/2"); await session.StoreAsync(new Order { Employee = "Daniel" }, "orders/1"); await session.SaveChangesAsync(); } for (int i = 0; i < 500; i++) { using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Toli" + i }, "users/1"); await session.StoreAsync(new User { Name = "Mitzi" + i }, "users/2"); await session.StoreAsync(new Order { Employee = "Daniel" + i }, "orders/1"); await session.SaveChangesAsync(); } } for (int i = 500; i < 630; i++) { using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Mitzi" + i }, "users/2"); await session.SaveChangesAsync(); } } using (var session = store.OpenAsyncSession()) { var revisionCount = session.Advanced.Revisions.GetForAsync <User>("users/1", 0, 1000).Result.Count; Assert.Equal(501, revisionCount); revisionCount = session.Advanced.Revisions.GetForAsync <User>("users/2", 0, 1000).Result.Count; Assert.Equal(631, revisionCount); revisionCount = session.Advanced.Revisions.GetForAsync <User>("orders/1", 0, 1000).Result.Count; Assert.Equal(501, revisionCount); } configuration = new RevisionsConfiguration { Default = new RevisionsCollectionConfiguration { Disabled = false, MinimumRevisionsToKeep = 10, MaximumRevisionsToDeleteUponDocumentUpdate = 100 }, Collections = new Dictionary <string, RevisionsCollectionConfiguration> { ["Orders"] = new RevisionsCollectionConfiguration { Disabled = false, MinimumRevisionsToKeep = 33, MaximumRevisionsToDeleteUponDocumentUpdate = 35 } } }; await RevisionsHelper.SetupRevisions(store, Server.ServerStore, configuration); var db = await Databases.GetDocumentDatabaseInstanceFor(store); using (var token = new OperationCancelToken(db.Configuration.Databases.OperationTimeout.AsTimeSpan, db.DatabaseShutdown, CancellationToken.None)) await db.DocumentsStorage.RevisionsStorage.EnforceConfiguration(_ => { }, token); WaitForUserToContinueTheTest(store); using (var session = store.OpenAsyncSession()) { var revisionCount = session.Advanced.Revisions.GetForAsync <User>("users/1", 0, 100).Result.Count; Assert.Equal(10, revisionCount); revisionCount = session.Advanced.Revisions.GetForAsync <User>("users/2", 0, 100).Result.Count; Assert.Equal(10, revisionCount); revisionCount = session.Advanced.Revisions.GetForAsync <Order>("orders/1", 0, 100).Result.Count; Assert.Equal(33, revisionCount); } } }
public async Task RemoveRevertFlagAfterNewInfo1() { using (var store = GetDocumentStore()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database); DateTime last = default; using (var session = store.OpenAsyncSession()) { var person = new Person { Name = "Name1" }; await session.StoreAsync(person, "foo/bar"); await session.SaveChangesAsync(); last = DateTime.UtcNow; } using (var session = store.OpenAsyncSession()) { var person = new Person { Name = "Name2" }; await session.StoreAsync(person, "foo/bar"); await session.SaveChangesAsync(); } var db = await Databases.GetDocumentDatabaseInstanceFor(store); RevertResult result; using (var token = new OperationCancelToken(db.Configuration.Databases.OperationTimeout.AsTimeSpan, db.DatabaseShutdown, CancellationToken.None)) { result = (RevertResult)await db.DocumentsStorage.RevisionsStorage.RevertRevisions(last, TimeSpan.FromMinutes(60), onProgress : null, token : token); } Assert.Equal(2, result.ScannedRevisions); Assert.Equal(1, result.ScannedDocuments); Assert.Equal(1, result.RevertedDocuments); using (var session = store.OpenAsyncSession()) { var persons = await session.Advanced.Revisions.GetForAsync <Person>("foo/bar"); Assert.Equal(3, persons.Count); Assert.Equal("Name1", persons[0].Name); var metadata = session.Advanced.GetMetadataFor(persons[0]); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.Revision | DocumentFlags.Reverted).ToString(), metadata.GetString(Constants.Documents.Metadata.Flags)); await using (var backgroundStream = new MemoryStream(new byte[] { 10, 20, 30, 40, 50 })) { session.Advanced.Attachments.Store("foo/bar", "background-photo.jpg", backgroundStream, "ImGgE/jPeG"); await session.SaveChangesAsync(); } } using (var session = store.OpenAsyncSession()) { var foo = await session.LoadAsync <Person>("foo/bar"); var metadata = session.Advanced.GetMetadataFor(foo); var flags = metadata.GetString(Constants.Documents.Metadata.Flags); Assert.False(flags.Contains(DocumentFlags.Reverted.ToString())); var persons = await session.Advanced.Revisions.GetForAsync <Person>("foo/bar"); Assert.Equal(4, persons.Count); Assert.Equal("Name1", persons[0].Name); metadata = session.Advanced.GetMetadataFor(persons[0]); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.Revision | DocumentFlags.HasAttachments).ToString(), metadata.GetString(Constants.Documents.Metadata.Flags)); } } }
public async Task CanExportAndImportWithRevisionDocumentsFromCollection() { var file = GetTempFileName(); try { using (var store1 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store1" })) { using (var session = store1.OpenAsyncSession()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database); await session.StoreAsync(new Person { Name = "Name1" }); await session.StoreAsync(new Person { Name = "Name2" }); await session.StoreAsync(new Company { Name = "Hibernating Rhinos " }); await session.SaveChangesAsync(); } for (int i = 0; i < 2; i++) { using (var session = store1.OpenAsyncSession()) { var company = await session.LoadAsync <Company>("companies/1-A"); var person = await session.LoadAsync <Person>("people/1-A"); company.Name += " update " + i; person.Name += " update " + i; await session.StoreAsync(company); await session.StoreAsync(person); await session.SaveChangesAsync(); } } var operation = await store1.Smuggler.ExportAsync(new DatabaseSmugglerExportOptions { Collections = new List <string>() { "Companies" } }, file); await operation.WaitForCompletionAsync(TimeSpan.FromMinutes(1)); var stats = await store1.Maintenance.SendAsync(new GetStatisticsOperation()); Assert.Equal(5, stats.CountOfDocuments); Assert.Equal(7, stats.CountOfRevisionDocuments); } using (var store2 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store2" })) { var operation = await store2.Smuggler.ImportAsync(new DatabaseSmugglerImportOptions() { SkipRevisionCreation = true }, file); await operation.WaitForCompletionAsync(TimeSpan.FromMinutes(1)); WaitForUserToContinueTheTest(store2); var stats = await store2.Maintenance.SendAsync(new GetStatisticsOperation()); Assert.Equal(1, stats.CountOfDocuments); Assert.Equal(3, stats.CountOfRevisionDocuments); } } finally { File.Delete(file); } }
public async Task RevertToTombstone() { using (var store = GetDocumentStore()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database); DateTime last = default; using (var session = store.OpenAsyncSession()) { var person = new Person { Name = "Name1" }; await session.StoreAsync(person, "foo/bar"); session.CountersFor("foo/bar").Increment("Downloads", 100); await session.SaveChangesAsync(); } using (var session = store.OpenAsyncSession()) { session.Delete("foo/bar"); await session.SaveChangesAsync(); last = DateTime.UtcNow; } using (var session = store.OpenAsyncSession()) { var person = new Person { Name = "Name2" }; await session.StoreAsync(person, "foo/bar"); await session.SaveChangesAsync(); } var db = await Databases.GetDocumentDatabaseInstanceFor(store); RevertResult result; using (var token = new OperationCancelToken(db.Configuration.Databases.OperationTimeout.AsTimeSpan, db.DatabaseShutdown, CancellationToken.None)) { result = (RevertResult)await db.DocumentsStorage.RevisionsStorage.RevertRevisions(last, TimeSpan.FromMinutes(60), onProgress : null, token : token); } Assert.Equal(4, result.ScannedRevisions); Assert.Equal(1, result.ScannedDocuments); Assert.Equal(1, result.RevertedDocuments); using (var session = store.OpenAsyncSession()) { var foo = await session.LoadAsync <User>("foo/bar"); Assert.Null(foo); var persons = await session.Advanced.Revisions.GetForAsync <Person>("foo/bar"); Assert.Equal(5, persons.Count); var metadata = session.Advanced.GetMetadataFor(persons[0]); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.DeleteRevision | DocumentFlags.Reverted).ToString(), metadata.GetString(Constants.Documents.Metadata.Flags)); } using (var session = store.OpenAsyncSession()) { var person = new Person { Name = "Name1" }; await session.StoreAsync(person, "foo/bar"); await session.SaveChangesAsync(); } using (var session = store.OpenAsyncSession()) { var foo = await session.LoadAsync <User>("foo/bar"); var metadata = session.Advanced.GetMetadataFor(foo); var flags = metadata.GetString(Constants.Documents.Metadata.Flags); Assert.DoesNotContain(DocumentFlags.HasCounters.ToString(), flags); } } }
public async Task ShouldAvoidCreatingNewRevisionsDuringImport() { var file = GetTempFileName(); try { using (var store1 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store1" })) { using (var session = store1.OpenAsyncSession()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database); await session.StoreAsync(new Person { Name = "Name1" }); await session.StoreAsync(new Person { Name = "Name2" }); await session.StoreAsync(new Company { Name = "Hibernating Rhinos " }); await session.SaveChangesAsync(); } for (int i = 0; i < 2; i++) { using (var session = store1.OpenAsyncSession()) { var company = await session.LoadAsync <Company>("companies/1-A"); var person = await session.LoadAsync <Person>("people/1-A"); company.Name += " update " + i; person.Name += " update " + i; await session.StoreAsync(company); await session.StoreAsync(person); await session.SaveChangesAsync(); } } using (var session = store1.OpenAsyncSession()) { var person = await session.LoadAsync <Person>("people/2-A"); Assert.NotNull(person); session.Delete(person); await session.SaveChangesAsync(); } var operation = await store1.Smuggler.ExportAsync(new DatabaseSmugglerExportOptions(), file); await operation.WaitForCompletionAsync(TimeSpan.FromMinutes(1)); var stats = await store1.Maintenance.SendAsync(new GetStatisticsOperation()); Assert.Equal(4, stats.CountOfDocuments); Assert.Equal(8, stats.CountOfRevisionDocuments); } using (var store2 = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_store2" })) { var operation = await store2.Smuggler.ImportAsync(new DatabaseSmugglerImportOptions { SkipRevisionCreation = true }, file); await operation.WaitForCompletionAsync(TimeSpan.FromMinutes(1)); var stats = await store2.Maintenance.SendAsync(new GetStatisticsOperation()); Assert.Equal(4, stats.CountOfDocuments); Assert.Equal(8, stats.CountOfRevisionDocuments); } } finally { File.Delete(file); } }
public async Task RevertRevisionWithTimeSeries() { DateTime last = default; using (var store = GetDocumentStore()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database); using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Name1", LastName = "LastName1" }, "users/1"); var ts = session.TimeSeriesFor("users/1", "Toli"); ts.Append(DateTime.Today.AddDays(-1), 10); await session.SaveChangesAsync(); last = DateTime.UtcNow; } using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Name2", LastName = "LastName1" }, "users/1"); var ts = session.TimeSeriesFor("users/1", "Mitzi"); ts.Append(DateTime.Today, 30); await session.SaveChangesAsync(); } var db = await Databases.GetDocumentDatabaseInstanceFor(store); RevertResult result; using (var token = new OperationCancelToken(db.Configuration.Databases.OperationTimeout.AsTimeSpan, db.DatabaseShutdown, CancellationToken.None)) { result = (RevertResult)await db.DocumentsStorage.RevisionsStorage.RevertRevisions(last, TimeSpan.FromMinutes(60), onProgress : null, token : token); } using (var session = store.OpenAsyncSession()) { var rev = await session.Advanced.Revisions.GetForAsync <User>("users/1"); Assert.Equal(5, rev.Count); Assert.Equal("Name1", rev[0].Name); var ts = await session.TimeSeriesFor(rev[0], "Toli").GetAsync(); Assert.Equal(1, ts.Length); Assert.Equal(10, ts[0].Value); ts = await session.TimeSeriesFor(rev[0], "Mitzi").GetAsync(); Assert.Equal(1, ts.Length); Assert.Equal(30, ts[0].Value); var user = await session.LoadAsync <User>("users/1"); Assert.Equal("Name1", user.Name); var metadata = session.Advanced.GetMetadataFor(user); Assert.True(metadata.Keys.Contains(Constants.Documents.Metadata.TimeSeries)); Assert.False(metadata.Keys.Contains(Constants.Documents.Metadata.RevisionTimeSeries)); var flags = metadata.GetString(Constants.Documents.Metadata.Flags); Assert.Contains(DocumentFlags.HasTimeSeries.ToString(), flags); ts = await session.TimeSeriesFor(user, "Toli").GetAsync(); Assert.Equal(1, ts.Length); Assert.Equal(10, ts[0].Value); ts = await session.TimeSeriesFor(user, "Mitzi").GetAsync(); Assert.Equal(1, ts.Length); Assert.Equal(30, ts[0].Value); } } }
public async Task ReplicateExpiredRevisions() { var revisionsAgeLimit = TimeSpan.FromSeconds(10); Action <RevisionsConfiguration> modifyConfiguration = configuration => configuration.Collections["Users"] = new RevisionsCollectionConfiguration { Disabled = false, MinimumRevisionAgeToKeep = revisionsAgeLimit }; using (var store1 = GetDocumentStore()) using (var store2 = GetDocumentStore()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database, modifyConfiguration); await RevisionsHelper.SetupRevisions(Server.ServerStore, store2.Database, modifyConfiguration); using (var session = store1.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Aviv" }, "users/1-A"); await session.SaveChangesAsync(); } for (int i = 2; i <= 10; i++) { using (var session = store1.OpenAsyncSession()) { var user = await session.LoadAsync <User>("users/1-A"); user.Name = "Aviv" + i; await session.SaveChangesAsync(); } } using (var session = store1.OpenAsyncSession()) { var revisions = await session.Advanced.Revisions.GetForAsync <User>("users/1-A"); Assert.Equal(10, revisions.Count); } // wait until revisions are expired await Task.Delay(revisionsAgeLimit); // setup replication await SetupReplicationAsync(store1, store2); WaitForMarker(store1, store2); // store1 should still have 10 revisions // store2 should have no revisions using (var session = store1.OpenAsyncSession()) { var revisions = await session.Advanced.Revisions.GetForAsync <User>("users/1-A"); Assert.Equal(10, revisions.Count); } using (var session = store2.OpenAsyncSession()) { var revisions = await session.Advanced.Revisions.GetForAsync <User>("users/1-A"); Assert.Equal(0, revisions.Count); } /* * // TODO : RavenDB-13359 * using (var session = store2.OpenAsyncSession()) * { * var doc = await session.LoadAsync<User>("users/1-A"); * var md = session.Advanced.GetMetadataFor(doc); * md.TryGetValue(Constants.Documents.Metadata.Flags, out var flags); * * Assert.DoesNotContain(nameof(DocumentFlags.HasRevisions), flags); * } */ // modify doc on store1 to create another revision using (var session = store1.OpenAsyncSession()) { var user = await session.LoadAsync <User>("users/1-A"); user.Name = "Grisha"; await session.SaveChangesAsync(); } WaitForMarker(store1, store2); // assert that both stores have just one revision using (var session = store1.OpenAsyncSession()) { var revisions = await session.Advanced.Revisions.GetForAsync <User>("users/1-A"); Assert.Equal(1, revisions.Count); Assert.Equal("Grisha", revisions[0].Name); } using (var session = store2.OpenAsyncSession()) { var revisions = await session.Advanced.Revisions.GetForAsync <User>("users/1-A"); Assert.Equal(1, revisions.Count); Assert.Equal("Grisha", revisions[0].Name); } } }
public async Task RemoveResolveFlagAfterRevert() { using (var store1 = GetDocumentStore()) using (var store2 = GetDocumentStore()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database); DateTime last = default; using (var session = store1.OpenAsyncSession()) { var person = new Person { Name = "Name1" }; await session.StoreAsync(person, "foo/bar"); await session.SaveChangesAsync(); } using (var session = store2.OpenAsyncSession()) { var person = new Person { Name = "Name2" }; await session.StoreAsync(person, "foo/bar"); await session.SaveChangesAsync(); await session.StoreAsync(new Person(), "marker"); await session.SaveChangesAsync(); } await SetupReplicationAsync(store2, store1); WaitForDocument(store1, "marker"); last = DateTime.UtcNow; using (var session = store1.OpenAsyncSession()) { var person = new Person { Name = "Name3" }; await session.StoreAsync(person, "foo/bar"); await session.SaveChangesAsync(); } var db = await Databases.GetDocumentDatabaseInstanceFor(store1); WaitForUserToContinueTheTest(store1); RevertResult result; using (var token = new OperationCancelToken(db.Configuration.Databases.OperationTimeout.AsTimeSpan, db.DatabaseShutdown, CancellationToken.None)) { result = (RevertResult)await db.DocumentsStorage.RevisionsStorage.RevertRevisions(last, TimeSpan.FromMinutes(60), onProgress : null, token : token); } Assert.Equal(5, result.ScannedRevisions); Assert.Equal(2, result.ScannedDocuments); Assert.Equal(1, result.RevertedDocuments); using (var session = store1.OpenAsyncSession()) { var foo = await session.LoadAsync <User>("foo/bar"); var metadata = session.Advanced.GetMetadataFor(foo); var flags = metadata.GetString(Constants.Documents.Metadata.Flags); Assert.Contains(DocumentFlags.Reverted.ToString(), flags); Assert.False(flags.Contains(DocumentFlags.Resolved.ToString())); var persons = await session.Advanced.Revisions.GetForAsync <Person>("foo/bar"); Assert.Equal(5, persons.Count); Assert.Equal("Name2", persons[0].Name); metadata = session.Advanced.GetMetadataFor(persons[0]); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.Revision | DocumentFlags.Reverted).ToString(), metadata.GetString(Constants.Documents.Metadata.Flags)); Assert.Equal("Name3", persons[1].Name); metadata = session.Advanced.GetMetadataFor(persons[1]); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.Revision).ToString(), metadata.GetString(Constants.Documents.Metadata.Flags)); Assert.Equal("Name2", persons[2].Name); metadata = session.Advanced.GetMetadataFor(persons[2]); Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.Revision | DocumentFlags.Resolved).ToString(), metadata.GetString(Constants.Documents.Metadata.Flags)); } } }
public async Task ReplicateRevisionTombstones() { var revisionsAgeLimit = TimeSpan.FromSeconds(10); Action <RevisionsConfiguration> modifyConfiguration = configuration => configuration.Collections["Users"] = new RevisionsCollectionConfiguration { Disabled = false, MinimumRevisionAgeToKeep = revisionsAgeLimit }; using (var store1 = GetDocumentStore()) using (var store2 = GetDocumentStore()) { //setup revisions on both stores and setup replication await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database, modifyConfiguration); await RevisionsHelper.SetupRevisions(Server.ServerStore, store2.Database, modifyConfiguration); await SetupReplicationAsync(store1, store2); // create some revisions on store1 using (var session = store1.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Aviv" }, "users/1-A"); await session.SaveChangesAsync(); } for (int i = 2; i <= 10; i++) { using (var session = store1.OpenAsyncSession()) { var user = await session.LoadAsync <User>("users/1-A"); user.Name = "Aviv" + i; await session.SaveChangesAsync(); } } // wait for replication WaitForMarker(store1, store2); // wait until revisions are expired await Task.Delay(revisionsAgeLimit); // modify document using (var session = store1.OpenAsyncSession()) { var user = await session.LoadAsync <User>("users/1-A"); user.Name = "Grisha"; await session.SaveChangesAsync(); } WaitForMarker(store1, store2); // expired revisions should be deleted // assert that both stores have 10 revision tombstones foreach (var store in new[] { store1, store2 }) { var documentDatabase = await GetDocumentDatabaseInstanceFor(store); using (documentDatabase.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext ctx)) using (ctx.OpenReadTransaction()) { var tombstones = documentDatabase.DocumentsStorage.GetTombstonesFrom(ctx, 0, 0, int.MaxValue).ToList(); Assert.Equal(10, tombstones.Count); foreach (var tombstone in tombstones) { Assert.Equal(Tombstone.TombstoneType.Revision, tombstone.Type); } } } } }
public async Task RevertRevisionOutsideTheWindow() { var company = new Company { Name = "Company Name" }; using (var store = GetDocumentStore()) { await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database); using (var session = store.OpenAsyncSession()) { await session.StoreAsync(company); await session.SaveChangesAsync(); } await Task.Delay(2000); using (var session = store.OpenAsyncSession()) { company.Name = "Hibernating Rhinos"; await session.StoreAsync(company); await session.SaveChangesAsync(); } await Task.Delay(5000); DateTime last = DateTime.UtcNow; last = last.AddSeconds(-3); using (var session = store.OpenAsyncSession()) { company.Name = "Hibernating Rhinos 2"; await session.StoreAsync(company); await session.SaveChangesAsync(); } await Task.Delay(2000); var db = await GetDocumentDatabaseInstanceFor(store); RevertResult result; using (var token = new OperationCancelToken(db.Configuration.Databases.OperationTimeout.AsTimeSpan, db.DatabaseShutdown)) { result = (RevertResult)await db.DocumentsStorage.RevisionsStorage.RevertRevisions(last, TimeSpan.FromSeconds(1), onProgress : null, token : token); } Assert.Equal(3, result.ScannedRevisions); Assert.Equal(1, result.ScannedDocuments); Assert.Equal(1, result.RevertedDocuments); using (var session = store.OpenAsyncSession()) { var companiesRevisions = await session.Advanced.Revisions.GetForAsync <Company>(company.Id); Assert.Equal(4, companiesRevisions.Count); Assert.Equal("Hibernating Rhinos", companiesRevisions[0].Name); Assert.Equal("Hibernating Rhinos 2", companiesRevisions[1].Name); Assert.Equal("Hibernating Rhinos", companiesRevisions[2].Name); Assert.Equal("Company Name", companiesRevisions[3].Name); } } }