All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sleepycat.je.txn.HandleLocker Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle Berkeley
 * DB Java Edition made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle Berkeley DB Java Edition for a copy of the
 * license and additional information.
 */

package com.sleepycat.je.txn;

import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.dbi.EnvironmentImpl;

/**
 * Extends BasicLocker to share locks with another Locker that is being used to
 * open a database.  HandleLocker holds a read lock on the NameLN for the
 * Database object, to prevent rename, removal or truncation of a database
 * while it is open.  The HandleLocker and the Locker used to open the database
 * both hold a NameLN lock at the same time, so they must share locks to avoid
 * conflicts.
 *
 * Accounts for the fact that the Txn may end before this locker ends by only
 * keeping the Txn ID rather than a reference to the Txn object.  A reference
 * to a non-tnxl Locker is OK, OTOH, because it is short lived.
 *
 * Handle Locking Overview
 * ------------------------
 * Environment.openDatabase calls Environment.setupDatabase, which calls
 * Database.initHandleLocker to create the HandleLocker. setupDatabase ensures
 * that the HandleLocker is passed to DbTree.getDb and createDb.  These latter
 * methods acquire a read lock on the NameLN for the HandleLocker, in addition
 * to acquiring a read or write lock for the openDatabase locker.
 *
 * If setupDatabase is not successful, it ensures that locks are released via
 * HandleLocker.endOperation.  If setupDatabase is successful, the handle is
 * returned by openDatabase, and Database.close must be called to release the
 * lock.  The handle lock is released by calling HandleLocker.endOperation.
 *
 * A special case is when a user txn is passed to openDatabase.  If the txn
 * aborts, the Database handle must be invalidated.  When setupDatabase
 * succeeds it passes the handle to Txn.addOpenedDatabase, which remembers the
 * handle.  Txn.abort invalidates the handle.
 * 
 * NameLN Migration and LSN Changes [#20617]
 * -----------------------------------------
 * When the log cleaner migrates a NameLN, its LSN changes and the new LSN is
 * locked on behalf of all existing lockers by CursorImpl.lockAfterLsnChange.
 * lockAfterLsnChange is also used when a dirty deferred-write LN is logged by
 * BIN.logDirtyLN, as part of flushing a BIN during a checkpoint or eviction.
 *
 * Because handle lockers are legitimately very long lived, it is important
 * that lockAfterLsnChange releases the locks on the old LSN, to avoid a steady
 * accumulation of locks in a HandleLocker.  Therefore, lockAfterLsnChange will
 * release the lock on the old LSN, for HandleLockers only.  Although it may be
 * desirable to release the old LSN lock on other long lived lockers, it is too
 * risky.  In an experiment, this caused problems with demotion and upgrade,
 * when a lock being demoted or upgraded was released.
 *
 * Because LSNs can change, it is also important that we don't rely on a single
 * NameLN locker ID (LSN) as a data structure key for handle locks.  This was
 * acceptable when a stable Node ID was used as a lock ID, but is no longer
 * appropriate now that mutable LSNs are used as lock IDs.
 */
public class HandleLocker extends BasicLocker {

    private final long shareWithTxnId;
    private final Locker shareWithNonTxnlLocker;

    /**
     * Creates a HandleLocker.
     */
    protected HandleLocker(EnvironmentImpl env, Locker buddy) {
        super(env);
        shareWithTxnId =
            buddy.isTransactional() ? buddy.getId() : TxnManager.NULL_TXN_ID;
        shareWithNonTxnlLocker = 
            buddy.isTransactional() ? null : buddy;
    }

    public static HandleLocker createHandleLocker(EnvironmentImpl env,
                                                  Locker buddy)
        throws DatabaseException {

        return new HandleLocker(env, buddy);
    }

    /**
     * Returns whether this locker can share locks with the given locker.
     */
    @Override
    public boolean sharesLocksWith(Locker other) {

        if (super.sharesLocksWith(other)) {
            return true;
        }
        if (shareWithTxnId != TxnManager.NULL_TXN_ID &&
            shareWithTxnId == other.getId()) {
            return true;
        }
        if (shareWithNonTxnlLocker != null &&
            shareWithNonTxnlLocker == other) {
            return true;
        }
        return false;
    }

    /**
     * Because handle lockers are legitimately very long lived, it is important
     * that lockAfterLsnChange releases the locks on the old LSN, to avoid a
     * steady accumulation of locks in a HandleLocker
     */
    @Override
    public boolean allowReleaseLockAfterLsnChange() {
        return true;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy