wp
This commit is contained in:
141
mqe/total.go
Normal file
141
mqe/total.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package mqe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gordenko.dev/dima/qb/timeutil"
|
||||
)
|
||||
|
||||
// TOTALS
|
||||
|
||||
type TotalMetricSpec struct {
|
||||
MetricID int64
|
||||
LastDayOfMonth int
|
||||
FirstHourOfDay int
|
||||
}
|
||||
|
||||
// LastDayOfMonth и FirstHourOfDay нет, ибо у каждой метрики свои настройки
|
||||
type RangeTotalsFilter struct {
|
||||
Since string
|
||||
Until string
|
||||
// Если учитывать не нужно - программа уровнем выше может передать нули.
|
||||
// FIX у каждой метрики свои настройки
|
||||
// искать ли конец периода в следующих периодах, если в последние 3 минуты периода
|
||||
// не было показаний
|
||||
CumulativeMetrics []TotalMetricSpec // metricID
|
||||
}
|
||||
|
||||
// SelectRangeTotalsInParallel - метод рассчитывает накопленный объем от Since до Until
|
||||
// по каждой метрике из массива CumulativeMetricIDs. Учитываются настройки объекта
|
||||
// (LastDayOfMonth, FirstHourOfDay).
|
||||
// ВАЖНО!
|
||||
// Запросы по каждой метрике отправляются в БД в отдельной горутине, что позволяет
|
||||
// в разы ускорить получение данных по объекту.
|
||||
func (s *MeasureQueryEngine) GetRangeTotalsInParallel(req RangeTotalsFilter) (resultMap map[int64]RangeTotal, err error) {
|
||||
if req.Since == "" {
|
||||
err = fix.Field(EmptyValue, "since")
|
||||
return
|
||||
}
|
||||
|
||||
if req.Until == "" {
|
||||
err = fix.Field(EmptyValue, "until")
|
||||
return
|
||||
}
|
||||
|
||||
// ВАЖНО!
|
||||
// Если в строке нет таймзоны - Parse функция возвращает время в UTC.
|
||||
// Поэтому нужно использовать ParseInLocation!
|
||||
|
||||
since, err := time.ParseInLocation("2006-01-02", req.Since, s.location)
|
||||
if err != nil {
|
||||
err = fix.Field(WrongValue, "since")
|
||||
return
|
||||
}
|
||||
|
||||
until, err := time.ParseInLocation("2006-01-02", req.Until, s.location)
|
||||
if err != nil {
|
||||
err = fix.Field(WrongValue, "until")
|
||||
return
|
||||
}
|
||||
|
||||
// Корректируем, чтобы указывало на последнюю секунду суток
|
||||
until = timeutil.LastSecondInPeriod(until, string(ByDay))
|
||||
|
||||
var metrics []string
|
||||
|
||||
for _, metric := range req.CumulativeMetrics {
|
||||
metrics = append(metrics, fmt.Sprintf(` {
|
||||
MetricID: %d
|
||||
LastDayOfMonth: %d
|
||||
FirstHourOfDay: %d
|
||||
}`, metric.MetricID, metric.LastDayOfMonth, metric.FirstHourOfDay))
|
||||
}
|
||||
|
||||
fmt.Printf(`GetRangeTotalsInParallel: {
|
||||
Since: %s
|
||||
Until: %s
|
||||
CumulativeMetrics [
|
||||
%s
|
||||
]
|
||||
}
|
||||
`, since, until, strings.Join(metrics, "\n"))
|
||||
if since.Unix() >= until.Unix() {
|
||||
err = fix.Error(WrongDateRange)
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
tasks []*CumulativeTotalTask
|
||||
wg = new(sync.WaitGroup)
|
||||
)
|
||||
|
||||
resultMap = make(map[int64]RangeTotal)
|
||||
|
||||
// CUMULATIVE METRIC TOTALS
|
||||
if len(req.CumulativeMetrics) > 0 {
|
||||
for _, metric := range req.CumulativeMetrics {
|
||||
// У каждой метрики индивидуальные настройки,
|
||||
// ибо метрики принадлежат разным объектам!!!
|
||||
metricSince := since
|
||||
metricUntil := until
|
||||
|
||||
if metric.FirstHourOfDay > 0 {
|
||||
metricSince = since.Add(time.Duration(metric.FirstHourOfDay) * time.Hour)
|
||||
metricUntil = until.Add(time.Duration(metric.FirstHourOfDay) * time.Hour)
|
||||
}
|
||||
|
||||
task := &CumulativeTotalTask{
|
||||
In: getCumulativeTotalFilter{
|
||||
MetricID: metric.MetricID,
|
||||
Since: metricSince.Unix(),
|
||||
Until: metricUntil.Unix(),
|
||||
LastDayOfMonth: metric.LastDayOfMonth,
|
||||
FirstHourOfDay: metric.FirstHourOfDay,
|
||||
},
|
||||
WaitGroup: wg,
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
s.cumulativeTotalCh <- task
|
||||
tasks = append(tasks, task)
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
for _, task := range tasks {
|
||||
if task.Err != nil {
|
||||
err = fmt.Errorf("get data for the metric %d: %s", task.In.MetricID, task.Err)
|
||||
return
|
||||
}
|
||||
if task.Result != nil {
|
||||
resultMap[task.In.MetricID] = *task.Result
|
||||
} else {
|
||||
resultMap[task.In.MetricID] = RangeTotal{}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user