Files
qb/database/api.go

629 lines
16 KiB
Go
Raw Normal View History

2026-02-10 14:02:11 +00:00
package database
import (
"errors"
"fmt"
"io"
"net"
2026-06-10 06:18:45 +03:00
bin "gordenko.dev/dima/bin/little"
"gordenko.dev/dima/qb"
2026-02-10 14:02:11 +00:00
"gordenko.dev/dima/qb/bufreader"
"gordenko.dev/dima/qb/proto"
2026-06-14 23:12:03 +03:00
"gordenko.dev/dima/qb/storage"
2026-06-15 10:47:24 +00:00
"gordenko.dev/dima/qb/timeutil"
2026-02-10 14:02:11 +00:00
"gordenko.dev/dima/qb/transform"
2026-06-13 22:43:17 +00:00
"gordenko.dev/dima/qb/worker"
2026-02-10 14:02:11 +00:00
)
var (
ErrWrongResultCodeBug = errors.New("bug: wrong result code")
successMsg = []byte{
proto.RespSuccess,
}
)
func reply(conn io.Writer, errcode uint16) {
var answer []byte
if errcode == 0 {
answer = successMsg
} else {
answer = []byte{
proto.RespError,
0, 0,
}
bin.PutUint16(answer[1:], errcode)
}
_, err := conn.Write(answer)
if err != nil {
return
}
}
func (s *Database) handleTCPConn(conn net.Conn) {
defer conn.Close()
2026-06-13 19:53:27 +03:00
fmt.Println("client connected")
2026-02-10 14:02:11 +00:00
r := bufreader.New(conn, 128)
for {
err := s.processRequest(conn, r)
if err != nil {
if err != io.EOF {
s.logger.Println(err)
}
return
}
}
}
func (s *Database) processRequest(conn net.Conn, r *bufreader.BufferedReader) (err error) {
messageType, err := r.ReadByte()
if err != nil {
if err != io.EOF {
return fmt.Errorf("read messageType: %s", err)
} else {
return err
}
}
switch messageType {
case proto.TypeGetMetric:
req, err := proto.ReadGetMetricReq(r)
if err != nil {
return fmt.Errorf("proto.ReadGetMetricReq: %s", err)
}
if err = s.GetMetric(conn, req); err != nil {
return fmt.Errorf("GetMetric: %s", err)
}
case proto.TypeAddMetric:
req, err := proto.ReadAddMetricReq(r)
if err != nil {
return fmt.Errorf("proto.ReadAddMetricReq: %s", err)
}
2026-06-13 19:53:27 +03:00
fmt.Println("ReadAddMetricReq:", req)
2026-02-10 14:02:11 +00:00
reply(conn, s.AddMetric(req))
case proto.TypeDeleteMetric:
req, err := proto.ReadDeleteMetricReq(r)
if err != nil {
return fmt.Errorf("proto.ReadDeleteMetricReq: %s", err)
}
reply(conn, s.DeleteMetric(req))
2026-05-10 19:15:11 +00:00
// case proto.TypeAppendMeasure:
// req, err := proto.ReadAppendMeasureReq(r)
// if err != nil {
// return fmt.Errorf("proto.ReadAppendMeasureReq: %s", err)
// }
// //fmt.Println("append measure", req.MetricID, conn.RemoteAddr().String())
// reply(conn, s.AppendMeasure(req))
2026-02-10 14:02:11 +00:00
case proto.TypeAppendMeasures:
req, err := proto.ReadAppendMeasuresReq(r)
if err != nil {
return fmt.Errorf("proto.ReadAppendMeasuresReq: %s", err)
}
2026-06-14 23:12:03 +03:00
if err = s.AppendMeasures(conn, req); err != nil {
return fmt.Errorf("AppendMeasures: %s", err)
}
2026-02-10 14:02:11 +00:00
case proto.TypeListInstantMeasures:
req, err := proto.ReadListInstantMeasuresReq(r)
if err != nil {
return fmt.Errorf("proto.ReadListInstantMeasuresReq: %s", err)
}
if err = s.ListInstantMeasures(conn, req); err != nil {
return fmt.Errorf("ListInstantMeasures: %s", err)
}
case proto.TypeListCumulativeMeasures:
req, err := proto.ReadListCumulativeMeasuresReq(r)
if err != nil {
return fmt.Errorf("proto.ReadListCumulativeMeasuresReq: %s", err)
}
if err = s.ListCumulativeMeasures(conn, req); err != nil {
return fmt.Errorf("ListCumulativeMeasures: %s", err)
}
case proto.TypeListInstantPeriods:
req, err := proto.ReadListInstantPeriodsReq(r)
if err != nil {
return fmt.Errorf("proto.ReadListInstantPeriodsReq: %s", err)
}
if err = s.ListInstantPeriods(conn, req); err != nil {
return fmt.Errorf("ListInstantPeriods: %s", err)
}
case proto.TypeListCumulativePeriods:
req, err := proto.ReadListCumulativePeriodsReq(r)
if err != nil {
return fmt.Errorf("proto.ReadListCumulativePeriodsReq: %s", err)
}
if err = s.ListCumulativePeriods(conn, req); err != nil {
return fmt.Errorf("ListCumulativePeriods: %s", err)
}
case proto.TypeListCurrentValues:
req, err := proto.ReadListCurrentValuesReq(r)
if err != nil {
return fmt.Errorf("proto.ListCurrentValuesReq: %s", err)
}
if err = s.ListCurrentValues(conn, req); err != nil {
return fmt.Errorf("ListCurrentValues: %s", err)
}
case proto.TypeDeleteMeasures:
req, err := proto.ReadDeleteMeasuresReq(r)
if err != nil {
return fmt.Errorf("proto.ReadDeleteMeasuresReq: %s", err)
}
reply(conn, s.DeleteMeasures(req))
case proto.TypeListAllInstantMeasures:
req, err := proto.ReadListAllInstantMeasuresReq(r)
if err != nil {
return fmt.Errorf("proto.ReadListAllInstantMeasuresReq: %s", err)
}
if err = s.ListAllInstantMeasures(conn, req); err != nil {
return fmt.Errorf("ListAllInstantMeasures: %s", err)
}
case proto.TypeListAllCumulativeMeasures:
req, err := proto.ReadListAllCumulativeMeasuresReq(r)
if err != nil {
return fmt.Errorf("proto.ReadListAllCumulativeMeasuresReq: %s", err)
}
if err = s.ListAllCumulativeMeasures(conn, req); err != nil {
return fmt.Errorf("ListAllCumulativeMeasures: %s", err)
}
default:
2026-06-13 19:53:27 +03:00
fmt.Printf("unknown messageType: %d\n", messageType)
2026-02-10 14:02:11 +00:00
return fmt.Errorf("unknown messageType: %d", messageType)
}
return
}
// API
func (s *Database) AddMetric(req proto.AddMetricReq) uint16 {
if req.MetricID == 0 {
return proto.ErrEmptyMetricID
}
2026-05-21 23:38:52 +03:00
if byte(req.FracDigits) > qb.MaxFracDigits {
2026-02-10 14:02:11 +00:00
return proto.ErrWrongFracDigits
}
switch req.MetricType {
2026-05-21 23:38:52 +03:00
case qb.Cumulative, qb.Instant:
2026-02-10 14:02:11 +00:00
// ok
default:
return proto.ErrWrongMetricType
}
resultCh := make(chan byte, 1)
2026-06-13 22:43:17 +00:00
s.workerInbox.Push(worker.AddMetricReq{
2026-06-14 23:12:03 +03:00
MetricID: req.MetricID,
MetricType: req.MetricType,
FracDigits: req.FracDigits,
ResultCh: resultCh,
2026-02-10 14:02:11 +00:00
})
resultCode := <-resultCh
switch resultCode {
2026-06-13 22:43:17 +00:00
case worker.Succeed:
2026-06-15 10:47:24 +00:00
//
2026-06-13 22:43:17 +00:00
case worker.MetricDuplicate:
2026-02-10 14:02:11 +00:00
return proto.ErrDuplicate
default:
2026-05-21 23:38:52 +03:00
qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug)
2026-02-10 14:02:11 +00:00
}
return 0
}
func (s *Database) GetMetric(conn io.Writer, req proto.GetMetricReq) error {
2026-06-13 22:43:17 +00:00
resultCh := make(chan worker.GetMetricResult, 1)
2026-02-10 14:02:11 +00:00
2026-06-13 22:43:17 +00:00
s.workerInbox.Push(worker.GetMetricReq{
2026-02-10 14:02:11 +00:00
MetricID: req.MetricID,
ResultCh: resultCh,
})
result := <-resultCh
switch result.ResultCode {
2026-06-13 22:43:17 +00:00
case worker.Succeed:
2026-02-10 14:02:11 +00:00
answer := []byte{
proto.RespValue,
0, 0, 0, 0, // metricID
byte(result.MetricType),
result.FracDigits,
}
bin.PutUint32(answer[1:], req.MetricID)
_, err := conn.Write(answer)
if err != nil {
return err
}
2026-06-13 22:43:17 +00:00
case worker.NoMetric:
2026-02-10 14:02:11 +00:00
reply(conn, proto.ErrNoMetric)
default:
2026-05-21 23:38:52 +03:00
qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug)
2026-02-10 14:02:11 +00:00
}
return nil
}
func (s *Database) DeleteMetric(req proto.DeleteMetricReq) uint16 {
2026-06-15 05:32:15 +03:00
// resultCh := make(chan worker.DeleteMetricResult, 1)
2026-02-10 14:02:11 +00:00
2026-06-15 05:32:15 +03:00
// s.workerInbox.Push(worker.DeleteMetricReq{
// MetricID: req.MetricID,
// ResultCh: resultCh,
// })
2026-02-10 14:02:11 +00:00
2026-06-15 05:32:15 +03:00
// result := <-resultCh
// switch result.ResultCode {
// case worker.Succeed:
// // var (
// // //freePageNumbers []uint32
// // )
// // if result.RootPageNo > 0 {
// // var err error
// // freePageNumbers, err = s.atree.GetAllPages(result.RootPageNo)
// // if err != nil {
// // qb.Abort(qb.FailedAtreeRequest, err)
// // }
// // }
// // s.storage.Append(storage.DeletedMetric{
// // MetricID: req.MetricID,
// // //FreePageNumbers: freePageNumbers, FIX
// // })
// //<-waitCh
// case worker.NoMetric:
// return proto.ErrNoMetric
// default:
// qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug)
// }
2026-02-10 14:02:11 +00:00
return 0
}
type FilledPage struct {
Since uint32
RootPageNo uint32
PrevPageNo uint32
TimestampsChunks [][]byte
TimestampsSize uint16
ValuesChunks [][]byte
ValuesSize uint16
}
2026-06-14 23:12:03 +03:00
func (s *Database) AppendMeasures(conn io.Writer, req proto.AppendMeasuresReq) error {
resultCh := make(chan storage.MeasuresAppendResult, 1)
2026-02-10 14:02:11 +00:00
2026-06-13 22:43:17 +00:00
s.workerInbox.Push(worker.AppendMeasuresReq{
2026-02-10 14:02:11 +00:00
MetricID: req.MetricID,
2026-05-10 19:15:11 +00:00
Measures: req.Measures,
2026-02-10 14:02:11 +00:00
ResultCh: resultCh,
})
result := <-resultCh
switch result.ResultCode {
2026-06-14 23:12:03 +03:00
case worker.Succeed, worker.NonMonotonicValue, worker.ExpiredMeasure:
var errorCode byte
switch result.ResultCode {
case worker.NonMonotonicValue:
errorCode = proto.ErrNonMonotonicValue
case worker.ExpiredMeasure:
errorCode = proto.ErrExpiredMeasure
}
answer := []byte{
proto.RespValue,
0, 0, // written count
errorCode,
}
bin.PutIntAsUint16(answer[1:], result.WrittenCount)
_, err := conn.Write(answer)
if err != nil {
return err
}
2026-06-13 22:43:17 +00:00
case worker.NoMetric:
2026-06-14 23:12:03 +03:00
reply(conn, proto.ErrNoMetric)
2026-02-10 14:02:11 +00:00
default:
2026-05-21 23:38:52 +03:00
qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug)
2026-02-10 14:02:11 +00:00
}
2026-06-14 23:12:03 +03:00
return nil
2026-02-10 14:02:11 +00:00
}
func (s *Database) DeleteMeasures(req proto.DeleteMeasuresReq) uint16 {
2026-06-15 05:32:15 +03:00
resultCh := make(chan byte, 1)
2026-02-10 14:02:11 +00:00
2026-06-13 22:43:17 +00:00
s.workerInbox.Push(worker.DeleteMeasuresReq{
2026-02-10 14:02:11 +00:00
MetricID: req.MetricID,
Since: req.Since,
ResultCh: resultCh,
})
result := <-resultCh
2026-06-15 05:32:15 +03:00
_ = result
2026-02-10 14:02:11 +00:00
2026-06-15 05:32:15 +03:00
// switch result.ResultCode {
// case worker.NoMeasuresToDelete:
// // ok
2026-06-14 23:12:03 +03:00
2026-06-15 05:32:15 +03:00
// //case worker.DeleteFromAtreeNotNeeded:
2026-06-14 23:12:03 +03:00
// // регистрирую удаление в TransactionLog
2026-06-15 05:32:15 +03:00
// // s.storage.Append(storage.MeasuresDeleteRecord{
// // MetricID: req.MetricID,
// // })
// //<-waitCh
// //case worker.DeleteFromAtreeRequired:
// // собираю номера всех data и index страниц метрики (типа запись REDO лога).
// // pageNumbers, err := s.atree.GetAllPages(req.MetricID)
// // if err != nil {
// // qb.Abort(qb.FailedAtreeRequest, err)
// // }
// // // регистрирую удаление в TransactionLog
// // s.storage.Append(storage.DeletedMeasures{
// // MetricID: req.MetricID,
// // FreePageNumbers: pageNumbers,
// // })
// //<-waitCh
// case worker.NoMetric:
// return proto.ErrNoMetric
// default:
// qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug)
// }
2026-02-10 14:02:11 +00:00
return 0
}
// SELECT
func (s *Database) ListAllInstantMeasures(conn net.Conn, req proto.ListAllInstantMetricMeasuresReq) error {
responseWriter := transform.NewInstantMeasureWriter(conn, 0)
return s.fullScan(fullScanReq{
MetricID: req.MetricID,
2026-05-21 23:38:52 +03:00
MetricType: qb.Instant,
2026-02-10 14:02:11 +00:00
Conn: conn,
ResponseWriter: responseWriter,
})
}
func (s *Database) ListAllCumulativeMeasures(conn io.Writer, req proto.ListAllCumulativeMeasuresReq) error {
responseWriter := transform.NewCumulativeMeasureWriter(conn, 0)
return s.fullScan(fullScanReq{
MetricID: req.MetricID,
2026-05-21 23:38:52 +03:00
MetricType: qb.Cumulative,
2026-02-10 14:02:11 +00:00
Conn: conn,
ResponseWriter: responseWriter,
})
}
func (s *Database) ListInstantMeasures(conn net.Conn, req proto.ListInstantMeasuresReq) error {
if req.Since > req.Until {
reply(conn, proto.ErrInvalidRange)
return nil
}
responseWriter := transform.NewInstantMeasureWriter(conn, req.Since)
return s.rangeScan(rangeScanReq{
MetricID: req.MetricID,
2026-05-21 23:38:52 +03:00
MetricType: qb.Instant,
2026-02-10 14:02:11 +00:00
Since: req.Since,
Until: req.Until - 1,
Conn: conn,
ResponseWriter: responseWriter,
})
}
func (s *Database) ListCumulativeMeasures(conn net.Conn, req proto.ListCumulativeMeasuresReq) error {
if req.Since > req.Until {
reply(conn, proto.ErrInvalidRange)
return nil
}
responseWriter := transform.NewCumulativeMeasureWriter(conn, req.Since)
return s.rangeScan(rangeScanReq{
MetricID: req.MetricID,
2026-05-21 23:38:52 +03:00
MetricType: qb.Cumulative,
2026-02-10 14:02:11 +00:00
Since: req.Since,
Until: req.Until - 1,
Conn: conn,
ResponseWriter: responseWriter,
})
}
func (s *Database) ListInstantPeriods(conn net.Conn, req proto.ListInstantPeriodsReq) error {
2026-06-15 10:47:24 +00:00
since, until := timeutil.TimeBoundsOfAggregation(req.Since, req.Until, req.GroupBy, req.FirstHourOfDay)
2026-02-10 14:02:11 +00:00
if since.After(until) {
reply(conn, proto.ErrInvalidRange)
return nil
}
responseWriter, err := transform.NewInstantPeriodsWriter(transform.InstantPeriodsWriterOptions{
Dst: conn,
GroupBy: req.GroupBy,
AggregateFuncs: req.AggregateFuncs,
FirstHourOfDay: req.FirstHourOfDay,
})
if err != nil {
reply(conn, proto.ErrUnexpected)
return nil
}
return s.rangeScan(rangeScanReq{
MetricID: req.MetricID,
2026-05-21 23:38:52 +03:00
MetricType: qb.Instant,
2026-02-10 14:02:11 +00:00
Since: uint32(since.Unix()),
Until: uint32(until.Unix()),
Conn: conn,
ResponseWriter: responseWriter,
})
}
func (s *Database) ListCumulativePeriods(conn net.Conn, req proto.ListCumulativePeriodsReq) error {
2026-06-15 10:47:24 +00:00
since, until := timeutil.TimeBoundsOfAggregation(req.Since, req.Until, req.GroupBy, req.FirstHourOfDay)
2026-02-10 14:02:11 +00:00
if since.After(until) {
reply(conn, proto.ErrInvalidRange)
return nil
}
responseWriter, err := transform.NewCumulativePeriodsWriter(transform.CumulativePeriodsWriterOptions{
Dst: conn,
GroupBy: req.GroupBy,
FirstHourOfDay: req.FirstHourOfDay,
})
if err != nil {
reply(conn, proto.ErrUnexpected)
return nil
}
return s.rangeScan(rangeScanReq{
MetricID: req.MetricID,
2026-05-21 23:38:52 +03:00
MetricType: qb.Cumulative,
2026-02-10 14:02:11 +00:00
Since: uint32(since.Unix()),
Until: uint32(until.Unix()),
Conn: conn,
ResponseWriter: responseWriter,
})
}
type rangeScanReq struct {
MetricID uint32
2026-05-21 23:38:52 +03:00
MetricType qb.MetricType
2026-02-10 14:02:11 +00:00
Since uint32
Until uint32
Conn io.Writer
2026-06-15 10:47:24 +00:00
ResponseWriter qb.PeriodsWriter
2026-02-10 14:02:11 +00:00
}
func (s *Database) rangeScan(req rangeScanReq) error {
2026-06-13 22:43:17 +00:00
resultCh := make(chan worker.RangeScanResult, 1)
2026-02-10 14:02:11 +00:00
2026-06-13 22:43:17 +00:00
s.workerInbox.Push(worker.RangeScanReq{
2026-02-10 14:02:11 +00:00
MetricID: req.MetricID,
Since: req.Since,
Until: req.Until,
MetricType: req.MetricType,
ResponseWriter: req.ResponseWriter,
ResultCh: resultCh,
})
result := <-resultCh
switch result.ResultCode {
2026-06-13 22:43:17 +00:00
case worker.QueryDone:
2026-02-10 14:02:11 +00:00
req.ResponseWriter.Close()
2026-06-13 22:43:17 +00:00
case worker.UntilFound:
2026-06-15 10:47:24 +00:00
err := s.ContinueRangeScan(ContinueRangeScanReq{
MetricType: req.MetricType,
FracDigits: result.FracDigits,
ResponseWriter: req.ResponseWriter,
LastPageNo: result.LastPageNo,
Since: req.Since,
})
//s.metricRUnlock(req.MetricID) fix release unlock
if err != nil {
reply(req.Conn, proto.ErrUnexpected)
} else {
req.ResponseWriter.Close()
}
2026-06-13 22:43:17 +00:00
case worker.UntilNotFound:
2026-06-15 10:47:24 +00:00
err := s.RangeScan(RangeScanReq{
MetricType: req.MetricType,
FracDigits: result.FracDigits,
ResponseWriter: req.ResponseWriter,
Since: req.Since,
Until: req.Until,
LastPageNo: result.LastPageNo,
IsDataPage: result.IsDataPage,
})
//s.metricRUnlock(req.MetricID) // fix release
if err != nil {
reply(req.Conn, proto.ErrUnexpected)
} else {
req.ResponseWriter.Close()
}
2026-06-13 22:43:17 +00:00
case worker.NoMetric:
2026-02-10 14:02:11 +00:00
reply(req.Conn, proto.ErrNoMetric)
2026-06-13 22:43:17 +00:00
case worker.WrongMetricType:
2026-02-10 14:02:11 +00:00
reply(req.Conn, proto.ErrWrongMetricType)
default:
2026-05-21 23:38:52 +03:00
qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug)
2026-02-10 14:02:11 +00:00
}
return nil
}
type fullScanReq struct {
MetricID uint32
2026-05-21 23:38:52 +03:00
MetricType qb.MetricType
2026-02-10 14:02:11 +00:00
Conn io.Writer
2026-06-15 10:47:24 +00:00
ResponseWriter qb.PeriodsWriter
2026-02-10 14:02:11 +00:00
}
func (s *Database) fullScan(req fullScanReq) error {
2026-06-13 22:43:17 +00:00
resultCh := make(chan worker.FullScanResult, 1)
2026-02-10 14:02:11 +00:00
2026-06-13 22:43:17 +00:00
s.workerInbox.Push(worker.FullScanReq{
2026-02-10 14:02:11 +00:00
MetricID: req.MetricID,
MetricType: req.MetricType,
ResponseWriter: req.ResponseWriter,
ResultCh: resultCh,
})
result := <-resultCh
switch result.ResultCode {
2026-06-13 22:43:17 +00:00
case worker.QueryDone:
2026-02-10 14:02:11 +00:00
req.ResponseWriter.Close()
2026-06-13 22:43:17 +00:00
case worker.UntilFound:
2026-06-15 10:47:24 +00:00
err := s.ContinueFullScan(ContinueFullScanReq{
MetricType: req.MetricType,
2026-06-15 05:32:15 +03:00
FracDigits: result.FracDigits,
ResponseWriter: req.ResponseWriter,
LastPageNo: result.LastPageNo,
})
//s.worker.AddJobToQueue(req.MetricID) // FIX release rlock
if err != nil {
reply(req.Conn, proto.ErrUnexpected)
} else {
req.ResponseWriter.Close()
}
2026-06-13 22:43:17 +00:00
case worker.NoMetric:
2026-02-10 14:02:11 +00:00
reply(req.Conn, proto.ErrNoMetric)
2026-06-13 22:43:17 +00:00
case worker.WrongMetricType:
2026-02-10 14:02:11 +00:00
reply(req.Conn, proto.ErrWrongMetricType)
default:
2026-05-21 23:38:52 +03:00
qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug)
2026-02-10 14:02:11 +00:00
}
return nil
}
func (s *Database) ListCurrentValues(conn net.Conn, req proto.ListCurrentValuesReq) error {
responseWriter := transform.NewCurrentValueWriter(conn)
defer responseWriter.Close()
resultCh := make(chan struct{})
2026-06-13 22:43:17 +00:00
s.workerInbox.Push(worker.ListCurrentValuesReq{
2026-02-10 14:02:11 +00:00
MetricIDs: req.MetricIDs,
ResponseWriter: responseWriter,
ResultCh: resultCh,
})
<-resultCh
return nil
}