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

oracle.kv.impl.rep.VersionManager Maven / Gradle / Ivy

Go to download

NoSQL Database Server - supplies build and runtime support for the server (store) side of the Oracle NoSQL Database.

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

package oracle.kv.impl.rep;

import java.util.logging.Logger;

import oracle.kv.KVVersion;
import oracle.kv.impl.fault.ProcessFaultException;
import oracle.kv.impl.util.SerializationUtil;
import oracle.kv.impl.util.TxnUtil;
import oracle.kv.impl.util.VersionUtil;

import com.sleepycat.bind.tuple.StringBinding;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DiskLimitException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.rep.InsufficientReplicasException;

/**
 * Maintains the local and replicated version information, consolidating the
 * information whenever necessary.
 */
public class VersionManager {

    /* The name of the local database used to store version information. */
    private static final String VERSION_DATABASE_NAME = "VersionDatabase";
    private static final String VERSION_KEY = "LocalVersion";
    private static final DatabaseEntry VKEY_ENTRY = new DatabaseEntry();

    static {
        StringBinding.stringToEntry(VERSION_KEY, VKEY_ENTRY);
    }

    /* The RN whose version information is being managed. */
    private final RepNode repNode;

    private final Logger logger;

    public VersionManager(Logger logger, RepNode repNode) {
        this.logger = logger;
        this.repNode = repNode;
    }

    /**
     * Invoked at RN startup to check that the code version matches the version
     * stored in the RN's environment. This method is invoked as soon as the
     * environment is opened before any access to its contents. If the
     * version database contains an older version than the current code
     * version, it updates the version database with KVVersion.CURRENT_VERSION.
     *
     * @param env the environment associated with the RN. Note the deliberate
     * typing of the parameter as Environment instead of ReplicateEnvironment
     * to emphasize that no changes are made to any replicated databases.
     */
    void checkCompatibility(Environment env) {

        Database vdb = null;

        try {
            vdb = openDb(env);

            final KVVersion localVersion = getLocalVersion(vdb);

            if (localVersion != null) {

                /* If the old version is the same, nothing to do */
                if (localVersion.equals(KVVersion.CURRENT_VERSION)) {
                    return;
                }

                /* Check for upgrade (or downgrade) compatibility. */
                try {
                    VersionUtil.checkUpgrade(localVersion);
                } catch (IllegalStateException ise) {
                    throw new ProcessFaultException(ise.getMessage(), ise);
                }
            }

            /* Note that the version change may be a patch downgrade */
            repNode.versionChange(env, localVersion);

            /*
             * Finally, install the current version after any earlier changes
             * have been completed.
             */
            final DatabaseEntry vdata = new DatabaseEntry(SerializationUtil.
                getBytes(KVVersion.CURRENT_VERSION));
            try {
                final OperationStatus status = vdb.put(null, VKEY_ENTRY, vdata);
                if (status != OperationStatus.SUCCESS) {
                    throw new IllegalStateException(
                            "Could not install new version");
                }
            } catch (DiskLimitException | InsufficientReplicasException dle) {
                /*
                 * We should not keep going when disk limit exception happens.
                 * One scenario that could cause trouble is that:
                 * (1) we keep going without persist version;
                 * (2) we do some in-memory operations for upgrade;
                 * (3) disk limit problem is solved and we do some more
                 * persistent operation. At this point, we would have
                 * inconsistency among persisted/in-memory data and our
                 * version.
                 *
                 * Plus the chance that we run into disk limit while upgrading
                 * is small, so better safe than sorry.
                 */
                throw new IllegalStateException(
                        "Could not install new version " +
                        "since disk limit reached");
            }

            /*
             * Ensure it's in stable storage, since the version database is
             * non-transactional.
             */
            env.flushLog(true /*fsync*/);
            logger.info("Local Environment version updated to: " +
                        KVVersion.CURRENT_VERSION +
                        " Previous version: " +
                        ((localVersion == null) ?
                         " none" :
                         localVersion.getVersionString()));
        } finally {
            /*
             * Note that since the version database is a non-transactional
             * local database, there is no transaction to abort and undo
             * changes, so the sequence of changes must be carefully organized
             * so they can be retried at a higher level without an undo. That
             * is, they must be idempotent.
             */
            if (vdb != null) {
                TxnUtil.close(logger, vdb, "version");
            }
        }
    }

    /**
     * Returns the current version stored in the version database.
     *
     * @param env the RN's environment
     *
     * @return the KVVersion or null
     */
    public static KVVersion getLocalVersion(Logger logger, Environment env) {
        Database db = null;

        try {
            db = openDb(env);
            return getLocalVersion(db);
        } finally {
            if (db != null) {
                TxnUtil.close(logger, db, "version");
            }
        }
    }

    /* Returns the version from the version database. */
    private static KVVersion getLocalVersion(Database versionDB) {
        final DatabaseEntry vdata = new DatabaseEntry();
        final OperationStatus status =
            versionDB.get(null, VKEY_ENTRY, vdata, LockMode.DEFAULT);

        return (status == OperationStatus.SUCCESS) ?
            SerializationUtil.getObject(vdata.getData(), KVVersion.class) :
            null;
    }

    /**
     * Returns a handle to the version DB.
     */
    private static Database openDb(Environment env) {
        final DatabaseConfig dbConfig =
            new DatabaseConfig().
                setTransactional(false). /* local db cannot be transactional */
                setAllowCreate(true).
                setReplicated(false);

        return env.openDatabase(null, VERSION_DATABASE_NAME, dbConfig);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy