Brian O'Connor

Adding version stamping/checking to SimpleIntentStore

Change-Id: I08c0bf5e0f5a89275a72fa0900e52ca996942b79
......@@ -56,10 +56,28 @@ public class IntentData { //FIXME need to make this "immutable"
return intent.key();
}
public Timestamp version() {
return version;
}
public void setState(IntentState newState) {
this.state = newState;
}
/**
* Sets the version for this intent data.
* <p>
* The store should call this method only once when the IntentData is
* first passed into the pending map. Ideally, an IntentData is timestamped
* on the same thread that the called used to submit the intents.
* </p>
*
* @param version the version/timestamp for this intent data
*/
public void setVersion(Timestamp version) {
this.version = version;
}
public void setInstallables(List<Intent> installables) {
this.installables = ImmutableList.copyOf(installables);
}
......
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed 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.onosproject.store.impl;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ComparisonChain;
import org.onosproject.store.Timestamp;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkArgument;
/**
* A Timestamp that derives its value from the system clock time (in ns)
* on the controller where it is generated.
*/
public class SystemClockTimestamp implements Timestamp {
private final long unixTimestamp;
public SystemClockTimestamp() {
unixTimestamp = System.nanoTime();
}
@Override
public int compareTo(Timestamp o) {
checkArgument(o instanceof SystemClockTimestamp,
"Must be SystemClockTimestamp", o);
SystemClockTimestamp that = (SystemClockTimestamp) o;
return ComparisonChain.start()
.compare(this.unixTimestamp, that.unixTimestamp)
.result();
}
@Override
public int hashCode() {
return Objects.hash(unixTimestamp);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof SystemClockTimestamp)) {
return false;
}
SystemClockTimestamp that = (SystemClockTimestamp) obj;
return Objects.equals(this.unixTimestamp, that.unixTimestamp);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("unixTimestamp", unixTimestamp)
.toString();
}
public long systemTimestamp() {
return unixTimestamp;
}
}
......@@ -71,6 +71,8 @@ import static org.onosproject.net.intent.IntentState.INSTALL_REQ;
import static org.onosproject.net.intent.IntentState.WITHDRAWN;
import static org.slf4j.LoggerFactory.getLogger;
//TODO Note: this store will be removed
@Component(immediate = true, enabled = false)
@Service
public class DistributedIntentStore
......
......@@ -69,6 +69,8 @@ import static org.onlab.metrics.MetricsUtil.stopTimer;
import static org.onosproject.net.intent.IntentState.*;
import static org.slf4j.LoggerFactory.getLogger;
//TODO Note: this store will be removed
@Component(immediate = true, enabled = false)
@Service
public class HazelcastIntentStore
......
......@@ -30,6 +30,7 @@ import org.onosproject.net.intent.IntentStore;
import org.onosproject.net.intent.IntentStoreDelegate;
import org.onosproject.net.intent.Key;
import org.onosproject.store.AbstractStore;
import org.onosproject.store.impl.SystemClockTimestamp;
import org.slf4j.Logger;
import java.util.List;
......@@ -39,6 +40,8 @@ import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
//TODO Note: this store will be removed once the GossipIntentStore is stable
@Component(immediate = true)
@Service
//FIXME remove this
......@@ -48,9 +51,8 @@ public class SimpleIntentStore
private final Logger log = getLogger(getClass());
// current state maps FIXME.. make this a IntentData map
private final Map<Key, IntentData> current = Maps.newConcurrentMap();
private final Map<Key, IntentData> pending = Maps.newConcurrentMap(); //String is "key"
private final Map<Key, IntentData> pending = Maps.newConcurrentMap();
@Activate
public void activate() {
......@@ -160,17 +162,30 @@ public class SimpleIntentStore
@Override
public void write(IntentData newData) {
//FIXME need to compare the versions
current.put(newData.key(), newData);
try {
notifyDelegate(IntentEvent.getEvent(newData));
} catch (IllegalArgumentException e) {
//no-op
log.trace("ignore this exception: {}", e);
}
IntentData old = pending.get(newData.key());
if (old != null /* && FIXME version check */) {
pending.remove(newData.key());
synchronized (this) {
// TODO this could be refactored/cleaned up
IntentData currentData = current.get(newData.key());
IntentData pendingData = pending.get(newData.key());
if (currentData == null ||
// current version is less than or equal to newData's
// Note: current and newData's versions will be equal for state updates
currentData.version().compareTo(newData.version()) <= 0) {
current.put(newData.key(), newData);
if (pendingData != null
// pendingData version is less than or equal to newData's
// Note: a new update for this key could be pending (it's version will be greater)
&& pendingData.version().compareTo(newData.version()) <= 0) {
pending.remove(newData.key());
}
try {
notifyDelegate(IntentEvent.getEvent(newData));
} catch (IllegalArgumentException e) {
//no-op
log.trace("ignore this exception: {}", e);
}
}
}
}
......@@ -187,14 +202,26 @@ public class SimpleIntentStore
return (data != null) ? data.intent() : null;
}
@Override
public void addPending(IntentData data) {
//FIXME need to compare versions
pending.put(data.key(), data);
checkNotNull(delegate, "Store delegate is not set")
.process(data);
notifyDelegate(IntentEvent.getEvent(data));
data.setVersion(new SystemClockTimestamp());
synchronized (this) {
IntentData existingData = pending.get(data.key());
if (existingData == null ||
// existing version is strictly less than data's version
// Note: if they are equal, we already have the update
// TODO maybe we should still make this <= to be safe?
existingData.version().compareTo(data.version()) < 0) {
pending.put(data.key(), data);
checkNotNull(delegate, "Store delegate is not set")
.process(data);
notifyDelegate(IntentEvent.getEvent(data));
} else {
log.debug("IntentData {} is older than existing: {}",
data, existingData);
}
//TODO consider also checking the current map at this point
}
}
......
......@@ -39,6 +39,8 @@ import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
//TODO Note: this store will be removed
@Component(immediate = true)
@Service
public class SimpleIntentStore
......