/*
* INESC-ID, Instituto de Engenharia de Sistemas e Computadores Investigação e Desevolvimento em Lisboa
* Copyright 2013 INESC-ID and/or its affiliates and other
* contributors as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3.0 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.container.versioning.gmu;
import org.infinispan.Cache;
import org.infinispan.cacheviews.CacheView;
import org.infinispan.commons.hash.Hash;
import org.infinispan.commons.hash.MurmurHash3;
import org.infinispan.container.versioning.EntryVersion;
import org.infinispan.container.versioning.IncrementableEntryVersion;
import org.infinispan.dataplacement.ClusterSnapshot;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.factories.annotations.Start;
import org.infinispan.remoting.rpc.RpcManager;
import org.infinispan.remoting.transport.Address;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.infinispan.container.versioning.gmu.GMUVersion.NON_EXISTING;
import static org.infinispan.transaction.gmu.GMUHelper.toGMUVersion;
/**
* // TODO: Document this
*
* @author Pedro Ruivo
* @since 5.2
*/
public class ReplGMUVersionGenerator implements GMUVersionGenerator {
private static final Hash HASH = new MurmurHash3();
private RpcManager rpcManager;
private String cacheName;
private ClusterSnapshot currentClusterSnapshot;
private int currentViewId;
public ReplGMUVersionGenerator() {
}
@Inject
public final void init(RpcManager rpcManager, Cache cache) {
this.rpcManager = rpcManager;
this.cacheName = cache.getName();
}
@Start(priority = 11) // needs to happen *after* the transport starts.
public final void setEmptyViewId() {
currentViewId = -1;
currentClusterSnapshot = new ClusterSnapshot(Collections.singleton(rpcManager.getAddress()), HASH);
}
@Override
public final IncrementableEntryVersion generateNew() {
return new GMUReplicatedVersion(cacheName, currentViewId, this, 0);
}
@Override
public final IncrementableEntryVersion increment(IncrementableEntryVersion initialVersion) {
GMUVersion gmuEntryVersion = toGMUVersion(initialVersion);
return new GMUReplicatedVersion(cacheName, currentViewId, this, gmuEntryVersion.getThisNodeVersionValue() + 1);
}
@Override
public final GMUVersion mergeAndMax(EntryVersion... entryVersions) {
//validate the entry versions
for (EntryVersion entryVersion : entryVersions) {
if (entryVersion instanceof GMUReplicatedVersion) {
continue;
}
throw new IllegalArgumentException("Expected an array of GMU entry version but it has " +
entryVersion.getClass().getSimpleName());
}
return new GMUReplicatedVersion(cacheName, currentViewId, this, merge(entryVersions));
}
@Override
public final GMUVersion mergeAndMin(EntryVersion... entryVersions) {
if (entryVersions.length == 0) {
return new GMUReplicatedVersion(cacheName, currentViewId, this, NON_EXISTING);
}
long minVersion = NON_EXISTING;
for (EntryVersion entryVersion : entryVersions) {
long value = toGMUVersion(entryVersion).getThisNodeVersionValue();
if (minVersion == NON_EXISTING) {
minVersion = value;
} else if (value != NON_EXISTING) {
minVersion = Math.min(minVersion, value);
}
}
return new GMUReplicatedVersion(cacheName, currentViewId, this, minVersion);
}
@Override
public final GMUVersion calculateCommitVersion(EntryVersion mergedPrepareVersion,
Collection<Address> affectedOwners) {
return updatedVersion(mergedPrepareVersion);
}
@Override
public final GMUCacheEntryVersion convertVersionToWrite(EntryVersion version, int subVersion) {
GMUVersion gmuVersion = toGMUVersion(version);
return new GMUCacheEntryVersion(cacheName, currentViewId, this, gmuVersion.getThisNodeVersionValue(), subVersion);
}
@Override
public final GMUReadVersion convertVersionToRead(EntryVersion version) {
if (version == null) {
return null;
}
GMUVersion gmuVersion = toGMUVersion(version);
return new GMUReadVersion(cacheName, currentViewId, this, gmuVersion.getThisNodeVersionValue());
}
@Override
public GMUVersion calculateMaxVersionToRead(EntryVersion transactionVersion,
Collection<Address> alreadyReadFrom) {
if (alreadyReadFrom == null || alreadyReadFrom.isEmpty()) {
return null;
}
return updatedVersion(transactionVersion);
}
@Override
public GMUVersion calculateMinVersionToRead(EntryVersion transactionVersion,
Collection<Address> alreadyReadFrom) {
return updatedVersion(transactionVersion);
}
@Override
public GMUVersion setNodeVersion(EntryVersion version, long value) {
return new GMUReplicatedVersion(cacheName, currentViewId, this, value);
}
@Override
public GMUVersion updatedVersion(EntryVersion entryVersion) {
if (entryVersion instanceof GMUReplicatedVersion) {
return new GMUReplicatedVersion(cacheName, currentViewId, this,
((GMUReplicatedVersion) entryVersion).getThisNodeVersionValue());
} else if (entryVersion instanceof GMUDistributedVersion) {
int viewId = currentViewId;
ClusterSnapshot clusterSnapshot = getClusterSnapshot(viewId);
long[] newVersions = new long[clusterSnapshot.size()];
for (int i = 0; i < clusterSnapshot.size(); ++i) {
newVersions[i] = ((GMUDistributedVersion) entryVersion).getVersionValue(clusterSnapshot.get(i));
}
return new GMUDistributedVersion(cacheName, viewId, this, newVersions);
}
throw new IllegalArgumentException("Cannot handle " + entryVersion);
}
@Override
public synchronized final ClusterSnapshot getClusterSnapshot(int viewId) {
return currentClusterSnapshot;
}
@Override
public final Address getAddress() {
return rpcManager.getAddress();
}
@Override
public void updateViewHistory(List<CacheView> viewHistory) {
for (CacheView cacheView : viewHistory) {
addCacheView(cacheView);
}
}
@Override
public synchronized void addCacheView(CacheView cacheView) {
if (currentViewId >= cacheView.getViewId()) {
return;
}
currentViewId = cacheView.getViewId();
currentClusterSnapshot = new ClusterSnapshot(cacheView.getMembers(), HASH);
notifyAll();
}
@Override
public void gcCacheView(int minViewId) {
//no-op
}
@Override
public int getViewHistorySize() {
return 1; //only the most recent in saved
}
private long merge(EntryVersion... entryVersions) {
long max = NON_EXISTING;
for (EntryVersion entryVersion : entryVersions) {
if (entryVersion == null) {
continue;
}
GMUVersion gmuVersion = toGMUVersion(entryVersion);
max = Math.max(max, gmuVersion.getThisNodeVersionValue());
}
return max;
}
}