diff options
| author | Jesse Morgan <jesse@jesterpm.net> | 2013-10-25 01:36:27 +0000 | 
|---|---|---|
| committer | Jesse Morgan <jesse@jesterpm.net> | 2013-10-25 01:36:27 +0000 | 
| commit | c4be3cddd66744623a229c3a05b125b2a5cdd234 (patch) | |
| tree | 5f79d7b2e5f3b56b881f8378d04cbd8fabd92692 /src/test/java | |
| parent | 2607738aabab4d97a9528b22b6f5f7a3cea09e02 (diff) | |
Fixing ReplicatedRepository so that transactions may be entered when the master
is unavailable as long as no changes are made to replicated storables.
Diffstat (limited to 'src/test/java')
| -rw-r--r-- | src/test/java/com/amazon/carbonado/TestUtilities.java | 12 | ||||
| -rw-r--r-- | src/test/java/com/amazon/carbonado/repo/replicated/TestBrokenMaster.java | 219 | 
2 files changed, 229 insertions, 2 deletions
diff --git a/src/test/java/com/amazon/carbonado/TestUtilities.java b/src/test/java/com/amazon/carbonado/TestUtilities.java index d7be174..cbee9c1 100644 --- a/src/test/java/com/amazon/carbonado/TestUtilities.java +++ b/src/test/java/com/amazon/carbonado/TestUtilities.java @@ -38,7 +38,7 @@ public class TestUtilities {      public static final String FILE_PATH_KEY = "filepath";
      // Keep memory usage low to prevent spurious out-of-memory errors while running tests.
 -    private static final int DEFAULT_CAPACITY = 100000;
 +    public static final int DEFAULT_CAPACITY = 100000;
      private static final Random sRandom = new Random();
 @@ -122,12 +122,20 @@ public class TestUtilities {                                                               int capacity,
                                                               boolean isMaster)
      {
 +        return newTempRepositoryBuilder(name, capacity, isMaster, true);
 +    }
 +
 +    public static RepositoryBuilder newTempRepositoryBuilder(String name,
 +                                                             int capacity,
 +                                                             boolean isMaster,
 +                                                             boolean inMemory)
 +    {
          BDBRepositoryBuilder builder = new BDBRepositoryBuilder();
          builder.setProduct("JE");
          builder.setName(name);
          builder.setTransactionNoSync(true);
          builder.setCacheSize(capacity);
 -        builder.setLogInMemory(true);
 +        builder.setLogInMemory(inMemory);
          builder.setMaster(isMaster);
          builder.setEnvironmentHome(makeTestDirectoryString(name));
          // Makes it easier to get a thread dump during a deadlock.
 diff --git a/src/test/java/com/amazon/carbonado/repo/replicated/TestBrokenMaster.java b/src/test/java/com/amazon/carbonado/repo/replicated/TestBrokenMaster.java new file mode 100644 index 0000000..3238a90 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/repo/replicated/TestBrokenMaster.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2013 Amazon.com Inc. All Rights Reserved. + * AMAZON.COM CONFIDENTIAL + */ + +package com.amazon.carbonado.repo.replicated; + +import java.util.concurrent.atomic.AtomicReference; + +import com.amazon.carbonado.ConfigurationException; +import com.amazon.carbonado.Repository; +import com.amazon.carbonado.RepositoryBuilder; +import com.amazon.carbonado.RepositoryException; +import com.amazon.carbonado.FetchException; +import com.amazon.carbonado.PersistException; +import com.amazon.carbonado.Storable; +import com.amazon.carbonado.Storage; +import com.amazon.carbonado.TestUtilities; +import com.amazon.carbonado.TriggerFactory; + +import com.amazon.carbonado.layout.TestLayout; +import com.amazon.carbonado.stored.StorableTestMinimal; + +import org.cojen.classfile.TypeDesc; + +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import static org.junit.Assert.fail; + +/** + * Test ReplicatedRepository when the master repository builder throws an + * Exception at build time. + * + * @author Jesse Morgan (morganjm) + */ +public class TestBrokenMaster extends TestCase { +    private static final String STORABLE_NAME = "test.TestStorable"; + +    public static void main(String[] args) { +        junit.textui.TestRunner.run(suite()); +    } + +    public static TestSuite suite() { +        TestSuite suite = new TestSuite(); +        suite.addTestSuite(TestBrokenMaster.class); +        return suite; +    } + +    public TestBrokenMaster(String name) { +        super(name); +    } + +    private RepositoryBuilder mReplicaBuilder; +    private BrokenRepositoryBuilder mMasterBuilder; +    private Repository mRepository; + +    private Class<? extends StorableTestMinimal> mTestStorableClazz; + +    @Override +    protected void setUp() throws Exception { +        super.setUp(); + +        mReplicaBuilder = TestUtilities.newTempRepositoryBuilder("rr-replica", TestUtilities.DEFAULT_CAPACITY, false, false); +        mMasterBuilder = new BrokenRepositoryBuilder(TestUtilities.newTempRepositoryBuilder("rr-writer", TestUtilities.DEFAULT_CAPACITY, true, false)); + +        // Prepare the Storable class +        mTestStorableClazz = TestLayout.defineStorable(STORABLE_NAME, 0, TypeDesc.INT); + +        // Populate data +        mRepository = buildRepository(false); +        Storage<? extends StorableTestMinimal> storage = mRepository.storageFor(mTestStorableClazz); +        StorableTestMinimal s = storage.prepare(); +        s.setId(1); +        s.insert(); +        mRepository.close(); +    } + +    @Override +    protected void tearDown() throws Exception { +        super.tearDown(); + +        if (mRepository != null) { +            mRepository.close(); +            mRepository = null; +        } +    } + +    private Repository buildRepository(boolean broken) throws RepositoryException { +        if (broken) { +            mMasterBuilder.setFailures(1); +        } +        ReplicatedRepositoryBuilder builder = new ReplicatedRepositoryBuilder(); +        builder.setMaster(true); +        builder.setReplicaRepositoryBuilder(mReplicaBuilder); +        builder.setMasterRepositoryBuilder(mMasterBuilder); +        return builder.build(); +    } + +    /** +     * Verify that reads work and inserts fail on a broken ReplicatedRepository. +     */ +    public void testLoadAndInsert() throws Exception { +        mRepository = buildRepository(true); +        Storage<? extends StorableTestMinimal> storage = mRepository.storageFor(mTestStorableClazz); + +        // Test loading existing an Storable with a broken master. +        // This should succeed. +        StorableTestMinimal s = storage.prepare(); +        s.setId(1); +        s.load(); + +        // Test inserting a Storable with broken master. +        // This should fail. +        try { +            s = storage.prepare(); +            s.setId(2); +            s.insert(); + +            fail("Expected exception."); +        } catch (PersistException e) { +            assertEquals("Current transaction is read-only.", e.getMessage()); +        } +    } + +    /** +     * Verify that Storables can't change on a broken ReplicatedRepository. +     */ +    public void testStorableChanged() throws Exception { +        // Change the Storable definition +        mTestStorableClazz = TestLayout.defineStorable(STORABLE_NAME, 1, TypeDesc.INT); + +        // Open the repository. +        mRepository = buildRepository(true); + +        // Test loading existing an Storable with a broken master. +        // This should fail because the layout has changed and the new layout +        // can not be persisted. +        try { +            Storage<? extends StorableTestMinimal> storage = mRepository.storageFor(mTestStorableClazz); +            StorableTestMinimal s = storage.prepare(); +            s.setId(1); +            s.load(); +            fail("Expected exception"); +        } catch (PersistException e) { +            assertEquals("com.amazon.carbonado.PersistException: Current transaction is read-only.", e.getMessage()); +        } +    } + +    /** +     * This class wraps a RepositoryBuilder and fails to build the first n times. +     * +     * This is used to imitate a JDBC timeout at startup. +     */ +    class BrokenRepositoryBuilder implements RepositoryBuilder { +        private RepositoryBuilder mWrappedBuilder; +        private int mFailuresRemaining; + +        public BrokenRepositoryBuilder(RepositoryBuilder builder) { +            mWrappedBuilder = builder; +        } + +        public void setFailures(int i) { +            mFailuresRemaining = i; +        } + +        @Override +        public Repository build() throws ConfigurationException, RepositoryException { +            return mWrappedBuilder.build(); +        } + +        @Override +        public Repository build(AtomicReference<Repository> rootReference) +            throws ConfigurationException, RepositoryException { + +            if (mFailuresRemaining > 0) { +                mFailuresRemaining--; +                throw new FetchException("Failed Build Enabled"); +            } + +            return mWrappedBuilder.build(rootReference); +        } + +        @Override +        public String getName() { +            return mWrappedBuilder.getName(); +        } + +        @Override +        public void setName(String name) { +            mWrappedBuilder.setName(name); +        } + +        @Override +        public boolean isMaster() { +            return mWrappedBuilder.isMaster(); +        } + +        @Override +        public void setMaster(boolean b) { +            mWrappedBuilder.setMaster(b); +        } + +        @Override +        public boolean addTriggerFactory(TriggerFactory factory) { +            return mWrappedBuilder.addTriggerFactory(factory); +        } + +        @Override +        public boolean removeTriggerFactory(TriggerFactory factory) { +            return mWrappedBuilder.removeTriggerFactory(factory); +        } + +        @Override +        public Iterable<TriggerFactory> getTriggerFactories() { +            return mWrappedBuilder.getTriggerFactories(); +        } +    } +}  | 
