co.cask.tephra.persist.LocalTransactionStateStorageTest.java Source code

Java tutorial

Introduction

Here is the source code for co.cask.tephra.persist.LocalTransactionStateStorageTest.java

Source

/*
 * Copyright  2012-2014 Cask Data, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package co.cask.tephra.persist;

import co.cask.tephra.ChangeId;
import co.cask.tephra.TransactionManager;
import co.cask.tephra.TransactionType;
import co.cask.tephra.TxConstants;
import co.cask.tephra.metrics.TxMetricsCollector;
import co.cask.tephra.snapshot.DefaultSnapshotCodec;
import co.cask.tephra.snapshot.SnapshotCodecProvider;
import co.cask.tephra.snapshot.SnapshotCodecV4;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.hadoop.conf.Configuration;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Runs transaction persistence tests against the {@link LocalFileTransactionStateStorage} and
 * {@link LocalFileTransactionLog} implementations.
 */
public class LocalTransactionStateStorageTest extends AbstractTransactionStateStorageTest {
    @ClassRule
    public static TemporaryFolder tmpDir = new TemporaryFolder();

    @Override
    protected Configuration getConfiguration(String testName) throws IOException {
        File testDir = tmpDir.newFolder(testName);
        Configuration conf = new Configuration();
        conf.set(TxConstants.Manager.CFG_TX_SNAPSHOT_LOCAL_DIR, testDir.getAbsolutePath());
        conf.set(TxConstants.Persist.CFG_TX_SNAPHOT_CODEC_CLASSES, SnapshotCodecV4.class.getName());
        return conf;
    }

    @Override
    protected AbstractTransactionStateStorage getStorage(Configuration conf) {
        return new LocalFileTransactionStateStorage(conf, new SnapshotCodecProvider(conf),
                new TxMetricsCollector());
    }

    // v2 TransactionEdit
    @SuppressWarnings("deprecation")
    private class TransactionEditV2 extends TransactionEdit {
        public TransactionEditV2(long writePointer, long visibilityUpperBound, State state, long expirationDate,
                Set<ChangeId> changes, long commitPointer, boolean canCommit, TransactionType type) {
            super(writePointer, visibilityUpperBound, state, expirationDate, changes, commitPointer, canCommit,
                    type, null, 0L, 0L, null);
        }

        @Override
        public void write(DataOutput out) throws IOException {
            TransactionEditCodecs.encode(this, out, new TransactionEditCodecs.TransactionEditCodecV2());
        }
    }

    // Note: this test cannot run in AbstractTransactionStateStorageTest, since SequenceFile throws exception saying
    // TransactionEditV2 is not TransactionEdit. Since the code path this test is verifying is the same path between
    // HDFS and Local Storage, having this only over here is fine.
    @SuppressWarnings("deprecation")
    @Test
    public void testLongTxnBackwardsCompatibility() throws Exception {
        Configuration conf = getConfiguration("testLongTxnBackwardsCompatibility");

        // Use SnapshotCodec version 1
        String latestSnapshotCodec = conf.get(TxConstants.Persist.CFG_TX_SNAPHOT_CODEC_CLASSES);
        conf.set(TxConstants.Persist.CFG_TX_SNAPHOT_CODEC_CLASSES, DefaultSnapshotCodec.class.getName());

        TransactionStateStorage storage = null;
        try {
            storage = getStorage(conf);
            storage.startAndWait();

            // Create transaction snapshot and transaction edits with version when long running txns had -1 expiration.
            Collection<Long> invalid = Lists.newArrayList();
            NavigableMap<Long, TransactionManager.InProgressTx> inProgress = Maps.newTreeMap();
            long time1 = System.currentTimeMillis();
            long wp1 = time1 * TxConstants.MAX_TX_PER_MS;
            inProgress.put(wp1, new TransactionManager.InProgressTx(wp1 - 5, -1L));
            long time2 = time1 + 100;
            long wp2 = time2 * TxConstants.MAX_TX_PER_MS;
            inProgress.put(wp2, new TransactionManager.InProgressTx(wp2 - 50, time2 + 1000));
            Map<Long, Set<ChangeId>> committing = Maps.newHashMap();
            Map<Long, Set<ChangeId>> committed = Maps.newHashMap();
            TransactionSnapshot snapshot = new TransactionSnapshot(time2, 0, wp2, invalid, inProgress, committing,
                    committed);
            long time3 = time1 + 200;
            long wp3 = time3 * TxConstants.MAX_TX_PER_MS;
            TransactionEdit edit1 = new TransactionEditV2(wp3, wp3 - 10, TransactionEdit.State.INPROGRESS, -1L,
                    null, 0L, false, null);
            long time4 = time1 + 300;
            long wp4 = time4 * TxConstants.MAX_TX_PER_MS;
            TransactionEdit edit2 = new TransactionEditV2(wp4, wp4 - 10, TransactionEdit.State.INPROGRESS,
                    time4 + 1000, null, 0L, false, null);

            // write snapshot and transaction edit
            storage.writeSnapshot(snapshot);
            TransactionLog log = storage.createLog(time2);
            log.append(edit1);
            log.append(edit2);
            log.close();

            // Start transaction manager
            conf.set(TxConstants.Persist.CFG_TX_SNAPHOT_CODEC_CLASSES, latestSnapshotCodec);
            long longTimeout = TimeUnit.SECONDS.toMillis(conf.getLong(TxConstants.Manager.CFG_TX_LONG_TIMEOUT,
                    TxConstants.Manager.DEFAULT_TX_LONG_TIMEOUT));
            TransactionManager txm = new TransactionManager(conf, storage, new TxMetricsCollector());
            txm.startAndWait();
            try {
                // Verify that the txns in old format were read correctly.
                // There should be four in-progress transactions, and no invalid transactions
                TransactionSnapshot snapshot1 = txm.getCurrentState();
                Assert.assertEquals(ImmutableSortedSet.of(wp1, wp2, wp3, wp4), snapshot1.getInProgress().keySet());
                verifyInProgress(snapshot1.getInProgress().get(wp1), TransactionType.LONG, time1 + longTimeout);
                verifyInProgress(snapshot1.getInProgress().get(wp2), TransactionType.SHORT, time2 + 1000);
                verifyInProgress(snapshot1.getInProgress().get(wp3), TransactionType.LONG, time3 + longTimeout);
                verifyInProgress(snapshot1.getInProgress().get(wp4), TransactionType.SHORT, time4 + 1000);
                Assert.assertEquals(0, snapshot1.getInvalid().size());
            } finally {
                txm.stopAndWait();
            }
        } finally {
            if (storage != null) {
                storage.stopAndWait();
            }
        }
    }

    // Note: this test cannot run in AbstractTransactionStateStorageTest, since SequenceFile throws exception saying
    // TransactionEditV2 is not TransactionEdit. Since the code path this test is verifying is the same path between
    // HDFS and Local Storage, having this only over here is fine.
    @SuppressWarnings("deprecation")
    @Test
    public void testAbortEditBackwardsCompatibility() throws Exception {
        Configuration conf = getConfiguration("testAbortEditBackwardsCompatibility");

        TransactionStateStorage storage = null;
        try {
            storage = getStorage(conf);
            storage.startAndWait();

            // Create edits for transaction type addition to abort
            long time1 = System.currentTimeMillis();
            long wp1 = time1 * TxConstants.MAX_TX_PER_MS;
            TransactionEdit edit1 = new TransactionEditV2(wp1, wp1 - 10, TransactionEdit.State.INPROGRESS, -1L,
                    null, 0L, false, null);
            TransactionEdit edit2 = new TransactionEditV2(wp1, 0L, TransactionEdit.State.ABORTED, 0L, null, 0L,
                    false, null);

            long time2 = time1 + 400;
            long wp2 = time2 * TxConstants.MAX_TX_PER_MS;
            TransactionEdit edit3 = new TransactionEditV2(wp2, wp2 - 10, TransactionEdit.State.INPROGRESS,
                    time2 + 10000, null, 0L, false, null);
            TransactionEdit edit4 = new TransactionEditV2(wp2, 0L, TransactionEdit.State.INVALID, 0L, null, 0L,
                    false, null);
            // Simulate case where we cannot determine txn state during abort
            TransactionEdit edit5 = new TransactionEditV2(wp2, 0L, TransactionEdit.State.ABORTED, 0L, null, 0L,
                    false, null);

            // write snapshot and transaction edit
            TransactionLog log = storage.createLog(time1);
            log.append(edit1);
            log.append(edit2);
            log.append(edit3);
            log.append(edit4);
            log.append(edit5);
            log.close();

            // Start transaction manager
            TransactionManager txm = new TransactionManager(conf, storage, new TxMetricsCollector());
            txm.startAndWait();
            try {
                // Verify that the txns in old format were read correctly.
                // Both transactions should be in invalid state
                TransactionSnapshot snapshot1 = txm.getCurrentState();
                Assert.assertEquals(ImmutableList.of(wp1, wp2), snapshot1.getInvalid());
                Assert.assertEquals(0, snapshot1.getInProgress().size());
                Assert.assertEquals(0, snapshot1.getCommittedChangeSets().size());
                Assert.assertEquals(0, snapshot1.getCommittingChangeSets().size());
            } finally {
                txm.stopAndWait();
            }
        } finally {
            if (storage != null) {
                storage.stopAndWait();
            }
        }
    }

    private void verifyInProgress(TransactionManager.InProgressTx inProgressTx, TransactionType type,
            long expiration) throws Exception {
        Assert.assertEquals(type, inProgressTx.getType());
        Assert.assertTrue(inProgressTx.getExpiration() == expiration);
    }
}