Package com.foundationdb.server.test.mt

Source Code of com.foundationdb.server.test.mt.InsertUpdateDeleteMT

/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.foundationdb.server.test.mt;

import com.foundationdb.qp.row.Row;
import com.foundationdb.qp.rowtype.TableRowType;
import com.foundationdb.qp.util.SchemaCache;
import com.foundationdb.server.test.mt.util.MonitoredThread;
import com.foundationdb.server.test.mt.util.ThreadHelper;
import com.foundationdb.server.test.mt.util.ThreadMonitor.Stage;
import com.foundationdb.server.test.mt.util.ConcurrentTestBuilderImpl;
import com.foundationdb.server.test.mt.util.OperatorCreator;
import com.foundationdb.server.test.mt.util.TimeMarkerComparison;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

import static org.junit.Assert.assertEquals;

/** Basic isolation between SELECTs and concurrent DML. */
public final class InsertUpdateDeleteMT extends MTBase
{
    private static final String SCHEMA = "test";
    private static final String TABLE = "t";

    private int tID;
    private int pkID;
    private int xID;
    TableRowType tableRowType;
    List<Row> groupRows;
    List<Row> pkRows;
    List<Row> indexRows;

    @Before
    public void createAndLoad() {
        tID = createTable(SCHEMA, TABLE, "id INT NOT NULL PRIMARY KEY, x INT, y INT");
        createIndex(SCHEMA, TABLE, "x", "x");
        tableRowType = SchemaCache.globalSchema(ais()).tableRowType(tID);
        pkID = tableRowType.table().getPrimaryKey().getIndex().getIndexId();
        xID = tableRowType.table().getIndex("x").getIndexId();
        writeRows(row(tID, 3, 30, 300),
                  row(tID, 4, 40, 400),
                  row(tID, 6, 60, 600),
                  row(tID, 7, 70, 700));
        groupRows = runPlanTxn(groupScanCreator(tID));
        pkRows = runPlanTxn(indexScanCreator(tID, pkID));
        indexRows = runPlanTxn(indexScanCreator(tID, xID));
    }

    //
    // Insert
    //

    @Test
    public void groupScanAndInsert() {
        Row row = testRow(tableRowType, 5, 50, 500);
        test(groupScanCreator(tID), insertCreator(tID, row), groupRows, insert(groupRows, 2, row));
    }

    @Test
    public void pkScanAndInsert() {
        Row row = testRow(tableRowType, 5, 50, 500);
        Row pkRow = testRow(tableRowType.indexRowType(pkID), 5);
        test(indexScanCreator(tID, pkID), insertCreator(tID, row), pkRows, insert(pkRows, 2, pkRow));
    }

    @Test
    public void indexScanAndInsert() {
        Row row = testRow(tableRowType, 5, 50, 500);
        Row xRow = testRow(tableRowType.indexRowType(xID), 50, 5);
        test(indexScanCreator(tID, xID), insertCreator(tID, row), indexRows, insert(indexRows, 2, xRow));
    }

    //
    // Update
    //

    @Test
    public void groupScanAndUpdate() {
        Row oldRow = groupRows.get(0);
        Row newRow = testRow(tableRowType, 3, 30, 301); // Does not affect position
        test(groupScanCreator(tID), updateCreator(tID, oldRow, newRow), groupRows, replace(groupRows, 0, newRow));
    }

    @Test
    public void pkScanAndUpdate() {
        Row oldRow = groupRows.get(0);
        Row newRow = testRow(tableRowType, 1, 30, 300); // Moves row prior to first
        Row newPKRow = testRow(tableRowType.indexRowType(pkID), 1);
        test(indexScanCreator(tID, pkID), updateCreator(tID, oldRow, newRow), pkRows, replace(pkRows, 0, newPKRow));
    }

    @Test
    public void indexScanAndUpdate() {
        Row oldRow = groupRows.get(0);
        Row newRow = testRow(tableRowType, 3, 300, 300); // Moves row after last
        Row newXRow = testRow(tableRowType.indexRowType(xID), 300, 3);
        test(indexScanCreator(tID, xID), updateCreator(tID, oldRow, newRow), indexRows, combine(remove(indexRows, 0), newXRow));
    }

    //
    // Delete
    //

    @Test
    public void groupScanAndDelete() {
        Row row = groupRows.get(0);
        test(groupScanCreator(tID), deleteCreator(tID, row), groupRows, groupRows.subList(1, groupRows.size()));
    }

    @Test
    public void pkScanAndDelete() {
        Row row = groupRows.get(0);
        test(indexScanCreator(tID, pkID), deleteCreator(tID, row), pkRows, pkRows.subList(1, pkRows.size()));
    }

    @Test
    public void indexScanAndDelete() {
        Row row = groupRows.get(0);
        test(indexScanCreator(tID, xID), deleteCreator(tID, row), indexRows, indexRows.subList(1, indexRows.size()));
    }

    //
    // Internal
    //

    private void test(OperatorCreator scan, OperatorCreator dml, List<Row> startingRows, List<Row> finalRows) {
        List<MonitoredThread> threads = readAndDML(scan, dml);
        assertEquals("dml scan size", 1, threads.get(1).getScannedRows().size());
        compareRows(startingRows, threads.get(0).getScannedRows());
        compareRows(finalRows, runPlanTxn(scan));
    }

    /**  Plan that ensures reader starts transaction first but doesn't perform any reads until write is finished. */
    private List<MonitoredThread> readAndDML(OperatorCreator readPlan, OperatorCreator dmlPlan) {
        List<MonitoredThread> threads = ConcurrentTestBuilderImpl
            .create()
            .add("Scan", readPlan)
            .sync("a", Stage.POST_BEGIN)
            .sync("b", Stage.PRE_SCAN)
            .mark(Stage.PRE_BEGIN, Stage.PRE_SCAN)
            .add("DML", dmlPlan)
            .sync("a", Stage.PRE_BEGIN)
            .sync("b", Stage.FINISH)
            .mark(Stage.PRE_BEGIN, Stage.POST_COMMIT)
            .build(this);
        ThreadHelper.runAndCheck(threads);
        new TimeMarkerComparison(threads).verify("Scan:PRE_BEGIN",
                                                 "DML:PRE_BEGIN",
                                                 "DML:POST_COMMIT",
                                                 "Scan:PRE_SCAN");
        return threads;
    }
}
TOP

Related Classes of com.foundationdb.server.test.mt.InsertUpdateDeleteMT

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.