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

oracle.kv.impl.admin.SysTableMonitor 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.admin;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import oracle.kv.KVVersion;
import oracle.kv.impl.admin.plan.Plan;
import oracle.kv.impl.admin.plan.TablePlanGenerator;
import oracle.kv.impl.api.table.TableImpl;
import oracle.kv.impl.api.table.TableMetadata;
import oracle.kv.impl.metadata.Metadata;
import oracle.kv.impl.systables.SysTableDescriptor;
import oracle.kv.impl.systables.SysTableRegistry;
import oracle.kv.impl.util.SerialVersion;

import com.sleepycat.je.utilint.StoppableThread;


/**
 * A task that monitors system tables. It will create the tables if they don't
 * exist and will upgrade existing tables if needed. This task should be run
 * when the Admin becomes the master. If any action is taken (creating or
 * evolving a table) the task will reschedule itself so that the actions can
 * be retried in case they fail.
 *
 * This task is designed so it is not necessary to monitor plan failures. If
 * the plan fails a new one will be created the next go around. An attempt is
 * made to cancel plans that end in non-terminal state. However, if they are
 * missed the plan pruning mechanism will eventually remove them.
 */
class SysTableMonitor extends StoppableThread {

    private static final int THREAD_SOFT_SHUTDOWN_MS = 10000;

    private final Admin admin;

    /**
     * Map of descriptors to plan IDs. The map is initialized to the
     * set of table descriptors with the plan ID set to NO_PLAN. If the table
     * needs attention the resulting plan ID is added. If no action is taken,
     * the descriptor is removed. The task will retry (up to 10 times) as
     * long as the map is not empty.
     */
    private final Map descriptors;

    /**
     * Value in descriptors map to indicate there is no plan associated with
     * the table.
     */
    private static final int NO_PLAN = -1;

    private int attempts = 0;

    private volatile boolean isShutdown = false;

    SysTableMonitor(Admin admin) {
        super("System table monitor");
        this.admin = admin;
        descriptors = new HashMap<>(SysTableRegistry.descriptors.length);
        for (SysTableDescriptor desc : SysTableRegistry.descriptors) {
            descriptors.put(desc, NO_PLAN);
        }
    }

    /**
     * Checks to see if the system tables in the descriptors map are up to
     * the correct version. If they are not, a plan is created to upgrade them.
     */
    @Override
    public void run() {
        if (isShutdown) {
            return;
        }
        getLogger().log(Level.INFO, "Starting {0}", this);

        while (!isShutdown && !descriptors.isEmpty()) {
            boolean needUpgrade = checkTables();

            if (isShutdown || descriptors.isEmpty()) {
                break;
            }
            attempts++;
            /* If the store needs to be upgraded, no limit on attempts. */
            if (!needUpgrade && attempts > 10) {
                getLogger().log(Level.SEVERE,
                                "Failed to create or upgrade system " +
                                "tables after {0} attempts.", attempts);
                break;
            }
            waitFor(needUpgrade ? 30000 : 10000);
        }
        getLogger().log(Level.INFO, "Exit {0}", this);
    }

    /*
     * Waits for the specified time.
     */
    private synchronized void waitFor(int millis) {
        if (isShutdown) {
            return;
        }
        try {
            wait(millis);
        } catch (InterruptedException ie) {
            /* The sleep may be interrupted by shutdown */
            if (!isShutdown) {
                getLogger().log(Level.SEVERE,
                                "Unexpected exception in {0}: {1}",
                                new Object[]{this, ie});
            }
        }
    }

    private boolean checkTables() {
        getLogger().log(Level.INFO, "Checking status of system tables");

        final TableMetadata md = admin.getMetadata(TableMetadata.class,
                                                   Metadata.MetadataType.TABLE);

        final Iterator> itr =
                descriptors.entrySet().iterator();

        boolean needsUpgrade = false;

        while (!isShutdown && itr.hasNext()) {
            final Entry entry = itr.next();
            final SysTableDescriptor desc = entry.getKey();
            int planId = entry.getValue();

            /*
             * If there is already a plan in place to handle this table, check
             * on its state.
             */
            if (planId != NO_PLAN) {
                if (checkPlanState(planId,  desc)) {
                    /* Plan succeeded, remove the descriptor */
                    itr.remove();
                }
                continue;
            }
            getLogger().log(Level.FINE,
                            "Checking status of system table {0}",
                            desc.getTableName());

            /*
             * If the table does not exist, or the metadata does not exist
             * create the table (the plan will also create the metadata if
             * needed).
             */
            final TableImpl table =
                    (md == null) ? null : md.getTable(desc.getTableName());
            if (table == null) {
                final TableImpl newTable = desc.buildTable();
                if (checkVersionRequirement(newTable)) {
                    needsUpgrade = true;
                    continue;
                }

                try {
                    planId = admin.getPlanner().
                                    createAddTablePlan("Create system table",
                                                       newTable,
                                                       null,
                                                       true /* systemTable */,
                                                       null);
                    admin.approvePlan(planId);
                    admin.executePlanOrFindMatch(planId);
                    descriptors.put(desc, planId);
                } catch (Exception e) {
                    if (!isShutdown) {
                        getLogger().log(Level.INFO,
                                        "Exception creating system" +
                                        " table {0} {1}",
                                        new Object[]{newTable.getName(),
                                                     e.getMessage()});
                    }
                }
                continue;
            }

            /*
             * See if the table needs to be upgraded.
             */
            final TableImpl newTable = desc.evolveTable(table, getLogger());
            if (newTable != null) {
                try {
                    assert !newTable.hasIdentityColumn();

                    if (checkVersionRequirement(newTable)) {
                        needsUpgrade = true;
                        continue;
                    }

                    planId = admin.getPlanner().
                        createEvolveTablePlan("Upgrade system table",
                            newTable.getInternalNamespace(),
                            newTable.getFullName(),
                            table.getTableVersion(),
                            newTable.getFieldMap(),
                            newTable.getDefaultTTL(),
                            newTable.getDescription(),
                            true /* systemTable */,
                            null /*identityColumnInfo*/,
                            null /*sequenceDefChange*/);
                    admin.approvePlan(planId);
                    admin.executePlanOrFindMatch(planId);
                    descriptors.put(desc, planId);
                } catch (Exception e) {
                    if (!isShutdown) {
                        getLogger().log(Level.INFO,
                                        "Exception evolving system" +
                                        " table {0} {1}",
                                        new Object[]{table.getName(),
                                                     e.getMessage()});
                    }
                }
                continue;
            }
            assert planId == NO_PLAN;
            /* Nothing needed to be done, remove the descriptor. */
            itr.remove();
        }
        return needsUpgrade;
    }

    /**
     * Checks the version requirement of the specified table. Returns true if
     * the store needs to be upgraded before the table can be updated (or
     * created) otherwise returns false;
     *
     * @return true if the store needs to be upgraded
     */
    private boolean checkVersionRequirement(TableImpl table) {
        final KVVersion requiredVersion = SerialVersion.
            getKVVersion(table.getRequiredSerialVersion());
        try {
            TablePlanGenerator.checkStoreVersion(admin, requiredVersion);
        } catch (IllegalCommandException ice) {
            getLogger().log(Level.INFO,
                            "Unable to create/update table {0}, store needs" +
                                        " to be at {1}, waiting for upgrade",
                            new Object[] { table.getName(), requiredVersion });
            return true;
        } catch (Exception e) {
            /*
             * There was an exception getting the store version. One or more
             * nodes may be down, perhaps due to an upgrade in progress. Return
             * true assuming it is an upgrade.
             */
            getLogger().log(Level.INFO,
                            "Exception getting store version: {0}",
                            e.getMessage());
            return true;
        }
        return false;
    }


    /**
     * Checks on the state of the plan handling the descriptor. Returns true
     * if the plan has completed with success and its descriptor can be
     * removed from the descriptors map.
     */
    private boolean checkPlanState(int planId, SysTableDescriptor desc) {
        final Plan plan = admin.getPlanById(planId);

        /*
         * In theory the plan could be pruned. In this case we don't know
         * what state it ended in, so retry just in case.
         */
        if (plan == null) {
            getLogger().log(Level.FINE,
                            "Plan {0} for {1} is missing, retrying",
                            new Object[]{planId, desc});
            descriptors.put(desc, NO_PLAN);
            return false;
        }

        switch (plan.getState()) {
        case PENDING:
        case APPROVED:
        case RUNNING:
            /* Plan is still running. */
            return false;

        case SUCCEEDED:
            getLogger().log(Level.FINE,
                            "Plan {0} for {1} completed",
                            new Object[]{planId, desc});
            return true;

        case INTERRUPTED:
        case ERROR:
        case INTERRUPT_REQUESTED:
            /* Cancel old plan */
            admin.cancelPlan(planId);

            //$FALL-THROUGH$
        case CANCELED:
             /* Plan failed or was canceled. Retry. */
            getLogger().log(Level.FINE,
                            "Plan {0} for {1} did not complete, retrying",
                            new Object[]{planId, desc});
            descriptors.put(desc, NO_PLAN);
            return false;
        }
        throw new IllegalStateException("Unknown plan state: " +
                                        plan.getState());
    }

    @Override
    protected synchronized int initiateSoftShutdown() {
        isShutdown = true;
        notifyAll();
        return THREAD_SOFT_SHUTDOWN_MS;
    }

    @Override
    protected Logger getLogger() {
        return admin.getLogger();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy