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.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 { await store2.Smuggler.ImportAsync(new DatabaseSmugglerImportOptions(), file); var stats = await store2.Maintenance.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.Attachments.Get("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 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, 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 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 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.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); await RevisionsHelper.SetupRevisions(Server.ServerStore, store2.Database); await store2.Smuggler.ImportAsync(new DatabaseSmugglerImportOptions(), file); var stats = await store2.Maintenance.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.Attachments.Get("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 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, CancellationToken.None)) { 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); } } }
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, CancellationToken.None)) { 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 UpdateTheSameRevisionWhenGettingExistingRevision() { using (var storeA = GetDocumentStore(options: new Options { ModifyDatabaseRecord = record => { record.ConflictSolverConfig = new ConflictSolver { ResolveToLatest = false, ResolveByCollection = new Dictionary <string, ScriptResolver>() }; } })) using (var storeB = GetDocumentStore(options: new Options { ModifyDatabaseRecord = record => { record.ConflictSolverConfig = new ConflictSolver { ResolveToLatest = false, ResolveByCollection = new Dictionary <string, ScriptResolver>() }; } })) using (var storeC = GetDocumentStore(options: new Options { ModifyDatabaseRecord = record => { record.ConflictSolverConfig = new ConflictSolver { ResolveToLatest = false, ResolveByCollection = new Dictionary <string, ScriptResolver>() }; } })) { await RevisionsHelper.SetupRevisions(Server.ServerStore, storeA.Database); await RevisionsHelper.SetupRevisions(Server.ServerStore, storeB.Database); await RevisionsHelper.SetupRevisions(Server.ServerStore, storeC.Database); await SetupReplicationAsync(storeA, storeB); using (var session = storeA.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Fitzchak" }, "users/1"); await session.SaveChangesAsync(); } using (var session = storeA.OpenSession()) { Assert.Equal(1, WaitForValue(() => session.Advanced.Revisions.GetMetadataFor("users/1").Count, 1)); } using (var session = storeB.OpenSession()) { Assert.Equal(1, WaitForValue(() => session.Advanced.Revisions.GetMetadataFor("users/1").Count, 1)); } Assert.True(WaitForDocument(storeB, "users/1")); await SetupReplicationAsync(storeA, storeC); await SetupReplicationAsync(storeB, storeC); using (var session = storeA.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Marker" }, "marker"); await session.SaveChangesAsync(); } Assert.True(WaitForDocument(storeC, "marker")); using (var session = storeB.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Marker" }, "marker"); await session.SaveChangesAsync(); } Assert.True(WaitForDocument(storeB, "marker")); using (var session = storeC.OpenSession()) { Assert.Equal(1, WaitForValue(() => session.Advanced.Revisions.GetMetadataFor("users/1").Count, 1)); } } }
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.DocumentTombstoneCleaner.Subscribe(this); database2.DocumentTombstoneCleaner.Subscribe(this); await RevisionsHelper.SetupRevisions(Server.ServerStore, store1.Database, false); await RevisionsHelper.SetupRevisions(Server.ServerStore, store2.Database, 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 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 CanDeleteFromDifferentCollections() { using (var store = GetDocumentStore()) { var storage = await Server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database); await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database); using (var session = store.OpenSession()) using (var ms = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 })) { session.Store(new User { Name = "Karmel" }, "foo/bar"); session.Advanced.Attachments.Store("foo/bar", "dummy", ms); session.SaveChanges(); } using (var session = store.OpenSession()) { session.Delete("foo/bar"); session.SaveChanges(); } using (var session = store.OpenSession()) { session.Store(new Company { Name = "Karmel" }, "foo/bar"); session.SaveChanges(); } using (var session = store.OpenSession()) { session.Delete("foo/bar"); session.SaveChanges(); } using (var session = store.OpenSession()) using (var ms = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 })) { session.Store(new User { Name = "Karmel" }, "foo/bar"); session.Advanced.Attachments.Store("foo/bar", "dummy", ms); session.SaveChanges(); } using (var session = store.OpenSession()) { session.Delete("foo/bar"); session.SaveChanges(); } using (var session = store.OpenSession()) { session.Store(new Employee { FirstName = "Karmel" }, "foo/bar"); session.SaveChanges(); } using (var session = store.OpenSession()) { session.Delete("foo/bar"); session.SaveChanges(); } await storage.TombstoneCleaner.ExecuteCleanup(); using (storage.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext ctx)) using (ctx.OpenReadTransaction()) { Assert.Equal(0, storage.DocumentsStorage.GetNumberOfTombstones(ctx)); } } }
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 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 CanGetRevisionsByDate() { using (var store = GetDocumentStore()) { var id = "users/1"; await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database, modifyConfiguration : conf => conf.Default.MinimumRevisionsToKeep = 1000); using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new Company { Name = "Fitzchak" }, id); await session.SaveChangesAsync(); } var db = await GetDocumentDatabaseInstanceFor(store); var fst = db.Time.GetUtcNow(); db.Time.UtcDateTime = () => DateTime.UtcNow.AddMinutes(5); for (int i = 0; i < 3; i++) { using (var session = store.OpenAsyncSession()) { var user = await session.LoadAsync <Company>(id); user.Name = "Fitzchak " + i; await session.SaveChangesAsync(); } } var snd = db.Time.GetUtcNow(); db.Time.UtcDateTime = () => DateTime.UtcNow.AddMinutes(15); for (int i = 0; i < 3; i++) { using (var session = store.OpenAsyncSession()) { var user = await session.LoadAsync <Company>(id); user.Name = "Oren " + i; await session.SaveChangesAsync(); } } using (var session = store.OpenAsyncSession()) { var rev1 = await session.Advanced.Revisions.GetAsync <Company>(id, fst); Assert.Equal("Fitzchak", rev1.Name); var rev2 = await session.Advanced.Revisions.GetAsync <Company>(id, snd); Assert.Equal("Fitzchak 2", rev2.Name); var rev3 = await session.Advanced.Revisions.GetAsync <Company>(id, db.Time.GetUtcNow()); Assert.Equal("Oren 2", rev3.Name); } } }
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 count = await WaitForValueAsync((async() => { var list = await session.Advanced.Revisions.GetForAsync <User>("foo/bar"); return(list.Count); }), 2); Assert.Equal(2, 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)); count = await WaitForValueAsync((async() => { var list = await session.Advanced.Revisions.GetForAsync <User>("foo/bar"); return(list.Count); }), 2); Assert.Equal(2, count); } } } }
public async Task CreateRevisionsAndReplicateThemAll() { var company = new Company { Name = "Company Name" }; var company2 = new Company { Name = "Company Name2" }; var company3 = new Company { Name = "Company Name3" }; var company4 = new Company { Name = "Company Name4" }; using (var master = GetDocumentStore(options: new Options { ModifyDatabaseRecord = record => { record.ConflictSolverConfig = new ConflictSolver { ResolveToLatest = false, ResolveByCollection = new Dictionary <string, ScriptResolver>() }; } })) using (var slave = GetDocumentStore(options: new Options { ModifyDatabaseRecord = record => { record.ConflictSolverConfig = new ConflictSolver { ResolveToLatest = false, ResolveByCollection = new Dictionary <string, ScriptResolver>() }; } })) { await RevisionsHelper.SetupRevisions(Server.ServerStore, master.Database); await RevisionsHelper.SetupRevisions(Server.ServerStore, slave.Database); using (var session = master.OpenAsyncSession()) { await session.StoreAsync(company, "foo/bar"); await session.SaveChangesAsync(); } using (var session = master.OpenAsyncSession()) { await session.StoreAsync(company2, "foo/bar"); await session.SaveChangesAsync(); } using (var session = master.OpenAsyncSession()) { await session.StoreAsync(company3, "foo/bar"); await session.SaveChangesAsync(); } using (var session = master.OpenAsyncSession()) { await session.StoreAsync(company4, "foo/bar"); await session.SaveChangesAsync(); } await SetupReplicationAsync(master, slave); using (var session = slave.OpenSession()) { Assert.True(WaitForDocument(slave, "foo/bar")); Assert.Equal(4, WaitForValue(() => session.Advanced.Revisions.GetMetadataFor("foo/bar").Count, 4)); } } }
public async Task WillNotCreateMoreRevisionsAfterImport() { var file = Path.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(); } await store1.Smuggler.ExportAsync(new DatabaseSmugglerExportOptions(), file); var stats = await store1.Admin.SendAsync(new GetStatisticsOperation()); Assert.Equal(4, stats.CountOfDocuments); Assert.Equal(8, stats.CountOfRevisionDocuments); await store1.Smuggler.ImportAsync(new DatabaseSmugglerImportOptions(), file); stats = await store1.Admin.SendAsync(new GetStatisticsOperation()); Assert.Equal(4, stats.CountOfDocuments); Assert.Equal(8, stats.CountOfRevisionDocuments); } } finally { File.Delete(file); } }
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, 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 CanImportRevisions1(string file) { using (var stream = GetType().Assembly.GetManifestResourceStream(file)) using (var store = GetDocumentStore()) { Assert.NotNull(stream); await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database); var operation = await store.Smuggler.ImportAsync(new DatabaseSmugglerImportOptions(), stream); await operation.WaitForCompletionAsync(TimeSpan.FromMinutes(1)); var stats = await store.Maintenance.SendAsync(new GetStatisticsOperation()); Assert.Equal(2, stats.CountOfDocuments); Assert.Equal(5, stats.CountOfRevisionDocuments); Assert.Equal(10, stats.LastDocEtag); var collectionStats = await store.Maintenance.SendAsync(new GetCollectionStatisticsOperation()); Assert.Equal(2, collectionStats.CountOfDocuments); Assert.Equal(2, collectionStats.Collections.Count); Assert.Equal(1, collectionStats.Collections["@empty"]); Assert.Equal(1, collectionStats.Collections["Users"]); using (var session = store.OpenSession()) { var user = session.Load <Order>("users/1"); Assert.NotNull(user); var metadata = session.Advanced.GetMetadataFor(user); Assert.Equal(5, metadata.Count); Assert.Equal("Users", metadata.GetString(Constants.Documents.Metadata.Collection)); Assert.Equal($"{DocumentFlags.HasRevisions}", metadata.GetString(Constants.Documents.Metadata.Flags)); Assert.Equal("users/1", metadata.GetString(Constants.Documents.Metadata.Id)); Assert.NotEqual(DateTime.MinValue.ToString(DefaultFormat.DateTimeOffsetFormatsToWrite), metadata.GetString(Constants.Documents.Metadata.LastModified)); var changeVector = session.Advanced.GetChangeVectorFor(user); Assert.StartsWith("RV:", changeVector); var revisions = session.Advanced.Revisions.GetFor <User>("users/1"); Assert.Equal(4, revisions.Count); for (int i = 0; i <= 3; i++) { metadata = session.Advanced.GetMetadataFor(revisions[i]); Assert.Equal(5, metadata.Count); Assert.Equal("Users", metadata.GetString(Constants.Documents.Metadata.Collection)); Assert.Equal($"{DocumentFlags.HasRevisions}, {DocumentFlags.Revision}", metadata.GetString(Constants.Documents.Metadata.Flags)); Assert.Equal("users/1", metadata.GetString(Constants.Documents.Metadata.Id)); Assert.NotEqual(DateTime.MinValue.ToString(DefaultFormat.DateTimeOffsetFormatsToWrite), metadata.GetString(Constants.Documents.Metadata.LastModified)); var revisionChangeVector = metadata.GetString(Constants.Documents.Metadata.ChangeVector); Assert.Equal($"RV:{4 - i}-AAAAAQAAAQAAAAAAAAAAAw", revisionChangeVector); if (i == 0) { Assert.Equal(changeVector, revisionChangeVector); } } } } }
public async Task RevisionsAreReplicatedBackWithTombstone(bool configureVersioning) { using (var storeA = GetDocumentStore()) using (var storeB = GetDocumentStore()) { var expectedRevisionsCount = 3; if (configureVersioning) { await RevisionsHelper.SetupRevisions(Server.ServerStore, storeA.Database); await RevisionsHelper.SetupRevisions(Server.ServerStore, storeB.Database); expectedRevisionsCount = 4; } var company = new Company { Name = "Name" }; var company2 = new Company { Name = "Name2" }; using (var session = storeA.OpenAsyncSession()) { await session.StoreAsync(new Company(), "keep-conflicted-revision-insert-order"); await session.SaveChangesAsync(); await session.StoreAsync(company, "foo/bar"); await session.SaveChangesAsync(); session.Delete("foo/bar"); await session.SaveChangesAsync(); } using (var session = storeB.OpenAsyncSession()) { await session.StoreAsync(company2, "foo/bar"); await session.SaveChangesAsync(); } await SetupReplicationAsync(storeA, storeB); using (var session = storeB.OpenSession()) { Assert.Equal(expectedRevisionsCount, WaitForValue(() => session.Advanced.Revisions.GetMetadataFor("foo/bar").Count, expectedRevisionsCount)); var metadata = session.Advanced.Revisions.GetMetadataFor("foo/bar"); var flags = metadata[0]["@flags"]; Assert.Equal((DocumentFlags.Revision | DocumentFlags.HasRevisions | DocumentFlags.Resolved).ToString(), flags); flags = metadata[1]["@flags"]; Assert.Equal((DocumentFlags.Revision | DocumentFlags.HasRevisions | DocumentFlags.Conflicted).ToString(), flags); flags = metadata[2]["@flags"]; Assert.Equal((DocumentFlags.DeleteRevision | DocumentFlags.HasRevisions | DocumentFlags.FromReplication | DocumentFlags.Conflicted).ToString(), flags); if (configureVersioning) { flags = metadata[3]["@flags"]; Assert.Equal((DocumentFlags.Revision | DocumentFlags.HasRevisions | DocumentFlags.FromReplication).ToString(), flags); } } await SetupReplicationAsync(storeB, storeA); using (var session = storeA.OpenSession()) { Assert.Equal(expectedRevisionsCount, WaitForValue(() => session.Advanced.Revisions.GetMetadataFor("foo/bar").Count, expectedRevisionsCount)); var metadata = session.Advanced.Revisions.GetMetadataFor("foo/bar"); var flags = metadata[0]["@flags"]; Assert.Equal((DocumentFlags.Revision | DocumentFlags.HasRevisions | DocumentFlags.FromReplication | DocumentFlags.Resolved).ToString(), flags); flags = metadata[1]["@flags"]; Assert.Equal((DocumentFlags.Revision | DocumentFlags.HasRevisions | DocumentFlags.FromReplication | DocumentFlags.Conflicted).ToString(), flags); flags = metadata[2]["@flags"]; if (configureVersioning) { Assert.Equal((DocumentFlags.DeleteRevision | DocumentFlags.HasRevisions | DocumentFlags.Conflicted).ToString(), flags); flags = metadata[3]["@flags"]; Assert.Equal((DocumentFlags.Revision | DocumentFlags.HasRevisions).ToString(), flags); } else { Assert.Equal((DocumentFlags.DeleteRevision | DocumentFlags.HasRevisions | DocumentFlags.FromReplication | DocumentFlags.Conflicted).ToString(), flags); } } } }