Package org.apache.cassandra.service

Source Code of org.apache.cassandra.service.AntiEntropyServiceTest

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cassandra.service;

import java.io.IOException;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.*;

import org.apache.cassandra.concurrent.StageManager;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.filter.QueryPath;
import org.apache.cassandra.db.ColumnFamilyStoreUtils;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.CompactionManager;
import org.apache.cassandra.db.DecoratedKey;
import org.apache.cassandra.db.RowMutation;
import org.apache.cassandra.db.Table;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.io.CompactionIterator.CompactedRow;
import org.apache.cassandra.io.DataOutputBuffer;
import org.apache.cassandra.io.SSTableReader;
import static org.apache.cassandra.service.AntiEntropyService.*;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.MerkleTree;

import org.apache.cassandra.CleanupHelper;
import org.apache.cassandra.io.SSTableUtils;

import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;

public class AntiEntropyServiceTest extends CleanupHelper
{
    public static InetAddress LOCAL = FBUtilities.getLocalAddress();

    // table and column family to test against
    public AntiEntropyService aes;
    public String tablename;
    public String cfname;

    static
    {
        try
        {
            StorageService.instance().initServer();
        }
        catch(Exception e)
        {
            throw new RuntimeException(e);
        }
    }

    @Before
    public void prepare() throws Exception
    {
        aes = AntiEntropyService.instance();

        tablename = DatabaseDescriptor.getTables().get(0);
        cfname = Table.open(tablename).getColumnFamilies().iterator().next();
    }

    @Test
    public void testInstance() throws Throwable
    {
        assert null != aes;
        assert aes == AntiEntropyService.instance();
    }
   
    @Test
    public void testValidatorPrepare() throws Throwable
    {
        Validator validator;

        // write
        List<RowMutation> rms = new LinkedList<RowMutation>();
        RowMutation rm;
        rm = new RowMutation(tablename, "key1");
        rm.add(new QueryPath(cfname, null, "Column1".getBytes()), "asdf".getBytes(), 0);
        rms.add(rm);
        ColumnFamilyStoreUtils.writeColumnFamily(rms);

        // sample
        validator = new Validator(new CFTuple(tablename, cfname), LOCAL);
        validator.prepare();

        // and confirm that the tree was split
        assertTrue(validator.tree.size() > 1);
    }
   
    @Test
    public void testValidatorComplete() throws Throwable
    {
        Validator validator = new Validator(new CFTuple(tablename, cfname), LOCAL);
        validator.prepare();
        validator.complete();

        // confirm that the tree was validated
        Token min = validator.tree.partitioner().getMinimumToken();
        assert null != validator.tree.hash(new Range(min, min));

        // wait for queued operations to be flushed
        flushAES().get(5000, TimeUnit.MILLISECONDS);
    }

    @Test
    public void testValidatorAdd() throws Throwable
    {
        Validator validator = new Validator(new CFTuple(tablename, cfname),
                                            LOCAL);
        IPartitioner part = validator.tree.partitioner();
        Token min = part.getMinimumToken();
        Token mid = part.midpoint(min, min);
        validator.prepare();

        // add a row with the minimum token
        validator.add(new CompactedRow(new DecoratedKey(min, "nonsense!"),
                                       new DataOutputBuffer()));

        // and a row after it
        validator.add(new CompactedRow(new DecoratedKey(mid, "inconceivable!"),
                                       new DataOutputBuffer()));
        validator.complete();

        // confirm that the tree was validated
        assert null != validator.tree.hash(new Range(min, min));
    }

    /**
     * Build a column family with 2 or more SSTables, and then force a major compaction
     */
    @Test
    public void testTreeCaching() throws Throwable
    {
        // populate column family
        List<RowMutation> rms = new LinkedList<RowMutation>();
        RowMutation rm = new RowMutation(tablename, "key");
        rm.add(new QueryPath(cfname, null, "Column1".getBytes()), "asdf".getBytes(), 0);
        rms.add(rm);
        // with two SSTables
        ColumnFamilyStoreUtils.writeColumnFamily(rms);
        ColumnFamilyStore store = ColumnFamilyStoreUtils.writeColumnFamily(rms);
       
        // force a major compaction, and wait for it to finish
        MerkleTree old = aes.getCachedTree(tablename, cfname, LOCAL);
        CompactionManager.instance().submitMajor(store, 0).get(5000, TimeUnit.MILLISECONDS);

        // check that a tree was created and cached
        flushAES().get(5000, TimeUnit.MILLISECONDS);
        assert old != aes.getCachedTree(tablename, cfname, LOCAL);
    }

    @Test
    public void testNotifyNeighbors() throws Throwable
    {
        // generate empty tree
        Validator validator = new Validator(new CFTuple(tablename, cfname), LOCAL);
        validator.prepare();
        validator.complete();

        // grab reference to the tree
        MerkleTree tree = validator.tree;

        // notify ourself (should immediately be delivered into AE_STAGE)
        aes.notifyNeighbors(validator, LOCAL, Arrays.asList(LOCAL));
        flushAES().get(5, TimeUnit.SECONDS);
       
        // confirm that our reference is not equal to the original due
        // to (de)serialization
        assert tree != aes.getCachedTree(tablename, cfname, LOCAL);
    }

    @Test
    public void testDifferencer() throws Throwable
    {
        // generate a tree
        Validator validator = new Validator(new CFTuple("ltable", "lcf"), LOCAL);
        validator.prepare();

        // create a clone with no values filled

        validator.complete();
        MerkleTree ltree = validator.tree;
        validator = new Validator(new CFTuple("rtable", "rcf"), LOCAL);
        validator.prepare();
        validator.complete();
        MerkleTree rtree = validator.tree;

        // change a range in one of the trees
        Token min = StorageService.instance().getPartitioner().getMinimumToken();
        ltree.invalidate(min);
        MerkleTree.TreeRange changed = ltree.invalids(new Range(min, min)).next();
        changed.hash("non-empty hash!".getBytes());

        // difference the trees
        Differencer diff = new Differencer(new CFTuple(tablename, cfname),
                                           LOCAL, LOCAL, ltree, rtree);
        diff.run();
       
        // ensure that the changed range was recorded
        assertEquals("Wrong number of differing ranges", 1, diff.differences.size());
        assertEquals("Wrong differing range", changed, diff.differences.get(0));
    }

    Future<Object> flushAES()
    {
        return StageManager.getStage(AE_SERVICE_STAGE).execute(new Callable<Object>(){
            public Boolean call()
            {
                return true;
            }
            });
    }
}
TOP

Related Classes of org.apache.cassandra.service.AntiEntropyServiceTest

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.