package org.apache.cassandra.utils;
/*
*
* 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.
*
*/
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.util.StringTokenizer;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.cassandra.concurrent.DebuggableScheduledThreadPoolExecutor;
import org.apache.cassandra.gms.ApplicationState;
import org.apache.cassandra.gms.EndpointState;
import org.apache.cassandra.gms.Gossiper;
import org.apache.cassandra.gms.VersionedValue;
import org.apache.cassandra.service.StorageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.AtomicDouble;
public class BackgroundActivityMonitor
{
private static final Logger logger = LoggerFactory.getLogger(BackgroundActivityMonitor.class);
public static final int USER_INDEX = 0;
public static final int NICE_INDEX = 1;
public static final int SYS_INDEX = 2;
public static final int IDLE_INDEX = 3;
public static final int IOWAIT_INDEX = 4;
public static final int IRQ_INDEX = 5;
public static final int SOFTIRQ_INDEX = 6;
private static final int NUM_CPUS = Runtime.getRuntime().availableProcessors();
private static final String PROC_STAT_PATH = "/proc/stat";
private final AtomicDouble compaction_severity = new AtomicDouble();
private final AtomicDouble manual_severity = new AtomicDouble();
private final ScheduledExecutorService reportThread = new DebuggableScheduledThreadPoolExecutor("Background_Reporter");
private RandomAccessFile statsFile;
private long[] lastReading;
public BackgroundActivityMonitor()
{
try
{
statsFile = new RandomAccessFile(PROC_STAT_PATH, "r");
lastReading = readAndCompute();
}
catch (IOException ex)
{
if (FBUtilities.hasProcFS())
logger.warn("Couldn't open /proc/stats");
statsFile = null;
}
reportThread.scheduleAtFixedRate(new BackgroundActivityReporter(), 1, 1, TimeUnit.SECONDS);
}
private long[] readAndCompute() throws IOException
{
statsFile.seek(0);
StringTokenizer tokenizer = new StringTokenizer(statsFile.readLine());
String name = tokenizer.nextToken();
assert name.equalsIgnoreCase("cpu");
long[] returned = new long[tokenizer.countTokens()];
for (int i = 0; i < returned.length; i++)
returned[i] = Long.parseLong(tokenizer.nextToken());
return returned;
}
private float compareAtIndex(long[] reading1, long[] reading2, int index)
{
long total1 = 0, total2 = 0;
for (int i = 0; i <= SOFTIRQ_INDEX; i++)
{
total1 += reading1[i];
total2 += reading2[i];
}
float totalDiff = total2 - total1;
long intrested1 = reading1[index], intrested2 = reading2[index];
float diff = intrested2 - intrested1;
if (diff == 0)
return 0f;
return (diff / totalDiff) * 100; // yes it is hard coded to 100 [update
// unit?]
}
public void incrCompactionSeverity(double sev)
{
compaction_severity.addAndGet(sev);
}
public void incrManualSeverity(double sev)
{
manual_severity.addAndGet(sev);
}
public double getIOWait() throws IOException
{
if (statsFile == null)
return -1d;
long[] newComp = readAndCompute();
double value = compareAtIndex(lastReading, newComp, IOWAIT_INDEX);
lastReading = newComp;
return value;
}
public double getNormalizedLoadAvg()
{
double avg = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();
return avg / NUM_CPUS;
}
public double getSeverity(InetAddress endpoint)
{
VersionedValue event;
EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint);
if (state != null && (event = state.getApplicationState(ApplicationState.SEVERITY)) != null)
return Double.parseDouble(event.value);
return 0.0;
}
public class BackgroundActivityReporter implements Runnable
{
public void run()
{
double report = -1;
try
{
report = getIOWait();
}
catch (IOException e)
{
// ignore;
if (FBUtilities.hasProcFS())
logger.warn("Couldn't read /proc/stats");
}
if (report == -1d)
report = compaction_severity.get();
if (!Gossiper.instance.isEnabled())
return;
report += manual_severity.get(); // add manual severity setting.
VersionedValue updated = StorageService.instance.valueFactory.severity(report);
Gossiper.instance.addLocalApplicationState(ApplicationState.SEVERITY, updated);
}
}
}