486 lines
15 KiB
Python
Executable File
486 lines
15 KiB
Python
Executable File
#!/usr/bin/python3.8
|
|
|
|
from abc import abstractmethod
|
|
|
|
import time
|
|
from datetime import datetime
|
|
|
|
from influxdb_client import InfluxDBClient, Point, WritePrecision, BucketsApi
|
|
from influxdb_client.client.write_api import SYNCHRONOUS
|
|
|
|
import argparse
|
|
import textwrap
|
|
import subprocess
|
|
import sys
|
|
|
|
import taos
|
|
|
|
from crash_gen.crash_gen_main import Database, TdSuperTable
|
|
from crash_gen.service_manager import TdeInstance
|
|
|
|
from crash_gen.shared.config import Config
|
|
from crash_gen.shared.db import DbConn
|
|
from crash_gen.shared.misc import Dice, Logging, Helper
|
|
from crash_gen.shared.types import TdDataType
|
|
|
|
|
|
# NUM_PROCESSES = 10
|
|
# NUM_REPS = 1000
|
|
|
|
tick = int(time.time() - 5000000.0) # for now we will create max 5M record
|
|
value = 101
|
|
|
|
DB_NAME = 'mydb'
|
|
TIME_SERIES_NAME = 'widget'
|
|
|
|
MAX_SHELF = 500 # shelf number runs up to this, non-inclusive
|
|
ITEMS_PER_SHELF = 5
|
|
BATCH_SIZE = 2000 # Number of data points per request
|
|
|
|
# None_RW:
|
|
# INFLUX_TOKEN='RRzVQZs8ERCpV9cS2RXqgtM_Y6FEZuJ7Tuk0aHtZItFTfcM9ajixtGDhW8HzqNIBmG3hmztw-P4sHOstfJvjFA=='
|
|
# DevOrg_RW:
|
|
# INFLUX_TOKEN='o1P8sEhBmXKhxBmNuiCyOUKv8d7qm5wUjMff9AbskBu2LcmNPQzU77NrAn5hDil8hZ0-y1AGWpzpL-4wqjFdkA=='
|
|
# DevOrg_All_Access
|
|
INFLUX_TOKEN='T2QTr4sloJhINH_oSrwSS-WIIZYjDfD123NK4ou3b7ajRs0c0IphCh3bNc0OsDZQRW1HyCby7opdEndVYFGTWQ=='
|
|
INFLUX_ORG="DevOrg"
|
|
INFLUX_BUCKET="Bucket01"
|
|
|
|
def writeTaosBatch(dbc, tblName):
|
|
# Database.setupLastTick()
|
|
global value, tick
|
|
|
|
data = []
|
|
for i in range(0, 100):
|
|
data.append("('{}', {})".format(Database.getNextTick(), value) )
|
|
value += 1
|
|
|
|
sql = "INSERT INTO {} VALUES {}".format(tblName, ''.join(data))
|
|
dbc.execute(sql)
|
|
|
|
class PerfGenError(taos.error.ProgrammingError):
|
|
pass
|
|
|
|
class Benchmark():
|
|
|
|
# @classmethod
|
|
# def create(cls, dbType):
|
|
# if dbType == 'taos':
|
|
# return TaosBenchmark()
|
|
# elif dbType == 'influx':
|
|
# return InfluxBenchmark()
|
|
# else:
|
|
# raise RuntimeError("Unknown DB type: {}".format(dbType))
|
|
|
|
def __init__(self, dbType, loopCount = 0):
|
|
self._dbType = dbType
|
|
self._setLoopCount(loopCount)
|
|
|
|
def _setLoopCount(self, loopCount):
|
|
cfgLoopCount = Config.getConfig().loop_count
|
|
if loopCount == 0: # use config
|
|
self._loopCount = cfgLoopCount
|
|
else:
|
|
if cfgLoopCount :
|
|
Logging.warning("Ignoring loop count for fixed-loop-count benchmarks: {}".format(cfgLoopCount))
|
|
self._loopCount = loopCount
|
|
|
|
@abstractmethod
|
|
def doIterate(self):
|
|
'''
|
|
Execute the benchmark directly, without invoking sub processes,
|
|
effectively using one execution thread.
|
|
'''
|
|
pass
|
|
|
|
@abstractmethod
|
|
def prepare(self):
|
|
'''
|
|
Preparation needed to run a certain benchmark
|
|
'''
|
|
pass
|
|
|
|
@abstractmethod
|
|
def execute(self):
|
|
'''
|
|
Actually execute the benchmark
|
|
'''
|
|
Logging.warning("Unexpected execution")
|
|
|
|
@property
|
|
def name(self):
|
|
return self.__class__.__name__
|
|
|
|
def run(self):
|
|
print("Running benchmark: {}, class={} ...".format(self.name, self.__class__))
|
|
startTime = time.time()
|
|
|
|
# Prepare to execute the benchmark
|
|
self.prepare()
|
|
|
|
# Actually execute the benchmark
|
|
self.execute()
|
|
|
|
# if Config.getConfig().iterate_directly: # execute directly
|
|
# Logging.debug("Iterating...")
|
|
# self.doIterate()
|
|
# else:
|
|
# Logging.debug("Executing via sub process...")
|
|
# startTime = time.time()
|
|
# self.prepare()
|
|
# self.spawnProcesses()
|
|
# self.waitForProcecess()
|
|
# duration = time.time() - startTime
|
|
# Logging.info("Benchmark execution completed in {:.3f} seconds".format(duration))
|
|
Logging.info("Benchmark {} finished in {:.3f} seconds".format(
|
|
self.name, time.time()-startTime))
|
|
|
|
def spawnProcesses(self):
|
|
self._subProcs = []
|
|
for j in range(0, Config.getConfig().subprocess_count):
|
|
ON_POSIX = 'posix' in sys.builtin_module_names
|
|
tblName = 'cars_reg_{}'.format(j)
|
|
cmdLineStr = './perf_gen.sh -t {} -i -n {} -l {}'.format(
|
|
self._dbType,
|
|
tblName,
|
|
Config.getConfig().loop_count
|
|
)
|
|
if Config.getConfig().debug:
|
|
cmdLineStr += ' -d'
|
|
subProc = subprocess.Popen(cmdLineStr,
|
|
shell = True,
|
|
close_fds = ON_POSIX)
|
|
self._subProcs.append(subProc)
|
|
|
|
def waitForProcecess(self):
|
|
for sp in self._subProcs:
|
|
sp.wait(300)
|
|
|
|
|
|
class TaosBenchmark(Benchmark):
|
|
|
|
def __init__(self, loopCount):
|
|
super().__init__('taos', loopCount)
|
|
# self._dbType = 'taos'
|
|
tInst = TdeInstance()
|
|
self._dbc = DbConn.createNative(tInst.getDbTarget())
|
|
self._dbc.open()
|
|
self._sTable = TdSuperTable(TIME_SERIES_NAME + '_s', DB_NAME)
|
|
|
|
def doIterate(self):
|
|
tblName = Config.getConfig().target_table_name
|
|
print("Benchmarking TAOS database (1 pass) for: {}".format(tblName))
|
|
self._dbc.execute("USE {}".format(DB_NAME))
|
|
|
|
self._sTable.ensureRegTable(None, self._dbc, tblName)
|
|
try:
|
|
lCount = Config.getConfig().loop_count
|
|
print("({})".format(lCount))
|
|
for i in range(0, lCount):
|
|
writeTaosBatch(self._dbc, tblName)
|
|
except taos.error.ProgrammingError as err:
|
|
Logging.error("Failed to write batch")
|
|
|
|
def prepare(self):
|
|
self._dbc.execute("CREATE DATABASE IF NOT EXISTS {}".format(DB_NAME))
|
|
self._dbc.execute("USE {}".format(DB_NAME))
|
|
# Create the super table
|
|
self._sTable.drop(self._dbc, True)
|
|
self._sTable.create(self._dbc,
|
|
{'ts': TdDataType.TIMESTAMP,
|
|
'temperature': TdDataType.INT,
|
|
'pressure': TdDataType.INT,
|
|
'notes': TdDataType.BINARY200
|
|
},
|
|
{'rack': TdDataType.INT,
|
|
'shelf': TdDataType.INT,
|
|
'barcode': TdDataType.BINARY16
|
|
})
|
|
|
|
def execSql(self, sql):
|
|
try:
|
|
self._dbc.execute(sql)
|
|
except taos.error.ProgrammingError as err:
|
|
Logging.warning("SQL Error: 0x{:X}, {}, SQL: {}".format(
|
|
Helper.convertErrno(err.errno), err.msg, sql))
|
|
raise
|
|
|
|
def executeWrite(self):
|
|
# Sample: INSERT INTO t1 USING st TAGS(1) VALUES(now, 1) t2 USING st TAGS(2) VALUES(now, 2)
|
|
sqlPrefix = "INSERT INTO "
|
|
dataTemplate = "{} USING {} TAGS({},{},'barcode_{}') VALUES('{}',{},{},'{}') "
|
|
|
|
stName = self._sTable.getName()
|
|
BATCH_SIZE = 2000 # number of items per request batch
|
|
ITEMS_PER_SHELF = 5
|
|
|
|
# rackSize = 10 # shelves per rack
|
|
# shelfSize = 100 # items per shelf
|
|
batchCount = self._loopCount // BATCH_SIZE
|
|
lastRack = 0
|
|
for i in range(batchCount):
|
|
sql = sqlPrefix
|
|
for j in range(BATCH_SIZE):
|
|
n = i*BATCH_SIZE + j # serial number
|
|
# values first
|
|
# rtName = 'rt_' + str(n) # table name contains serial number, has info
|
|
temperature = 20 + (n % 10)
|
|
pressure = 70 + (n % 10)
|
|
# tags
|
|
shelf = (n // ITEMS_PER_SHELF) % MAX_SHELF # shelf number
|
|
rack = n // (ITEMS_PER_SHELF * MAX_SHELF) # rack number
|
|
barcode = rack + shelf
|
|
# table name
|
|
tableName = "reg_" + str(rack) + '_' + str(shelf)
|
|
# now the SQL
|
|
sql += dataTemplate.format(tableName, stName,# table name
|
|
rack, shelf, barcode, # tags
|
|
Database.getNextTick(), temperature, pressure, 'xxx') # values
|
|
lastRack = rack
|
|
self.execSql(sql)
|
|
Logging.info("Last Rack: {}".format(lastRack))
|
|
|
|
class TaosWriteBenchmark(TaosBenchmark):
|
|
def execute(self):
|
|
self.executeWrite()
|
|
|
|
class Taos100kWriteBenchmark(TaosWriteBenchmark):
|
|
def __init__(self):
|
|
super().__init__(100*1000)
|
|
|
|
class Taos10kWriteBenchmark(TaosWriteBenchmark):
|
|
def __init__(self):
|
|
super().__init__(10*1000)
|
|
|
|
class Taos1mWriteBenchmark(TaosWriteBenchmark):
|
|
def __init__(self):
|
|
super().__init__(1000*1000)
|
|
|
|
class Taos5mWriteBenchmark(TaosWriteBenchmark):
|
|
def __init__(self):
|
|
super().__init__(5*1000*1000)
|
|
|
|
class Taos1kQueryBenchmark(TaosBenchmark):
|
|
def __init__(self):
|
|
super().__init__(1000)
|
|
|
|
class Taos1MCreationBenchmark(TaosBenchmark):
|
|
def __init__(self):
|
|
super().__init__(1000000)
|
|
|
|
|
|
class InfluxBenchmark(Benchmark):
|
|
def __init__(self, loopCount):
|
|
super().__init__('influx', loopCount)
|
|
# self._dbType = 'influx'
|
|
|
|
|
|
# self._client = InfluxDBClient(host='localhost', port=8086)
|
|
|
|
# def _writeBatch(self, tblName):
|
|
# global value, tick
|
|
# data = []
|
|
# for i in range(0, 100):
|
|
# line = "{},device={} value={} {}".format(
|
|
# TIME_SERIES_NAME,
|
|
# tblName,
|
|
# value,
|
|
# tick*1000000000)
|
|
# # print(line)
|
|
# data.append(line)
|
|
# value += 1
|
|
# tick +=1
|
|
|
|
# self._client.write(data, {'db':DB_NAME}, protocol='line')
|
|
|
|
def executeWrite(self):
|
|
global tick # influx tick #TODO refactor
|
|
|
|
lineTemplate = TIME_SERIES_NAME + ",rack={},shelf={},barcode='barcode_{}' temperature={},pressure={} {}"
|
|
|
|
batchCount = self._loopCount // BATCH_SIZE
|
|
for i in range(batchCount):
|
|
lineBatch = []
|
|
for j in range(BATCH_SIZE):
|
|
n = i*BATCH_SIZE + j # serial number
|
|
# values first
|
|
# rtName = 'rt_' + str(n) # table name contains serial number, has info
|
|
temperature = 20 + (n % 10)
|
|
pressure = 70 + (n % 10)
|
|
# tags
|
|
shelf = (n // ITEMS_PER_SHELF) % MAX_SHELF # shelf number
|
|
rack = n // (ITEMS_PER_SHELF * MAX_SHELF) # rack number
|
|
barcode = rack + shelf
|
|
# now the SQL
|
|
line = lineTemplate.format(
|
|
rack, shelf, barcode, # tags
|
|
temperature, pressure, # values
|
|
tick * 1000000000 )
|
|
tick += 1
|
|
lineBatch.append(line)
|
|
write_api = self._client.write_api(write_options=SYNCHRONOUS)
|
|
write_api.write(INFLUX_BUCKET, INFLUX_ORG, lineBatch)
|
|
# self._client.write(lineBatch, {'db':DB_NAME}, protocol='line')
|
|
|
|
# def doIterate(self):
|
|
# tblName = Config.getConfig().target_table_name
|
|
# print("Benchmarking INFLUX database (1 pass) for: {}".format(tblName))
|
|
|
|
# for i in range(0, Config.getConfig().loop_count):
|
|
# self._writeBatch(tblName)
|
|
|
|
def _getOrgIdByName(self, orgName):
|
|
"""Find org by name.
|
|
|
|
"""
|
|
orgApi = self._client.organizations_api()
|
|
orgs = orgApi.find_organizations()
|
|
for org in orgs:
|
|
if org.name == orgName:
|
|
return org.id
|
|
raise PerfGenError("Org not found with name: {}".format(orgName))
|
|
|
|
def _fetchAuth(self):
|
|
authApi = self._client.authorizations_api()
|
|
auths = authApi.find_authorizations()
|
|
for auth in auths:
|
|
if auth.token == INFLUX_TOKEN :
|
|
return auth
|
|
raise PerfGenError("No proper auth found")
|
|
|
|
def _verifyPermissions(self, perms: list):
|
|
if list:
|
|
return #OK
|
|
raise PerfGenError("No permission found")
|
|
|
|
def prepare(self):
|
|
self._client = InfluxDBClient(
|
|
url="http://127.0.0.1:8086",
|
|
token=INFLUX_TOKEN,
|
|
org=INFLUX_ORG)
|
|
|
|
auth = self._fetchAuth()
|
|
|
|
self._verifyPermissions(auth.permissions)
|
|
|
|
bktApi = self._client.buckets_api()
|
|
# Delete
|
|
bkt = bktApi.find_bucket_by_name(INFLUX_BUCKET)
|
|
if bkt:
|
|
bktApi.delete_bucket(bkt)
|
|
# Recreate
|
|
|
|
orgId = self._getOrgIdByName(INFLUX_ORG)
|
|
bktApi.create_bucket(bucket=None, bucket_name=INFLUX_BUCKET, org_id=orgId)
|
|
|
|
# self._client.drop_database(DB_NAME)
|
|
# self._client.create_database(DB_NAME)
|
|
# self._client.switch_database(DB_NAME)
|
|
|
|
class InfluxWriteBenchmark(InfluxBenchmark):
|
|
def execute(self):
|
|
return self.executeWrite()
|
|
|
|
class Influx10kWriteBenchmark(InfluxWriteBenchmark):
|
|
def __init__(self):
|
|
super().__init__(10*1000)
|
|
|
|
class Influx100kWriteBenchmark(InfluxWriteBenchmark):
|
|
def __init__(self):
|
|
super().__init__(100*1000)
|
|
|
|
class Influx1mWriteBenchmark(InfluxWriteBenchmark):
|
|
def __init__(self):
|
|
super().__init__(1000*1000)
|
|
|
|
class Influx5mWriteBenchmark(InfluxWriteBenchmark):
|
|
def __init__(self):
|
|
super().__init__(5*1000*1000)
|
|
|
|
def _buildCmdLineParser():
|
|
parser = argparse.ArgumentParser(
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
description=textwrap.dedent('''\
|
|
TDengine Performance Benchmarking Tool
|
|
---------------------------------------------------------------------
|
|
|
|
'''))
|
|
|
|
parser.add_argument(
|
|
'-b',
|
|
'--benchmark-name',
|
|
action='store',
|
|
default='Taos1kQuery',
|
|
type=str,
|
|
help='Benchmark to use (default: Taos1kQuery)')
|
|
|
|
parser.add_argument(
|
|
'-d',
|
|
'--debug',
|
|
action='store_true',
|
|
help='Turn on DEBUG mode for more logging (default: false)')
|
|
|
|
parser.add_argument(
|
|
'-i',
|
|
'--iterate-directly',
|
|
action='store_true',
|
|
help='Execution operations directly without sub-process (default: false)')
|
|
|
|
parser.add_argument(
|
|
'-l',
|
|
'--loop-count',
|
|
action='store',
|
|
default=1000,
|
|
type=int,
|
|
help='Number of loops to perform, 100 operations per loop. (default: 1000)')
|
|
|
|
parser.add_argument(
|
|
'-n',
|
|
'--target-table-name',
|
|
action='store',
|
|
default=None,
|
|
type=str,
|
|
help='Regular table name in target DB (default: None)')
|
|
|
|
parser.add_argument(
|
|
'-s',
|
|
'--subprocess-count',
|
|
action='store',
|
|
default=4,
|
|
type=int,
|
|
help='Number of sub processes to spawn. (default: 10)')
|
|
|
|
parser.add_argument(
|
|
'-t',
|
|
'--target-database',
|
|
action='store',
|
|
default='taos',
|
|
type=str,
|
|
help='Benchmark target: taos, influx (default: taos)')
|
|
|
|
return parser
|
|
|
|
def main():
|
|
parser = _buildCmdLineParser()
|
|
Config.init(parser)
|
|
Logging.clsInit(Config.getConfig().debug)
|
|
Dice.seed(0) # initial seeding of dice
|
|
|
|
bName = Config.getConfig().benchmark_name
|
|
bClassName = bName + 'Benchmark'
|
|
x = globals()
|
|
if bClassName in globals():
|
|
bClass = globals()[bClassName]
|
|
bm = bClass() # Benchmark object
|
|
bm.run()
|
|
else:
|
|
raise PerfGenError("No such benchmark: {}".format(bName))
|
|
|
|
# bm = Benchmark.create(Config.getConfig().target_database)
|
|
# bm.run()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|
|
|