blob: dfeb18cbe56c102b7cf22f334f3a2a2182ecd721 [file] [log] [blame]
/*
* 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.iotdb.db.utils;
import org.apache.iotdb.db.conf.IoTDBConfig;
import org.apache.iotdb.db.conf.IoTDBConstant;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
import org.apache.iotdb.db.conf.directories.DirectoryManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
// Notice : statistics in this class may not be accurate because of limited user authority.
public class OpenFileNumUtil {
private static final Logger logger = LoggerFactory.getLogger(OpenFileNumUtil.class);
private static final int PID_ERROR_CODE = -1;
private static final int UNSUPPORTED_OS_ERROR_CODE = -2;
private static final int UNKNOWN_STATISTICS_ERROR_CODE = -3;
private static final String IOTDB_PROCESS_KEY_WORD = IoTDBConstant.GLOBAL_DB_NAME;
private static final String LINUX_OS_NAME = "linux";
private static final String MAC_OS_NAME = "mac";
private static final String SEARCH_PID_LINUX = "ps -aux | grep -i %s | grep -v grep";
private static final String SEARCH_PID_MAC = "ps aux | grep -i %s | grep -v grep";
// command 'lsof -p' is available on most Linux distro except CentOS.
private static final String SEARCH_OPEN_DATA_FILE_BY_PID = "lsof -p %d";
private static IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig();
private static DirectoryManager directoryManager = DirectoryManager.getInstance();
private static final String[] COMMAND_TEMPLATE = {"/bin/bash", "-c", ""};
private static boolean isOutputValid = false;
private int pid;
/** constructor, process key word is defined by IOTDB_PROCESS_KEY_WORD. */
private OpenFileNumUtil() {
pid = getIotdbPid();
}
/**
* singleton instance.
*
* @return instance
*/
public static OpenFileNumUtil getInstance() {
return OpenFileNumUtilHolder.INSTANCE;
}
/**
* check if the string is numeric.
*
* @param str string need to be checked
* @return whether the string is a number
*/
private static boolean isNumeric(String str) {
if (str == null || "".equals(str)) {
return false;
} else {
for (int i = str.length(); --i >= 0; ) {
if (!Character.isDigit(str.charAt(i))) {
return false;
}
}
}
return true;
}
/**
* get IoTDB server process ID by executing command.
*
* @return pid of IoTDB server process
*/
private static int getIotdbPid() {
int iotdbPid = -1;
Process pro1;
Runtime r = Runtime.getRuntime();
// System.getProperty("os.name") can detect which type of OS is using now.
// this code can detect Windows, Mac, Unix and Solaris.
String os = System.getProperty("os.name");
String osName = os.toLowerCase();
if (osName.startsWith(LINUX_OS_NAME) || osName.startsWith(MAC_OS_NAME)) {
try {
String command;
if (osName.startsWith(LINUX_OS_NAME)) {
command = String.format(SEARCH_PID_LINUX, IOTDB_PROCESS_KEY_WORD);
} else {
command = String.format(SEARCH_PID_MAC, IOTDB_PROCESS_KEY_WORD);
}
COMMAND_TEMPLATE[2] = command;
pro1 = r.exec(COMMAND_TEMPLATE);
BufferedReader in1 = new BufferedReader(new InputStreamReader(pro1.getInputStream()));
String line;
while ((line = in1.readLine()) != null) {
line = line.trim();
String[] temp = line.split("\\s+");
if (temp.length > 1 && isNumeric(temp[1])) {
iotdbPid = Integer.parseInt(temp[1]);
break;
}
}
in1.close();
pro1.destroy();
} catch (IOException e) {
logger.error("Cannot get PID of IoTDB process because ", e);
}
} else {
logger.warn("Unsupported OS {} for OpenFileNumUtil to get the PID of IoTDB.", os);
}
return iotdbPid;
}
/**
* set pid.
*
* @param pid is the process ID of IoTDB service process
*/
void setPid(int pid) {
this.pid = pid;
}
/**
* return statistic Map, whose key belongs to enum OpenFileNumStatistics: TOTAL_OPEN_FILE_NUM is
* the current total open file number of IoTDB service process; SEQUENCE_FILE_OPEN_NUM is the
* current open file number under data directory; DELTA_OPEN_FILE_NUM is the current open file
* number of TsFile; UNSEQUENCE_FILE_OPEN_NUM is the current open file number of unsequence file;
* WAL_OPEN_FILE_NUM is the current open file number of WAL file; METADATA_OPEN_FILE_NUM is the
* current open file number of metadata; DIGEST_OPEN_FILE_NUM is the current open file number of
* fileNodeDir; SOCKET_OPEN_FILE_NUM is the current open socket connection of IoTDB service
* process.
*
* @param pid : IoTDB service pid
* @return list : statistics list
*/
private static EnumMap<OpenFileNumStatistics, Integer> getOpenFile(int pid) {
EnumMap<OpenFileNumStatistics, Integer> resultMap = new EnumMap<>(OpenFileNumStatistics.class);
// initialize resultMap
for (OpenFileNumStatistics openFileNumStatistics : OpenFileNumStatistics.values()) {
resultMap.put(openFileNumStatistics, 0);
}
Process pro;
int lineCount = 0;
Runtime r = Runtime.getRuntime();
try {
String command = String.format(SEARCH_OPEN_DATA_FILE_BY_PID, pid);
COMMAND_TEMPLATE[2] = command;
pro = r.exec(COMMAND_TEMPLATE);
String line;
try (BufferedReader in = new BufferedReader(new InputStreamReader(pro.getInputStream()))) {
while ((line = in.readLine()) != null) {
lineCount++;
countOneFile(line, pid, resultMap);
}
}
if (lineCount < OpenFileNumStatistics.values().length) {
isOutputValid = false;
for (OpenFileNumStatistics statistics : OpenFileNumStatistics.values()) {
resultMap.put(statistics, UNSUPPORTED_OS_ERROR_CODE);
}
} else {
isOutputValid = true;
}
pro.destroy();
} catch (Exception e) {
logger.error("Cannot get open file number of IoTDB process because ", e);
}
return resultMap;
}
private static void countOneFile(
String line, int pid, EnumMap<OpenFileNumStatistics, Integer> resultMap) {
String[] temp = line.split("\\s+");
if (!line.contains(Integer.toString(pid)) || temp.length <= 8) {
return;
}
int oldValue = resultMap.get(OpenFileNumStatistics.TOTAL_OPEN_FILE_NUM);
resultMap.put(OpenFileNumStatistics.TOTAL_OPEN_FILE_NUM, oldValue + 1);
for (OpenFileNumStatistics openFileNumStatistics : OpenFileNumStatistics.values()) {
if (openFileNumStatistics.path == null) {
continue;
}
for (String path : openFileNumStatistics.path) {
if (temp[8].contains(path)) {
oldValue = resultMap.get(openFileNumStatistics);
resultMap.put(openFileNumStatistics, oldValue + 1);
}
}
}
if (temp[7].contains("TCP") || temp[7].contains("UDP")) {
oldValue = resultMap.get(OpenFileNumStatistics.SOCKET_OPEN_FILE_NUM);
resultMap.put(OpenFileNumStatistics.SOCKET_OPEN_FILE_NUM, oldValue + 1);
}
}
/**
* Check if runtime OS is supported then return the result list. If pid is abnormal then all
* statistics returns -1, if OS is not supported then all statistics returns -2
*
* @return map
*/
private EnumMap<OpenFileNumStatistics, Integer> getStatisticMap() {
EnumMap<OpenFileNumStatistics, Integer> resultMap = new EnumMap<>(OpenFileNumStatistics.class);
String osName = System.getProperty("os.name").toLowerCase();
// get runtime OS name, currently only support Linux and MacOS
if (osName.startsWith(LINUX_OS_NAME) || osName.startsWith(MAC_OS_NAME)) {
// if pid is normal, then get statistics
if (pid > 0) {
resultMap = getOpenFile(pid);
} else {
// pid is abnormal, give all statistics abnormal value -1
for (OpenFileNumStatistics statistics : OpenFileNumStatistics.values()) {
resultMap.put(statistics, PID_ERROR_CODE);
}
}
} else {
// operation system not supported, give all statistics abnormal value -2
for (OpenFileNumStatistics statistics : OpenFileNumStatistics.values()) {
resultMap.put(statistics, UNSUPPORTED_OS_ERROR_CODE);
}
}
return resultMap;
}
/**
* get statistics.
*
* @param statistics get what statistics of open file number
* @return open file number
*/
public int get(OpenFileNumStatistics statistics) {
EnumMap<OpenFileNumStatistics, Integer> statisticsMap = getStatisticMap();
return statisticsMap.getOrDefault(statistics, UNKNOWN_STATISTICS_ERROR_CODE);
}
boolean isCommandValid() {
return isOutputValid;
}
public enum OpenFileNumStatistics {
TOTAL_OPEN_FILE_NUM(null),
SEQUENCE_FILE_OPEN_NUM(directoryManager.getAllSequenceFileFolders()),
UNSEQUENCE_FILE_OPEN_NUM(directoryManager.getAllUnSequenceFileFolders()),
WAL_OPEN_FILE_NUM(Collections.singletonList(config.getWalDir())),
DIGEST_OPEN_FILE_NUM(Collections.singletonList(config.getSystemDir())),
SOCKET_OPEN_FILE_NUM(null);
// path is a list of directory corresponding to the OpenFileNumStatistics enum element,
// e.g. data/data/ for SEQUENCE_FILE_OPEN_NUM
private List<String> path;
OpenFileNumStatistics(List<String> path) {
this.path = path;
}
public List<String> getPath() {
return path;
}
}
private static class OpenFileNumUtilHolder {
private OpenFileNumUtilHolder() {}
private static final OpenFileNumUtil INSTANCE = new OpenFileNumUtil();
}
}