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

com.sleepycat.je.util.verify.DataVerifier 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.util.verify;

import static com.sleepycat.je.config.EnvironmentParams.ENV_RUN_VERIFIER;
import static com.sleepycat.je.config.EnvironmentParams.VERIFY_BTREE;
import static com.sleepycat.je.config.EnvironmentParams.VERIFY_BTREE_BATCH_DELAY;
import static com.sleepycat.je.config.EnvironmentParams.VERIFY_BTREE_BATCH_SIZE;
import static com.sleepycat.je.config.EnvironmentParams.VERIFY_DATA_RECORDS;
import static com.sleepycat.je.config.EnvironmentParams.VERIFY_LOG;
import static com.sleepycat.je.config.EnvironmentParams.VERIFY_LOG_READ_DELAY;
import static com.sleepycat.je.config.EnvironmentParams.VERIFY_MAX_TARDINESS;
import static com.sleepycat.je.config.EnvironmentParams.VERIFY_OBSOLETE_RECORDS;
import static com.sleepycat.je.config.EnvironmentParams.VERIFY_SCHEDULE;
import static com.sleepycat.je.config.EnvironmentParams.VERIFY_SECONDARIES;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

import com.sleepycat.je.CorruptSecondariesException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentFailureException;
import com.sleepycat.je.VerifyConfig;
import com.sleepycat.je.dbi.DbConfigManager;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.util.DbVerifyLog;
import com.sleepycat.je.utilint.CronScheduleParser;
import com.sleepycat.je.utilint.LoggerUtils;
import com.sleepycat.je.utilint.PollCondition;
import com.sleepycat.je.utilint.StoppableThread;

/**
 *  Periodically perform checksum verification, Btree verification, or both,
 *  depending on {@link com.sleepycat.je.EnvironmentConfig#VERIFY_LOG} and
 *  {@link com.sleepycat.je.EnvironmentConfig#VERIFY_BTREE}.
 *  
 *  The first-time start time and the period of the verification is determined
 *  by {@link com.sleepycat.je.EnvironmentConfig#VERIFY_SCHEDULE}.
 */
public class DataVerifier {
    private final EnvironmentImpl envImpl;
    private final Timer timer;
    private VerifyTask verifyTask;
    private boolean verifyLog;
    private boolean verifyBtree;
    private final DbVerifyLog dbLogVerifier;
    private final BtreeVerifier dbTreeVerifier;

    private long verifyDelay;
    private long verifyInterval;
    private String cronSchedule;

    private boolean shutdownRequest = false;
    
    private final String VERIFIER_SCHEDULE = "test.je.env.verifierSchedule"; 

    public DataVerifier(EnvironmentImpl envImpl) {

        this.envImpl = envImpl;
        this.timer = new Timer(
            envImpl.makeDaemonThreadName(
                Environment.DATA_CORRUPTION_VERIFIER_NAME),
            true /*isDaemon*/);
        dbLogVerifier = new DbVerifyLog(envImpl, 0);
        dbTreeVerifier = new BtreeVerifier(envImpl);
    }

    /**
     * Applies the new configuration, then cancels and reschedules the verify
     * task as needed.
     */
    public void configVerifyTask(DbConfigManager configMgr) {

        if (!updateConfig(configMgr)) {
            return;
        }

        synchronized (this) {
            if (!shutdownRequest) {
                cancel();

                if (cronSchedule != null) {
                    verifyTask = new VerifyTask(envImpl);

                    /*
                     * Use Timer.scheduleAtFixedRate to instead of
                     * Timer.schedule, since this is a long running task and
                     * the next task is NOT expected to be scheduled for
                     * 24 hours later, it is expected to be scheduled at
                     * a fixed time.
                     */
                    timer.scheduleAtFixedRate(
                        verifyTask, verifyDelay, verifyInterval);
                }
            }
        }
    }

    private void cancel() {
        if (verifyTask != null) {
            verifyTask.cancel();
        }

        /*
         * Stop verifier as soon as possible when it is disabled via
         * EnvironmentMutableConfig.
         */
        dbLogVerifier.setStopVerifyFlag(true);
        dbTreeVerifier.setStopVerifyFlag(true);
    }

    public void requestShutdown() {
        synchronized (this) {
            shutdownRequest = true;
            cancel();
            timer.cancel();
        }
    }

    public void shutdown() {
        requestShutdown();

        final int timeoutMs = 30000;

        final PollCondition cond = new PollCondition(2, timeoutMs) {
            @Override
            protected boolean condition() {
                /* Copy verifyTask since it may change in another thread. */
                final VerifyTask task = verifyTask;
                return task == null || !task.isRunning;
            }
        };

        if (!cond.await()) {
            LoggerUtils.warning(
                envImpl.getLogger(), envImpl,
                "Unable to shutdown data verifier after " + timeoutMs + "ms");
        }
    }

    public long getVerifyDelay() {
        return verifyDelay;
    }

    public long getVerifyInterval() {
        return verifyInterval;
    }

    public VerifyTask getVerifyTask() {
        return verifyTask;
    }

    public String getCronSchedule() {
        return cronSchedule;
    }

    /**
     * Applies the new configuration and returns whether it changed.
     */
    private boolean updateConfig(DbConfigManager configMgr) {

        /*
         * If set to false (which is not the default).
         */
        if (!configMgr.getBoolean(ENV_RUN_VERIFIER)) {
            if (cronSchedule == null) {
                return false;
            }
            cronSchedule = null;
            verifyDelay = 0;
            verifyInterval = 0;
            return true;
        } else {
            String newCronSchedule = configMgr.get(VERIFY_SCHEDULE);

            /*
             * If the data verifier schedule is set by system property and
             * it is not set through the JE source code explicitly, then
             * the system property will be used.
             *
             * This is used for JE unit test and JE standalone test.
             */
            String sysPropVerifySchedule =
                System.getProperty(VERIFIER_SCHEDULE);
            if (sysPropVerifySchedule != null &&
                !configMgr.isSpecified(VERIFY_SCHEDULE)) {
                newCronSchedule = sysPropVerifySchedule;
            }

            if (CronScheduleParser.checkSame(cronSchedule, newCronSchedule)) {
                return false;
            }
            CronScheduleParser csp = new CronScheduleParser(newCronSchedule);
            verifyDelay = csp.getDelayTime();
            verifyInterval = csp.getInterval();
            cronSchedule = newCronSchedule;
            return true;
        }
    }

    class VerifyTask extends TimerTask {
        private final EnvironmentImpl envImpl;
        private volatile boolean isRunning;

        VerifyTask(EnvironmentImpl envImpl) {
            this.envImpl = envImpl;
        }
        
        private void updateConfig() {
            DbConfigManager configMgr = envImpl.getConfigManager();

            verifyLog = configMgr.getBoolean(VERIFY_LOG);
            verifyBtree = configMgr.getBoolean(VERIFY_BTREE);

            dbLogVerifier.setReadDelay(
                configMgr.getDuration(VERIFY_LOG_READ_DELAY),
                TimeUnit.MILLISECONDS);

            final VerifyConfig btreeVerifyConfig = new VerifyConfig();
            btreeVerifyConfig.setVerifySecondaries(
                configMgr.getBoolean(VERIFY_SECONDARIES));
            btreeVerifyConfig.setVerifyDataRecords(
                configMgr.getBoolean(VERIFY_DATA_RECORDS));
            btreeVerifyConfig.setVerifyObsoleteRecords(
                configMgr.getBoolean(VERIFY_OBSOLETE_RECORDS));
            btreeVerifyConfig.setBatchSize(
                configMgr.getInt(VERIFY_BTREE_BATCH_SIZE));
            btreeVerifyConfig.setBatchDelay(
                configMgr.getDuration(VERIFY_BTREE_BATCH_DELAY),
                TimeUnit.MILLISECONDS);
            dbTreeVerifier.setBtreeVerifyConfig(btreeVerifyConfig);

            /*
             * 1. Why call dbTreeVerifier.setVerifyFlag here, rather than
             * immediately after call cancel in configVerifyTask?
             *    If we do that, then maybe before BtreeVerifier really gets
             *  the stop status of the flag, we set the status of the flag
             *  to be OK again. Then the previous task can not be stopped
             *  as soon as possible.
             *    If we do here, this means that one new task has already
             *    started, so the previous task has already been stopped.
             * 2. Why use synchronized?
             *    Consider the following scenario:
             *           shutdown Thread                 task Thread
             *
             *                                         !shutdownRequest
             *      
             *      shutdownRequest = true
             *      dbTreeVerifier.setStopVerifyFlag
             *
             *                                     dbTreeVerifier.setVerifyFlag
             */
            synchronized (DataVerifier.this) {
                if (!shutdownRequest) {
                    dbLogVerifier.setStopVerifyFlag(false);
                    dbTreeVerifier.setStopVerifyFlag(false);
                }
            }
        }

        @Override
        public void run() {
            /*
             * If the scheduled execution time of the scheduled run lags
             * very far from the current time due to the long-time current
             * verification, the scheduled run is just skipped.
             */
            if (System.currentTimeMillis() - scheduledExecutionTime() >=
                envImpl.getConfigManager().getDuration(VERIFY_MAX_TARDINESS)) {
                return;
            }

            isRunning = true;
            boolean success = false;
            updateConfig();
            try {
                if (verifyLog) {
                    dbLogVerifier.verifyAll();
                }
                if (verifyBtree) {
                    dbTreeVerifier.verifyAll();
                }
                success = true;
            } catch (EnvironmentFailureException|
                    CorruptSecondariesException e) {
                /* Do nothing. Just cancel this timer in finally. */
            } catch (Throwable e) {
                if (envImpl.isValid()) {
                    StoppableThread.handleUncaughtException(
                        envImpl.getLogger(), envImpl, Thread.currentThread(),
                        e);
                }
            } finally {
                if (!success) {
                    requestShutdown();
                }
                isRunning = false;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy