package org.apache.usergrid.persistence.graph.serialization.impl.shard.impl;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.apache.commons.collections4.iterators.PushbackIterator;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
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.ShardGroupCompaction;
import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.schedulers.Schedulers;
/**
* Utility class that will take an iterator of all shards, and combine them into an iterator of ShardEntryGroups. These
* groups can then be used in a distributed system to handle concurrent reads and writes
*/
public class ShardEntryGroupIterator implements Iterator<ShardEntryGroup> {
private static final Logger logger = LoggerFactory.getLogger( ShardEntryGroupIterator.class );
private final ShardGroupCompaction shardGroupCompaction;
private final PushbackIterator<Shard> sourceIterator;
private final long minDelta;
private final ApplicationScope scope;
private final DirectedEdgeMeta directedEdgeMeta;
private ShardEntryGroup next;
/**
* Create a shard iterator
*
* @param shardIterator The iterator of all shards. Order is expected to be by the shard index from Long.MAX to
* Long.MIN
* @param minDelta The minimum delta we allow to consider shards the same group
*/
public ShardEntryGroupIterator( final Iterator<Shard> shardIterator, final long minDelta,
final ShardGroupCompaction shardGroupCompaction, final ApplicationScope scope,
final DirectedEdgeMeta directedEdgeMeta ) {
Preconditions.checkArgument( shardIterator.hasNext(), "Shard iterator must have shards present" );
this.scope = scope;
this.directedEdgeMeta = directedEdgeMeta;
this.sourceIterator = new PushbackIterator( shardIterator );
this.shardGroupCompaction = shardGroupCompaction;
this.minDelta = minDelta;
}
@Override
public boolean hasNext() {
if ( next == null ) {
advance();
}
return next != null;
}
@Override
public ShardEntryGroup next() {
if ( !hasNext() ) {
throw new NoSuchElementException( "No more elements exist in iterator" );
}
final ShardEntryGroup toReturn = next;
next = null;
return toReturn;
}
@Override
public void remove() {
throw new UnsupportedOperationException( "Remove is not supported" );
}
/**
* Advance to the next element
*/
private void advance() {
/**
* We loop through until we've exhausted our source, or we have 2 elements, which means
* they're > min time allocation from one another
*/
while ( sourceIterator.hasNext() ) {
if ( next == null ) {
next = new ShardEntryGroup( minDelta );
}
final Shard shard = sourceIterator.next();
//we can't add this one to the entries, it doesn't fit within the delta, allocate a new one and break
if ( next.addShard( shard ) ) {
if(logger.isTraceEnabled()) {
logger.trace("adding shard: {}", shard);
}
continue;
}
sourceIterator.pushback( shard );
if(logger.isTraceEnabled()) {
logger.trace("unable to add shard: {}, pushing back and stopping", shard);
}
break;
}
//now perform the audit (maybe)
if(next != null) {
shardGroupCompaction.evaluateShardGroup( scope, directedEdgeMeta, next );
}
}
}