/*
* The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
* (the "License"). You may not use this work except in compliance with the License, which is
* available at www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied, as more fully set forth in the License.
*
* See the NOTICE file distributed with this work for information regarding copyright ownership.
*/
package alluxio.worker.block;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.concurrent.ThreadSafe;
/**
* Represents the delta of the block store within one heartbeat period. For now, newly committed
* blocks do not pass through this master communication mechanism, instead it is synchronized
* through {@link alluxio.worker.block.BlockWorker#commitBlock(long, long)}.
*/
@ThreadSafe
public final class BlockHeartbeatReporter extends AbstractBlockStoreEventListener {
/** Lock for operations on the removed and added block collections. */
private final Object mLock;
/** List of blocks that were removed in the last heartbeat period. */
private final List<Long> mRemovedBlocks;
/** Map of storage tier alias to a list of blocks that were added in the last heartbeat period. */
private final Map<String, List<Long>> mAddedBlocks;
/**
* Creates a new instance of {@link BlockHeartbeatReporter}.
*/
public BlockHeartbeatReporter() {
mLock = new Object();
mRemovedBlocks = new ArrayList<>(100);
mAddedBlocks = new HashMap<>(20);
}
/**
* Generates the report of the block store delta in the last heartbeat period. Calling this method
* marks the end of a period and the start of a new heartbeat period.
*
* @return the block store delta report for the last heartbeat period
*/
public BlockHeartbeatReport generateReport() {
synchronized (mLock) {
// Copy added and removed blocks
Map<String, List<Long>> addedBlocks = new HashMap<>(mAddedBlocks);
List<Long> removedBlocks = new ArrayList<>(mRemovedBlocks);
// Clear added and removed blocks
mAddedBlocks.clear();
mRemovedBlocks.clear();
return new BlockHeartbeatReport(addedBlocks, removedBlocks);
}
}
@Override
public void onMoveBlockByClient(long sessionId, long blockId, BlockStoreLocation oldLocation,
BlockStoreLocation newLocation) {
synchronized (mLock) {
// Remove the block from our list of added blocks in this heartbeat, if it was added, to
// prevent adding the block twice.
removeBlockFromAddedBlocks(blockId);
// Add the block back with the new tier
addBlockToAddedBlocks(blockId, newLocation.tierAlias());
}
}
@Override
public void onRemoveBlockByClient(long sessionId, long blockId) {
synchronized (mLock) {
// Remove the block from list of added blocks, in case it was added in this heartbeat period.
removeBlockFromAddedBlocks(blockId);
// Add to the list of removed blocks in this heartbeat period.
if (!mRemovedBlocks.contains(blockId)) {
mRemovedBlocks.add(blockId);
}
}
}
@Override
public void onRemoveBlockByWorker(long sessionId, long blockId) {
synchronized (mLock) {
// Remove the block from list of added blocks, in case it was added in this heartbeat period.
removeBlockFromAddedBlocks(blockId);
// Add to the list of removed blocks in this heartbeat period.
if (!mRemovedBlocks.contains(blockId)) {
mRemovedBlocks.add(blockId);
}
}
}
@Override
public void onMoveBlockByWorker(long sessionId, long blockId, BlockStoreLocation oldLocation,
BlockStoreLocation newLocation) {
synchronized (mLock) {
// Remove the block from our list of added blocks in this heartbeat, if it was added, to
// prevent adding the block twice.
removeBlockFromAddedBlocks(blockId);
// Add the block back with the new storagedir.
addBlockToAddedBlocks(blockId, newLocation.tierAlias());
}
}
/**
* Adds a block to the list of added blocks in this heartbeat period.
*
* @param blockId the id of the block to add
* @param tierAlias alias of the storage tier containing the block
*/
private void addBlockToAddedBlocks(long blockId, String tierAlias) {
if (mAddedBlocks.containsKey(tierAlias)) {
mAddedBlocks.get(tierAlias).add(blockId);
} else {
mAddedBlocks.put(tierAlias, Lists.newArrayList(blockId));
}
}
/**
* Removes the block from the added blocks map, if it exists.
*
* @param blockId the block to remove
*/
private void removeBlockFromAddedBlocks(long blockId) {
Iterator<Entry<String, List<Long>>> iterator = mAddedBlocks.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, List<Long>> entry = iterator.next();
List<Long> blockList = entry.getValue();
if (blockList.contains(blockId)) {
blockList.remove(blockId);
if (blockList.isEmpty()) {
iterator.remove();
}
// exit the loop when already find and remove block id from mAddedBlocks
break;
}
}
}
}