You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
399 lines
10 KiB
399 lines
10 KiB
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/rand"
|
|
"os"
|
|
"time"
|
|
|
|
"gordenko.dev/dima/diploma"
|
|
"gordenko.dev/dima/diploma/client"
|
|
"gordenko.dev/dima/diploma/proto"
|
|
)
|
|
|
|
// METRICS INFO
|
|
|
|
type MetricInfo struct {
|
|
MetricID uint32 `json:"metricID"`
|
|
MetricType diploma.MetricType `json:"metricType"`
|
|
FracDigits int `json:"fracDigits"`
|
|
Since int64 `json:"since"`
|
|
Until int64 `json:"until"`
|
|
Qty int `json:"qty"`
|
|
}
|
|
|
|
func readMetricInfo(fileName string) (list []MetricInfo, err error) {
|
|
buf, err := os.ReadFile(fileName)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
err = json.Unmarshal(buf, &list)
|
|
return
|
|
}
|
|
|
|
// RANDOM QUERY GENERATOR
|
|
|
|
type QueryRecipe struct {
|
|
MetricID uint32
|
|
MetricIDs []uint32
|
|
Method int
|
|
RangeCode int
|
|
Since uint32
|
|
Until uint32
|
|
GroupBy diploma.GroupBy
|
|
}
|
|
|
|
type RandomQueryGenerator struct {
|
|
metrics []MetricInfo
|
|
groupByOptions []diploma.GroupBy
|
|
listCurrentValuesProbability int
|
|
listPeriodsProbability int
|
|
timeRangeProbDistribution []int
|
|
}
|
|
|
|
type RandomQueryGeneratorOptions struct {
|
|
Metrics []MetricInfo
|
|
ListCurrentValuesProbability int
|
|
ListPeriodsProbability int
|
|
TimeRangeProbabilities []int
|
|
}
|
|
|
|
func NewRandomQueryGenerator(opt RandomQueryGeneratorOptions) *RandomQueryGenerator {
|
|
if opt.ListCurrentValuesProbability >= 100 {
|
|
panic(fmt.Sprintf("wrong ListCurrentValuesProbability: %d", opt.ListCurrentValuesProbability))
|
|
}
|
|
|
|
if opt.ListPeriodsProbability >= 100 {
|
|
panic(fmt.Sprintf("wrong ListPeriodsProbability: %d", opt.ListPeriodsProbability))
|
|
}
|
|
// check total time range propability
|
|
var totalTimeRangeProbability int
|
|
for _, p := range opt.TimeRangeProbabilities {
|
|
totalTimeRangeProbability += p
|
|
}
|
|
if totalTimeRangeProbability != 100 {
|
|
panic(fmt.Sprintf("total time range probabilities != 100: %d", totalTimeRangeProbability))
|
|
}
|
|
|
|
// create time range probability distribution
|
|
timeRangeProbDistribution := make([]int, len(opt.TimeRangeProbabilities))
|
|
timeRangeProbDistribution[0] = opt.TimeRangeProbabilities[0]
|
|
for i := 1; i < len(opt.TimeRangeProbabilities); i++ {
|
|
timeRangeProbDistribution[i] = timeRangeProbDistribution[i-1] + opt.TimeRangeProbabilities[i]
|
|
}
|
|
|
|
return &RandomQueryGenerator{
|
|
metrics: opt.Metrics,
|
|
groupByOptions: []diploma.GroupBy{
|
|
diploma.GroupByHour,
|
|
diploma.GroupByDay,
|
|
diploma.GroupByMonth,
|
|
},
|
|
listCurrentValuesProbability: opt.ListCurrentValuesProbability,
|
|
listPeriodsProbability: opt.ListPeriodsProbability,
|
|
timeRangeProbDistribution: timeRangeProbDistribution,
|
|
}
|
|
}
|
|
|
|
func (s *RandomQueryGenerator) GetQueryRecipe() QueryRecipe {
|
|
metric := s.getRandomMetric()
|
|
|
|
num := rand.Intn(100)
|
|
if num < s.listCurrentValuesProbability {
|
|
qty := 5 + rand.Intn(100) // від 5 до 105
|
|
return QueryRecipe{
|
|
MetricIDs: s.listRandomUniqueMetricIDs(qty),
|
|
Method: listCurrentValues,
|
|
}
|
|
} else {
|
|
if metric.MetricType == diploma.Cumulative {
|
|
num = rand.Intn(100)
|
|
if num < s.listPeriodsProbability {
|
|
groupBy := s.groupByOptions[rand.Intn(len(s.groupByOptions))]
|
|
|
|
var (
|
|
minDays = 1
|
|
maxDays = 7
|
|
)
|
|
|
|
if groupBy == diploma.GroupByDay {
|
|
minDays = 1
|
|
maxDays = 30
|
|
} else if groupBy == diploma.GroupByMonth {
|
|
minDays = 1
|
|
maxDays = 30
|
|
}
|
|
|
|
rangeCode, since, until := s.getRandomTimeRange(
|
|
metric.Since, metric.Until, minDays, maxDays)
|
|
|
|
return QueryRecipe{
|
|
MetricID: metric.MetricID,
|
|
Method: listCumulativePeriods,
|
|
RangeCode: rangeCode,
|
|
Since: uint32(since),
|
|
Until: uint32(until),
|
|
GroupBy: groupBy,
|
|
}
|
|
} else {
|
|
var (
|
|
minDays = 1
|
|
maxDays = 3
|
|
)
|
|
|
|
rangeCode, since, until := s.getRandomTimeRange(
|
|
metric.Since, metric.Until, minDays, maxDays)
|
|
|
|
return QueryRecipe{
|
|
MetricID: metric.MetricID,
|
|
Method: listCumulativeMeasures,
|
|
RangeCode: rangeCode,
|
|
Since: uint32(since),
|
|
Until: uint32(until),
|
|
}
|
|
}
|
|
} else {
|
|
num = rand.Intn(100)
|
|
if num < s.listPeriodsProbability {
|
|
groupBy := s.groupByOptions[rand.Intn(len(s.groupByOptions))]
|
|
|
|
var (
|
|
minDays = 1
|
|
maxDays = 7
|
|
)
|
|
|
|
if groupBy == diploma.GroupByDay {
|
|
minDays = 1
|
|
maxDays = 30
|
|
} else if groupBy == diploma.GroupByMonth {
|
|
minDays = 1
|
|
maxDays = 30
|
|
}
|
|
|
|
rangeCode, since, until := s.getRandomTimeRange(
|
|
metric.Since, metric.Until, minDays, maxDays)
|
|
|
|
return QueryRecipe{
|
|
MetricID: metric.MetricID,
|
|
Method: listInstantPeriods,
|
|
RangeCode: rangeCode,
|
|
Since: uint32(since),
|
|
Until: uint32(until),
|
|
GroupBy: groupBy,
|
|
}
|
|
} else {
|
|
var (
|
|
minDays = 1
|
|
maxDays = 3
|
|
)
|
|
|
|
rangeCode, since, until := s.getRandomTimeRange(
|
|
metric.Since, metric.Until, minDays, maxDays)
|
|
|
|
return QueryRecipe{
|
|
MetricID: metric.MetricID,
|
|
Method: listInstantMeasures,
|
|
RangeCode: rangeCode,
|
|
Since: uint32(since),
|
|
Until: uint32(until),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Генерує випадковий набір унікальних metricID з [1, 100]
|
|
func (s *RandomQueryGenerator) listRandomUniqueMetricIDs(count int) []uint32 {
|
|
// переставляю індекси у випадковому порядку
|
|
indexes := rand.Perm(len(s.metrics))
|
|
// копіюю metricID із перших випадкових індексів
|
|
metricIDs := make([]uint32, count)
|
|
for i := range count {
|
|
metricIDs[i] = s.metrics[indexes[i]].MetricID
|
|
}
|
|
return metricIDs
|
|
}
|
|
|
|
const (
|
|
secondsPerDay = 86400
|
|
dayRange = 0
|
|
weekRange = 1
|
|
monthRange = 2
|
|
randomTimeRange = 3
|
|
)
|
|
|
|
// Випадковий часовий діапазон
|
|
func (s *RandomQueryGenerator) getRandomTimeRange(start, end int64, minDays, maxDays int) (int, int64, int64) {
|
|
var (
|
|
since int64
|
|
until int64
|
|
num = rand.Intn(100)
|
|
rangeCode int
|
|
threshold int
|
|
)
|
|
for rangeCode, threshold = range s.timeRangeProbDistribution {
|
|
if num < threshold {
|
|
break
|
|
}
|
|
}
|
|
|
|
switch rangeCode {
|
|
case dayRange:
|
|
since = end - secondsPerDay
|
|
until = end
|
|
|
|
case weekRange:
|
|
since = end - 7*secondsPerDay
|
|
until = end
|
|
|
|
case monthRange:
|
|
since = end - 30*secondsPerDay
|
|
until = end
|
|
|
|
case randomTimeRange:
|
|
if start == end {
|
|
return rangeCode, start, end
|
|
}
|
|
// Випадковий момент часу для since
|
|
since = start + rand.Int63n(end-start)
|
|
// Випадкова тривалість у днях (але не виходити за межу end)
|
|
durationInDays := minDays + rand.Intn(maxDays-minDays)
|
|
|
|
until = since + int64(durationInDays)*secondsPerDay
|
|
if until > end {
|
|
until = end
|
|
}
|
|
}
|
|
return rangeCode, since, until
|
|
}
|
|
|
|
func (s *RandomQueryGenerator) getRandomMetric() MetricInfo {
|
|
return s.metrics[rand.Intn(len(s.metrics))]
|
|
}
|
|
|
|
// EXECUTE QUERY
|
|
|
|
func execQuery(conn *client.Connection, queryGenerator *RandomQueryGenerator, stat *WorkerStat) (err error) {
|
|
recipe := queryGenerator.GetQueryRecipe()
|
|
|
|
var elapsedTime time.Duration
|
|
|
|
switch recipe.Method {
|
|
case listCurrentValues:
|
|
t1 := time.Now()
|
|
_, err := conn.ListCurrentValues(recipe.MetricIDs)
|
|
elapsedTime = time.Since(t1)
|
|
stat.ElapsedTime += elapsedTime
|
|
stat.Queries++
|
|
stat.ElapsedTimeByMethods[recipe.Method] += elapsedTime
|
|
stat.MethodCalls[recipe.Method]++
|
|
if err != nil {
|
|
return fmt.Errorf("ListCurrentValues: %s", err)
|
|
}
|
|
|
|
case listInstantMeasures:
|
|
t1 := time.Now()
|
|
_, err := conn.ListInstantMeasures(proto.ListInstantMeasuresReq{
|
|
MetricID: recipe.MetricID,
|
|
Since: recipe.Since,
|
|
Until: recipe.Until,
|
|
})
|
|
elapsedTime = time.Since(t1)
|
|
stat.ElapsedTime += elapsedTime
|
|
stat.Queries++
|
|
stat.ElapsedTimeByMethods[recipe.Method] += elapsedTime
|
|
stat.MethodCalls[recipe.Method]++
|
|
stat.ElapsedTimeByTimeRanges[recipe.RangeCode] += elapsedTime
|
|
stat.TimeRangeCalls[recipe.RangeCode]++
|
|
if err != nil {
|
|
return fmt.Errorf("ListInstantMeasures(%d): %s",
|
|
recipe.MetricID, err)
|
|
}
|
|
|
|
case listCumulativeMeasures:
|
|
t1 := time.Now()
|
|
_, err := conn.ListCumulativeMeasures(proto.ListCumulativeMeasuresReq{
|
|
MetricID: recipe.MetricID,
|
|
Since: recipe.Since,
|
|
Until: recipe.Until,
|
|
})
|
|
elapsedTime = time.Since(t1)
|
|
stat.ElapsedTime += elapsedTime
|
|
stat.Queries++
|
|
stat.ElapsedTimeByMethods[recipe.Method] += elapsedTime
|
|
stat.MethodCalls[recipe.Method]++
|
|
stat.ElapsedTimeByTimeRanges[recipe.RangeCode] += elapsedTime
|
|
stat.TimeRangeCalls[recipe.RangeCode]++
|
|
if err != nil {
|
|
return fmt.Errorf("ListCumulativeMeasures(%d): %s",
|
|
recipe.MetricID, err)
|
|
}
|
|
|
|
case listInstantPeriods:
|
|
since := time.Unix(int64(recipe.Since), 0)
|
|
until := time.Unix(int64(recipe.Until), 0)
|
|
//
|
|
t1 := time.Now()
|
|
_, err := conn.ListInstantPeriods(proto.ListInstantPeriodsReq{
|
|
MetricID: recipe.MetricID,
|
|
Since: proto.TimeBound{
|
|
Year: since.Year(),
|
|
Month: since.Month(),
|
|
Day: since.Day(),
|
|
},
|
|
Until: proto.TimeBound{
|
|
Year: until.Year(),
|
|
Month: until.Month(),
|
|
Day: until.Day(),
|
|
},
|
|
GroupBy: recipe.GroupBy,
|
|
AggregateFuncs: diploma.AggregateMin | diploma.AggregateMax | diploma.AggregateAvg,
|
|
})
|
|
elapsedTime = time.Since(t1)
|
|
stat.ElapsedTime += elapsedTime
|
|
stat.Queries++
|
|
stat.ElapsedTimeByMethods[recipe.Method] += elapsedTime
|
|
stat.MethodCalls[recipe.Method]++
|
|
stat.ElapsedTimeByTimeRanges[recipe.RangeCode] += elapsedTime
|
|
stat.TimeRangeCalls[recipe.RangeCode]++
|
|
if err != nil {
|
|
return fmt.Errorf("ListInstantPeriods(%d): %s",
|
|
recipe.MetricID, err)
|
|
}
|
|
|
|
case listCumulativePeriods:
|
|
since := time.Unix(int64(recipe.Since), 0)
|
|
until := time.Unix(int64(recipe.Until), 0)
|
|
//
|
|
t1 := time.Now()
|
|
_, err := conn.ListCumulativePeriods(proto.ListCumulativePeriodsReq{
|
|
MetricID: recipe.MetricID,
|
|
Since: proto.TimeBound{
|
|
Year: since.Year(),
|
|
Month: since.Month(),
|
|
Day: since.Day(),
|
|
},
|
|
Until: proto.TimeBound{
|
|
Year: until.Year(),
|
|
Month: until.Month(),
|
|
Day: until.Day(),
|
|
},
|
|
GroupBy: recipe.GroupBy,
|
|
})
|
|
elapsedTime = time.Since(t1)
|
|
stat.ElapsedTime += elapsedTime
|
|
stat.Queries++
|
|
stat.ElapsedTimeByMethods[recipe.Method] += elapsedTime
|
|
stat.MethodCalls[recipe.Method]++
|
|
stat.ElapsedTimeByTimeRanges[recipe.RangeCode] += elapsedTime
|
|
stat.TimeRangeCalls[recipe.RangeCode]++
|
|
if err != nil {
|
|
return fmt.Errorf("ListCumulativePeriods(%d): %s",
|
|
recipe.MetricID, err)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|