This commit is contained in:
2026-06-19 07:25:46 +03:00
parent 06823ee547
commit 2d73ce1727
52 changed files with 5173 additions and 547 deletions

168
mqe/period_producers.go Normal file
View File

@@ -0,0 +1,168 @@
package mqe
import (
"database/sql"
"fmt"
"time"
)
type periodProducer interface {
Next() (time.Time, PeriodBound, PeriodBound, bool, error)
Last() (PeriodBound, bool, error)
}
type PeriodBound struct {
Time int64
Value float64
}
type PeriodProducer struct {
metricID int64
parsePeriodLayout string
location *time.Location
until int64
corrections []_f64Correction
rows *sql.Rows
tx *sql.Tx
}
type PeriodProducerOptions struct {
MetricID int64
ParsePeriodLayout string
Location *time.Location
Until int64
Corrections []_f64Correction
Rows *sql.Rows
Tx *sql.Tx
}
func NewPeriodProducer(opt PeriodProducerOptions) *PeriodProducer {
if opt.Location == nil {
panic("Location option is required")
}
if opt.MetricID == 0 {
panic("MetricID option is required")
}
if opt.ParsePeriodLayout == "" {
panic("ParsePeriodLayout option is required")
}
if opt.Until == 0 {
panic("Until option is required")
}
if opt.Rows == nil {
panic("Rows option is required")
}
if opt.Tx == nil {
panic("Tx option is required")
}
s := new(PeriodProducer)
s.metricID = opt.MetricID
s.parsePeriodLayout = opt.ParsePeriodLayout
s.location = opt.Location
s.until = opt.Until
s.corrections = opt.Corrections
s.rows = opt.Rows
s.tx = opt.Tx
return s
}
func (s *PeriodProducer) Next() (period time.Time, first PeriodBound, last PeriodBound, found bool, err error) {
var frameStr string
hasNext := s.rows.Next()
if hasNext {
err = s.rows.Scan(&frameStr, &first.Time, &first.Value, &last.Time, &last.Value)
if err != nil {
return
}
first.Value = applyCorrectionsToMeasure(s.corrections, first.Time, first.Value)
last.Value = applyCorrectionsToMeasure(s.corrections, last.Time, last.Value)
period, err = time.ParseInLocation(s.parsePeriodLayout, frameStr, s.location)
if err != nil {
err = fmt.Errorf("time.ParseInLocation: %s; layout=%q; str=%q",
err, s.parsePeriodLayout, frameStr)
return
}
found = true
} else {
err = s.rows.Err()
}
return
}
func (s *PeriodProducer) Last() (next PeriodBound, found bool, err error) {
// Находим следующее показание метрики.
err = s.tx.QueryRow(`
SELECT tm, value, true
FROM f64
WHERE metricID=? AND tm > ?
ORDER BY tm ASC
LIMIT 1`,
s.metricID, s.until).Scan(&next.Time, &next.Value, &found)
if err != nil {
if err != sql.ErrNoRows {
return
}
// Нет записей
err = nil
}
next.Value = applyCorrectionsToMeasure(s.corrections, next.Time, next.Value)
return
}
// ДЛЯ ТЕСТИРОВАНИЯ
type stubPeriod struct {
Period time.Time
First PeriodBound
Last PeriodBound
}
type StubPeriodProducerOptions struct {
Last *PeriodBound
Periods []stubPeriod
}
func NewStubPeriodProducer(opt StubPeriodProducerOptions) *StubPeriodProducer {
s := new(StubPeriodProducer)
s.last = opt.Last
s.periods = opt.Periods
return s
}
type StubPeriodProducer struct {
last *PeriodBound // для отдельного запроса
periods []stubPeriod
idx int
}
func (s *StubPeriodProducer) Next() (period time.Time, first PeriodBound, last PeriodBound, found bool, err error) {
if s.idx < len(s.periods) {
p := s.periods[s.idx]
s.idx++
period = p.Period
first = p.First
last = p.Last
found = true
}
return
}
func (s *StubPeriodProducer) Last() (next PeriodBound, found bool, err error) {
if s.last != nil {
next = *s.last
found = true
}
return
}