/*
* 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.master.block.meta;
import alluxio.Constants;
import alluxio.StorageTierAssoc;
import alluxio.WorkerStorageTierAssoc;
import alluxio.util.CommonUtils;
import alluxio.wire.WorkerInfo;
import alluxio.wire.WorkerNetAddress;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe;
/**
* Metadata for an Alluxio worker. This class is not thread safe, so external locking is required.
*/
@NotThreadSafe
public final class MasterWorkerInfo {
private static final Logger LOG = LoggerFactory.getLogger(MasterWorkerInfo.class);
/** Worker's address. */
private final WorkerNetAddress mWorkerAddress;
/** The id of the worker. */
private final long mId;
/** Start time of the worker in ms. */
private final long mStartTimeMs;
/** Capacity of worker in bytes. */
private long mCapacityBytes;
/** Worker's used bytes. */
private long mUsedBytes;
/** Worker's last updated time in ms. */
private long mLastUpdatedTimeMs;
/** If true, the worker is considered registered. */
private boolean mIsRegistered;
/** Worker-specific mapping between storage tier alias and storage tier ordinal. */
private StorageTierAssoc mStorageTierAssoc;
/** Mapping from storage tier alias to total bytes. */
private Map<String, Long> mTotalBytesOnTiers;
/** Mapping from storage tier alias to used bytes. */
private Map<String, Long> mUsedBytesOnTiers;
/** ids of blocks the worker contains. */
private Set<Long> mBlocks;
/** ids of blocks the worker should remove. */
private Set<Long> mToRemoveBlocks;
/**
* Creates a new instance of {@link MasterWorkerInfo}.
*
* @param id the worker id to use
* @param address the worker address to use
*/
public MasterWorkerInfo(long id, WorkerNetAddress address) {
mWorkerAddress = Preconditions.checkNotNull(address);
mId = id;
mStartTimeMs = System.currentTimeMillis();
mLastUpdatedTimeMs = System.currentTimeMillis();
mIsRegistered = false;
mStorageTierAssoc = null;
mTotalBytesOnTiers = new HashMap<>();
mUsedBytesOnTiers = new HashMap<>();
mBlocks = new HashSet<>();
mToRemoveBlocks = new HashSet<>();
}
/**
* Marks the worker as registered, while updating all of its metadata.
*
* @param globalStorageTierAssoc global mapping between storage aliases and ordinal position
* @param storageTierAliases list of storage tier aliases in order of their position in the
* hierarchy
* @param totalBytesOnTiers mapping from storage tier alias to total bytes
* @param usedBytesOnTiers mapping from storage tier alias to used byes
* @param blocks set of block ids on this worker
* @return A Set of blocks removed (or lost) from this worker
*/
public Set<Long> register(final StorageTierAssoc globalStorageTierAssoc,
final List<String> storageTierAliases, final Map<String, Long> totalBytesOnTiers,
final Map<String, Long> usedBytesOnTiers, final Set<Long> blocks) {
// If the storage aliases do not have strictly increasing ordinal value based on the total
// ordering, throw an error
for (int i = 0; i < storageTierAliases.size() - 1; i++) {
if (globalStorageTierAssoc.getOrdinal(storageTierAliases.get(i)) >= globalStorageTierAssoc
.getOrdinal(storageTierAliases.get(i + 1))) {
throw new IllegalArgumentException(
"Worker cannot place storage tier " + storageTierAliases.get(i) + " above "
+ storageTierAliases.get(i + 1) + " in the hierarchy");
}
}
mStorageTierAssoc = new WorkerStorageTierAssoc(storageTierAliases);
// validate the number of tiers
if (mStorageTierAssoc.size() != totalBytesOnTiers.size()
|| mStorageTierAssoc.size() != usedBytesOnTiers.size()) {
throw new IllegalArgumentException(
"totalBytesOnTiers and usedBytesOnTiers should have the same number of tiers as "
+ "storageTierAliases, but storageTierAliases has " + mStorageTierAssoc.size()
+ " tiers, while totalBytesOnTiers has " + totalBytesOnTiers.size()
+ " tiers and usedBytesOnTiers has " + usedBytesOnTiers.size() + " tiers");
}
// defensive copy
mTotalBytesOnTiers = new HashMap<>(totalBytesOnTiers);
mUsedBytesOnTiers = new HashMap<>(usedBytesOnTiers);
mCapacityBytes = 0;
for (long bytes : mTotalBytesOnTiers.values()) {
mCapacityBytes += bytes;
}
mUsedBytes = 0;
for (long bytes : mUsedBytesOnTiers.values()) {
mUsedBytes += bytes;
}
Set<Long> removedBlocks;
if (mIsRegistered) {
// This is a re-register of an existing worker. Assume the new block ownership data is more
// up-to-date and update the existing block information.
LOG.info("re-registering an existing workerId: {}", mId);
// Compute the difference between the existing block data, and the new data.
removedBlocks = Sets.difference(mBlocks, blocks);
} else {
removedBlocks = Collections.emptySet();
}
// Set the new block information.
mBlocks = new HashSet<>(blocks);
mIsRegistered = true;
return removedBlocks;
}
/**
* Adds a block to the worker.
*
* @param blockId the id of the block to be added
*/
public void addBlock(long blockId) {
mBlocks.add(blockId);
}
/**
* Removes a block from the worker.
*
* @param blockId the id of the block to be removed
*/
public void removeBlock(long blockId) {
mBlocks.remove(blockId);
mToRemoveBlocks.remove(blockId);
}
/**
* @return generated {@link WorkerInfo} for this worker
*/
public WorkerInfo generateClientWorkerInfo() {
return new WorkerInfo()
.setId(mId)
.setAddress(mWorkerAddress)
.setLastContactSec(
(int) ((CommonUtils.getCurrentMs() - mLastUpdatedTimeMs) / Constants.SECOND_MS))
.setState("In Service").setCapacityBytes(mCapacityBytes).setUsedBytes(mUsedBytes)
.setStartTimeMs(mStartTimeMs);
}
/**
* @return the worker's address
*/
public WorkerNetAddress getWorkerAddress() {
return mWorkerAddress;
}
/**
* @return the available space of the worker in bytes
*/
public long getAvailableBytes() {
return mCapacityBytes - mUsedBytes;
}
/**
* @return ids of all blocks the worker contains
*/
public Set<Long> getBlocks() {
return new HashSet<>(mBlocks);
}
/**
* @return the capacity of the worker in bytes
*/
public long getCapacityBytes() {
return mCapacityBytes;
}
/**
* @return the id of the worker
*/
public long getId() {
return mId;
}
/**
* @return the last updated time of the worker in ms
*/
public long getLastUpdatedTimeMs() {
return mLastUpdatedTimeMs;
}
/**
* @return ids of blocks the worker should remove
*/
public List<Long> getToRemoveBlocks() {
return new ArrayList<>(mToRemoveBlocks);
}
/**
* @return used space of the worker in bytes
*/
public long getUsedBytes() {
return mUsedBytes;
}
/**
* @return the storage tier mapping for the worker
*/
public StorageTierAssoc getStorageTierAssoc() {
return mStorageTierAssoc;
}
/**
* @return the total bytes on each storage tier
*/
public Map<String, Long> getTotalBytesOnTiers() {
return mTotalBytesOnTiers;
}
/**
* @return the used bytes on each storage tier
*/
public Map<String, Long> getUsedBytesOnTiers() {
return mUsedBytesOnTiers;
}
/**
* @return the start time in milliseconds
*/
public long getStartTime() {
return mStartTimeMs;
}
/**
* @return whether the worker has been registered yet
*/
public boolean isRegistered() {
return mIsRegistered;
}
/**
* @return the free bytes on each storage tier
*/
public Map<String, Long> getFreeBytesOnTiers() {
Map<String, Long> freeCapacityBytes = new HashMap<>();
for (Map.Entry<String, Long> entry : mTotalBytesOnTiers.entrySet()) {
freeCapacityBytes.put(entry.getKey(),
entry.getValue() - mUsedBytesOnTiers.get(entry.getKey()));
}
return freeCapacityBytes;
}
@Override
public String toString() {
return Objects.toStringHelper(this).add("id", mId).add("workerAddress", mWorkerAddress)
.add("capacityBytes", mCapacityBytes).add("usedBytes", mUsedBytes)
.add("lastUpdatedTimeMs", mLastUpdatedTimeMs).add("blocks", mBlocks).toString();
}
/**
* Updates the last updated time of the worker in ms.
*/
public void updateLastUpdatedTimeMs() {
mLastUpdatedTimeMs = System.currentTimeMillis();
}
/**
* Adds or removes a block from the to-be-removed blocks set of the worker.
*
* @param add true if to add, to remove otherwise
* @param blockId the id of the block to be added or removed
*/
public void updateToRemovedBlock(boolean add, long blockId) {
if (add) {
if (mBlocks.contains(blockId)) {
mToRemoveBlocks.add(blockId);
}
} else {
mToRemoveBlocks.remove(blockId);
}
}
/**
* Sets the used space of the worker in bytes.
*
* @param usedBytesOnTiers used bytes on each storage tier
*/
public void updateUsedBytes(Map<String, Long> usedBytesOnTiers) {
mUsedBytes = 0;
mUsedBytesOnTiers = usedBytesOnTiers;
for (long t : mUsedBytesOnTiers.values()) {
mUsedBytes += t;
}
}
/**
* Sets the used space of the worker in bytes.
*
* @param tierAlias alias of storage tier
* @param usedBytesOnTier used bytes on certain storage tier
*/
public void updateUsedBytes(String tierAlias, long usedBytesOnTier) {
mUsedBytes += usedBytesOnTier - mUsedBytesOnTiers.get(tierAlias);
mUsedBytesOnTiers.put(tierAlias, usedBytesOnTier);
}
}