summaryrefslogtreecommitdiff
path: root/src/test/java/com/amazon/carbonado
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java/com/amazon/carbonado')
-rw-r--r--src/test/java/com/amazon/carbonado/layout/TestLayout.java29
-rw-r--r--src/test/java/com/amazon/carbonado/repo/replicated/TestRepair.java222
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);
+ }
+ }
}