package io.blobkeeper.cluster.service; /* * Copyright (C) 2015 by Denis M. Gabaydulin * * 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. */ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedMap; import com.google.inject.AbstractModule; import com.google.inject.Provides; import io.blobkeeper.cluster.configuration.ClusterModule; import io.blobkeeper.cluster.domain.DifferenceInfo; import io.blobkeeper.cluster.domain.MerkleTreeInfo; import io.blobkeeper.cluster.domain.Node; import io.blobkeeper.cluster.domain.Role; import io.blobkeeper.cluster.util.ReplicationStatistic; import io.blobkeeper.common.configuration.MetricModule; import io.blobkeeper.common.configuration.RootModule; import io.blobkeeper.common.util.Block; import io.blobkeeper.common.util.BlockElt; import io.blobkeeper.common.util.MerkleTree; import io.blobkeeper.common.util.Utils; import io.blobkeeper.file.domain.File; import io.blobkeeper.file.service.DiskService; import io.blobkeeper.file.service.FileListService; import io.blobkeeper.file.service.FileStorage; import io.blobkeeper.file.service.PartitionService; import io.blobkeeper.index.dao.IndexDao; import io.blobkeeper.index.dao.PartitionDao; import io.blobkeeper.index.domain.IndexElt; import io.blobkeeper.index.domain.Partition; import io.blobkeeper.index.service.IndexService; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.jgroups.Address; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.View; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Guice; import org.testng.annotations.Test; import javax.inject.Inject; import javax.inject.Singleton; import java.util.Arrays; import java.util.Optional; import static com.google.common.collect.Range.closedOpen; import static java.lang.Thread.sleep; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; @Guice(modules = {RootModule.class, ReplicationClientServiceTest.Mocks.class, MetricModule.class, ClusterModule.class}) public class ReplicationClientServiceTest { private static final Logger log = LoggerFactory.getLogger(ReplicationClientServiceTest.class); @Inject private FileStorage fileStorage; @Inject private IndexService indexService; @Inject private ReplicationClientService replicationClientService; @Inject private ClusterMembershipService clusterMembershipService; @Inject private JChannel channel; @Inject private ReplicationHandlerService replicationHandlerService; @Inject private IndexDao indexDao; @Inject private PartitionDao partitionDao; @Inject private PartitionService partitionService; @Inject private RepairService repairService; @Inject private FileListService fileListService; @Inject private DiskService diskService; @Test public void doNotReplicatePartitionWithoutIndex() throws Exception { Address masterAddress = mock(Address.class); Address slaveAddress = mock(Address.class); Node master = new Node(Role.MASTER, masterAddress, System.currentTimeMillis()); Node slave = new Node(Role.SLAVE, slaveAddress, System.currentTimeMillis()); when(clusterMembershipService.getMaster()).thenReturn(Optional.of(master)); when(clusterMembershipService.getSelfNode()).thenReturn(master); when(clusterMembershipService.getChannel()).thenReturn(channel); View view = mock(View.class); when(channel.getView()).thenReturn(view); when(view.getMembers()).thenReturn(ImmutableList.of(masterAddress, slaveAddress)); Partition partition = new Partition(0, 0); when(partitionService.getPartitions(eq(0))).thenReturn(ImmutableList.of(partition)); when(partitionService.getActivePartition(eq(0))).thenReturn(partition); when(partitionService.getById(eq(0), eq(0))).thenReturn(partition); when(diskService.getDisks()).thenReturn(ImmutableList.of(0)); MerkleTree masterTree = Utils.createTree( closedOpen(0L, 100L), 32, ImmutableSortedMap.of(42L, new Block(1L, Arrays.asList(new BlockElt(1, 0, 2, 3, 4)))) ); MerkleTreeInfo masterInfo = new MerkleTreeInfo(); masterInfo.setTree(masterTree); masterInfo.setDisk(0); masterInfo.setPartition(0); // index already exists MerkleTree slaveTree = Utils.createTree( closedOpen(0L, 100L), 32, ImmutableSortedMap.of() ); MerkleTreeInfo slaveInfo = new MerkleTreeInfo(); slaveInfo.setTree(slaveTree); slaveInfo.setDisk(0); slaveInfo.setPartition(0); DifferenceInfo differenceInfo = new DifferenceInfo(); differenceInfo.setDisk(0); differenceInfo.setPartition(0); differenceInfo.setDifference(MerkleTree.difference(masterInfo.getTree(), slaveInfo.getTree())); when(clusterMembershipService.getMerkleTreeInfo(eq(masterAddress), eq(0), eq(0))).thenReturn(masterInfo); replicationClientService.replicate(differenceInfo, slaveAddress); sleep(100); verify(channel, times(0)).send(any(Message.class)); } @Test public void replicateActivePartition() throws Exception { Address masterAddress = mock(Address.class); Address slaveAddress = mock(Address.class); Node master = new Node(Role.MASTER, masterAddress, System.currentTimeMillis()); Node slave = new Node(Role.SLAVE, slaveAddress, System.currentTimeMillis()); when(clusterMembershipService.getMaster()).thenReturn(Optional.of(master)); when(clusterMembershipService.getSelfNode()).thenReturn(master); when(clusterMembershipService.getChannel()).thenReturn(channel); View view = mock(View.class); when(channel.getView()).thenReturn(view); when(view.getMembers()).thenReturn(ImmutableList.of(masterAddress, slaveAddress)); Partition partition = new Partition(0, 0); when(partitionService.getPartitions(eq(0))).thenReturn(ImmutableList.of(partition)); when(partitionService.getActivePartition(eq(0))).thenReturn(partition); when(partitionService.getById(eq(0), eq(0))).thenReturn(partition); when(diskService.getDisks()).thenReturn(ImmutableList.of(0)); File file = mock(File.class); when(fileListService.getFile(eq(0), eq(0))).thenReturn(file); IndexElt expected1 = new IndexElt.IndexEltBuilder() .id(42L) .type(1) .partition(partition) .offset(0L) .length(128L) .metadata(ImmutableMap.of("key", "value")) .crc(42L) .build(); IndexElt expected2 = new IndexElt.IndexEltBuilder() .id(43L) .type(1) .partition(partition) .offset(128L) .length(128L) .metadata(ImmutableMap.of("key", "value")) .crc(42L) .build(); when(indexService.getListByPartition(eq(partition))).thenReturn(ImmutableList.of(expected1, expected2)); DifferenceInfo differenceInfo = new DifferenceInfo(); differenceInfo.setDisk(0); differenceInfo.setPartition(0); differenceInfo.setCompletelyDifferent(true); replicationClientService.replicate(differenceInfo, slaveAddress); sleep(100); // TODO: FileUtils should be injected verify(file, times(2)).getFileChannel(); } @BeforeClass private void init() { initMocks(this); } @BeforeMethod private void clear() { reset(fileStorage, indexService, clusterMembershipService, channel, fileListService); } private static class MessageMatcher implements Matcher<Message> { private final Address dst; private final Address src; private final DifferenceInfo info; public MessageMatcher(Address dst, Address src, DifferenceInfo info) { this.dst = dst; this.src = src; this.info = info; } @Override public boolean matches(Object address) { Message message = ((Message) address); return dst == message.getDest() && src == message.getSrc() && info.equals(message.getObject()); } @Override public void _dont_implement_Matcher___instead_extend_BaseMatcher_() { } @Override public void describeTo(Description description) { } } public static class Mocks extends AbstractModule { @Provides @Singleton PartitionDao partitionDao() { return mock(PartitionDao.class); } @Provides @Singleton IndexDao indexDao() { return mock(IndexDao.class); } @Provides @Singleton IndexService indexService() { return mock(IndexService.class); } @Provides @Singleton FileStorage fileStorage() { return mock(FileStorage.class); } @Provides @Singleton ClusterMembershipService clusterMembershipService() { return mock(ClusterMembershipService.class); } @Provides @Singleton JChannel jChannel() { return mock(JChannel.class); } @Provides @Singleton ReplicationHandlerService replicationHandlerService() { return mock(ReplicationHandlerService.class); } @Provides @Singleton PartitionService partitionService() { return mock(PartitionService.class); } @Provides @Singleton FileListService fileListService() { return mock(FileListService.class); } @Provides @Singleton DiskService diskService() { return mock(DiskService.class); } @Override protected void configure() { } } }