/* * Copyright 2014 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.map; import net.openhft.lang.io.Bytes; import org.jetbrains.annotations.NotNull; import java.io.Closeable; /** * @author Rob Austin. */ public interface Replica extends Closeable { /** * Provides the unique Identifier associated with this map instance.
An identifier is used to * determine which replicating node made the change.
If two nodes update their map at the same time * with different values, we have to deterministically resolve which update wins, because of eventual * consistency both nodes should end up locally holding the same data. Although it is rare two remote * nodes could receive an update to their maps at exactly the same time for the same key, we have to * handle this edge case, its therefore important not to rely on timestamps alone to reconcile the * updates. Typically the update with the newest timestamp should win, but in this example both * timestamps are the same, and the decision made to one node should be identical to the decision made to * the other. We resolve this simple dilemma by using a node identifier, each node will have a unique * identifier, the update from the node with the smallest identifier wins. * * @return the unique Identifier associated with this map instance */ byte identifier(); /** * Gets (if it does not exist, creates) an instance of ModificationIterator associated with a remote node, * this weak associated is bound using the {@code identifier}. * * @param remoteIdentifier the identifier of the remote node * @param modificationNotifier called when ever there is a change applied to the modification iterator * @return the ModificationIterator dedicated for replication to the remote node with the given identifier * @see #identifier() */ ModificationIterator acquireModificationIterator(short remoteIdentifier, ModificationNotifier modificationNotifier); /** * Returns the timestamp of the last change from the specified remote node, already replicated * to this Replica. *
*Used in conjunction with replication, to back fill data from a remote node. This node * may have missed updates while it was not been running or connected via TCP. * * @param remoteIdentifier the identifier of the remote node to check last replicated update * time from * @return a timestamp of the last modification to an entry, or 0 if there are no entries. * @see #identifier() */ long lastModificationTime(byte remoteIdentifier); /** * notifies when there is a changed to the modification iterator */ interface ModificationNotifier { public static final ModificationNotifier NOP = new ModificationNotifier() { @Override public void onChange() { } }; /** * called when ever there is a change applied to the modification iterator */ void onChange(); } /** * Holds a record of which entries have modification. Each remote map supported will require a * corresponding ModificationIterator instance */ interface ModificationIterator { /** * @return {@code true} if the is another entry to be received via {@link * #nextEntry(Replica.EntryCallback, int chronicleId)} */ boolean hasNext(); /** * A non-blocking call that provides the entry that has changed to {@code callback.onEntry()}. * * @param callback a callback which will be called when a new entry becomes available. * @param chronicleId only assigned when clustering * @return {@code true} if the entry was accepted by the {@code callback.onEntry()} method, {@code * false} if the entry was not accepted or was not available */ boolean nextEntry(@NotNull final EntryCallback callback, final int chronicleId); /** * Dirties all entries with a modification time equal to {@code fromTimeStamp} or newer. It means all * these entries will be considered as "new" by this ModificationIterator and iterated once again no * matter if they have already been. *
*This functionality is used to publish recently modified entries to a new remote node as it
* connects.
*
* @param fromTimeStamp the timestamp from which all entries should be dirty
*/
void dirtyEntries(long fromTimeStamp);
}
/**
* supports reading and writing serialize entries
*/
interface EntryExternalizable {
/**
* The map implements this method to save its contents.
*
* @param entry the byte location of the entry to be stored
* @param destination a buffer the entry will be written to, the segment may reject this operation and
* add zeroBytes, if the identifier in the entry did not match the maps local
* @param chronicleId used in cluster into identify the canonical map or queue
*/
void writeExternalEntry(@NotNull Bytes entry, @NotNull Bytes destination, int chronicleId);
/**
* The map implements this method to restore its contents. This method must read the values in the
* same sequence and with the same types as were written by {@code writeExternalEntry()}. This method
* is typically called when we receive a remote replication event, this event could originate from
* either a remote {@code put(K key, V value)} or {@code remove(Object key)}
*
* @param source bytes to read an entry from
*/
void readExternalEntry(@NotNull Bytes source);
}
/**
* provides a key and value from NativeBytes, this can be used in conjunction with the modification
* iterator to get the key and value out of the NativeBytes
*/
interface EntryResolver