Package com.sleepycat.je.rep.stream

Source Code of com.sleepycat.je.rep.stream.FeederTxns$TxnInfo

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002, 2011 Oracle and/or its affiliates.  All rights reserved.
*
*/

package com.sleepycat.je.rep.stream;

import static com.sleepycat.je.rep.stream.FeederTxnStatDefinition.ACK_WAIT_MS;
import static com.sleepycat.je.rep.stream.FeederTxnStatDefinition.TXNS_ACKED;
import static com.sleepycat.je.rep.stream.FeederTxnStatDefinition.TXNS_NOT_ACKED;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import com.sleepycat.je.rep.InsufficientAcksException;
import com.sleepycat.je.rep.impl.RepImpl;
import com.sleepycat.je.rep.impl.node.FeederManager;
import com.sleepycat.je.rep.txn.MasterTxn;
import com.sleepycat.je.txn.Txn;
import com.sleepycat.je.utilint.AtomicLongStat;
import com.sleepycat.je.utilint.LoggerUtils;
import com.sleepycat.je.utilint.StatGroup;
import com.sleepycat.je.utilint.VLSN;

/**
* FeederTxns manages transactions that need acknowledgments.
*/
public class FeederTxns {

    /*
     * Tracks transactions that have not yet been acknowledged for the entire
     * replication node.
     */
    private final Map<Long, TxnInfo> txnMap;

    private final RepImpl repImpl;
    private final StatGroup statistics;
    private final AtomicLongStat txnsAcked;
    private final AtomicLongStat txnsNotAcked;
    private final AtomicLongStat ackWaitMs;

    public FeederTxns(RepImpl repImpl) {

        txnMap = new ConcurrentHashMap<Long, TxnInfo>();
        this.repImpl = repImpl;
        statistics = new StatGroup(FeederTxnStatDefinition.GROUP_NAME,
                                   FeederTxnStatDefinition.GROUP_DESC);
        txnsAcked = new AtomicLongStat(statistics, TXNS_ACKED);
        txnsNotAcked = new AtomicLongStat(statistics, TXNS_NOT_ACKED);
        ackWaitMs = new AtomicLongStat(statistics, ACK_WAIT_MS);
    }

    /**
     * Create a new TxnInfo so that transaction commit can wait onthe latch it
     * sets up.
     *
     * @param txn identifies the transaction.
     */
    public void setupForAcks(MasterTxn txn) {
        if (txn.getRequiredAckCount() == 0) {
            /* No acks called for, no setup needed. */
            return;
        }
        TxnInfo txnInfo = new TxnInfo(txn);
        TxnInfo  prevInfo = txnMap.put(txn.getId(), txnInfo);
        assert(prevInfo == null);
    }

    /**
     * Returns the transaction if it's waiting for acknowledgments. Returns
     * null otherwise.
     */
    public MasterTxn getAckTxn(long txnId) {
        TxnInfo txnInfo = txnMap.get(txnId);
        return (txnInfo == null) ? null : txnInfo.txn;
    }

    /*
     * Clears any ack requirements associated with the transaction. It's
     * typically invoked on a transaction abort.
     */
    public void clearTransactionAcks(Txn txn) {
        txnMap.remove(txn.getId());
    }

    /**
     * Notes that an acknowledgment was received from a replica.
     *
     * @param txnId the locally committed transaction that was acknowledged.
     *
     * @return the transaction VLSN, if txnId needs an ack, null otherwise
     */
    public VLSN noteReplicaAck(long txnId) {
        final TxnInfo txnInfo = txnMap.get(txnId);
        if (txnInfo == null) {
            return null;
        }
        txnInfo.countDown();
        return txnInfo.getCommitVLSN();
    }

    /**
     * Waits for the required number of replica acks to come through.
     *
     * @param txn identifies the transaction to wait for.
     *
     * @param timeoutMs the amount of time to wait for the acknowledgments
     * before giving up.
     *
     * @throws InsufficientAcksException if the ack requirements were not met
     */
    public void awaitReplicaAcks(MasterTxn txn, int timeoutMs)
        throws InterruptedException {

        TxnInfo txnInfo = txnMap.get(txn.getId());
        if (txnInfo == null) {
            return;
        }
        txnInfo.await(timeoutMs);
        txnMap.remove(txn.getId());
        int pendingAcks = txnInfo.getPendingAcks();

        if (pendingAcks > 0) {
            final int requiredAcks = txn.getCurrentRequiredAckCount();
            int requiredAckDelta = txn.getRequiredAckCount() -
                requiredAcks;
            if (requiredAckDelta >= pendingAcks) {

                /*
                 * The group size was reduced while waiting for acks and the
                 * acks received are sufficient given the new reduced group
                 * size.
                 */
                return;
            }
            /* Snapshot the state to be used in the error message */
            final String dumpState = repImpl.dumpFeederState();
            final FeederManager feederManager =
                repImpl.getRepNode().feederManager();
            int currentFeederCount =
                feederManager.getNumCurrentAckFeeders(txn.getCommitVLSN());
            /*
             * Repeat the check to ensure that acks have not been received in
             * the time between the completion of the await() call above and
             * the creation of the exception message. This tends to happen when
             * there are lots of threads in the process thus potentially
             * delaying the resumption of this thread following the timeout
             * resulting from the await.
             */
            if (currentFeederCount >= requiredAcks) {
                String msg = "txn " + txn.getId() +
                " commit vlsn:" + txnInfo.getCommitVLSN() +
                " acknowledged after explicit feeder check" +
                " latch count:" + txnInfo.getPendingAcks() +
                " state:" + dumpState +
                " required acks:" + requiredAcks;

                LoggerUtils.info(repImpl.getLogger(), repImpl, msg);
                return;
            }
            throw new InsufficientAcksException
                (txn, pendingAcks, timeoutMs, dumpState);
        }
    }

    /*
     * @see com.sleepycat.je.rep.stream.FeederSource#close()
     */
    public void close() {
        /* Free any blocked commits. */

        for (TxnInfo txnInfo : txnMap.values()) {
            long remaining = txnInfo.getPendingAcks();
            for (long i = 0; i < remaining; i++) {
                txnInfo.countDown();
            }
        }
    }

    /**
     * Used to track the latch and the transaction information associated with
     * a transaction needing an acknowledgment.
     */
    private class TxnInfo {
        /* The latch used to track transaction acknowledgments. */
        final private CountDownLatch latch;
        final MasterTxn txn;

        private TxnInfo(MasterTxn txn) {
            assert(txn != null);
            final int numRequiredAcks = txn.getRequiredAckCount();
            this.latch = (numRequiredAcks == 0) ?
                null :
                new CountDownLatch(numRequiredAcks);
            this.txn = txn;
        }

        /**
         * Returns the VLSN associated with the committed txn, or null if the
         * txn has not yet been committed.
         */
        public VLSN getCommitVLSN() {
            return txn.getCommitVLSN();
        }

        private final boolean await(int timeoutMs)
            throws InterruptedException {

            final long start = System.currentTimeMillis();
            boolean isZero = (latch == null) ||
                latch.await(timeoutMs, TimeUnit.MILLISECONDS);
            if (isZero) {
                txnsAcked.increment();
                ackWaitMs.add(System.currentTimeMillis() - start);
            } else {
                txnsNotAcked.increment();
            }
            return isZero;
        }

        private final void countDown() {
            if (latch == null) {
                return;
            }

            latch.countDown();
        }

        private final int getPendingAcks() {
            if (latch == null) {
                return 0;
            }

            return (int) latch.getCount();
        }
    }

    public StatGroup getStats() {
        StatGroup ret = statistics.cloneGroup(false);

        return ret;
    }

    public void resetStats() {
        statistics.clear();
    }
}
TOP

Related Classes of com.sleepycat.je.rep.stream.FeederTxns$TxnInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.