From 72b03fe96c91a27a2c065860cd4d3f6b458b6cbc Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Wed, 8 Nov 2006 00:27:19 +0000 Subject: Tests and fixes for corruption repair. --- .../com/amazon/carbonado/layout/TestLayout.java | 29 ++- .../carbonado/repo/replicated/TestRepair.java | 222 ++++++++++++++++++++- 2 files changed, 241 insertions(+), 10 deletions(-) diff --git a/src/test/java/com/amazon/carbonado/layout/TestLayout.java b/src/test/java/com/amazon/carbonado/layout/TestLayout.java index 9fb97b1..f1edef3 100644 --- a/src/test/java/com/amazon/carbonado/layout/TestLayout.java +++ b/src/test/java/com/amazon/carbonado/layout/TestLayout.java @@ -773,9 +773,17 @@ public class TestLayout extends TestCase { } } - private Class defineStorable(String name, - int extraPropCount, - TypeDesc propType) + /** + * Defines a new Storable with a variable number of extra properties. + * + * @param name name of class to generate + * @param extraPropCount number of properties named "propN" to add, where N + * is the zero-based property number + * @param propType type of each extra property + */ + public static Class defineStorable(String name, + int extraPropCount, + TypeDesc propType) { ClassInjector ci = ClassInjector.createExplicit(name, null); ClassFile cf = new ClassFile(ci.getClassName()); @@ -793,9 +801,16 @@ public class TestLayout extends TestCase { return ci.defineClass(cf); } - private Class defineStorable(String name, - TypeDesc propType, - TypeDesc propType2) + /** + * Defines a new Storable with two extra properties named "prop0" and "prop1". + * + * @param name name of class to generate + * @param propType type of property 0 + * @param propType2 type of property 1 + */ + public static Class defineStorable(String name, + TypeDesc propType, + TypeDesc propType2) { ClassInjector ci = ClassInjector.createExplicit(name, null); ClassFile cf = new ClassFile(ci.getClassName()); @@ -816,7 +831,7 @@ public class TestLayout extends TestCase { return ci.defineClass(cf); } - private void definePrimaryKey(ClassFile cf) { + private static void definePrimaryKey(ClassFile cf) { // Add primary key on inherited "id" property. // @PrimaryKey(value={"id"}) Annotation pk = cf.addRuntimeVisibleAnnotation(TypeDesc.forClass(PrimaryKey.class)); diff --git a/src/test/java/com/amazon/carbonado/repo/replicated/TestRepair.java b/src/test/java/com/amazon/carbonado/repo/replicated/TestRepair.java index d21fceb..4a58746 100644 --- a/src/test/java/com/amazon/carbonado/repo/replicated/TestRepair.java +++ b/src/test/java/com/amazon/carbonado/repo/replicated/TestRepair.java @@ -18,28 +18,47 @@ package com.amazon.carbonado.repo.replicated; +import java.lang.reflect.Method; +import java.util.Random; + import junit.framework.TestCase; import junit.framework.TestSuite; +import com.amazon.carbonado.CorruptEncodingException; +import com.amazon.carbonado.Cursor; import com.amazon.carbonado.OptimisticLockException; import com.amazon.carbonado.Repository; import com.amazon.carbonado.RepositoryBuilder; import com.amazon.carbonado.Storage; import com.amazon.carbonado.UniqueConstraintException; +import com.amazon.carbonado.capability.ResyncCapability; + +import com.amazon.carbonado.layout.StoredLayout; +import com.amazon.carbonado.layout.StoredLayoutProperty; + import com.amazon.carbonado.repo.replicated.ReplicatedRepository; import com.amazon.carbonado.repo.replicated.ReplicatedRepositoryBuilder; +import com.amazon.carbonado.repo.sleepycat.BDBRepositoryBuilder; + import com.amazon.carbonado.TestUtilities; import com.amazon.carbonado.stored.StorableTestBasic; +import com.amazon.carbonado.stored.StorableTestMinimal; import com.amazon.carbonado.stored.StorableVersioned; +import com.amazon.carbonado.layout.TestLayout; +import org.cojen.classfile.TypeDesc; + /** * * * @author Brian S O'Neill */ public class TestRepair extends TestCase { + private static final String REPLICA_NAME = "rr-replica"; + private static final String MASTER_NAME = "rr-master"; + public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } @@ -57,8 +76,8 @@ public class TestRepair extends TestCase { } protected void setUp() throws Exception { - RepositoryBuilder replica = TestUtilities.newTempRepositoryBuilder("rr-replica"); - RepositoryBuilder master = TestUtilities.newTempRepositoryBuilder("rr-master"); + RepositoryBuilder replica = TestUtilities.newTempRepositoryBuilder(REPLICA_NAME); + RepositoryBuilder master = TestUtilities.newTempRepositoryBuilder(MASTER_NAME); ReplicatedRepositoryBuilder builder = new ReplicatedRepositoryBuilder(); builder.setName("rr"); @@ -73,12 +92,62 @@ public class TestRepair extends TestCase { } protected void tearDown() throws Exception { - mReplicated.close(); + if (mReplicated != null) { + mReplicated.close(); + } + if (mReplica != null) { + mReplica.close(); + } + if (mMaster != null) { + mMaster.close(); + } mReplica = null; mMaster = null; mReplicated = null; } + /** + * @return repository locations + */ + private String[] reOpenPersistent(String[] locations) throws Exception { + tearDown(); + + String replicaLocation, masterLocation; + + if (locations != null) { + replicaLocation = locations[0]; + masterLocation = locations[1]; + } else { + replicaLocation = TestUtilities.makeTestDirectoryString(REPLICA_NAME); + masterLocation = TestUtilities.makeTestDirectoryString(MASTER_NAME); + } + + { + BDBRepositoryBuilder replica = new BDBRepositoryBuilder(); + replica.setName(REPLICA_NAME); + replica.setTransactionNoSync(true); + replica.setEnvironmentHome(replicaLocation); + + BDBRepositoryBuilder master = new BDBRepositoryBuilder(); + master.setName(MASTER_NAME); + master.setTransactionNoSync(true); + master.setEnvironmentHome(masterLocation); + + ReplicatedRepositoryBuilder builder = new ReplicatedRepositoryBuilder(); + builder.setName("rr"); + builder.setReplicaRepositoryBuilder(replica); + builder.setMasterRepositoryBuilder(master); + + ReplicatedRepository rr = (ReplicatedRepository) builder.build(); + + mReplica = rr.getReplicaRepository(); + mMaster = rr.getMasterRepository(); + mReplicated = rr; + } + + return new String[] {replicaLocation, masterLocation}; + } + public void testMissingEntry() throws Exception { // Insert an entry into master. { @@ -291,4 +360,151 @@ public class TestRepair extends TestCase { assertEquals("ciao", sv.getValue()); } } + + public void testCorruptEntry() throws Exception { + // Close and open repository again, this time on disk. We need to close + // and re-open the repository as part of the test. + String[] locations = reOpenPersistent(null); + + // Insert different versions of the same record... + + final String recordName = "test.TheTestRecord"; + + Class type0 = + TestLayout.defineStorable(recordName, 1, TypeDesc.INT); + + Class type1 = + TestLayout.defineStorable(recordName, 2, TypeDesc.INT); + + Storage storage0 = mReplicated.storageFor(type0); + Storage storage1 = mReplicated.storageFor(type1); + + final int seed = 5469232; + final int count = 20; + + { + Random rnd = new Random(seed); + + Method prop_0_of_0 = type0.getMethod("setProp0", int.class); + + Method prop_0_of_1 = type1.getMethod("setProp0", int.class); + Method prop_1_of_1 = type1.getMethod("setProp1", int.class); + + boolean anyType0 = false; + boolean anyType1 = false; + + for (int i=0; i cursor = storage0.query().fetch(); + while (cursor.hasNext()) { + StorableTestMinimal stm = cursor.next(); + //System.out.println(stm); + } + } + + // Verify records can be read via storage1, which may have zero for the new property. + { + Cursor cursor = storage1.query().fetch(); + while (cursor.hasNext()) { + StorableTestMinimal stm = cursor.next(); + //System.out.println(stm); + } + } + + // Close and open only replica repository and create corruption by + // deleting all information regarding generation 1 in the replica. + locations = reOpenPersistent(locations); + + storage0 = mReplicated.storageFor(type0); + + // Replace all the masters with only type 0 records. + { + mMaster.storageFor(type0).query().deleteAll(); + Method prop_0_of_0 = type0.getMethod("setProp0", int.class); + + for (int i=0; i cursor = storage0.query().fetch(); + while (cursor.hasNext()) { + StorableTestMinimal stm = cursor.next(); + } + fail(); + } catch (CorruptEncodingException e) { + } + + // Resync to repair. + ResyncCapability cap = mReplicated.getCapability(ResyncCapability.class); + cap.resync(type0, 1.0, null); + + // Verify records can be read out now. + { + Cursor cursor = storage0.query().fetch(); + int actual = 0; + while (cursor.hasNext()) { + StorableTestMinimal stm = cursor.next(); + //System.out.println(stm); + actual++; + } + assertEquals(count, actual); + } + + storage1 = mReplicated.storageFor(type1); + + { + Cursor cursor = storage1.query().fetch(); + int actual = 0; + while (cursor.hasNext()) { + StorableTestMinimal stm = cursor.next(); + //System.out.println(stm); + actual++; + } + assertEquals(count, actual); + } + } } -- cgit v1.2.3