diff options
Diffstat (limited to 'src/test/java')
| -rw-r--r-- | src/test/java/com/amazon/carbonado/layout/TestLayout.java | 29 | ||||
| -rw-r--r-- | src/test/java/com/amazon/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<? extends StorableTestMinimal> 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<? extends StorableTestMinimal> 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<? extends StorableTestMinimal> 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<? extends StorableTestMinimal> 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<? extends StorableTestMinimal> type0 = 
 +            TestLayout.defineStorable(recordName, 1, TypeDesc.INT);
 +
 +        Class<? extends StorableTestMinimal> type1 = 
 +            TestLayout.defineStorable(recordName, 2, TypeDesc.INT);
 +
 +        Storage<? extends StorableTestMinimal> storage0 = mReplicated.storageFor(type0);
 +        Storage<? extends StorableTestMinimal> 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<count; i++) {
 +                StorableTestMinimal stm;
 +
 +                if (rnd.nextBoolean()) {
 +                    stm = storage0.prepare();
 +                    prop_0_of_0.invoke(stm, i + 1000);
 +                    anyType0 = true;
 +                } else {
 +                    stm = storage1.prepare();
 +                    prop_0_of_1.invoke(stm, i + 2000);
 +                    prop_1_of_1.invoke(stm, i + 4000);
 +                    anyType1 = true;
 +                }
 +
 +                stm.setId(i);
 +                stm.insert();
 +            }
 +
 +            // Assert mix of types.
 +            assertTrue(anyType0);
 +            assertTrue(anyType1);
 +        }
 +
 +        // Verify records can be read via storage0, which will ignore the new property.
 +        {
 +            Cursor<? extends StorableTestMinimal> 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<? extends StorableTestMinimal> 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<count; i++) {
 +                StorableTestMinimal stm = mMaster.storageFor(type0).prepare();
 +                prop_0_of_0.invoke(stm, i + 1000);
 +                stm.setId(i);
 +                stm.insert();
 +            }
 +        }
 +
 +        // Delete all knowledge of type 1.
 +        StoredLayout layout = mReplicated.storageFor(StoredLayout.class).prepare();
 +        layout.setStorableTypeName(recordName);
 +        layout.setGeneration(1);
 +        layout.load();
 +        layout.delete();
 +
 +        mReplicated.storageFor(StoredLayoutProperty.class)
 +            .query("layoutID = ?").with(layout.getLayoutID()).deleteAll();
 +
 +        // Close and open to rebuild replicated repository.
 +        locations = reOpenPersistent(locations);
 +
 +        storage0 = mReplicated.storageFor(type0);
 +
 +        // Verify corruption. (Replica is unable to figure out what layout generation 1 is)
 +        try {
 +            Cursor<? extends StorableTestMinimal> 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<? extends StorableTestMinimal> 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<? extends StorableTestMinimal> cursor = storage1.query().fetch();
 +            int actual = 0;
 +            while (cursor.hasNext()) {
 +                StorableTestMinimal stm = cursor.next();
 +                //System.out.println(stm);
 +                actual++;
 +            }
 +            assertEquals(count, actual);
 +        }
 +    }
  }
  | 
