2026-02-10 14:02:11 +00:00
|
|
|
|
package database
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
2026-05-14 16:06:37 +03:00
|
|
|
|
qb "gordenko.dev/dima/qb"
|
2026-02-10 14:02:11 +00:00
|
|
|
|
"gordenko.dev/dima/qb/atree"
|
|
|
|
|
|
"gordenko.dev/dima/qb/chunkenc"
|
|
|
|
|
|
"gordenko.dev/dima/qb/conbuf"
|
2026-05-10 19:15:11 +00:00
|
|
|
|
"gordenko.dev/dima/qb/proto"
|
2026-02-10 14:02:11 +00:00
|
|
|
|
"gordenko.dev/dima/qb/transform"
|
|
|
|
|
|
"gordenko.dev/dima/qb/txlog"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
// AddMetric - create metric in map and set Xlock, rwad - append to queue
|
|
|
|
|
|
// DeleteMetric - set Xlock
|
|
|
|
|
|
// DeleteSince - xLock (тупо редкая операция)
|
|
|
|
|
|
// Якийсь прапор поставити для pendingDelete, щоб read операції вставали в чергу
|
|
|
|
|
|
|
2026-02-10 14:02:11 +00:00
|
|
|
|
const (
|
|
|
|
|
|
QueryDone = 1
|
|
|
|
|
|
UntilFound = 2
|
|
|
|
|
|
UntilNotFound = 3
|
|
|
|
|
|
RangeFound = 15
|
|
|
|
|
|
NoMeasures = 16
|
|
|
|
|
|
NoMetric = 4
|
|
|
|
|
|
MetricDuplicate = 5
|
|
|
|
|
|
Succeed = 6
|
|
|
|
|
|
NewPage = 7
|
|
|
|
|
|
ExpiredMeasure = 8
|
|
|
|
|
|
NonMonotonicValue = 9
|
|
|
|
|
|
CanAppend = 10
|
|
|
|
|
|
WrongMetricType = 11
|
|
|
|
|
|
NoMeasuresToDelete = 12
|
|
|
|
|
|
DeleteFromAtreeNotNeeded = 13
|
|
|
|
|
|
DeleteFromAtreeRequired = 14
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) worker() {
|
|
|
|
|
|
for {
|
|
|
|
|
|
select {
|
|
|
|
|
|
case <-s.workerSignalCh:
|
|
|
|
|
|
s.DoWork()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) DoWork() {
|
|
|
|
|
|
s.mutex.Lock()
|
|
|
|
|
|
rLocksToRelease := s.rLocksToRelease
|
|
|
|
|
|
workerQueue := s.workerQueue
|
|
|
|
|
|
s.rLocksToRelease = nil
|
|
|
|
|
|
s.workerQueue = nil
|
|
|
|
|
|
s.mutex.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
for _, metricID := range rLocksToRelease {
|
2026-05-31 20:01:28 +00:00
|
|
|
|
metric, ok := s.metrics[metricID]
|
2026-02-10 14:02:11 +00:00
|
|
|
|
if !ok {
|
2026-05-31 20:01:28 +00:00
|
|
|
|
qb.Abort(qb.NoMetricBug,
|
|
|
|
|
|
fmt.Errorf("drainQueues: metric %d not found", metricID))
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
if metric.XLock {
|
2026-05-14 16:06:37 +03:00
|
|
|
|
qb.Abort(qb.XLockBug,
|
2026-02-10 14:02:11 +00:00
|
|
|
|
fmt.Errorf("drainQueues: xlock is set for the metric %d",
|
|
|
|
|
|
metricID))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
if metric.RLocks <= 0 {
|
2026-05-14 16:06:37 +03:00
|
|
|
|
qb.Abort(qb.NoRLockBug,
|
2026-02-10 14:02:11 +00:00
|
|
|
|
fmt.Errorf("drainQueues: rlock not set for the metric %d",
|
|
|
|
|
|
metricID))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
metric.RLocks--
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
if len(metric.WaitQueue) > 0 {
|
|
|
|
|
|
s.processMetricQueue(metricID, metric)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, untyped := range workerQueue {
|
|
|
|
|
|
switch req := untyped.(type) {
|
|
|
|
|
|
case tryAppendMeasuresReq:
|
|
|
|
|
|
s.tryAppendMeasures(req)
|
|
|
|
|
|
|
|
|
|
|
|
case txlog.Changes:
|
|
|
|
|
|
s.applyChanges(req) // all metrics only
|
|
|
|
|
|
|
|
|
|
|
|
case tryListCurrentValuesReq:
|
|
|
|
|
|
s.tryListCurrentValues(req) // all metrics only
|
|
|
|
|
|
|
|
|
|
|
|
case tryRangeScanReq:
|
|
|
|
|
|
s.tryRangeScan(req)
|
|
|
|
|
|
|
|
|
|
|
|
case tryFullScanReq:
|
|
|
|
|
|
s.tryFullScan(req)
|
|
|
|
|
|
|
|
|
|
|
|
case tryAddMetricReq:
|
|
|
|
|
|
s.tryAddMetric(req)
|
|
|
|
|
|
|
|
|
|
|
|
case tryDeleteMetricReq:
|
|
|
|
|
|
s.tryDeleteMetric(req)
|
|
|
|
|
|
|
|
|
|
|
|
case tryDeleteMeasuresReq:
|
|
|
|
|
|
s.tryDeleteMeasures(req)
|
|
|
|
|
|
|
|
|
|
|
|
case tryGetMetricReq:
|
|
|
|
|
|
s.tryGetMetric(req)
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
2026-05-14 16:06:37 +03:00
|
|
|
|
qb.Abort(qb.UnknownWorkerQueueItemBug,
|
2026-02-10 14:02:11 +00:00
|
|
|
|
fmt.Errorf("bug: unknown worker queue item type %T", req))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
// суть у тому що треба запускати запити, пока не зустріну XLock
|
|
|
|
|
|
func (s *Database) processMetricQueue(metricID uint32, metric *_metric) {
|
|
|
|
|
|
if len(metric.WaitQueue) == 0 {
|
2026-02-10 14:02:11 +00:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
for _, untyped := range metric.WaitQueue {
|
2026-02-10 14:02:11 +00:00
|
|
|
|
switch req := untyped.(type) {
|
|
|
|
|
|
case tryRangeScanReq:
|
2026-05-31 20:01:28 +00:00
|
|
|
|
metric.StartRangeScan(req)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
|
|
|
|
|
case tryFullScanReq:
|
2026-05-31 20:01:28 +00:00
|
|
|
|
metric.StartFullScan(req)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
|
|
|
|
|
case tryGetMetricReq:
|
|
|
|
|
|
s.tryGetMetric(req)
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
case tryAppendMeasuresReq:
|
|
|
|
|
|
metric.StartAppendMeasures(req, s.txlog.Append)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
case tryDeleteMetricReq:
|
|
|
|
|
|
s.startDeleteMetric(metric, req)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
case tryDeleteMeasuresReq:
|
|
|
|
|
|
s.startDeleteMeasures(metric, req)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
default:
|
|
|
|
|
|
qb.Abort(qb.UnknownMetricWaitQueueItemBug,
|
|
|
|
|
|
fmt.Errorf("bug: unknown metric wait queue item type %T", req))
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type tryAddMetricReq struct {
|
|
|
|
|
|
MetricID uint32
|
|
|
|
|
|
ResultCh chan byte
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) tryAddMetric(req tryAddMetricReq) {
|
|
|
|
|
|
_, ok := s.metrics[req.MetricID]
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
req.ResultCh <- MetricDuplicate
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-05-10 19:15:11 +00:00
|
|
|
|
req.ResultCh <- Succeed // new
|
|
|
|
|
|
|
|
|
|
|
|
// lockEntry, ok := s.metricLockEntries[req.MetricID]
|
|
|
|
|
|
// if ok {
|
|
|
|
|
|
// lockEntry.WaitQueue = append(lockEntry.WaitQueue, req)
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// s.metricLockEntries[req.MetricID] = &metricLockEntry{
|
|
|
|
|
|
// XLock: true,
|
|
|
|
|
|
// }
|
|
|
|
|
|
// req.ResultCh <- Succeed
|
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) processTryAddMetricReqsImmediatelyAfterDelete(reqs []tryAddMetricReq) {
|
|
|
|
|
|
if len(reqs) == 0 {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
var (
|
|
|
|
|
|
req = reqs[0]
|
|
|
|
|
|
waitQueue []any
|
|
|
|
|
|
)
|
|
|
|
|
|
if len(reqs) > 1 {
|
|
|
|
|
|
for _, req := range reqs[1:] {
|
|
|
|
|
|
waitQueue = append(waitQueue, req)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-10 19:15:11 +00:00
|
|
|
|
// FIX
|
|
|
|
|
|
// s.metricLockEntries[req.MetricID] = &metricLockEntry{
|
|
|
|
|
|
// XLock: true,
|
|
|
|
|
|
// WaitQueue: waitQueue,
|
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
req.ResultCh <- Succeed
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type tryGetMetricReq struct {
|
|
|
|
|
|
MetricID uint32
|
|
|
|
|
|
ResultCh chan Metric
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) tryGetMetric(req tryGetMetricReq) {
|
|
|
|
|
|
metric, ok := s.metrics[req.MetricID]
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
req.ResultCh <- Metric{
|
|
|
|
|
|
ResultCode: Succeed,
|
|
|
|
|
|
MetricType: metric.MetricType,
|
|
|
|
|
|
FracDigits: metric.FracDigits,
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
req.ResultCh <- Metric{
|
|
|
|
|
|
ResultCode: NoMetric,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type tryDeleteMetricReq struct {
|
|
|
|
|
|
MetricID uint32
|
|
|
|
|
|
ResultCh chan tryDeleteMetricResult
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) tryDeleteMetric(req tryDeleteMetricReq) {
|
2026-05-10 19:15:11 +00:00
|
|
|
|
// FIX
|
|
|
|
|
|
// metric, ok := s.metrics[req.MetricID]
|
|
|
|
|
|
// if !ok {
|
|
|
|
|
|
// req.ResultCh <- tryDeleteMetricResult{
|
|
|
|
|
|
// ResultCode: NoMetric,
|
|
|
|
|
|
// }
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// lockEntry, ok := s.metricLockEntries[req.MetricID]
|
|
|
|
|
|
// if ok {
|
|
|
|
|
|
// lockEntry.WaitQueue = append(lockEntry.WaitQueue, req)
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// s.startDeleteMetric(metric, req)
|
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) startDeleteMetric(metric *_metric, req tryDeleteMetricReq) {
|
2026-05-10 19:15:11 +00:00
|
|
|
|
// FIX
|
|
|
|
|
|
// s.metricLockEntries[req.MetricID] = &metricLockEntry{
|
|
|
|
|
|
// XLock: true,
|
|
|
|
|
|
// }
|
|
|
|
|
|
// req.ResultCh <- tryDeleteMetricResult{
|
|
|
|
|
|
// ResultCode: Succeed,
|
|
|
|
|
|
// RootPageNo: metric.RootPageNo,
|
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type tryDeleteMeasuresReq struct {
|
|
|
|
|
|
MetricID uint32
|
|
|
|
|
|
Since uint32
|
|
|
|
|
|
ResultCh chan tryDeleteMeasuresResult
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) tryDeleteMeasures(req tryDeleteMeasuresReq) {
|
2026-05-10 19:15:11 +00:00
|
|
|
|
// FIX
|
|
|
|
|
|
// metric, ok := s.metrics[req.MetricID]
|
|
|
|
|
|
// if !ok {
|
|
|
|
|
|
// req.ResultCh <- tryDeleteMeasuresResult{
|
|
|
|
|
|
// ResultCode: NoMetric,
|
|
|
|
|
|
// }
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// if metric.Since == 0 || (req.Since > 0 && metric.Until < req.Since) {
|
|
|
|
|
|
// req.ResultCh <- tryDeleteMeasuresResult{
|
|
|
|
|
|
// ResultCode: NoMeasuresToDelete,
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// lockEntry, ok := s.metricLockEntries[req.MetricID]
|
|
|
|
|
|
// if ok {
|
|
|
|
|
|
// lockEntry.WaitQueue = append(lockEntry.WaitQueue, req)
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// s.startDeleteMeasures(metric, req)
|
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) startDeleteMeasures(metric *_metric, req tryDeleteMeasuresReq) {
|
2026-05-10 19:15:11 +00:00
|
|
|
|
// FIX
|
|
|
|
|
|
// s.metricLockEntries[req.MetricID] = &metricLockEntry{
|
|
|
|
|
|
// XLock: true,
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// if metric.RootPageNo > 0 {
|
|
|
|
|
|
// req.ResultCh <- tryDeleteMeasuresResult{
|
|
|
|
|
|
// ResultCode: DeleteFromAtreeRequired,
|
|
|
|
|
|
// RootPageNo: metric.RootPageNo,
|
|
|
|
|
|
// }
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// req.ResultCh <- tryDeleteMeasuresResult{
|
|
|
|
|
|
// ResultCode: DeleteFromAtreeNotNeeded,
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
func (s *Database) finAppendMeasures(rec txlog.AppendMeasuresSummary) {
|
2026-02-10 14:02:11 +00:00
|
|
|
|
metric, ok := s.metrics[rec.MetricID]
|
|
|
|
|
|
if !ok {
|
2026-05-14 16:06:37 +03:00
|
|
|
|
qb.Abort(qb.NoMetricBug,
|
2026-05-31 20:01:28 +00:00
|
|
|
|
fmt.Errorf("finAppendMeasures: metric %d not found",
|
2026-02-10 14:02:11 +00:00
|
|
|
|
rec.MetricID))
|
|
|
|
|
|
}
|
2026-05-31 20:01:28 +00:00
|
|
|
|
metric.FinAppendMeasures(rec)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type tryAppendMeasuresReq struct {
|
|
|
|
|
|
MetricID uint32
|
2026-05-10 19:15:11 +00:00
|
|
|
|
Measures []proto.Measure
|
2026-02-10 14:02:11 +00:00
|
|
|
|
ResultCh chan tryAppendMeasuresResult
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) tryAppendMeasures(req tryAppendMeasuresReq) {
|
|
|
|
|
|
metric, ok := s.metrics[req.MetricID]
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
req.ResultCh <- tryAppendMeasuresResult{
|
|
|
|
|
|
ResultCode: NoMetric,
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-05-31 20:01:28 +00:00
|
|
|
|
if metric.XLock {
|
|
|
|
|
|
metric.WaitQueue = append(metric.WaitQueue, req)
|
|
|
|
|
|
return
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
2026-05-31 20:01:28 +00:00
|
|
|
|
metric.StartAppendMeasures(req, s.txlog.Append)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type tryRangeScanReq struct {
|
|
|
|
|
|
MetricID uint32
|
|
|
|
|
|
Since uint32
|
|
|
|
|
|
Until uint32
|
2026-05-14 16:06:37 +03:00
|
|
|
|
MetricType qb.MetricType
|
2026-02-10 14:02:11 +00:00
|
|
|
|
ResponseWriter atree.WorkerMeasureConsumer
|
|
|
|
|
|
ResultCh chan rangeScanResult
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) tryRangeScan(req tryRangeScanReq) {
|
|
|
|
|
|
metric, ok := s.metrics[req.MetricID]
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
req.ResultCh <- rangeScanResult{
|
|
|
|
|
|
ResultCode: NoMetric,
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if metric.MetricType != req.MetricType {
|
|
|
|
|
|
req.ResultCh <- rangeScanResult{
|
|
|
|
|
|
ResultCode: WrongMetricType,
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
if metric.XLock {
|
|
|
|
|
|
metric.WaitQueue = append(metric.WaitQueue, req)
|
|
|
|
|
|
return
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
metric.StartRangeScan(req)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type tryFullScanReq struct {
|
|
|
|
|
|
MetricID uint32
|
2026-05-14 16:06:37 +03:00
|
|
|
|
MetricType qb.MetricType
|
2026-02-10 14:02:11 +00:00
|
|
|
|
ResponseWriter atree.WorkerMeasureConsumer
|
|
|
|
|
|
ResultCh chan fullScanResult
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) tryFullScan(req tryFullScanReq) {
|
|
|
|
|
|
metric, ok := s.metrics[req.MetricID]
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
req.ResultCh <- fullScanResult{
|
|
|
|
|
|
ResultCode: NoMetric,
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if metric.MetricType != req.MetricType {
|
|
|
|
|
|
req.ResultCh <- fullScanResult{
|
|
|
|
|
|
ResultCode: WrongMetricType,
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
if metric.XLock {
|
|
|
|
|
|
metric.WaitQueue = append(metric.WaitQueue, req)
|
|
|
|
|
|
return
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
2026-05-31 20:01:28 +00:00
|
|
|
|
metric.StartFullScan(req)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type tryListCurrentValuesReq struct {
|
|
|
|
|
|
MetricIDs []uint32
|
|
|
|
|
|
ResponseWriter *transform.CurrentValueWriter
|
|
|
|
|
|
ResultCh chan struct{}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) tryListCurrentValues(req tryListCurrentValuesReq) {
|
|
|
|
|
|
for _, metricID := range req.MetricIDs {
|
|
|
|
|
|
metric, ok := s.metrics[metricID]
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
req.ResponseWriter.BufferValue(transform.CurrentValue{
|
|
|
|
|
|
MetricID: metricID,
|
|
|
|
|
|
Timestamp: metric.Until,
|
|
|
|
|
|
Value: metric.UntilValue,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
req.ResultCh <- struct{}{}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) applyChanges(req txlog.Changes) {
|
|
|
|
|
|
for _, untyped := range req.Records {
|
|
|
|
|
|
switch rec := untyped.(type) {
|
|
|
|
|
|
case txlog.AddedMetric:
|
|
|
|
|
|
s.addMetric(rec)
|
|
|
|
|
|
|
|
|
|
|
|
case txlog.DeletedMetric:
|
|
|
|
|
|
s.deleteMetric(rec)
|
|
|
|
|
|
|
2026-05-10 19:15:11 +00:00
|
|
|
|
// case txlog.AppendedMeasure:
|
|
|
|
|
|
// s.appendMeasure(rec)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
case txlog.AppendMeasuresSummary:
|
|
|
|
|
|
s.finAppendMeasures(rec)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-05-10 19:15:11 +00:00
|
|
|
|
// case txlog.AppendedMeasureWithOverflowExtended:
|
|
|
|
|
|
// s.appendMeasureAfterOverflow(rec)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
|
|
|
|
|
case txlog.DeletedMeasures:
|
|
|
|
|
|
s.deleteMeasures(rec)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
if req.MetricsCh != nil {
|
|
|
|
|
|
waitCh := make(chan struct{})
|
|
|
|
|
|
req.MetricsCh <- txlog.MetricsState{
|
|
|
|
|
|
Metrics: s.createMetricsState(),
|
|
|
|
|
|
WaitCh: waitCh,
|
|
|
|
|
|
}
|
|
|
|
|
|
// чекаю поки txlog запише снапшот, а отже можна змінювати буфери
|
|
|
|
|
|
<-waitCh
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
2026-05-31 20:01:28 +00:00
|
|
|
|
}
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
func (s *Database) createMetricsState() (metrics []txlog.Metric) {
|
|
|
|
|
|
for metricID, metric := range s.metrics {
|
|
|
|
|
|
x := txlog.Metric{
|
|
|
|
|
|
MetricID: metricID,
|
|
|
|
|
|
MetricType: metric.MetricType,
|
|
|
|
|
|
FracDigits: metric.FracDigits,
|
|
|
|
|
|
LastPageNo: metric.LastPageNo,
|
|
|
|
|
|
Since: metric.Since,
|
|
|
|
|
|
SinceValue: metric.SinceValue,
|
|
|
|
|
|
Until: metric.Until, // ?
|
|
|
|
|
|
UntilValue: metric.UntilValue, // ?
|
|
|
|
|
|
}
|
|
|
|
|
|
x.Timestamps, x.TimestampsSize = metric.Timestamps.Snapshot()
|
|
|
|
|
|
x.Values, x.ValuesSize = metric.Values.Snapshot()
|
|
|
|
|
|
metrics = append(metrics, x)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
2026-05-31 20:01:28 +00:00
|
|
|
|
return
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) addMetric(rec txlog.AddedMetric) {
|
2026-05-31 20:01:28 +00:00
|
|
|
|
// fix lock
|
2026-02-10 14:02:11 +00:00
|
|
|
|
_, ok := s.metrics[rec.MetricID]
|
|
|
|
|
|
if ok {
|
2026-05-14 16:06:37 +03:00
|
|
|
|
qb.Abort(qb.MetricAddedBug,
|
2026-02-10 14:02:11 +00:00
|
|
|
|
fmt.Errorf("addMetric: metric %d already added",
|
|
|
|
|
|
rec.MetricID))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
// if !lockEntry.XLock {
|
|
|
|
|
|
// qb.Abort(qb.NoXLockBug,
|
|
|
|
|
|
// fmt.Errorf("addMetric: xlock not set for the metric %d",
|
|
|
|
|
|
// rec.MetricID))
|
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
|
|
|
|
|
var (
|
2026-05-14 16:06:37 +03:00
|
|
|
|
values qb.ValueCompressor
|
2026-02-10 14:02:11 +00:00
|
|
|
|
timestampsBuf = conbuf.New(nil)
|
|
|
|
|
|
valuesBuf = conbuf.New(nil)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-14 16:06:37 +03:00
|
|
|
|
if rec.MetricType == qb.Cumulative {
|
2026-02-10 14:02:11 +00:00
|
|
|
|
values = chunkenc.NewReverseCumulativeDeltaCompressor(
|
|
|
|
|
|
valuesBuf, 0, byte(rec.FracDigits))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
values = chunkenc.NewReverseInstantDeltaCompressor(
|
|
|
|
|
|
valuesBuf, 0, byte(rec.FracDigits))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
s.metrics[rec.MetricID] = &_metric{
|
2026-05-31 20:01:28 +00:00
|
|
|
|
MetricType: rec.MetricType,
|
|
|
|
|
|
FracDigits: byte(rec.FracDigits),
|
|
|
|
|
|
Timestamps: chunkenc.NewReverseTimeDeltaCompressor(timestampsBuf, 0),
|
|
|
|
|
|
Values: values,
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
lockEntry.XLock = false
|
|
|
|
|
|
delete(s.metricLockEntries, rec.MetricID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) deleteMetric(rec txlog.DeletedMetric) {
|
2026-05-31 20:01:28 +00:00
|
|
|
|
metric, ok := s.metrics[rec.MetricID]
|
2026-02-10 14:02:11 +00:00
|
|
|
|
if !ok {
|
2026-05-14 16:06:37 +03:00
|
|
|
|
qb.Abort(qb.NoMetricBug,
|
2026-02-10 14:02:11 +00:00
|
|
|
|
fmt.Errorf("deleteMetric: metric %d not found",
|
|
|
|
|
|
rec.MetricID))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
if !metric.XLock {
|
2026-05-14 16:06:37 +03:00
|
|
|
|
qb.Abort(qb.NoXLockBug,
|
2026-02-10 14:02:11 +00:00
|
|
|
|
fmt.Errorf("deleteMetric: xlock not set for the metric %d",
|
|
|
|
|
|
rec.MetricID))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var addMetricReqs []tryAddMetricReq
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
if len(metric.WaitQueue) > 0 {
|
|
|
|
|
|
for _, untyped := range metric.WaitQueue {
|
2026-02-10 14:02:11 +00:00
|
|
|
|
switch req := untyped.(type) {
|
2026-05-10 19:15:11 +00:00
|
|
|
|
// case tryAppendMeasureReq:
|
|
|
|
|
|
// req.ResultCh <- tryAppendMeasureResult{
|
|
|
|
|
|
// MetricID: req.MetricID,
|
|
|
|
|
|
// ResultCode: NoMetric,
|
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
|
|
|
|
|
case tryRangeScanReq:
|
|
|
|
|
|
req.ResultCh <- rangeScanResult{
|
|
|
|
|
|
ResultCode: NoMetric,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case tryFullScanReq:
|
|
|
|
|
|
req.ResultCh <- fullScanResult{
|
|
|
|
|
|
ResultCode: NoMetric,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case tryAddMetricReq:
|
|
|
|
|
|
addMetricReqs = append(addMetricReqs, req)
|
|
|
|
|
|
|
|
|
|
|
|
case tryDeleteMetricReq:
|
|
|
|
|
|
req.ResultCh <- tryDeleteMetricResult{
|
|
|
|
|
|
ResultCode: NoMetric,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case tryDeleteMeasuresReq:
|
|
|
|
|
|
req.ResultCh <- tryDeleteMeasuresResult{
|
|
|
|
|
|
ResultCode: NoMetric,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case tryGetMetricReq:
|
|
|
|
|
|
req.ResultCh <- Metric{
|
|
|
|
|
|
ResultCode: NoMetric,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
2026-05-14 16:06:37 +03:00
|
|
|
|
qb.Abort(qb.UnknownMetricWaitQueueItemBug,
|
2026-02-10 14:02:11 +00:00
|
|
|
|
fmt.Errorf("bug: unknown metric wait queue item type %T", req))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
delete(s.metrics, rec.MetricID)
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
// ADD in txlog
|
|
|
|
|
|
// if len(rec.FreePageNumbers) > 0 {
|
|
|
|
|
|
// s.freeList.AddPages(rec.FreePageNumbers)
|
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
|
|
|
|
|
if len(addMetricReqs) > 0 {
|
|
|
|
|
|
s.processTryAddMetricReqsImmediatelyAfterDelete(addMetricReqs)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Database) deleteMeasures(rec txlog.DeletedMeasures) {
|
|
|
|
|
|
metric, ok := s.metrics[rec.MetricID]
|
|
|
|
|
|
if !ok {
|
2026-05-14 16:06:37 +03:00
|
|
|
|
qb.Abort(qb.NoMetricBug,
|
2026-02-10 14:02:11 +00:00
|
|
|
|
fmt.Errorf("deleteMeasures: metric %d not found",
|
|
|
|
|
|
rec.MetricID))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
if !metric.XLock {
|
2026-05-14 16:06:37 +03:00
|
|
|
|
qb.Abort(qb.NoXLockBug,
|
2026-02-10 14:02:11 +00:00
|
|
|
|
fmt.Errorf("deleteMeasures: xlock not set for the metric %d",
|
|
|
|
|
|
rec.MetricID))
|
|
|
|
|
|
}
|
|
|
|
|
|
metric.DeleteMeasures()
|
2026-05-31 20:01:28 +00:00
|
|
|
|
metric.XLock = false
|
|
|
|
|
|
// FIX add in txlog
|
|
|
|
|
|
// if len(rec.FreePageNumbers) > 0 {
|
|
|
|
|
|
// s.freeList.AddPages(rec.FreePageNumbers)
|
|
|
|
|
|
// }
|
|
|
|
|
|
s.doAfterReleaseXLock(rec.MetricID, metric)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-31 20:01:28 +00:00
|
|
|
|
func (s *Database) doAfterReleaseXLock(metricID uint32, metric *_metric) {
|
|
|
|
|
|
if len(metric.WaitQueue) > 0 {
|
|
|
|
|
|
s.processMetricQueue(metricID, metric)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|