use of com.github.ambry.clustermap.DataNodeId in project ambry by linkedin.
the class ReplicaThread method fixMissingStoreKeys.
/**
* Gets all the messages from the remote node for the missing keys and writes them to the local store
* @param connectedChannel The connected channel that represents a connection to the remote replica
* @param replicasToReplicatePerNode The information about the replicas that is being replicated
* @param exchangeMetadataResponseList The missing keys in the local stores whose message needs to be retrieved
* from the remote stores
* @param remoteColoGetRequestForStandby boolean which indicates if we are getting missing keys for standby or
* non-leader replica pairs during leader-based replication.
* @throws IOException
* @throws ReplicationException
*/
void fixMissingStoreKeys(ConnectedChannel connectedChannel, List<RemoteReplicaInfo> replicasToReplicatePerNode, List<ExchangeMetadataResponse> exchangeMetadataResponseList, boolean remoteColoGetRequestForStandby) throws IOException, ReplicationException {
long fixMissingStoreKeysStartTimeInMs = time.milliseconds();
GetResponse getResponse = null;
try {
if (exchangeMetadataResponseList.size() != replicasToReplicatePerNode.size() || replicasToReplicatePerNode.size() == 0) {
throw new IllegalArgumentException("ExchangeMetadataResponseList size " + exchangeMetadataResponseList.size() + " and replicasToReplicatePerNode size " + replicasToReplicatePerNode.size() + " should be the same and greater than zero");
}
DataNodeId remoteNode = replicasToReplicatePerNode.get(0).getReplicaId().getDataNodeId();
getResponse = getMessagesForMissingKeys(connectedChannel, exchangeMetadataResponseList, replicasToReplicatePerNode, remoteNode, remoteColoGetRequestForStandby);
writeMessagesToLocalStoreAndAdvanceTokens(exchangeMetadataResponseList, getResponse, replicasToReplicatePerNode, remoteNode, remoteColoGetRequestForStandby);
} finally {
if (getResponse != null && getResponse.getInputStream() instanceof NettyByteBufDataInputStream) {
// if the InputStream is NettyByteBufDataInputStream based, it's time to release its buffer.
((NettyByteBufDataInputStream) (getResponse.getInputStream())).getBuffer().release();
}
long fixMissingStoreKeysTime = time.milliseconds() - fixMissingStoreKeysStartTimeInMs;
replicationMetrics.updateFixMissingStoreKeysTime(fixMissingStoreKeysTime, replicatingFromRemoteColo, replicatingOverSsl, datacenterName);
}
}
use of com.github.ambry.clustermap.DataNodeId in project ambry by linkedin.
the class ReplicaThread method applyTtlUpdate.
/**
* Applies a TTL update to the blob described by {@code messageInfo}.
* @param messageInfo the {@link MessageInfo} that will be transformed into a TTL update
* @param remoteReplicaInfo The remote replica that is being replicated from
* @throws StoreException
*/
private void applyTtlUpdate(MessageInfo messageInfo, RemoteReplicaInfo remoteReplicaInfo) throws StoreException {
DataNodeId remoteNode = remoteReplicaInfo.getReplicaId().getDataNodeId();
try {
// NOTE: It is possible that the key in question may have expired and this TTL update is being applied after it
// is deemed expired. The store will accept the op (BlobStore looks at whether the op was valid to do at the time
// of the op, not current time) but if compaction is running at the same time and has decided to clean up the
// record before this ttl update was applied (and this didn't find the key missing because compaction has not yet
// committed), then we have a bad situation where only a TTL update exists in the store. This problem has to be
// addressed. This can only happen if replication is far behind (for e.g due to a server being down for a long
// time). Won't happen if a server is being recreated.
messageInfo = new MessageInfo.Builder(messageInfo).isTtlUpdated(true).build();
remoteReplicaInfo.getLocalStore().updateTtl(Collections.singletonList(messageInfo));
logger.trace("Remote node: {} Thread name: {} Remote replica: {} Key ttl updated id: {}", remoteNode, threadName, remoteReplicaInfo.getReplicaId(), messageInfo.getStoreKey());
} catch (StoreException e) {
// The blob may be deleted or updated which is alright
if (e.getErrorCode() == StoreErrorCodes.ID_Deleted || e.getErrorCode() == StoreErrorCodes.Already_Updated) {
logger.trace("Remote node: {} Thread name: {} Remote replica: {} Key already updated: {}", remoteNode, threadName, remoteReplicaInfo.getReplicaId(), messageInfo.getStoreKey());
} else {
throw e;
}
}
// as long as the update is guaranteed to have taken effect locally.
if (notification != null) {
notification.onBlobReplicaUpdated(dataNodeId.getHostname(), dataNodeId.getPort(), messageInfo.getStoreKey().getID(), BlobReplicaSourceType.REPAIRED, UpdateType.TTL_UPDATE, messageInfo);
}
}
use of com.github.ambry.clustermap.DataNodeId in project ambry by linkedin.
the class CloudToStoreReplicationManager method addCloudReplica.
/**
* Add a replica of given partition and its {@link RemoteReplicaInfo}s to backup list.
* @param partitionName name of the partition of the replica to add.
* @throws ReplicationException if replicas initialization failed.
*/
private void addCloudReplica(String partitionName) throws ReplicationException {
// Adding cloud replica occurs when replica becomes leader from standby. Hence, if this a new added replica, it
// should be present in storage manager already.
ReplicaId localReplica = storeManager.getReplica(partitionName);
if (localReplica == null) {
logger.warn("Got partition leader notification for partition {} that is not present on the node", partitionName);
return;
}
PartitionId partitionId = localReplica.getPartitionId();
Store store = storeManager.getStore(partitionId);
if (store == null) {
logger.warn("Unable to add cloud replica for partition {} as store for the partition is not present or started.", partitionName);
return;
}
DataNodeId cloudDataNode = getCloudDataNode();
CloudReplica peerCloudReplica = new CloudReplica(partitionId, cloudDataNode);
FindTokenFactory findTokenFactory = tokenHelper.getFindTokenFactoryFromReplicaType(peerCloudReplica.getReplicaType());
RemoteReplicaInfo remoteReplicaInfo = new RemoteReplicaInfo(peerCloudReplica, localReplica, store, findTokenFactory.getNewFindToken(), storeConfig.storeDataFlushIntervalSeconds * SystemTime.MsPerSec * Replication_Delay_Multiplier, SystemTime.getInstance(), peerCloudReplica.getDataNodeId().getPortToConnectTo());
replicationMetrics.addMetricsForRemoteReplicaInfo(remoteReplicaInfo, trackPerDatacenterLagInMetric);
// Note that for each replica on a Ambry server node, there is only one cloud replica that it will be replicating from.
List<RemoteReplicaInfo> remoteReplicaInfos = Collections.singletonList(remoteReplicaInfo);
PartitionInfo partitionInfo = new PartitionInfo(remoteReplicaInfos, partitionId, store, localReplica);
partitionToPartitionInfo.put(partitionId, partitionInfo);
mountPathToPartitionInfos.computeIfAbsent(localReplica.getMountPath(), key -> ConcurrentHashMap.newKeySet()).add(partitionInfo);
logger.info("Cloud Partition {} added to {}. CloudNode {} port {}", partitionName, dataNodeId, cloudDataNode, cloudDataNode.getPortToConnectTo());
// Reload replication token if exist.
reloadReplicationTokenIfExists(localReplica, remoteReplicaInfos);
// Add remoteReplicaInfos to {@link ReplicaThread}.
addRemoteReplicaInfoToReplicaThread(remoteReplicaInfos, true);
if (replicationConfig.replicationTrackPerPartitionLagFromRemote) {
replicationMetrics.addLagMetricForPartition(partitionId, true);
}
replicationMetrics.addCatchUpPointMetricForPartition(partitionId);
}
use of com.github.ambry.clustermap.DataNodeId in project ambry by linkedin.
the class ReplicaThread method addRemoteReplicaInfo.
/**
* Add a {@link RemoteReplicaInfo} to current {@link ReplicaThread}.
* @param remoteReplicaInfo {@link RemoteReplicaInfo} to add.
*/
public void addRemoteReplicaInfo(RemoteReplicaInfo remoteReplicaInfo) {
lock.lock();
try {
allReplicatedPartitions.add(remoteReplicaInfo.getReplicaId().getPartitionId());
DataNodeId dataNodeId = remoteReplicaInfo.getReplicaId().getDataNodeId();
if (!replicasToReplicateGroupedByNode.computeIfAbsent(dataNodeId, key -> new HashSet<>()).add(remoteReplicaInfo)) {
replicationMetrics.remoteReplicaInfoAddError.inc();
// Since VCR is also listening Ambry Clustermap change, this may happen if events happens in following order:
// 1. VCR in memory ambry-clustermap updated
// 2. VcrClusterParticipantListener adds remote replicas for the newly added partition
// 3. ClusterMapChangeListener adds remote replicas
logger.warn("ReplicaThread: {}, RemoteReplicaInfo {} already exists.", threadName, remoteReplicaInfo);
}
} finally {
lock.unlock();
}
logger.trace("RemoteReplicaInfo {} is added to ReplicaThread {}. Now working on {} dataNodeIds.", remoteReplicaInfo, threadName, replicasToReplicateGroupedByNode.keySet().size());
}
use of com.github.ambry.clustermap.DataNodeId in project ambry by linkedin.
the class ReplicationTest method replicaFromOfflineToBootstrapTest.
/**
* Test that state transition in replication manager from OFFLINE to BOOTSTRAP
* @throws Exception
*/
@Test
public void replicaFromOfflineToBootstrapTest() throws Exception {
MockClusterMap clusterMap = new MockClusterMap();
ClusterMapConfig clusterMapConfig = new ClusterMapConfig(verifiableProperties);
MockHelixParticipant.metricRegistry = new MetricRegistry();
MockHelixParticipant mockHelixParticipant = new MockHelixParticipant(clusterMapConfig);
DataNodeId currentNode = clusterMap.getDataNodeIds().get(0);
Pair<StorageManager, ReplicationManager> managers = createStorageManagerAndReplicationManager(clusterMap, clusterMapConfig, mockHelixParticipant);
StorageManager storageManager = managers.getFirst();
MockReplicationManager replicationManager = (MockReplicationManager) managers.getSecond();
assertTrue("State change listener in cluster participant should contain replication manager listener", mockHelixParticipant.getPartitionStateChangeListeners().containsKey(StateModelListenerType.ReplicationManagerListener));
// 1. test partition not found case (should throw exception)
try {
mockHelixParticipant.onPartitionBecomeBootstrapFromOffline("-1");
fail("should fail because replica is not found");
} catch (StateTransitionException e) {
assertEquals("Transition error doesn't match", ReplicaNotFound, e.getErrorCode());
}
// 2. create a new partition and test replica addition success case
ReplicaId newReplicaToAdd = getNewReplicaToAdd(clusterMap);
PartitionId newPartition = newReplicaToAdd.getPartitionId();
assertTrue("Adding new replica to Storage Manager should succeed", storageManager.addBlobStore(newReplicaToAdd));
assertFalse("partitionToPartitionInfo should not contain new partition", replicationManager.partitionToPartitionInfo.containsKey(newPartition));
mockHelixParticipant.onPartitionBecomeBootstrapFromOffline(newPartition.toPathString());
assertTrue("partitionToPartitionInfo should contain new partition", replicationManager.partitionToPartitionInfo.containsKey(newPartition));
// 3. test replica addition failure case
replicationManager.partitionToPartitionInfo.remove(newPartition);
replicationManager.addReplicaReturnVal = false;
try {
mockHelixParticipant.onPartitionBecomeBootstrapFromOffline(newPartition.toPathString());
fail("should fail due to replica addition failure");
} catch (StateTransitionException e) {
assertEquals("Transition error doesn't match", ReplicaOperationFailure, e.getErrorCode());
}
replicationManager.addReplicaReturnVal = null;
// 4. test OFFLINE -> BOOTSTRAP on existing replica (should be no-op)
ReplicaId existingReplica = clusterMap.getReplicaIds(currentNode).get(0);
assertTrue("partitionToPartitionInfo should contain existing partition", replicationManager.partitionToPartitionInfo.containsKey(existingReplica.getPartitionId()));
mockHelixParticipant.onPartitionBecomeBootstrapFromOffline(existingReplica.getPartitionId().toPathString());
storageManager.shutdown();
}
Aggregations