/*
* 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 alluxio.Configuration;
import alluxio.PropertyKey;
import alluxio.Sessions;
import alluxio.StorageTierAssoc;
import alluxio.WorkerStorageTierAssoc;
import alluxio.exception.BlockAlreadyExistsException;
import alluxio.exception.BlockDoesNotExistException;
import alluxio.exception.InvalidWorkerStateException;
import alluxio.exception.WorkerOutOfSpaceException;
import alluxio.heartbeat.HeartbeatExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.concurrent.NotThreadSafe;
/**
* {@link SpaceReserver} periodically checks if there is enough space reserved on each storage tier,
* if there is no enough free space on some tier, free space from it.
*/
@NotThreadSafe
public class SpaceReserver implements HeartbeatExecutor {
private static final Logger LOG = LoggerFactory.getLogger(SpaceReserver.class);
private final BlockWorker mBlockWorker;
/** Association between storage tier aliases and ordinals for the worker. */
private final StorageTierAssoc mStorageTierAssoc;
/** Mapping from tier alias to high watermark in bytes. */
private final Map<String, Long> mHighWaterMarkInBytesOnTiers = new HashMap<>();
/** Mapping from tier alias to space size to be reserved on the tier. */
private final Map<String, Long> mReservedBytesOnTiers = new HashMap<>();
/**
* Creates a new instance of {@link SpaceReserver}.
*
* @param blockWorker the block worker handle
*/
public SpaceReserver(BlockWorker blockWorker) {
mBlockWorker = blockWorker;
mStorageTierAssoc = new WorkerStorageTierAssoc();
Map<String, Long> capOnTiers = blockWorker.getStoreMeta().getCapacityBytesOnTiers();
long lastTierReservedBytes = 0;
for (int ordinal = 0; ordinal < mStorageTierAssoc.size(); ordinal++) {
String tierAlias = mStorageTierAssoc.getAlias(ordinal);
long capOnTier = capOnTiers.get(tierAlias);
long reservedBytes;
PropertyKey tierReservedSpaceProp =
PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_RESERVED_RATIO.format(ordinal);
if (Configuration.containsKey(tierReservedSpaceProp)) {
LOG.warn("The property reserved.ratio is deprecated and high/low water mark "
+ "should be used instead.");
reservedBytes =
(long) (capOnTier * Configuration.getDouble(tierReservedSpaceProp));
} else {
// HighWatemark defines when to start the space reserving process
PropertyKey tierHighWatermarkProp =
PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_HIGH_WATERMARK_RATIO
.format(ordinal);
long highWatermarkInBytes =
(long) (capOnTier * Configuration.getDouble(tierHighWatermarkProp));
// LowWatemark defines when to stop the space reserving process if started
PropertyKey tierLowWatermarkProp =
PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_LOW_WATERMARK_RATIO
.format(ordinal);
reservedBytes =
(long) (capOnTier - capOnTier * Configuration.getDouble(tierLowWatermarkProp));
mHighWaterMarkInBytesOnTiers.put(tierAlias, highWatermarkInBytes);
}
mReservedBytesOnTiers.put(tierAlias, reservedBytes + lastTierReservedBytes);
lastTierReservedBytes += reservedBytes;
}
}
private void reserveSpace() {
Map<String, Long> usedBytesOnTiers = mBlockWorker.getStoreMeta().getUsedBytesOnTiers();
for (int ordinal = mStorageTierAssoc.size() - 1; ordinal >= 0; ordinal--) {
String tierAlias = mStorageTierAssoc.getAlias(ordinal);
long reservedBytes = mReservedBytesOnTiers.get(tierAlias);
if (mHighWaterMarkInBytesOnTiers.containsKey(tierAlias)) {
long highWatermarkInBytes = mHighWaterMarkInBytesOnTiers.get(tierAlias);
if (highWatermarkInBytes > reservedBytes
&& usedBytesOnTiers.get(tierAlias) >= highWatermarkInBytes) {
try {
mBlockWorker.freeSpace(Sessions.MIGRATE_DATA_SESSION_ID, reservedBytes, tierAlias);
} catch (WorkerOutOfSpaceException | BlockDoesNotExistException
| BlockAlreadyExistsException | InvalidWorkerStateException | IOException e) {
LOG.warn("SpaceReserver failed to free tier {} to {} bytes used",
tierAlias, reservedBytes, e.getMessage());
}
}
} else {
try {
mBlockWorker.freeSpace(Sessions.MIGRATE_DATA_SESSION_ID, reservedBytes, tierAlias);
} catch (WorkerOutOfSpaceException | BlockDoesNotExistException
| BlockAlreadyExistsException | InvalidWorkerStateException | IOException e) {
LOG.warn(e.getMessage());
}
}
}
}
@Override
public void heartbeat() {
reserveSpace();
}
@Override
public void close() {
// Nothing to close.
}
}