/*
* 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.model.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.junit.Test;
import com.fasterxml.uuid.UUIDComparator;
import com.google.common.collect.Sets;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/** @author tnine */
public class UUIDGeneratorTest {
@Test
public void testOrderingConcurrency() throws InterruptedException, ExecutionException {
//either all processor count, or 2 threads
final int numberThreads = Math.max( Runtime.getRuntime().availableProcessors(), 2 );
/**
* 10k uuids per thread
*/
final int count = 10000;
ExecutorService executor = Executors.newFixedThreadPool( numberThreads );
List<UUIDConsumer> consumers = new ArrayList<UUIDConsumer>( numberThreads );
for ( int i = 0; i < numberThreads; i++ ) {
consumers.add( new UUIDConsumer( count ) );
}
List<Future<Void>> futures = executor.invokeAll( consumers );
//wait for them all to finish
for(Future<Void> future: futures){
future.get();
}
//now validate each one is in order and does not intersect with any other
for(int i = 0; i < numberThreads; i ++){
UUIDConsumer current = consumers.get( i );
current.validateOrder();
for(int j = i+1; j < numberThreads; j++){
current.noIntersect( consumers.get( j ) );
}
}
}
private static class UUIDConsumer implements Callable<Void> {
private final int toGenerate;
private final List<UUID> results;
private UUIDConsumer( final int toGenerate ) {
this.toGenerate = toGenerate;
this.results = new ArrayList<UUID>( toGenerate );
}
/**
* Validate that each UUID is greater than than it's previous entry when comparing them
*/
public void validateOrder() {
for(int i = 0; i < toGenerate -1; i ++){
int comparison = UUIDComparator.staticCompare( results.get( i ), results.get( i+1 ) );
assertTrue(comparison < 0);
}
}
/**
* Validate the other UUID consumer does not have any intersection with this consumer
* @param other
*/
public void noIntersect(UUIDConsumer other){
Set<UUID> firstSet = new HashSet<UUID>(results);
Set<UUID> otherSet = new HashSet<UUID>(other.results);
Set<UUID> intersection = Sets.intersection(firstSet, otherSet);
assertEquals("No UUID Generator should have a UUID that intersects with another UUID", 0, intersection.size());
}
@Override
public Void call() throws Exception {
for ( int i = 0; i < toGenerate; i++ ) {
this.results.add( UUIDGenerator.newTimeUUID() );
}
return null;
}
}
}