/* * 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.usergrid.persistence.graph.serialization.impl.shard.impl; import java.util.Collections; import java.util.Iterator; import java.util.concurrent.ExecutionException; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.apache.usergrid.persistence.core.consistency.TimeService; import org.apache.usergrid.persistence.core.executor.TaskExecutorFactory; import org.apache.usergrid.persistence.core.scope.ApplicationScopeImpl; import org.apache.usergrid.persistence.graph.MarkedEdge; import org.apache.usergrid.persistence.graph.impl.SimpleMarkedEdge; import org.apache.usergrid.persistence.graph.serialization.impl.shard.AsyncTaskExecutor; import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta; import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeShardSerialization; import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard; import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardEntryGroup; import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardGroupDeletion; import org.apache.usergrid.persistence.model.entity.Id; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.netflix.astyanax.MutationBatch; import com.netflix.astyanax.connectionpool.exceptions.ConnectionException; import static org.apache.usergrid.persistence.core.util.IdGenerator.createId; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @Ignore("Pending re-enable of delete functionality") public class ShardGroupDeletionImplTest { protected AsyncTaskExecutor asyncTaskExecutor; protected ListeningExecutorService listeningExecutorService; private ApplicationScopeImpl scope; @Before public void setup() { this.scope = new ApplicationScopeImpl( createId( "application" ) ); } @After public void shutDown() { listeningExecutorService.shutdownNow(); } @Test public void shardCannotBeCompacted() throws ExecutionException, InterruptedException { final long createTime = 10000; final long currentTime = createTime; final Shard shard0 = new Shard( 0, createTime, true ); final Shard shard1 = new Shard( 1000, createTime, false ); //set a 1 delta for testing final ShardEntryGroup group = new ShardEntryGroup( 1 ); group.addShard( shard1 ); group.addShard( shard0 ); assertTrue( "this should return true for our test to succeed", group.isCompactionPending() ); final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class ); final TimeService timeService = mock( TimeService.class ); when( timeService.getCurrentTime() ).thenReturn( currentTime ); initExecutor( 1, 1 ); final ShardGroupDeletionImpl shardGroupDeletion = new ShardGroupDeletionImpl( asyncTaskExecutor, edgeShardSerialization, timeService ); final DirectedEdgeMeta directedEdgeMeta = getDirectedEdgeMeta(); final ListenableFuture<ShardGroupDeletion.DeleteResult> future = shardGroupDeletion.maybeDeleteShard( this.scope, directedEdgeMeta, group, Collections.emptyIterator() ); final ShardGroupDeletion.DeleteResult result = future.get(); assertEquals( "should not delete with pending compaction", ShardGroupDeletion.DeleteResult.COMPACTION_PENDING, result ); } @Test public void shardIsMinShard() throws ExecutionException, InterruptedException { final long currentTime = 1000; final Shard shard0 = Shard.MIN_SHARD; //set a 1 delta for testing final ShardEntryGroup group = new ShardEntryGroup( 1 ); group.addShard( shard0 ); assertTrue( "this should return false for our test to succeed", shard0.isMinShard() ); final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class ); final TimeService timeService = mock( TimeService.class ); when( timeService.getCurrentTime() ).thenReturn( currentTime ); initExecutor( 1, 1 ); final ShardGroupDeletionImpl shardGroupDeletion = new ShardGroupDeletionImpl( asyncTaskExecutor, edgeShardSerialization, timeService ); final DirectedEdgeMeta directedEdgeMeta = getDirectedEdgeMeta(); final ListenableFuture<ShardGroupDeletion.DeleteResult> future = shardGroupDeletion.maybeDeleteShard( this.scope, directedEdgeMeta, group, Collections.emptyIterator() ); final ShardGroupDeletion.DeleteResult result = future.get(); assertEquals( "should not delete min shard", ShardGroupDeletion.DeleteResult.NO_OP, result ); } @Test public void shardTooNew() throws ExecutionException, InterruptedException { final long createTime = 10000; final long currentTime = createTime; final Shard shard0 = new Shard( 0, createTime, true ); ////set a delta for way in the future final ShardEntryGroup group = new ShardEntryGroup( 1 ); group.addShard( shard0 ); assertFalse( "this should return false for our test to succeed", group.isCompactionPending() ); assertTrue( "this should return true for our test to succeed", group.isNew( currentTime ) ); final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class ); final TimeService timeService = mock( TimeService.class ); when( timeService.getCurrentTime() ).thenReturn( currentTime ); initExecutor( 1, 1 ); final ShardGroupDeletionImpl shardGroupDeletion = new ShardGroupDeletionImpl( asyncTaskExecutor, edgeShardSerialization, timeService ); final DirectedEdgeMeta directedEdgeMeta = getDirectedEdgeMeta(); final ListenableFuture<ShardGroupDeletion.DeleteResult> future = shardGroupDeletion.maybeDeleteShard( this.scope, directedEdgeMeta, group, Collections.emptyIterator() ); final ShardGroupDeletion.DeleteResult result = future.get(); assertEquals( "should not delete within timeout period", ShardGroupDeletion.DeleteResult.TOO_NEW, result ); } @Test public void hasEdges() throws ExecutionException, InterruptedException { final long createTime = 10000; final long currentTime = createTime * 2; final Shard shard0 = new Shard( 0, createTime, true ); ////set a delta for way in the future final ShardEntryGroup group = new ShardEntryGroup( 1 ); group.addShard( shard0 ); assertFalse( "this should return false for our test to succeed", group.isCompactionPending() ); assertFalse( "this should return false for our test to succeed", group.isNew( currentTime ) ); final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class ); final TimeService timeService = mock( TimeService.class ); when( timeService.getCurrentTime() ).thenReturn( currentTime ); initExecutor( 1, 1 ); final ShardGroupDeletionImpl shardGroupDeletion = new ShardGroupDeletionImpl( asyncTaskExecutor, edgeShardSerialization, timeService ); final DirectedEdgeMeta directedEdgeMeta = getDirectedEdgeMeta(); final Iterator<MarkedEdge> notMarkedIterator = Collections.singleton( ( MarkedEdge ) new SimpleMarkedEdge( createId( "source" ), "type", createId( "target" ), 1000, false ) ) .iterator(); final ListenableFuture<ShardGroupDeletion.DeleteResult> future = shardGroupDeletion.maybeDeleteShard( this.scope, directedEdgeMeta, group, notMarkedIterator ); final ShardGroupDeletion.DeleteResult result = future.get(); assertEquals( "should not delete with edges", ShardGroupDeletion.DeleteResult.CONTAINS_EDGES, result ); //now check when marked we also retain them final Iterator<MarkedEdge> markedEdgeIterator = Collections.singleton( ( MarkedEdge ) new SimpleMarkedEdge( createId( "source" ), "type", createId( "target" ), 1000, true ) ) .iterator(); final ListenableFuture<ShardGroupDeletion.DeleteResult> markedFuture = shardGroupDeletion.maybeDeleteShard( this.scope, directedEdgeMeta, group, markedEdgeIterator ); final ShardGroupDeletion.DeleteResult markedResult = future.get(); assertEquals( "should not delete with edges", ShardGroupDeletion.DeleteResult.CONTAINS_EDGES, markedResult ); } @Test public void testDeletion() throws ExecutionException, InterruptedException, ConnectionException { final long createTime = 10000; final long currentTime = createTime * 2; final Shard shard0 = new Shard( 1000, createTime, true ); ////set a delta for way in the future final ShardEntryGroup group = new ShardEntryGroup( 1 ); group.addShard( shard0 ); assertFalse( "this should return false for our test to succeed", group.isCompactionPending() ); assertFalse( "this should return false for our test to succeed", group.isNew( currentTime ) ); final DirectedEdgeMeta directedEdgeMeta = getDirectedEdgeMeta(); //mock up returning a mutation final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class ); final MutationBatch batch = mock( MutationBatch.class ); when( edgeShardSerialization.removeShardMeta( same( scope ), same( shard0 ), same( directedEdgeMeta ) ) ) .thenReturn( batch ); final TimeService timeService = mock( TimeService.class ); when( timeService.getCurrentTime() ).thenReturn( currentTime ); initExecutor( 1, 1 ); final ShardGroupDeletionImpl shardGroupDeletion = new ShardGroupDeletionImpl( asyncTaskExecutor, edgeShardSerialization, timeService ); final ListenableFuture<ShardGroupDeletion.DeleteResult> future = shardGroupDeletion.maybeDeleteShard( this.scope, directedEdgeMeta, group, Collections.emptyIterator() ); final ShardGroupDeletion.DeleteResult result = future.get(); assertEquals( "should delete", ShardGroupDeletion.DeleteResult.DELETED, result ); verify(batch).execute(); } private DirectedEdgeMeta getDirectedEdgeMeta() { final Id sourceId = createId( "source" ); final String edgeType = "test"; final DirectedEdgeMeta directedEdgeMeta = DirectedEdgeMeta.fromSourceNode( sourceId, edgeType ); return directedEdgeMeta; } private void initExecutor( final int numberThreads, final int queueLength ) { listeningExecutorService = MoreExecutors.listeningDecorator( TaskExecutorFactory .createTaskExecutor( "GraphTaskExecutor", numberThreads, queueLength, TaskExecutorFactory.RejectionAction.ABORT ) ); asyncTaskExecutor = mock( AsyncTaskExecutor.class ); when( asyncTaskExecutor.getExecutorService() ).thenReturn( listeningExecutorService ); } }