/** * @param replica * @param externalizable * @param tcpReplicatorBuilder * @param maxEntrySizeBytes used to check that the last entry will fit into the buffer, it can not * be smaller than the size of and entry, if it is set smaller the buffer will over flow, it * can be larger then the entry, but setting it too large reduces the workable space in the * buffer. * @throws IOException */ TcpReplicator( @NotNull final Replica replica, @NotNull final EntryExternalizable externalizable, @NotNull final TcpReplicatorBuilder tcpReplicatorBuilder, final int maxEntrySizeBytes) throws IOException { super("TcpSocketReplicator-" + replica.identifier(), tcpReplicatorBuilder, maxEntrySizeBytes); serverInetSocketAddress = tcpReplicatorBuilder.serverInetSocketAddress(); heartBeatInterval = tcpReplicatorBuilder.heartBeatInterval(MILLISECONDS); long throttleBucketInterval = tcpReplicatorBuilder.throttleBucketInterval(MILLISECONDS); selectorTimeout = Math.min(heartBeatInterval, throttleBucketInterval); packetSize = tcpReplicatorBuilder.packetSize(); endpoints = tcpReplicatorBuilder.endpoints(); this.replica = replica; this.localIdentifier = replica.identifier(); this.maxEntrySizeBytes = maxEntrySizeBytes; this.externalizable = externalizable; start(); }
/** * used to exchange identifiers and timestamps and heartbeat intervals between the server and * client * * @param key the SelectionKey relating to the this cha * @throws java.io.IOException * @throws InterruptedException */ private void doHandShaking(@NotNull final SelectionKey key) throws IOException, InterruptedException { final Attached attached = (Attached) key.attachment(); final TcpSocketChannelEntryWriter writer = attached.entryWriter; final TcpSocketChannelEntryReader reader = attached.entryReader; if (attached.remoteIdentifier == Byte.MIN_VALUE) { final byte remoteIdentifier = reader.identifierFromBuffer(); if (remoteIdentifier == Byte.MIN_VALUE) return; attached.remoteIdentifier = remoteIdentifier; // we use the as iterating the activeKeys via the bitset wont create and Objects // but if we use the selector.keys() this will. selectionKeysStore[remoteIdentifier] = key; activeKeys.set(remoteIdentifier); if (LOG.isDebugEnabled()) { LOG.debug( "server-connection id={}, remoteIdentifier={}", localIdentifier, remoteIdentifier); } if (remoteIdentifier == localIdentifier) { throw new IllegalStateException( "Where are connecting to a remote " + "map with the same " + "identifier as this map, identifier=" + localIdentifier + ", " + "please change either this maps identifier or the remote one"); } attached.remoteModificationIterator = replica.acquireModificationIterator(remoteIdentifier, attached); writer.writeRemoteBootstrapTimestamp(replica.lastModificationTime(remoteIdentifier)); // tell the remote node, what are heartbeat interval is writer.writeRemoteHeartbeatInterval(heartBeatInterval); } if (attached.remoteBootstrapTimestamp == Long.MIN_VALUE) { attached.remoteBootstrapTimestamp = reader.remoteBootstrapTimestamp(); if (attached.remoteBootstrapTimestamp == Long.MIN_VALUE) return; } if (!attached.hasRemoteHeartbeatInterval) { long value = reader.remoteHeartbeatIntervalFromBuffer(); if (value == Long.MIN_VALUE) return; if (value < 0) { LOG.error("value=" + value); } // we add a 10% safety margin to the timeout time due to latency fluctuations on the network, // in other words we wont consider a connection to have // timed out, unless the heartbeat interval has exceeded 25% of the expected time. attached.remoteHeartbeatInterval = (long) (value * 1.25); // we have to make our selector poll interval at least as short as the minimum selector // timeout selectorTimeout = Math.min(selectorTimeout, value); if (selectorTimeout < 0) LOG.info(""); attached.hasRemoteHeartbeatInterval = true; // now we're finished we can get on with reading the entries attached.setHandShakingComplete(); attached.remoteModificationIterator.dirtyEntries(attached.remoteBootstrapTimestamp); reader.entriesFromBuffer(); } }