/**
* Copyright 2008 the original author or authors.
*
* Licensed 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 net.sf.katta.operation.master;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sf.katta.master.Master;
import net.sf.katta.node.Node;
import net.sf.katta.operation.node.DeployResult;
import net.sf.katta.operation.node.NodeOperation;
import net.sf.katta.operation.node.OperationResult;
import net.sf.katta.operation.node.ShardDeployOperation;
import net.sf.katta.protocol.MasterQueue;
import net.sf.katta.protocol.NodeQueue;
import net.sf.katta.protocol.metadata.IndexDeployError;
import net.sf.katta.protocol.metadata.IndexMetaData;
import net.sf.katta.protocol.metadata.IndexDeployError.ErrorType;
import net.sf.katta.protocol.metadata.IndexMetaData.Shard;
import net.sf.katta.testutil.Mocks;
import org.junit.Test;
public class IndexDeployOperationTest extends AbstractMasterNodeZkTest {
@Test
public void testDeployError_NoNodes() throws Exception {
IndexDeployOperation deployCommand = new IndexDeployOperation(_indexName, _indexPath, 3);
deployCommand.execute(_context, EMPTY_LIST);
checkDeployError(ErrorType.NO_NODES_AVAILIBLE, _shardCount);
}
@Test
public void testDeployError_IndexNotAccessable() throws Exception {
IndexDeployOperation deployCommand = new IndexDeployOperation(_indexName, "wrongIndexPath", 3);
deployCommand.execute(_context, EMPTY_LIST);
checkDeployError(ErrorType.INDEX_NOT_ACCESSIBLE, 0);
}
private void checkDeployError(ErrorType errorType, int shardCount) throws Exception {
// check results
assertEquals(1, _protocol.getIndices().size());
IndexMetaData indexMD = _protocol.getIndexMD(_indexName);
assertNotNull(indexMD);
assertTrue(indexMD.hasDeployError());
IndexDeployError error = indexMD.getDeployError();
assertNotNull(error);
assertEquals(errorType, error.getErrorType());
Set<Shard> shards = indexMD.getShards();
assertEquals(shardCount, shards.size());
for (Shard shard : shards) {
assertTrue(_protocol.getShardNodes(shard.getName()).isEmpty());
}
}
@Test
public void testDeployError_ShardsNotDeployable() throws Exception {
// add nodes
List<Node> nodes = Mocks.mockNodes(3);
Mocks.publisNodes(_protocol, nodes);
// add index
int replicationLevel = 3;
IndexDeployOperation operation = new IndexDeployOperation(_indexName, _indexPath, replicationLevel);
operation.execute(_context, EMPTY_LIST);
// now complete the deployment
operation.nodeOperationsComplete(_context, Collections.EMPTY_LIST);
checkDeployError(ErrorType.SHARDS_NOT_DEPLOYABLE, _shardCount);
}
@Test
public void testDeployErrorExceptions_ShardsNotDeployable() throws Exception {
// add nodes
List<Node> nodes = Mocks.mockNodes(3);
List<NodeQueue> nodeQueues = Mocks.publisNodes(_protocol, nodes);
// add index
int replicationLevel = 3;
IndexDeployOperation operation = new IndexDeployOperation(_indexName, _indexPath, replicationLevel);
operation.execute(_context, EMPTY_LIST);
// now complete the deployment
List<OperationResult> results = new ArrayList<OperationResult>();
for (NodeQueue nodeQueue : nodeQueues) {
NodeOperation nodeOperation = nodeQueue.peek();
DeployResult deployResult = new DeployResult(_indexName);
Set<String> nodeShards = ((ShardDeployOperation) nodeOperation).getShardNames();
for (String shardName : nodeShards) {
deployResult.addShardException(shardName, new Exception());
}
results.add(deployResult);
}
operation.nodeOperationsComplete(_context, results);
checkDeployError(ErrorType.SHARDS_NOT_DEPLOYABLE, _shardCount);
IndexMetaData indexMD = _protocol.getIndexMD(_indexName);
IndexDeployError error = indexMD.getDeployError();
Set<Shard> shards = indexMD.getShards();
for (Shard shard : shards) {
assertEquals(3, error.getShardErrors(shard.getName()).size());
}
}
@Test
public void testDeploy() throws Exception {
// add nodes
List<Node> nodes = Mocks.mockNodes(3);
List<NodeQueue> nodeQueues = Mocks.publisNodes(_protocol, nodes);
// add index
int replicationLevel = 3;
IndexDeployOperation operation = new IndexDeployOperation(_indexName, _indexPath, replicationLevel);
operation.execute(_context, EMPTY_LIST);
// check results
Set<String> shards = new HashSet<String>();
int shardOnNodeCount = 0;
for (NodeQueue nodeQueue : nodeQueues) {
assertEquals(1, nodeQueue.size());
NodeOperation nodeOperation = nodeQueue.peek();
assertNotNull(nodeOperation);
assertTrue(nodeOperation instanceof ShardDeployOperation);
Set<String> nodeShards = ((ShardDeployOperation) nodeOperation).getShardNames();
assertEquals(_shardCount, nodeShards.size());
shards.addAll(nodeShards);
shardOnNodeCount += nodeShards.size();
}
assertEquals(_shardCount * replicationLevel, shardOnNodeCount);
assertEquals(_shardCount, shards.size());
// now complete the deployment
publisShards(nodes, nodeQueues);
operation.nodeOperationsComplete(_context, Collections.EMPTY_LIST);
assertEquals(1, _protocol.getIndices().size());
IndexMetaData indexMD = _protocol.getIndexMD(_indexName);
assertNotNull(indexMD);
assertNull(indexMD.getDeployError());
}
@Test
public void testDeployRespectsCurrentRunningDeployments() throws Exception {
// add nodes
List<Node> nodes = Mocks.mockNodes(2 * _shardCount);
List<NodeQueue> nodeQueues = Mocks.publisNodes(_protocol, nodes);
// add index
int replicationLevel = 1;
IndexDeployOperation operation1 = new IndexDeployOperation(_indexName, _indexPath, replicationLevel);
operation1.execute(_context, EMPTY_LIST);
List<MasterOperation> runningOps = new ArrayList<MasterOperation>();
runningOps.add(operation1);
IndexDeployOperation operation2 = new IndexDeployOperation(_indexName + "2", _indexPath, replicationLevel);
operation2.execute(_context, runningOps);
// check results
List<Integer> nodeQueueSizes = new ArrayList<Integer>();
for (NodeQueue nodeQueue : nodeQueues) {
nodeQueueSizes.add(nodeQueue.size());
}
for (Integer integer : nodeQueueSizes) {
assertEquals("unequal shard distribution: " + nodeQueueSizes, 1, integer.intValue());
}
}
@Test
public void testDeployShardMd() throws Exception {
List<Node> nodes = Mocks.mockNodes(3);
List<NodeQueue> queues = Mocks.publisNodes(_protocol, nodes);
IndexDeployOperation operation = new IndexDeployOperation(_indexName, _indexPath, 3);
operation.execute(_context, EMPTY_LIST);
publisShards(nodes, queues);
ArrayList<OperationResult> results = new ArrayList<OperationResult>();
DeployResult deployResult1 = new DeployResult(nodes.get(0).getName());
DeployResult deployResult2 = new DeployResult(nodes.get(1).getName());
DeployResult deployResult3 = new DeployResult(nodes.get(2).getName());
Map<String, String> metaMap = new HashMap<String, String>();
metaMap.put("a", "1");
String shard1Name = AbstractIndexOperation.createShardName(_indexName, _indexFile.listFiles()[0].getAbsolutePath());
deployResult1.addShardMetaDataMap(shard1Name, metaMap);
deployResult2.addShardMetaDataMap(shard1Name, metaMap);
deployResult3.addShardMetaDataMap(shard1Name, metaMap);
results.add(deployResult1);
results.add(deployResult2);
results.add(deployResult3);
operation.nodeOperationsComplete(_context, results);
IndexMetaData indexMD = _protocol.getIndexMD(_indexName);
assertEquals(1, indexMD.getShard(shard1Name).getMetaDataMap().size());
assertEquals(metaMap, indexMD.getShard(shard1Name).getMetaDataMap());
}
@Test
public void testDeployShardMdWithMissingNodeResult() throws Exception {
List<Node> nodes = Mocks.mockNodes(3);
Mocks.publisNodes(_protocol, nodes);
List<NodeQueue> queues = Mocks.publisNodes(_protocol, nodes);
IndexDeployOperation operation = new IndexDeployOperation(_indexName, _indexPath, 3);
operation.execute(_context, EMPTY_LIST);
publisShards(nodes, queues);
ArrayList<OperationResult> results = new ArrayList<OperationResult>();
DeployResult deployResult1 = new DeployResult(nodes.get(0).getName());
DeployResult deployResult2 = null;
DeployResult deployResult3 = new DeployResult(nodes.get(2).getName());
Map<String, String> metaMap = new HashMap<String, String>();
metaMap.put("a", "1");
String shard1Name = AbstractIndexOperation.createShardName(_indexName, _indexFile.listFiles()[0].getAbsolutePath());
deployResult1.addShardMetaDataMap(shard1Name, metaMap);
deployResult3.addShardMetaDataMap(shard1Name, metaMap);
results.add(deployResult1);
results.add(deployResult2);
results.add(deployResult3);
operation.nodeOperationsComplete(_context, results);
IndexMetaData indexMD = _protocol.getIndexMD(_indexName);
assertEquals(1, indexMD.getShard(shard1Name).getMetaDataMap().size());
assertEquals(metaMap, indexMD.getShard(shard1Name).getMetaDataMap());
}
@Test
public void testDeployUnderreplicatedIndex() throws Exception {
// add nodes
List<Node> nodes = Mocks.mockNodes(3);
List<NodeQueue> nodeQueues = Mocks.publisNodes(_protocol, nodes);
// add index
int replicationLevel = 3;
IndexDeployOperation deployOperation = new IndexDeployOperation(_indexName, _indexPath, replicationLevel);
deployOperation.execute(_context, EMPTY_LIST);
// publis only for one node
publisShard(nodes.get(0), nodeQueues.get(0));
deployOperation.nodeOperationsComplete(_context, Collections.EMPTY_LIST);
assertEquals(1, _protocol.getIndices().size());
IndexMetaData indexMD = _protocol.getIndexMD(_indexName);
assertNotNull(indexMD);
assertNull(indexMD.getDeployError());
// balance index should have been be triggered
Master master = Mocks.mockMaster();
MasterQueue masterQueue = _protocol.publishMaster(master);
MasterOperation operation = masterQueue.peek();
assertNotNull(operation);
assertTrue(operation instanceof BalanceIndexOperation);
}
}