Updated SPI model and added examples

This commit is contained in:
leventov
2015-05-18 13:42:07 +01:00
parent 6c56813b5e
commit 5829cdbfba
10 changed files with 195 additions and 33 deletions
@@ -18,7 +18,6 @@
package net.openhft.chronicle.hash;
import net.openhft.chronicle.map.MapEntry;
import net.openhft.chronicle.map.MapEntryOperations;
import org.jetbrains.annotations.NotNull;
@@ -40,14 +39,10 @@ public interface HashEntry<K> {
@NotNull Value<K, ?> key();
/**
* Removes the entry from the {@code ChronicleHash}. Returns {@code true} if the remove was
* successful, {@code false} if it failed for any reason.
*
* <p>This method if the default implementation for {@link MapEntryOperations#remove(MapEntry)},
* which might be customized over the default.
* Removes the entry from the {@code ChronicleHash}.
*
* @throws IllegalStateException if some locking/state conditions required to perform remove
* operation are not met
*/
boolean defaultRemove();
void doRemove();
}
@@ -0,0 +1,32 @@
/*
* Copyright 2015 Higher Frequency Trading
*
* http://www.higherfrequencytrading.com
*
* 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 net.openhft.chronicle.hash.replication;
public final class DefaultEventualConsistencyStrategy {
public static boolean shouldApplyRemoteModification(
ReplicatedEntry entry, ReplicationContext context) {
long remoteTimestamp = context.remoteTimestamp();
long originTimestamp = entry.originTimestamp();
return remoteTimestamp > originTimestamp || (remoteTimestamp == originTimestamp &&
context.remoteIdentifier() <= entry.originIdentifier());
}
private DefaultEventualConsistencyStrategy() {}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2015 Higher Frequency Trading
*
* http://www.higherfrequencytrading.com
*
* 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 net.openhft.chronicle.hash.replication;
public interface ReplicatedEntry {
byte originIdentifier();
long originTimestamp();
}
@@ -16,7 +16,10 @@
* limitations under the License.
*/
package net.openhft.chronicle.hash;
package net.openhft.chronicle.hash.replication;
import net.openhft.chronicle.hash.ChronicleHash;
import net.openhft.chronicle.hash.HashContext;
/**
* Context of internal replication operation.
@@ -24,4 +27,8 @@ package net.openhft.chronicle.hash;
* @param <K> the key type of accessed {@link ChronicleHash}
*/
public interface ReplicationContext<K> extends HashContext<K> {
byte remoteIdentifier();
long remoteTimestamp();
}
@@ -45,16 +45,11 @@ public interface MapAbsentEntry<K, V> {
/**
* Inserts the new entry into the map, of {@link #absentKey() the key} and the given value.
* Returns {@code true} if the insertion was successful, {@code false} if it failed for
* any reason.
*
* <p>This method if the default implementation for {@link MapAbsentEntryOperations#insert},
* which might be customized over the default.
*
* @throws IllegalStateException if some locking/state conditions required to perform insertion
* operation are not met
*/
boolean defaultInsert(Value<V, ?> value);
void doInsert(Value<V, ?> value);
/**
* Returns the "nil" value, which should be inserted into the map in this context. This
@@ -19,8 +19,12 @@
package net.openhft.chronicle.map;
import net.openhft.chronicle.hash.Value;
import net.openhft.chronicle.hash.replication.ReplicatedEntry;
import net.openhft.chronicle.hash.replication.ReplicationContext;
import org.jetbrains.annotations.NotNull;
import static net.openhft.chronicle.hash.replication.DefaultEventualConsistencyStrategy.shouldApplyRemoteModification;
/**
* SPI interface for customizing "low-level" insertion of a new entry into {@link ChronicleMap}.
*
@@ -35,13 +39,21 @@ public interface MapAbsentEntryOperations<K, V> {
* Returns {@code true} if the insertion was successful, {@code false} if it failed
* for any reason.
*
* @implNote simply delegates to {@link MapAbsentEntry#defaultInsert(Value)}.
*
* @throws IllegalStateException if some locking/state conditions required to perform insertion
* operation are not met
*/
default boolean insert(@NotNull MapAbsentEntry<K, V> absentEntry, Value<V, ?> value) {
return absentEntry.defaultInsert(value);
MapContext<K, V> context = absentEntry.context();
boolean shouldInsert = !(context instanceof ReplicationContext) ||
!(absentEntry instanceof ReplicatedEntry) ||
shouldApplyRemoteModification((ReplicatedEntry) absentEntry,
(ReplicationContext) context);
if (shouldInsert) {
absentEntry.doInsert(value);
return true;
} else {
return false;
}
}
/**
@@ -42,15 +42,10 @@ public interface MapEntry<K, V> extends HashEntry<K> {
@NotNull Value<V, ?> value();
/**
* Replaces the entry's value with the new one. Returns {@code true} if the replace operation
* was successful, {@code false} if it failed for any reason.
*
* <p>This method if the default implementation for {@link MapEntryOperations#replaceValue},
* which might be customized over the default.
* Replaces the entry's value with the new one.
*
* @throws IllegalStateException if some locking/state conditions required to perform replace
* operation are not met
*/
boolean defaultReplaceValue(Value<V, ?> newValue);
void doReplaceValue(Value<V, ?> newValue);
}
@@ -19,8 +19,12 @@
package net.openhft.chronicle.map;
import net.openhft.chronicle.hash.Value;
import net.openhft.chronicle.hash.replication.ReplicatedEntry;
import net.openhft.chronicle.hash.replication.ReplicationContext;
import org.jetbrains.annotations.NotNull;
import static net.openhft.chronicle.hash.replication.DefaultEventualConsistencyStrategy.shouldApplyRemoteModification;
/**
* SPI interface for customizing "low-level" modification operations on the <i>present</i>
* {@link ChronicleMap} entry.
@@ -29,33 +33,45 @@ import org.jetbrains.annotations.NotNull;
* @param <V> type of the value in {@code ChronicleMap}
*/
public interface MapEntryOperations<K, V> {
static boolean shouldModify(@NotNull MapEntry<?, ?> entry) {
MapContext<?, ?> context = entry.context();
return !(context instanceof ReplicationContext) ||
shouldApplyRemoteModification((ReplicatedEntry) entry,
(ReplicationContext) context);
}
/**
* Removes the given entry from the {@code ChronicleHash}. Returns {@code true} if the remove
* was successful, {@code false} if it failed for any reason.
*
* @implNote simply delegates to {@link MapEntry#defaultRemove()}.
*
* @param entry the entry to remove
* @throws IllegalStateException if some locking/state conditions required to perform remove
* operation are not met
*/
default boolean remove(@NotNull MapEntry<K, V> entry) {
return entry.defaultRemove();
if (shouldModify(entry)) {
entry.doRemove();
return true;
} else {
return false;
}
}
/**
* Replaces the given entry's value with the new one. Returns {@code true} if the replace
* operation was successful, {@code false} if it failed for any reason.
*
* @implNote simply delegates to {@link MapEntry#defaultReplaceValue(Value)
* entry.defaultReplace(newValue)}.
*
* @param entry the entry to replace the value in
* @throws IllegalStateException if some locking/state conditions required to perform replace
* operation are not met
*/
default boolean replaceValue(@NotNull MapEntry<K, V> entry, Value<V, ?> newValue) {
return entry.defaultReplaceValue(newValue);
if (shouldModify(entry)) {
entry.doReplaceValue(newValue);
return true;
} else {
return false;
}
}
}