/*
* 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.ignite.internal.processors.cache.database;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.ignite.MemoryMetrics;
import org.apache.ignite.configuration.MemoryPolicyConfiguration;
import org.apache.ignite.internal.processors.cache.database.freelist.FreeListImpl;
import org.apache.ignite.internal.util.IgniteUtils;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.jsr166.LongAdder8;
/**
*
*/
public class MemoryMetricsImpl implements MemoryMetrics {
/** */
private FreeListImpl freeList;
/** */
private final LongAdder8 totalAllocatedPages = new LongAdder8();
/**
* Counter for number of pages occupied by large entries (one entry is larger than one page).
*/
private final LongAdder8 largeEntriesPages = new LongAdder8();
/** */
private volatile boolean metricsEnabled;
/** */
private volatile int subInts = 5;
/** */
private volatile LongAdder8[] allocRateCounters = new LongAdder8[subInts];
/** */
private final AtomicInteger counterIdx = new AtomicInteger(0);
/** */
private final AtomicLong lastUpdTime = new AtomicLong(0);
/** */
private final MemoryPolicyConfiguration memPlcCfg;
/** Time interval (in seconds) when allocations/evictions are counted to calculate rate.
* Default value is 60 seconds. */
private volatile int rateTimeInterval = 60;
/**
* @param memPlcCfg MemoryPolicyConfiguration.
*/
public MemoryMetricsImpl(MemoryPolicyConfiguration memPlcCfg) {
this.memPlcCfg = memPlcCfg;
metricsEnabled = memPlcCfg.isMetricsEnabled();
for (int i = 0; i < subInts; i++)
allocRateCounters[i] = new LongAdder8();
}
/** {@inheritDoc} */
@Override public String getName() {
return U.maskName(memPlcCfg.getName());
}
/** {@inheritDoc} */
@Override public long getTotalAllocatedPages() {
return metricsEnabled ? totalAllocatedPages.longValue() : 0;
}
/** {@inheritDoc} */
@Override public float getAllocationRate() {
if (!metricsEnabled)
return 0;
float res = 0;
for (int i = 0; i < subInts; i++)
res += allocRateCounters[i].floatValue();
return res / rateTimeInterval;
}
/** {@inheritDoc} */
@Override public float getEvictionRate() {
return 0;
}
/** {@inheritDoc} */
@Override public float getLargeEntriesPagesPercentage() {
if (!metricsEnabled)
return 0;
return totalAllocatedPages.longValue() != 0 ?
(float) largeEntriesPages.doubleValue() / totalAllocatedPages.longValue()
: 0;
}
/** {@inheritDoc} */
@Override public float getPagesFillFactor() {
if (!metricsEnabled || freeList == null)
return 0;
return freeList.fillFactor();
}
/**
* Increments totalAllocatedPages counter.
*/
public void incrementTotalAllocatedPages() {
if (metricsEnabled) {
totalAllocatedPages.increment();
try {
updateAllocationRateMetrics();
}
catch (ArrayIndexOutOfBoundsException | NullPointerException ignored) {
// No-op.
}
}
}
/**
*
*/
private void updateAllocationRateMetrics() {
if (subInts != allocRateCounters.length)
return;
long lastUpdT = lastUpdTime.get();
long currT = IgniteUtils.currentTimeMillis();
int currIdx = counterIdx.get();
long deltaT = currT - lastUpdT;
int subInts = this.subInts;
LongAdder8[] rateCntrs = allocRateCounters;
for (int i = 1; i <= subInts; i++) {
if (deltaT < subInt(i)) {
if (i > 1) {
if (!lastUpdTime.compareAndSet(lastUpdT, currT)) {
rateCntrs[counterIdx.get()].increment();
break;
}
}
if (rotateIndex(currIdx, i - 1)) {
currIdx = counterIdx.get();
resetCounters(currIdx, i - 1);
rateCntrs[currIdx].increment();
break;
}
else {
rateCntrs[counterIdx.get()].increment();
break;
}
}
else if (i == subInts && lastUpdTime.compareAndSet(lastUpdT, currT))
resetAll();
if (currIdx != counterIdx.get()) {
rateCntrs[counterIdx.get()].increment();
break;
}
}
}
/**
* @param intervalNum Interval number.
*/
private int subInt(int intervalNum) {
return (rateTimeInterval * 1000 * intervalNum) / subInts;
}
/**
* @param idx Index.
* @param rotateStep Rotate step.
*/
private boolean rotateIndex(int idx, int rotateStep) {
if (rotateStep == 0)
return true;
int subInts = this.subInts;
assert rotateStep < subInts;
int nextIdx = (idx + rotateStep) % subInts;
return counterIdx.compareAndSet(idx, nextIdx);
}
/**
*
*/
private void resetAll() {
LongAdder8[] cntrs = allocRateCounters;
for (LongAdder8 cntr : cntrs)
cntr.reset();
}
/**
* @param currIdx Current index.
* @param resettingCntrs Resetting allocRateCounters.
*/
private void resetCounters(int currIdx, int resettingCntrs) {
if (resettingCntrs == 0)
return;
for (int j = 0; j < resettingCntrs; j++) {
int cleanIdx = currIdx - j >= 0 ? currIdx - j : currIdx - j + subInts;
allocRateCounters[cleanIdx].reset();
}
}
/**
*
*/
public void incrementLargeEntriesPages() {
if (metricsEnabled)
largeEntriesPages.increment();
}
/**
*
*/
public void decrementLargeEntriesPages() {
if (metricsEnabled)
largeEntriesPages.decrement();
}
/**
* Enable metrics.
*/
public void enableMetrics() {
metricsEnabled = true;
}
/**
* Disable metrics.
*/
public void disableMetrics() {
metricsEnabled = false;
}
/**
* @param rateTimeInterval Time interval used to calculate allocation/eviction rate.
*/
public void rateTimeInterval(int rateTimeInterval) {
this.rateTimeInterval = rateTimeInterval;
}
/**
* Sets number of subintervals the whole rateTimeInterval will be split into to calculate allocation rate.
*
* @param subInts Number of subintervals.
*/
public void subIntervals(int subInts) {
assert subInts > 0;
if (this.subInts == subInts)
return;
int rateIntervalMs = rateTimeInterval * 1000;
if (rateIntervalMs / subInts < 10)
subInts = rateIntervalMs / 10;
LongAdder8[] newCounters = new LongAdder8[subInts];
for (int i = 0; i < subInts; i++)
newCounters[i] = new LongAdder8();
this.subInts = subInts;
allocRateCounters = newCounters;
}
/**
* @param freeList Free list.
*/
void freeList(FreeListImpl freeList) {
this.freeList = freeList;
}
}