356 lines
12 KiB
Python
356 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding:utf-8 -*-
|
|
#############################################################################
|
|
# Copyright (c) 2023 Huawei Technologies Co.,Ltd.
|
|
#
|
|
# openGauss is licensed under Mulan PSL v2.
|
|
# You can use this software according to the terms
|
|
# and conditions of the Mulan PSL v2.
|
|
# You may obtain a copy of Mulan PSL v2 at:
|
|
#
|
|
# http://license.coscl.org.cn/MulanPSL2
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OF ANY KIND,
|
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
# See the Mulan PSL v2 for more details.
|
|
# ----------------------------------------------------------------------------
|
|
# Description : perf_probe.py setup a information set for configure
|
|
#############################################################################
|
|
|
|
import os
|
|
from enum import Enum
|
|
from base_utils.common.dialog import DialogUtil
|
|
from impl.perf_config.basic.project import Project
|
|
from impl.perf_config.basic.probe import Probe
|
|
from impl.perf_config.preset.preset import Preset
|
|
|
|
"""
|
|
The business module is mainly used to investigate the user's business related information.
|
|
|
|
The business content will be investigated in the form of questions and answers.
|
|
|
|
You can also select a preset and read the configuration in it.
|
|
"""
|
|
|
|
class BsScenario(Enum):
|
|
TP_PRODUCE = 0
|
|
TP_PERFORMANCE = 1
|
|
AP = 2 # just beta or demo
|
|
|
|
@staticmethod
|
|
def isOLTPScenario(scenario):
|
|
return scenario in [BsScenario.TP_PERFORMANCE, BsScenario.TP_PRODUCE]
|
|
|
|
|
|
class TblKind(Enum):
|
|
COMMON_TBL = 0 # common heap table
|
|
PARTITION_TBL = 1 # common heap partition table
|
|
COLUMN_TBL = 2 # column table
|
|
PART_COLUMN_TBL = 3 # column partition table
|
|
|
|
@staticmethod
|
|
def isCommonTbl(kind):
|
|
return kind in [TblKind.COMMON_TBL]
|
|
|
|
@staticmethod
|
|
def isPartTbl(kind):
|
|
return kind in [TblKind.PARTITION_TBL, TblKind.PART_COLUMN_TBL]
|
|
|
|
@staticmethod
|
|
def havePartTbl(kinds):
|
|
for kind in kinds:
|
|
if TblKind.isPartTbl(kind):
|
|
return True
|
|
return False
|
|
|
|
@staticmethod
|
|
def isColumnTbl(kind):
|
|
return kind in [TblKind.COLUMN_TBL, TblKind.PART_COLUMN_TBL]
|
|
|
|
@staticmethod
|
|
def haveColumnTbl(kinds):
|
|
for kind in kinds:
|
|
if TblKind.isColumnTbl(kind):
|
|
return True
|
|
return False
|
|
|
|
|
|
class BusinessProbe(Probe):
|
|
def __init__(self):
|
|
super(BusinessProbe, self).__init__()
|
|
self._preset = None
|
|
|
|
self.scenario = BsScenario.TP_PRODUCE
|
|
self.parallel = 200
|
|
self.rel_count = 50
|
|
self.rel_kind = [TblKind.COMMON_TBL, TblKind.PARTITION_TBL]
|
|
self.part_count = 100
|
|
self.index_count = 100
|
|
self.data_size = 200 * 1024 # unit is MB
|
|
self.isolated_xlog = None
|
|
|
|
def __str__(self):
|
|
return str({
|
|
'_preset': self._preset,
|
|
'scenario': self.scenario,
|
|
'parallel': self.parallel,
|
|
'rel_count': self.rel_count,
|
|
'rel_kind': self.rel_kind,
|
|
'part_count': self.part_count,
|
|
'index_count': self.index_count,
|
|
'data_size': self.data_size,
|
|
'isolated_xlog': self.isolated_xlog
|
|
})
|
|
|
|
def relfilenode_count(self):
|
|
"""
|
|
Estimate the number of relfilenodes.
|
|
"""
|
|
rel_count = self.rel_count if self.rel_count is not None else 0
|
|
part_count = self.part_count if self.part_count is not None else 0
|
|
index_count = self.index_count if self.index_count is not None else 0
|
|
return (rel_count + part_count + index_count) * 4
|
|
|
|
def detect(self):
|
|
"""
|
|
detect the user business by some research questions.
|
|
"""
|
|
msg = 'Now we need to do some research to understand your business scenario.\n' \
|
|
'Fields marked with "*" are required.'
|
|
Project.msg(msg)
|
|
|
|
question = 'What kind of way to choose?'
|
|
options = [
|
|
'Default case',
|
|
'Preset',
|
|
'Customization'
|
|
]
|
|
check = DialogUtil.singleAnswerQuestion(question, options)
|
|
Project.log('user choose: ' + options[check])
|
|
|
|
if check == 0:
|
|
self._load_preset('default')
|
|
elif check == 1:
|
|
self._load_preset()
|
|
|
|
self._do_detect()
|
|
|
|
Project.log('business detect res:' + self.__str__())
|
|
|
|
def _load_preset(self, preset_name=None):
|
|
if preset_name is None:
|
|
question = 'Please select the desired preset.'
|
|
builtins, usersets = Preset.get_all_presets()
|
|
presets = builtins + usersets
|
|
|
|
check = DialogUtil.singleAnswerQuestion(question, presets)
|
|
preset_name = presets[check]
|
|
|
|
Project.log('business detect research\nquestion: {0}\nanwser: {1}'.format(question, preset_name))
|
|
|
|
self._preset = Preset(preset_name)
|
|
|
|
def _do_detect(self):
|
|
"""
|
|
do detect action. If we had load preset, just read it, otherwise do research.
|
|
|
|
Pay attention to the order of detection, some different detection items are
|
|
associated with each other. For example:
|
|
- whether a partition table is used?
|
|
- how many partitions you have.
|
|
If you are not using a partition table, you do not need to survey the
|
|
number of partitions.
|
|
"""
|
|
|
|
self._detect_scenario()
|
|
self._detect_parallel()
|
|
self._detect_rel_count()
|
|
self._detect_rel_kind()
|
|
|
|
if TblKind.havePartTbl(self.rel_kind):
|
|
self._detect_partition_count()
|
|
else:
|
|
self.part_count = 0
|
|
|
|
self._detect_index_count()
|
|
self._detect_data_size()
|
|
self._detect_isolated_xlog()
|
|
|
|
###############
|
|
# Below are survey questions or preset-load-functions for each option.
|
|
###############
|
|
# load or research scenario
|
|
def _load_scenario(self):
|
|
scenario = self._preset['scenario']
|
|
if scenario is None:
|
|
pass
|
|
elif scenario == 'OLTP-produce':
|
|
self.scenario = BsScenario.TP_PRODUCE
|
|
elif scenario == 'OLTP-performance':
|
|
self.scenario = BsScenario.TP_PERFORMANCE
|
|
else:
|
|
assert False
|
|
|
|
def _detect_scenario(self):
|
|
if self._preset is not None:
|
|
self._load_scenario()
|
|
return
|
|
|
|
question = 'What are the main scenarios for using databases?'
|
|
options = [
|
|
'OLTP performance first',
|
|
'OLTP produce first',
|
|
]
|
|
answer = [
|
|
BsScenario.TP_PERFORMANCE,
|
|
BsScenario.TP_PRODUCE
|
|
]
|
|
check = DialogUtil.singleAnswerQuestion(question, options)
|
|
Project.log('business detect research\nquestion:{0}\nanwser:{1}'.format(question, options[check]))
|
|
self.scenario = answer[check]
|
|
|
|
# load or research parallel
|
|
def _load_parallel(self):
|
|
parallel = self._preset['parallel']
|
|
self.parallel = parallel if parallel is not None else self.parallel
|
|
|
|
def _detect_parallel(self):
|
|
if self._preset is not None:
|
|
self._load_parallel()
|
|
return
|
|
|
|
question = 'What is the average number of concurrent transactions?'
|
|
num = DialogUtil.askANumber(question, lambda x:'Invalid number, please more than 0.' if x < 0 else None)
|
|
Project.log('business detect research\nquestion:{0}\nanwser:{1}'.format(question, num))
|
|
self.parallel = num
|
|
|
|
# load or research rel_count
|
|
def _load_rel_count(self):
|
|
rel_count = self._preset['rel_count']
|
|
self.rel_count = rel_count if rel_count is not None else self.rel_count
|
|
|
|
def _detect_rel_count(self):
|
|
if self._preset is not None:
|
|
self._load_rel_count()
|
|
return
|
|
|
|
question = 'Approximately how many tables you have?'
|
|
num = DialogUtil.askANumber(question, lambda x:'Invalid number, please more than 0.' if x < 0 else None)
|
|
Project.log('business detect research\nquestion:{0}\nanwser:{1}'.format(question, num))
|
|
self.rel_count = num
|
|
|
|
# load or research rel_kind
|
|
def _load_rel_kind(self):
|
|
rel_kind = self._preset['rel_kind']
|
|
if rel_kind is None:
|
|
pass
|
|
else:
|
|
options = ['heap-table', 'partition-table', 'column-table', 'column-partition-table']
|
|
checks = [False, False, False, False]
|
|
res = [TblKind.COMMON_TBL, TblKind.PARTITION_TBL, TblKind.COLUMN_TBL, TblKind.PART_COLUMN_TBL]
|
|
for kind in rel_kind:
|
|
assert kind in options
|
|
checks[options.index(kind)] = True
|
|
self.rel_kind = [res[i] for i in range(0,4) if checks[i]]
|
|
|
|
def _detect_rel_kind(self):
|
|
if self._preset is not None:
|
|
self._load_rel_kind()
|
|
return
|
|
|
|
question = 'What kind of table you used?'
|
|
options = [
|
|
'common heap table',
|
|
'partition heap table',
|
|
'column table',
|
|
'partition column table'
|
|
]
|
|
answer = [
|
|
TblKind.COMMON_TBL,
|
|
TblKind.PARTITION_TBL,
|
|
TblKind.COLUMN_TBL,
|
|
TblKind.PART_COLUMN_TBL
|
|
]
|
|
|
|
checks = DialogUtil.multipleAnswerQuestion(question, options)
|
|
Project.log('business detect research\nquestion:{0}\nanwser:{1}'.format(
|
|
question,
|
|
str([options[check] for check in checks]))
|
|
)
|
|
self.rel_kind = [answer[check] for check in checks]
|
|
|
|
# load or research part_count
|
|
def _load_part_count(self):
|
|
part_count = self._preset['part_count']
|
|
self.part_count = part_count if part_count is not None else self.part_count
|
|
|
|
def _detect_partition_count(self):
|
|
if self._preset is not None:
|
|
self._load_part_count()
|
|
return
|
|
|
|
question = 'Approximately how many partitions you have?'
|
|
num = DialogUtil.askANumber(question, lambda x:'Invalid number, please more than 0.' if x < 0 else None)
|
|
Project.log('business detect research\nquestion:{0}\nanwser:{1}'.format(question, num))
|
|
self.part_count = num
|
|
|
|
# load or research index_count
|
|
def _load_index_count(self):
|
|
index_count = self._preset['index_count']
|
|
self.index_count = index_count if index_count is not None else self.index_count
|
|
|
|
def _detect_index_count(self):
|
|
if self._preset is not None:
|
|
self._load_index_count()
|
|
return
|
|
|
|
question = 'Approximately how many index you have?'
|
|
num = DialogUtil.askANumber(question, lambda x:'Invalid number, please more than 0.' if x < 0 else None)
|
|
Project.log('business detect research\nquestion:{0}\nanwser:{1}'.format(question, num))
|
|
self.index_count = num
|
|
|
|
def _load_data_size(self):
|
|
data_size = self._preset['data_size']
|
|
self.data_size = data_size if data_size is not None else self.data_size
|
|
|
|
def _detect_data_size(self):
|
|
if self._preset is not None:
|
|
self._load_data_size()
|
|
return
|
|
|
|
question = 'How much data is there, unit by MB?'
|
|
num = DialogUtil.askANumber(question, lambda x:'Invalid number, please more than 0.' if x < 0 else None)
|
|
Project.log('business detect research\nquestion:{0}\nanwser:{1}'.format(question, num))
|
|
self.data_size = num
|
|
|
|
def _load_isolated_xlog(self):
|
|
isolated_xlog = self._preset['isolated_xlog']
|
|
if isolated_xlog is None:
|
|
self.isolated_xlog = None
|
|
return
|
|
|
|
if not os.access(isolated_xlog, os.F_OK) or not os.path.isdir(isolated_xlog):
|
|
Project.warning('Could not access ' + isolated_xlog + ' or it is not a dir.')
|
|
self.isolated_xlog = None
|
|
return
|
|
|
|
self.isolated_xlog = isolated_xlog
|
|
|
|
def _detect_isolated_xlog(self):
|
|
if self._preset is not None:
|
|
self._load_isolated_xlog()
|
|
return
|
|
|
|
question = 'Storing wal on a separate disk helps improve performance. Do you need to move them?\n' \
|
|
'Here is some disk information:\n'
|
|
infos = Project.getGlobalPerfProbe()
|
|
for device in infos.disk:
|
|
question += f' {device.simple_info()}\n'
|
|
|
|
path = DialogUtil.askAPath(question, check_access=True, required=False)
|
|
self.isolated_xlog = path
|
|
|
|
|