/*
* 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;
import com.google.common.base.Optional;
import com.google.inject.Inject;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.usergrid.StressTest;
import org.apache.usergrid.persistence.core.guice.MigrationManagerRule;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.core.test.ITRunner;
import org.apache.usergrid.persistence.core.test.UseModules;
import org.apache.usergrid.persistence.core.util.IdGenerator;
import org.apache.usergrid.persistence.graph.guice.TestGraphModule;
import org.apache.usergrid.persistence.graph.impl.SimpleSearchByEdgeType;
import org.apache.usergrid.persistence.model.entity.Id;
import org.apache.usergrid.persistence.model.util.UUIDGenerator;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observable;
import rx.Subscriber;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import static org.apache.usergrid.persistence.graph.test.util.EdgeTestUtils.createEdge;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(ITRunner.class)
@UseModules(TestGraphModule.class)
@Category(StressTest.class)
public class GraphManagerStressTest {
private static final Logger logger = LoggerFactory.getLogger( GraphManagerStressTest.class );
@Inject
private GraphManagerFactory factory;
@Inject
@Rule
public MigrationManagerRule migrationManagerRule;
protected ApplicationScope scope;
@Before
public void setup() {
scope = mock( ApplicationScope.class );
Id orgId = mock( Id.class );
when( orgId.getType() ).thenReturn( "organization" );
when( orgId.getUuid() ).thenReturn( UUIDGenerator.newTimeUUID() );
when( scope.getApplication() ).thenReturn( orgId );
}
@Test
@Category(StressTest.class)
public void writeThousands() throws InterruptedException {
EdgeGenerator generator = new EdgeGenerator() {
private Set<Id> sourceIds = new HashSet<Id>();
@Override
public Edge newEdge() {
Edge edge = createEdge( "source", "test", "target" );
sourceIds.add( edge.getSourceNode() );
return edge;
}
@Override
public Observable<MarkedEdge> doSearch( final GraphManager manager ) {
final long timestamp = System.currentTimeMillis();
return Observable.create( new Observable.OnSubscribe<MarkedEdge>() {
@Override
public void call( final Subscriber<? super MarkedEdge> subscriber ) {
try {
for ( Id sourceId : sourceIds ) {
final Iterable<MarkedEdge> edges = manager.loadEdgesFromSource(
new SimpleSearchByEdgeType( sourceId, "test", timestamp, SearchByEdgeType.Order.DESCENDING, Optional
.<Edge>absent() ) )
.toBlocking().toIterable();
for ( MarkedEdge edge : edges ) {
logger.debug( "Firing on next for edge {}", edge );
subscriber.onNext( edge );
}
}
}
catch ( Throwable throwable ) {
subscriber.onError( throwable );
}
}
} );
//TODO T.N keep this code it's exhibiting a failure /exception swallowing with RX when our scheduler
// is full
//
// return Observable.create( new Observable.OnSubscribe<Edge>() {
//
// @Override
// public void call( final Subscriber<? super Edge> subscriber ) {
// for ( Id sourceId : sourceIds ) {
//
// final Observable<Edge> edges =
// manager.loadEdgesFromSource( new
// SimpleSearchByEdgeType( sourceId, "test", uuid, null ) );
//
// edges.subscribe( new Action1<Edge>() {
// @Override
// public void call( final Edge edge ) {
// subscriber.onNext( edge );
// }
// },
//
// new Action1<Throwable>() {
// @Override
// public void call( final Throwable throwable ) {
// subscriber.onError( throwable );
// }
// });
// }
// }
// } ) ;
}
};
doTest( generator );
}
@Category(StressTest.class)
@Test
public void writeThousandsSingleSource() throws InterruptedException {
EdgeGenerator generator = new EdgeGenerator() {
private Id sourceId = IdGenerator.createId( "source" );
@Override
public Edge newEdge() {
Edge edge = createEdge( sourceId, "test", IdGenerator.createId( "target" ) );
return edge;
}
@Override
public Observable<MarkedEdge> doSearch( final GraphManager manager ) {
return manager.loadEdgesFromSource( new SimpleSearchByEdgeType( sourceId, "test", System.currentTimeMillis(), SearchByEdgeType.Order.DESCENDING, Optional.<Edge>absent() ) );
}
};
doTest( generator );
}
@Test
@Category(StressTest.class)
public void writeThousandsSingleTarget() throws InterruptedException {
EdgeGenerator generator = new EdgeGenerator() {
private Id targetId = IdGenerator.createId( "target" );
@Override
public Edge newEdge() {
Edge edge = createEdge( IdGenerator.createId( "source" ), "test", targetId );
return edge;
}
@Override
public Observable<MarkedEdge> doSearch( final GraphManager manager ) {
return manager.loadEdgesToTarget( new SimpleSearchByEdgeType( targetId, "test", System.currentTimeMillis(), SearchByEdgeType.Order.DESCENDING, Optional.<Edge>absent() ) );
}
};
doTest( generator );
}
/**
* Execute the test with the generator
*/
private void doTest( EdgeGenerator generator ) throws InterruptedException {
GraphManager manager = factory.createEdgeManager( scope );
int limit = 10000;
final StopWatch timer = new StopWatch();
timer.start();
final Set<Edge> ids = new HashSet<Edge>( limit );
for ( int i = 0; i < limit; i++ ) {
Edge edge = generator.newEdge();
Edge returned = manager.writeEdge( edge ).toBlocking().last();
assertNotNull( "Returned has a version", returned.getTimestamp() );
ids.add( returned );
if ( i % 1000 == 0 ) {
logger.info( " Wrote: " + i );
}
}
timer.stop();
logger.info( "Total time to write {} entries {}ms", limit, timer.getTime() );
timer.reset();
timer.start();
final CountDownLatch latch = new CountDownLatch( 1 );
generator.doSearch( manager ).subscribe( new Subscriber<Edge>() {
@Override
public void onCompleted() {
timer.stop();
latch.countDown();
}
@Override
public void onError( final Throwable throwable ) {
fail( "Exception occurced " + throwable );
}
@Override
public void onNext( final Edge edge ) {
ids.remove( edge );
}
} );
latch.await();
assertEquals( 0, ids.size() );
logger.info( "Total time to read {} entries {}ms", limit, timer.getTime() );
}
private interface EdgeGenerator {
/**
* Create a new edge to persiste
*/
public Edge newEdge();
public Observable<MarkedEdge> doSearch( final GraphManager manager );
}
}