Search in sources :

Example 56 with DataNodeId

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);
    }
}
Also used : NettyByteBufDataInputStream(com.github.ambry.utils.NettyByteBufDataInputStream) GetResponse(com.github.ambry.protocol.GetResponse) DataNodeId(com.github.ambry.clustermap.DataNodeId)

Example 57 with DataNodeId

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);
    }
}
Also used : DataNodeId(com.github.ambry.clustermap.DataNodeId) MessageInfo(com.github.ambry.store.MessageInfo) StoreException(com.github.ambry.store.StoreException)

Example 58 with DataNodeId

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);
}
Also used : CloudReplica(com.github.ambry.clustermap.CloudReplica) NotificationContext(org.apache.helix.NotificationContext) StoreManager(com.github.ambry.server.StoreManager) ClusterMapUtils(com.github.ambry.clustermap.ClusterMapUtils) CloudReplica(com.github.ambry.clustermap.CloudReplica) DataNodeId(com.github.ambry.clustermap.DataNodeId) LiveInstance(org.apache.helix.model.LiveInstance) Random(java.util.Random) AtomicReference(java.util.concurrent.atomic.AtomicReference) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) CloudConfig(com.github.ambry.config.CloudConfig) PortType(com.github.ambry.network.PortType) Map(java.util.Map) ClusterParticipant(com.github.ambry.clustermap.ClusterParticipant) SystemTime(com.github.ambry.utils.SystemTime) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) InstanceConfigChangeListener(org.apache.helix.api.listeners.InstanceConfigChangeListener) LinkedList(java.util.LinkedList) CloudDataNode(com.github.ambry.clustermap.CloudDataNode) ReplicationConfig(com.github.ambry.config.ReplicationConfig) NotificationSystem(com.github.ambry.notification.NotificationSystem) StateModelListenerType(com.github.ambry.clustermap.StateModelListenerType) StoreKeyConverterFactory(com.github.ambry.store.StoreKeyConverterFactory) StoreConfig(com.github.ambry.config.StoreConfig) MetricRegistry(com.codahale.metrics.MetricRegistry) ConnectionPool(com.github.ambry.network.ConnectionPool) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) VcrClusterSpectator(com.github.ambry.clustermap.VcrClusterSpectator) StoreKeyFactory(com.github.ambry.store.StoreKeyFactory) Set(java.util.Set) ClusterMap(com.github.ambry.clustermap.ClusterMap) Utils(com.github.ambry.utils.Utils) LiveInstanceChangeListener(org.apache.helix.api.listeners.LiveInstanceChangeListener) Collectors(java.util.stream.Collectors) InstanceConfig(org.apache.helix.model.InstanceConfig) TimeUnit(java.util.concurrent.TimeUnit) Store(com.github.ambry.store.Store) List(java.util.List) PartitionStateChangeListener(com.github.ambry.clustermap.PartitionStateChangeListener) ReplicaId(com.github.ambry.clustermap.ReplicaId) ClusterMapConfig(com.github.ambry.config.ClusterMapConfig) Port(com.github.ambry.network.Port) Collections(java.util.Collections) PartitionId(com.github.ambry.clustermap.PartitionId) Store(com.github.ambry.store.Store) PartitionId(com.github.ambry.clustermap.PartitionId) DataNodeId(com.github.ambry.clustermap.DataNodeId) ReplicaId(com.github.ambry.clustermap.ReplicaId)

Example 59 with DataNodeId

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());
}
Also used : DataNodeId(com.github.ambry.clustermap.DataNodeId)

Example 60 with DataNodeId

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();
}
Also used : MockHelixParticipant(com.github.ambry.clustermap.MockHelixParticipant) MetricRegistry(com.codahale.metrics.MetricRegistry) StorageManager(com.github.ambry.store.StorageManager) MockPartitionId(com.github.ambry.clustermap.MockPartitionId) PartitionId(com.github.ambry.clustermap.PartitionId) DataNodeId(com.github.ambry.clustermap.DataNodeId) MockDataNodeId(com.github.ambry.clustermap.MockDataNodeId) ClusterMapConfig(com.github.ambry.config.ClusterMapConfig) MockReplicaId(com.github.ambry.clustermap.MockReplicaId) ReplicaId(com.github.ambry.clustermap.ReplicaId) MockClusterMap(com.github.ambry.clustermap.MockClusterMap) StateTransitionException(com.github.ambry.clustermap.StateTransitionException) Test(org.junit.Test)

Aggregations

DataNodeId (com.github.ambry.clustermap.DataNodeId)92 ArrayList (java.util.ArrayList)45 Test (org.junit.Test)45 HashMap (java.util.HashMap)29 PartitionId (com.github.ambry.clustermap.PartitionId)28 MockDataNodeId (com.github.ambry.clustermap.MockDataNodeId)27 ReplicaId (com.github.ambry.clustermap.ReplicaId)25 MockClusterMap (com.github.ambry.clustermap.MockClusterMap)23 VerifiableProperties (com.github.ambry.config.VerifiableProperties)23 MetricRegistry (com.codahale.metrics.MetricRegistry)22 MockPartitionId (com.github.ambry.clustermap.MockPartitionId)22 List (java.util.List)22 Map (java.util.Map)22 Port (com.github.ambry.network.Port)21 ClusterMap (com.github.ambry.clustermap.ClusterMap)20 ClusterMapConfig (com.github.ambry.config.ClusterMapConfig)19 StoreKeyFactory (com.github.ambry.store.StoreKeyFactory)18 BlobIdFactory (com.github.ambry.commons.BlobIdFactory)17 HashSet (java.util.HashSet)16 Properties (java.util.Properties)16