/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tajo.tuple.memory;
import com.google.common.base.Preconditions;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tajo.exception.TajoRuntimeException;
import org.apache.tajo.exception.UnsupportedException;
import org.apache.tajo.util.FileUtil;
/**
* It specifies the maximum size or increasing ratio. In addition,
* it guarantees that all numbers are less than or equal to Integer.MAX_VALUE 2^31
* due to ByteBuffer.
*/
public class ResizableLimitSpec {
private static final Log LOG = LogFactory.getLog(ResizableLimitSpec.class);
public static final int MAX_SIZE_BYTES = Integer.MAX_VALUE;
public static final ResizableLimitSpec DEFAULT_LIMIT = new ResizableLimitSpec(Integer.MAX_VALUE);
private final long initSize;
private final long limitBytes;
private final float incRatio;
private final float allowedOVerflowRatio;
private final static float DEFAULT_ALLOWED_OVERFLOW_RATIO = 0.1f;
private final static float DEFAULT_INCREASE_RATIO = 1.0f;
public ResizableLimitSpec(long initSize) {
this(initSize, MAX_SIZE_BYTES, DEFAULT_ALLOWED_OVERFLOW_RATIO);
}
public ResizableLimitSpec(long initSize, long limitBytes) {
this(initSize, limitBytes, DEFAULT_ALLOWED_OVERFLOW_RATIO);
}
public ResizableLimitSpec(long initSize, long limitBytes, float allowedOverflow) {
this(initSize, limitBytes, allowedOverflow, DEFAULT_INCREASE_RATIO);
}
public ResizableLimitSpec(long initSize, long limitBytes, float allowedOverflowRatio, float incRatio) {
Preconditions.checkArgument(initSize > 0, "initial size must be greater than 0 bytes.");
Preconditions.checkArgument(initSize <= MAX_SIZE_BYTES, "The maximum initial size is 2GB.");
Preconditions.checkArgument(limitBytes > 0, "The limit size must be greater than 0 bytes.");
Preconditions.checkArgument(limitBytes <= MAX_SIZE_BYTES, "The maximum limit size is 2GB.");
Preconditions.checkArgument(incRatio > 0.0f, "Increase Ratio must be greater than 0.");
if (initSize == limitBytes) {
long overflowedSize = (long) (initSize + (initSize * allowedOverflowRatio));
if (overflowedSize > Integer.MAX_VALUE) {
overflowedSize = Integer.MAX_VALUE;
}
this.initSize = overflowedSize;
this.limitBytes = overflowedSize;
} else {
this.initSize = initSize;
limitBytes = (long) (limitBytes + (limitBytes * allowedOverflowRatio));
if (limitBytes > Integer.MAX_VALUE) {
this.limitBytes = Integer.MAX_VALUE;
} else {
this.limitBytes = limitBytes;
}
}
this.allowedOVerflowRatio = allowedOverflowRatio;
this.incRatio = incRatio;
}
public long initialSize() {
return initSize;
}
public long limit() {
return limitBytes;
}
public float remainRatio(long currentSize) {
Preconditions.checkArgument(currentSize > 0, "Size must be greater than 0 bytes.");
if (currentSize > Integer.MAX_VALUE) {
currentSize = Integer.MAX_VALUE;
}
return (float)currentSize / (float)limitBytes;
}
public boolean canIncrease(long currentSize) {
return remain(currentSize) > 0;
}
public long remain(long currentSize) {
Preconditions.checkArgument(currentSize > 0, "Size must be greater than 0 bytes.");
return limitBytes > Integer.MAX_VALUE ? Integer.MAX_VALUE - currentSize : limitBytes - currentSize;
}
public int increasedSize(int currentSize) {
if (currentSize < initSize) {
return (int) initSize;
}
if (currentSize == Integer.MAX_VALUE) {
throw new TajoRuntimeException(new UnsupportedException(
"Current size already exceeds the maximum size (" + Integer.MAX_VALUE + " bytes)"));
}
long nextSize = (long) (currentSize + ((float) currentSize * incRatio));
if (nextSize > limitBytes) {
LOG.info("Increasing reaches size limit (" + FileUtil.humanReadableByteCount(limitBytes, false) + ")");
nextSize = limitBytes;
}
if (nextSize > Integer.MAX_VALUE) {
LOG.info("Increasing reaches maximum size (" + FileUtil.humanReadableByteCount(Integer.MAX_VALUE, false) + ")");
nextSize = Integer.MAX_VALUE;
}
return (int) nextSize;
}
@Override
public String toString() {
return "init=" + FileUtil.humanReadableByteCount(initSize, false) + ",limit="
+ FileUtil.humanReadableByteCount(limitBytes, false) + ",overflow_ratio=" + allowedOVerflowRatio
+ ",inc_ratio=" + incRatio;
}
}