Package com.sleepycat.je.recovery

Source Code of com.sleepycat.je.recovery.CheckSplitsTest

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2004-2005
*      Sleepycat Software.  All rights reserved.
*
* $Id: CheckSplitsTest.java,v 1.4.4.1 2005/10/22 05:34:23 mark Exp $
*/
package com.sleepycat.je.recovery;

import java.util.logging.Level;

import com.sleepycat.bind.tuple.IntegerBinding;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.CheckpointConfig;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.DbInternal;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.config.EnvironmentParams;
import com.sleepycat.je.util.TestUtils;
import com.sleepycat.je.utilint.Tracer;

public class CheckSplitsTest extends CheckBase {

    private static final String DB_NAME = "simpleDB";

    /**
     * SR #10715
     * Splits must propagate up the tree at split time to avoid logging
     * inconsistent versions of ancestor INs.
     */
    public void testSplitPropagation()
        throws Throwable {

        EnvironmentConfig envConfig = TestUtils.initEnvConfig();
        turnOffEnvDaemons(envConfig);
        envConfig.setConfigParam(EnvironmentParams.NODE_MAX.getName(),
                                 "6");
        envConfig.setAllowCreate(true);
        envConfig.setTransactional(true);
                                
        DatabaseConfig dbConfig = new DatabaseConfig();
        dbConfig.setAllowCreate(true);
        dbConfig.setTransactional(true);

        EnvironmentConfig restartConfig = TestUtils.initEnvConfig();
        turnOffEnvDaemons(envConfig);
        envConfig.setConfigParam(EnvironmentParams.NODE_MAX.getName(),
                                 "6");
        envConfig.setTransactional(true);

        testOneCase(DB_NAME,
                    envConfig,
                    dbConfig,
                    new TestGenerator(){
                        void generateData(Database db)
                            throws DatabaseException {
                            addData(db);
                        }
                    },
                    restartConfig,
                    new DatabaseConfig());
    }

    private void addData(Database db)
        throws DatabaseException {

        int increment = 10;
        int max = 120;

        DatabaseEntry key = new DatabaseEntry();
        DatabaseEntry data = new DatabaseEntry();

        /* Populate a tree so it grows to 4 levels, then checkpoint. */

        for (int i = 0; i < max; i ++) {
            IntegerBinding.intToEntry(i*10, key);
            IntegerBinding.intToEntry(i*10, data);
            assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
        }


        CheckpointConfig ckptConfig = new CheckpointConfig();
        ckptConfig.setForce(true);
        env.checkpoint(ckptConfig);


        /* Add enough keys to split the left hand branch again. */
        for (int i = 50; i < 100; i+=2) {
            IntegerBinding.intToEntry(i, key);
            IntegerBinding.intToEntry(i, data);
            assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
        }

        /* Add enough keys to split the right hand branch. */
        for (int i = 630; i < 700; i ++) {
            IntegerBinding.intToEntry(i, key);
            IntegerBinding.intToEntry(i, data);
            assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
        }

        Tracer.trace(Level.SEVERE, DbInternal.envGetEnvironmentImpl(env),
                     "before split");

        /* Add enough keys to split the left hand branch again. */
        for (int i = 58; i < 75; i++) {
            IntegerBinding.intToEntry(i, key);
            IntegerBinding.intToEntry(i, data);
            assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
        }
    }

    /**
     * [#13435]  Checks that a DIN can be replayed with a full BIN parent.
     * When a DIN is replayed, it may already be present in the parent BIN.
     * Before fixing this bug, we searched without allowing splits and then
     * called IN.insertEntry, which would throw InconsistentNodeException if
     * the BIN was full.  We now search with splits allowed, which avoids the
     * exception; however, it causes a split when one is not needed.
     *
     * Note that an alternate fix would be to revert to an earlier version of
     * RecoveryManager.replaceOrInsertDuplicateRoot (differences are between
     * version 1.184 and 1.185).  The older version searches for an existing
     * entry, and then inserts if necessary.  This would avoid the extra split.
     * However, we had to search with splits allowed anyway to fix another
     * problem -- see testBINSplitDuringDeletedDINReplay.
     */
    public void testBINSplitDuringDINReplay()
        throws Throwable {

        EnvironmentConfig envConfig = TestUtils.initEnvConfig();
        turnOffEnvDaemons(envConfig);
        envConfig.setAllowCreate(true);

        DatabaseConfig dbConfig = new DatabaseConfig();
        dbConfig.setAllowCreate(true);
        dbConfig.setSortedDuplicates(true);

        testOneCase(DB_NAME,
                    envConfig,
                    dbConfig,
                    new TestGenerator(){
                        void generateData(Database db)
                            throws DatabaseException {
                            setupBINSplitDuringDINReplay(db);
                        }
                    },
                    envConfig,
                    dbConfig);
    }

    /**
     * Fill a BIN with entries, with a DIN in the first entry; then force the
     * BIN to be flushed, as might occur via eviction or checkpointing.
     */
    private void setupBINSplitDuringDINReplay(Database db)
        throws DatabaseException {

        final int max = 128;

        DatabaseEntry key = new DatabaseEntry();
        DatabaseEntry data = new DatabaseEntry();

        IntegerBinding.intToEntry(1, key);
        IntegerBinding.intToEntry(0, data);
        assertEquals(OperationStatus.SUCCESS,
                     db.putNoOverwrite(null, key, data));
        IntegerBinding.intToEntry(1, data);
        assertEquals(OperationStatus.SUCCESS,
                     db.putNoDupData(null, key, data));

        Cursor cursor = db.openCursor(null, null);

        for (int i = 2; i <= max; i ++) {
            IntegerBinding.intToEntry(i, key);
            IntegerBinding.intToEntry(0, data);
            assertEquals(OperationStatus.SUCCESS,
                         cursor.putNoOverwrite(key, data));
        }

        TestUtils.logBINAndIN(env, cursor);

        cursor.close();
    }

    /**
     * [#13435]  Checks that recovering a DIN causes a BIN split when needed.
     * This occurs when a DIN has been deleted and subsequently the BIN is
     * filled.  The DIN and the INDupDelete will be be replayed; we will insert
     * the DIN and then delete it.  In order to insert it, we may need to split
     * the BIN.  The sequence is:
     *
     * LN-a
     * (DupCountLN/) DIN (/DBIN/DupCountLN)
     * LN-b
     * DelDupLN-a (/DupCountLN)
     * DelDupLN-b (/DupCountLN)
     * INDupDelete compress
     * LN-c/etc to fill the BIN
     * BIN
     *
     * LN-a and LN-b are dups (same key).  After being compressed away, the
     * BIN is filled completely and flushed by the evictor or checkpointer.
     *
     * During recovery, when we replay the DIN and need to insert it into the
     * full BIN, therefore we need to split.  Before the bug fix, we did not
     * search with splits allowed, and got an InconsistentNodeException.
     */
    public void testBINSplitDuringDeletedDINReplay()
        throws Throwable {

        EnvironmentConfig envConfig = TestUtils.initEnvConfig();
        turnOffEnvDaemons(envConfig);
        envConfig.setAllowCreate(true);

        DatabaseConfig dbConfig = new DatabaseConfig();
        dbConfig.setAllowCreate(true);
        dbConfig.setSortedDuplicates(true);

        testOneCase(DB_NAME,
                    envConfig,
                    dbConfig,
                    new TestGenerator(){
                        void generateData(Database db)
                            throws DatabaseException {
                            setupBINSplitDuringDeletedDINReplay(db);
                        }
                    },
                    envConfig,
                    dbConfig);
    }

    /**
     * Insert two dups, delete them, and compress to free the BIN entry;
     * then fill the BIN with LNs and flush the BIN.
     */
    private void setupBINSplitDuringDeletedDINReplay(Database db)
        throws DatabaseException {

        int max = 128;

        DatabaseEntry key = new DatabaseEntry();
        DatabaseEntry data = new DatabaseEntry();

        IntegerBinding.intToEntry(0, key);
        IntegerBinding.intToEntry(0, data);
        assertEquals(OperationStatus.SUCCESS,
                     db.putNoOverwrite(null, key, data));
        IntegerBinding.intToEntry(1, data);
        assertEquals(OperationStatus.SUCCESS,
                     db.putNoDupData(null, key, data));

        assertEquals(OperationStatus.SUCCESS,
                     db.delete(null, key));

        env.compress();

        Cursor cursor = db.openCursor(null, null);

        for (int i = 1; i <= max; i ++) {
            IntegerBinding.intToEntry(i, key);
            IntegerBinding.intToEntry(0, data);
            assertEquals(OperationStatus.SUCCESS,
                         cursor.putNoOverwrite(key, data));
        }

        TestUtils.logBINAndIN(env, cursor);

        cursor.close();
    }
}
TOP

Related Classes of com.sleepycat.je.recovery.CheckSplitsTest

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.