package org.reveno.atp.acceptance.tests; import org.junit.Assert; import org.junit.Test; import org.reveno.atp.acceptance.api.events.AccountCreatedEvent; import org.reveno.atp.acceptance.api.events.OrderCreatedEvent; import org.reveno.atp.acceptance.views.AccountView; import org.reveno.atp.acceptance.views.OrderView; import org.reveno.atp.api.RepositorySnapshotter; import org.reveno.atp.api.Reveno; import org.reveno.atp.api.domain.RepositoryData; import org.reveno.atp.core.RevenoConfiguration; import org.reveno.atp.core.api.channel.Buffer; import org.reveno.atp.core.api.serialization.RepositoryDataSerializer; import org.reveno.atp.core.channel.NettyBasedBuffer; import org.reveno.atp.core.serialization.DefaultJavaSerializer; import org.reveno.atp.core.serialization.ProtostuffSerializer; import org.reveno.atp.core.snapshots.DefaultSnapshotter; import org.reveno.atp.core.storage.FileSystemStorage; import org.reveno.atp.utils.MeasureUtils; import org.reveno.atp.utils.RevenoUtils; import java.io.File; import java.util.Arrays; import java.util.function.Consumer; public class SnapshottingTest extends RevenoBaseTest { protected static final int SNAP_INTERVAL = 30; @Test public void testShutdownSnapshotting() throws Exception { Reveno reveno = createEngine(); reveno.config().snapshotting().atShutdown(true); reveno.startup(); generateAndSendCommands(reveno, 10_000); Assert.assertEquals(10_000, reveno.query().select(AccountView.class).size()); Assert.assertEquals(10_000, reveno.query().select(OrderView.class).size()); reveno.shutdown(); Arrays.asList(tempDir.listFiles((dir, name) -> !(name.startsWith("snp")))).forEach(File::delete); reveno = createEngine(); reveno.startup(); Assert.assertEquals(10_000, reveno.query().select(AccountView.class).size()); Assert.assertEquals(10_000, reveno.query().select(OrderView.class).size()); reveno.shutdown(); } @Test public void testSnapshottingEveryJavaSerializer() throws Exception { testSnapshottingEvery(new DefaultJavaSerializer()); } @Test public void testSnapshottingEveryProtostuffSerializer() throws Exception { testSnapshottingEvery(new ProtostuffSerializer()); } @Test public void testSnapshottingEveryInMemorySerializer() throws Exception { testSnapshottingEvery(null, new InMemorySnapshotter()); } @Test public void testSnapshottingIntervalJavaSerializer() throws Exception { testSnapshottingInterval(new DefaultJavaSerializer()); } @Test public void testSnapshottingIntervalProtostuffSerializer() throws Exception { testSnapshottingInterval(new ProtostuffSerializer()); } public void testSnapshottingEvery(RepositoryDataSerializer repoSerializer) throws Exception { testSnapshottingEvery(repoSerializer, null); } public void testSnapshottingInterval(RepositoryDataSerializer repoSerializer) throws Exception { testSnapshottingInterval(repoSerializer, null); } public void testSnapshottingInterval(RepositoryDataSerializer repoSerializer, RepositorySnapshotter snapshotter) throws Exception { Consumer<TestRevenoEngine> consumer = r -> { r.domain().resetSnapshotters(); if (snapshotter == null) { r.domain().snapshotWith(new DefaultSnapshotter(new FileSystemStorage(tempDir, new RevenoConfiguration.RevenoJournalingConfiguration()), repoSerializer)) .andRestoreWithIt(); } else { r.domain().snapshotWith(snapshotter).andRestoreWithIt(); } }; Reveno reveno = createEngine(consumer); try { reveno.config().snapshotting().interval(SNAP_INTERVAL); Assert.assertEquals(0, tempDir.listFiles((dir, name) -> name.startsWith("snp")).length); reveno.startup(); generateAndSendCommands(reveno, 1_005); if (snapshotter == null) { RevenoUtils.waitFor(() -> tempDir.listFiles((dir, name) -> name.startsWith("snp")).length > 0, MeasureUtils.sec(1)); } reveno.shutdown(); reveno = createEngine(consumer); reveno.startup(); Assert.assertEquals(1_005, reveno.query().select(AccountView.class).size()); Assert.assertEquals(1_005, reveno.query().select(OrderView.class).size()); } finally { reveno.shutdown(); } } public void testSnapshottingEvery(RepositoryDataSerializer repoSerializer, RepositorySnapshotter snapshotter) throws Exception { Consumer<TestRevenoEngine> consumer = r -> { r.domain().resetSnapshotters(); if (snapshotter == null) { r.domain().snapshotWith(new DefaultSnapshotter(new FileSystemStorage(tempDir, new RevenoConfiguration.RevenoJournalingConfiguration()), repoSerializer)) .andRestoreWithIt(); } else { r.domain().snapshotWith(snapshotter).andRestoreWithIt(); } }; Reveno reveno = createEngine(consumer); try { reveno.config().snapshotting().atShutdown(false); reveno.config().snapshotting().every(1002); reveno.startup(); generateAndSendCommands(reveno, 10_005); Assert.assertEquals(10_005, reveno.query().select(AccountView.class).size()); Assert.assertEquals(10_005, reveno.query().select(OrderView.class).size()); reveno.shutdown(); if (snapshotter == null) { Assert.assertEquals(19, tempDir.listFiles((dir, name) -> name.startsWith("snp")).length); } reveno = createEngine(consumer); reveno.startup(); Assert.assertEquals(10_005, reveno.query().select(AccountView.class).size()); Assert.assertEquals(10_005, reveno.query().select(OrderView.class).size()); generateAndSendCommands(reveno, 3); reveno.shutdown(); if (snapshotter == null) { Arrays.asList(tempDir.listFiles((dir, name) -> name.startsWith("snp"))).forEach(File::delete); } else if (snapshotter instanceof InMemorySnapshotter) { ((InMemorySnapshotter) snapshotter).lastIdentifier = null; ((InMemorySnapshotter) snapshotter).lastJournalVersion = -1; } reveno = createEngine(consumer); Waiter accountCreatedEvent = listenFor(reveno, AccountCreatedEvent.class); Waiter orderCreatedEvent = listenFor(reveno, OrderCreatedEvent.class); reveno.startup(); Assert.assertEquals(10_008, reveno.query().select(AccountView.class).size()); Assert.assertEquals(10_008, reveno.query().select(OrderView.class).size()); Assert.assertFalse(accountCreatedEvent.isArrived(1)); Assert.assertFalse(orderCreatedEvent.isArrived(1)); } finally { reveno.shutdown(); } } public static class InMemorySnapshotter implements RepositorySnapshotter { @Override public SnapshotIdentifier lastSnapshot() { return lastIdentifier; } @Override public long lastJournalVersionSnapshotted() { return lastJournalVersion; } @Override public SnapshotIdentifier prepare() { buffer.clear(); return new DefaultSnapshotIdentifier(); } @Override public void snapshot(RepositoryData repo, SnapshotIdentifier identifier) { serializer.serialize(repo, buffer); } @Override public void commit(long lastJournalVersion, SnapshotIdentifier identifier) { this.lastJournalVersion = lastJournalVersion; lastIdentifier = identifier; } @Override public RepositoryData load() { buffer.setReaderPosition(0); return serializer.deserialize(buffer); } protected Buffer buffer = new NettyBasedBuffer(false); protected volatile SnapshotIdentifier lastIdentifier; protected volatile long lastJournalVersion; protected ProtostuffSerializer serializer = new ProtostuffSerializer(); private class DefaultSnapshotIdentifier implements SnapshotIdentifier { @Override public byte getType() { return 0x34; } @Override public long getTime() { return 0; } } } }