wp
This commit is contained in:
@@ -54,6 +54,7 @@ func (s *Connection) mustSuccess(reader *bufreader.BufferedReader) (err error) {
|
|||||||
}
|
}
|
||||||
switch code {
|
switch code {
|
||||||
case proto.RespSuccess:
|
case proto.RespSuccess:
|
||||||
|
fmt.Println("SUCCESS")
|
||||||
return nil // ok
|
return nil // ok
|
||||||
case proto.RespError:
|
case proto.RespError:
|
||||||
return s.onError()
|
return s.onError()
|
||||||
@@ -70,6 +71,7 @@ func (s *Connection) AddMetric(req proto.AddMetricReq) (err error) {
|
|||||||
req.FracDigits,
|
req.FracDigits,
|
||||||
}
|
}
|
||||||
bin.PutUint32(arr[1:], req.MetricID)
|
bin.PutUint32(arr[1:], req.MetricID)
|
||||||
|
fmt.Println("arr", arr)
|
||||||
// req
|
// req
|
||||||
if _, err = s.conn.Write(arr); err != nil {
|
if _, err = s.conn.Write(arr); err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ func reply(conn io.Writer, errcode uint16) {
|
|||||||
func (s *Database) handleTCPConn(conn net.Conn) {
|
func (s *Database) handleTCPConn(conn net.Conn) {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
fmt.Println("client connected")
|
||||||
|
|
||||||
r := bufreader.New(conn, 128)
|
r := bufreader.New(conn, 128)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -59,6 +61,7 @@ func (s *Database) handleTCPConn(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) processRequest(conn net.Conn, r *bufreader.BufferedReader) (err error) {
|
func (s *Database) processRequest(conn net.Conn, r *bufreader.BufferedReader) (err error) {
|
||||||
|
fmt.Println("process request")
|
||||||
messageType, err := r.ReadByte()
|
messageType, err := r.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
@@ -68,6 +71,8 @@ func (s *Database) processRequest(conn net.Conn, r *bufreader.BufferedReader) (e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("messageType:", messageType)
|
||||||
|
|
||||||
switch messageType {
|
switch messageType {
|
||||||
case proto.TypeGetMetric:
|
case proto.TypeGetMetric:
|
||||||
req, err := proto.ReadGetMetricReq(r)
|
req, err := proto.ReadGetMetricReq(r)
|
||||||
@@ -83,6 +88,7 @@ func (s *Database) processRequest(conn net.Conn, r *bufreader.BufferedReader) (e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("proto.ReadAddMetricReq: %s", err)
|
return fmt.Errorf("proto.ReadAddMetricReq: %s", err)
|
||||||
}
|
}
|
||||||
|
fmt.Println("ReadAddMetricReq:", req)
|
||||||
reply(conn, s.AddMetric(req))
|
reply(conn, s.AddMetric(req))
|
||||||
|
|
||||||
case proto.TypeDeleteMetric:
|
case proto.TypeDeleteMetric:
|
||||||
@@ -154,6 +160,7 @@ func (s *Database) processRequest(conn net.Conn, r *bufreader.BufferedReader) (e
|
|||||||
}
|
}
|
||||||
|
|
||||||
case proto.TypeDeleteMeasures:
|
case proto.TypeDeleteMeasures:
|
||||||
|
//fmt.Println("delete metric")
|
||||||
req, err := proto.ReadDeleteMeasuresReq(r)
|
req, err := proto.ReadDeleteMeasuresReq(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("proto.ReadDeleteMeasuresReq: %s", err)
|
return fmt.Errorf("proto.ReadDeleteMeasuresReq: %s", err)
|
||||||
@@ -180,6 +187,7 @@ func (s *Database) processRequest(conn net.Conn, r *bufreader.BufferedReader) (e
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
fmt.Printf("unknown messageType: %d\n", messageType)
|
||||||
return fmt.Errorf("unknown messageType: %d", messageType)
|
return fmt.Errorf("unknown messageType: %d", messageType)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -188,6 +196,7 @@ func (s *Database) processRequest(conn net.Conn, r *bufreader.BufferedReader) (e
|
|||||||
// API
|
// API
|
||||||
|
|
||||||
func (s *Database) AddMetric(req proto.AddMetricReq) uint16 {
|
func (s *Database) AddMetric(req proto.AddMetricReq) uint16 {
|
||||||
|
fmt.Println("database.AddMetric")
|
||||||
// Валидация
|
// Валидация
|
||||||
if req.MetricID == 0 {
|
if req.MetricID == 0 {
|
||||||
return proto.ErrEmptyMetricID
|
return proto.ErrEmptyMetricID
|
||||||
@@ -204,26 +213,33 @@ func (s *Database) AddMetric(req proto.AddMetricReq) uint16 {
|
|||||||
|
|
||||||
resultCh := make(chan byte, 1)
|
resultCh := make(chan byte, 1)
|
||||||
|
|
||||||
s.appendJobToWorkerQueue(tryAddMetricReq{
|
fmt.Println("add job")
|
||||||
|
|
||||||
|
s.worker.AddJobToQueue(tryAddMetricReq{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
ResultCh: resultCh,
|
ResultCh: resultCh,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fmt.Println("job added")
|
||||||
|
|
||||||
resultCode := <-resultCh
|
resultCode := <-resultCh
|
||||||
|
|
||||||
switch resultCode {
|
switch resultCode {
|
||||||
case Succeed:
|
case Succeed:
|
||||||
s.storage.Append(storage.MetricAddRecord{
|
fmt.Println("OK")
|
||||||
MetricID: req.MetricID,
|
// s.storage.Append(storage.MetricAddRecord{
|
||||||
MetricType: req.MetricType,
|
// MetricID: req.MetricID,
|
||||||
FracDigits: req.FracDigits,
|
// MetricType: req.MetricType,
|
||||||
})
|
// FracDigits: req.FracDigits,
|
||||||
|
// })
|
||||||
//<-waitCh
|
//<-waitCh
|
||||||
|
|
||||||
case MetricDuplicate:
|
case MetricDuplicate:
|
||||||
|
fmt.Println("ErrDuplicate")
|
||||||
return proto.ErrDuplicate
|
return proto.ErrDuplicate
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
fmt.Println("ErrWrongResultCodeBug")
|
||||||
qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug)
|
qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug)
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
@@ -238,7 +254,7 @@ type Metric struct {
|
|||||||
func (s *Database) GetMetric(conn io.Writer, req proto.GetMetricReq) error {
|
func (s *Database) GetMetric(conn io.Writer, req proto.GetMetricReq) error {
|
||||||
resultCh := make(chan Metric, 1)
|
resultCh := make(chan Metric, 1)
|
||||||
|
|
||||||
s.appendJobToWorkerQueue(tryGetMetricReq{
|
s.worker.AddJobToQueue(tryGetMetricReq{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
ResultCh: resultCh,
|
ResultCh: resultCh,
|
||||||
})
|
})
|
||||||
@@ -277,7 +293,7 @@ type tryDeleteMetricResult struct {
|
|||||||
func (s *Database) DeleteMetric(req proto.DeleteMetricReq) uint16 {
|
func (s *Database) DeleteMetric(req proto.DeleteMetricReq) uint16 {
|
||||||
resultCh := make(chan tryDeleteMetricResult, 1)
|
resultCh := make(chan tryDeleteMetricResult, 1)
|
||||||
|
|
||||||
s.appendJobToWorkerQueue(tryDeleteMetricReq{
|
s.worker.AddJobToQueue(tryDeleteMetricReq{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
ResultCh: resultCh,
|
ResultCh: resultCh,
|
||||||
})
|
})
|
||||||
@@ -441,7 +457,7 @@ type tryAppendMeasuresResult struct {
|
|||||||
func (s *Database) AppendMeasures(req proto.AppendMeasuresReq) uint16 {
|
func (s *Database) AppendMeasures(req proto.AppendMeasuresReq) uint16 {
|
||||||
resultCh := make(chan tryAppendMeasuresResult, 1)
|
resultCh := make(chan tryAppendMeasuresResult, 1)
|
||||||
|
|
||||||
s.appendJobToWorkerQueue(tryAppendMeasuresReq{
|
s.worker.AddJobToQueue(tryAppendMeasuresReq{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
Measures: req.Measures,
|
Measures: req.Measures,
|
||||||
ResultCh: resultCh,
|
ResultCh: resultCh,
|
||||||
@@ -486,7 +502,7 @@ type tryDeleteMeasuresResult struct {
|
|||||||
func (s *Database) DeleteMeasures(req proto.DeleteMeasuresReq) uint16 {
|
func (s *Database) DeleteMeasures(req proto.DeleteMeasuresReq) uint16 {
|
||||||
resultCh := make(chan tryDeleteMeasuresResult, 1)
|
resultCh := make(chan tryDeleteMeasuresResult, 1)
|
||||||
|
|
||||||
s.appendJobToWorkerQueue(tryDeleteMeasuresReq{
|
s.worker.AddJobToQueue(tryDeleteMeasuresReq{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
Since: req.Since,
|
Since: req.Since,
|
||||||
ResultCh: resultCh,
|
ResultCh: resultCh,
|
||||||
@@ -667,7 +683,7 @@ type rangeScanReq struct {
|
|||||||
func (s *Database) rangeScan(req rangeScanReq) error {
|
func (s *Database) rangeScan(req rangeScanReq) error {
|
||||||
resultCh := make(chan rangeScanResult, 1)
|
resultCh := make(chan rangeScanResult, 1)
|
||||||
|
|
||||||
s.appendJobToWorkerQueue(tryRangeScanReq{
|
s.worker.AddJobToQueue(tryRangeScanReq{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
Since: req.Since,
|
Since: req.Since,
|
||||||
Until: req.Until,
|
Until: req.Until,
|
||||||
@@ -689,7 +705,7 @@ func (s *Database) rangeScan(req rangeScanReq) error {
|
|||||||
LastPageNo: result.LastPageNo,
|
LastPageNo: result.LastPageNo,
|
||||||
Since: req.Since,
|
Since: req.Since,
|
||||||
})
|
})
|
||||||
s.metricRUnlock(req.MetricID)
|
//s.metricRUnlock(req.MetricID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reply(req.Conn, proto.ErrUnexpected)
|
reply(req.Conn, proto.ErrUnexpected)
|
||||||
@@ -705,7 +721,7 @@ func (s *Database) rangeScan(req rangeScanReq) error {
|
|||||||
Since: req.Since,
|
Since: req.Since,
|
||||||
Until: req.Until,
|
Until: req.Until,
|
||||||
})
|
})
|
||||||
s.metricRUnlock(req.MetricID)
|
//s.metricRUnlock(req.MetricID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reply(req.Conn, proto.ErrUnexpected)
|
reply(req.Conn, proto.ErrUnexpected)
|
||||||
@@ -735,7 +751,7 @@ type fullScanReq struct {
|
|||||||
func (s *Database) fullScan(req fullScanReq) error {
|
func (s *Database) fullScan(req fullScanReq) error {
|
||||||
resultCh := make(chan fullScanResult, 1)
|
resultCh := make(chan fullScanResult, 1)
|
||||||
|
|
||||||
s.appendJobToWorkerQueue(tryFullScanReq{
|
s.worker.AddJobToQueue(tryFullScanReq{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
MetricType: req.MetricType,
|
MetricType: req.MetricType,
|
||||||
ResponseWriter: req.ResponseWriter,
|
ResponseWriter: req.ResponseWriter,
|
||||||
@@ -754,7 +770,7 @@ func (s *Database) fullScan(req fullScanReq) error {
|
|||||||
ResponseWriter: req.ResponseWriter,
|
ResponseWriter: req.ResponseWriter,
|
||||||
LastPageNo: result.LastPageNo,
|
LastPageNo: result.LastPageNo,
|
||||||
})
|
})
|
||||||
s.metricRUnlock(req.MetricID)
|
s.worker.AddJobToQueue(req.MetricID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reply(req.Conn, proto.ErrUnexpected)
|
reply(req.Conn, proto.ErrUnexpected)
|
||||||
} else {
|
} else {
|
||||||
@@ -779,7 +795,7 @@ func (s *Database) ListCurrentValues(conn net.Conn, req proto.ListCurrentValuesR
|
|||||||
|
|
||||||
resultCh := make(chan struct{})
|
resultCh := make(chan struct{})
|
||||||
|
|
||||||
s.appendJobToWorkerQueue(tryListCurrentValuesReq{
|
s.worker.AddJobToQueue(tryListCurrentValuesReq{
|
||||||
MetricIDs: req.MetricIDs,
|
MetricIDs: req.MetricIDs,
|
||||||
ResponseWriter: responseWriter,
|
ResponseWriter: responseWriter,
|
||||||
ResultCh: resultCh,
|
ResultCh: resultCh,
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/qb"
|
||||||
"gordenko.dev/dima/qb/atree"
|
"gordenko.dev/dima/qb/atree"
|
||||||
"gordenko.dev/dima/qb/freelist"
|
|
||||||
"gordenko.dev/dima/qb/storage"
|
"gordenko.dev/dima/qb/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,24 +30,20 @@ func JoinWALFileName(dir string, snapshotNumber int) string {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
type Database struct {
|
type Database struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
workerSignalCh chan struct{}
|
worker *Worker
|
||||||
workerQueue []any
|
|
||||||
rLocksToRelease []uint32
|
|
||||||
metrics map[uint32]*_metric
|
|
||||||
//metricLockEntries map[uint32]*metricLockEntry
|
//metricLockEntries map[uint32]*metricLockEntry
|
||||||
dataFreeList *freelist.FreeList
|
//dataFreeList *freelist.FreeList
|
||||||
indexFreeList *freelist.FreeList
|
//indexFreeList *freelist.FreeList
|
||||||
dir string
|
dir string
|
||||||
databaseName string
|
databaseName string
|
||||||
snapshotNumber int
|
storage *storage.Writer
|
||||||
storage *storage.Writer
|
atree *atree.Atree
|
||||||
atree *atree.Atree
|
tcpPort int
|
||||||
tcpPort int
|
logfile *os.File
|
||||||
logfile *os.File
|
logger *log.Logger
|
||||||
logger *log.Logger
|
exitCh chan struct{}
|
||||||
exitCh chan struct{}
|
waitGroup *sync.WaitGroup
|
||||||
waitGroup *sync.WaitGroup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
@@ -92,31 +88,41 @@ func New(opt Options) (_ *Database, err error) {
|
|||||||
// return nil, err
|
// return nil, err
|
||||||
// }
|
// }
|
||||||
|
|
||||||
dataFreeList, err := freelist.New(freelist.Options{
|
// dataFreeList, err := freelist.New(freelist.Options{
|
||||||
PageSize: 2048,
|
// PageSize: 2048,
|
||||||
BaseFilePath: filepath.Join(opt.Dir, opt.DatabaseName+".free_data_base"),
|
// BaseFilePath: filepath.Join(opt.Dir, opt.DatabaseName+".free_data"),
|
||||||
DeltaFilePath: filepath.Join(opt.Dir, opt.DatabaseName+".free_data_delta"),
|
// DeltaFilePath: filepath.Join(opt.Dir, opt.DatabaseName+".free_data_delta"),
|
||||||
})
|
// })
|
||||||
|
|
||||||
indexFreeList, err := freelist.New(freelist.Options{
|
// indexFreeList, err := freelist.New(freelist.Options{
|
||||||
PageSize: 2048,
|
// PageSize: 2048,
|
||||||
BaseFilePath: filepath.Join(opt.Dir, opt.DatabaseName+".free_index_base"),
|
// BaseFilePath: filepath.Join(opt.Dir, opt.DatabaseName+".free_index"),
|
||||||
DeltaFilePath: filepath.Join(opt.Dir, opt.DatabaseName+".free_index_delta"),
|
// DeltaFilePath: filepath.Join(opt.Dir, opt.DatabaseName+".free_index_delta"),
|
||||||
|
// })
|
||||||
|
|
||||||
|
worker := NewWorker(WorkerOptions{
|
||||||
|
//
|
||||||
|
Metrics: make(map[uint32]*_metric),
|
||||||
|
Dir: opt.Dir,
|
||||||
|
SaveToStorage: func(in any) {
|
||||||
|
fmt.Println("save to storage")
|
||||||
|
},
|
||||||
|
ExitCh: opt.ExitCh,
|
||||||
|
WaitGroup: opt.WaitGroup,
|
||||||
})
|
})
|
||||||
|
|
||||||
s := &Database{
|
s := &Database{
|
||||||
workerSignalCh: make(chan struct{}, 1),
|
worker: worker,
|
||||||
dir: opt.Dir,
|
dir: opt.Dir,
|
||||||
databaseName: opt.DatabaseName,
|
databaseName: opt.DatabaseName,
|
||||||
metrics: make(map[uint32]*_metric),
|
|
||||||
//metricLockEntries: make(map[uint32]*metricLockEntry),
|
//metricLockEntries: make(map[uint32]*metricLockEntry),
|
||||||
dataFreeList: dataFreeList,
|
//dataFreeList: dataFreeList,
|
||||||
indexFreeList: indexFreeList,
|
//indexFreeList: indexFreeList,
|
||||||
tcpPort: opt.TCPPort,
|
tcpPort: opt.TCPPort,
|
||||||
logfile: opt.Logfile,
|
logfile: opt.Logfile,
|
||||||
logger: log.New(opt.Logfile, "", log.LstdFlags),
|
logger: log.New(opt.Logfile, "", log.LstdFlags),
|
||||||
exitCh: opt.ExitCh,
|
exitCh: opt.ExitCh,
|
||||||
waitGroup: opt.WaitGroup,
|
waitGroup: opt.WaitGroup,
|
||||||
}
|
}
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
@@ -127,18 +133,32 @@ func (s *Database) ListenAndServe() (err error) {
|
|||||||
return fmt.Errorf("net.Listen: %s; port=%d", err, s.tcpPort)
|
return fmt.Errorf("net.Listen: %s; port=%d", err, s.tcpPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.atree, err = atree.New(atree.Options{
|
s.storage, err = storage.NewWriter(storage.WriterOptions{
|
||||||
Dir: s.dir,
|
Dir: s.dir,
|
||||||
DatabaseName: s.databaseName,
|
//LogNumber: logNumber,
|
||||||
|
AppendToWorkerQueue: s.worker.AddJobToQueue,
|
||||||
|
//FreeList: s.freeList,
|
||||||
|
//Atree: s.atree,
|
||||||
|
ExitCh: s.exitCh,
|
||||||
|
WaitGroup: s.waitGroup,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("atree.New: %s", err)
|
qb.Abort(qb.CreateChangesWriterFailed, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
s.atree.Run()
|
go s.storage.Run()
|
||||||
|
|
||||||
go s.worker()
|
// s.atree, err = atree.New(atree.Options{
|
||||||
|
// Dir: s.dir,
|
||||||
|
// DatabaseName: s.databaseName,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("atree.New: %s", err)
|
||||||
|
// }
|
||||||
|
// s.atree.Run()
|
||||||
|
|
||||||
s.recovery()
|
s.waitGroup.Add(1)
|
||||||
|
go s.worker.Run()
|
||||||
|
|
||||||
s.logger.Println("database started")
|
s.logger.Println("database started")
|
||||||
for {
|
for {
|
||||||
@@ -153,92 +173,6 @@ func (s *Database) ListenAndServe() (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) recovery() {
|
|
||||||
// FIX
|
|
||||||
// advisor, err := recovery.NewRecoveryAdvisor(recovery.RecoveryAdvisorOptions{
|
|
||||||
// Dir: s.dir,
|
|
||||||
// VerifySnapshot: s.verifySnapshot,
|
|
||||||
// })
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// recipe, err := advisor.GetRecipe()
|
|
||||||
// if err != nil {
|
|
||||||
// qb.Abort(qb.GetRecoveryRecipeFailed, err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var logNumber int
|
|
||||||
|
|
||||||
// if recipe != nil {
|
|
||||||
// if recipe.Snapshot != "" {
|
|
||||||
// err = s.loadSnapshot(recipe.Snapshot)
|
|
||||||
// if err != nil {
|
|
||||||
// qb.Abort(qb.LoadSnapshotFailed, err)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// for _, changesFileName := range recipe.Changes {
|
|
||||||
// err = s.replayChanges(changesFileName)
|
|
||||||
// if err != nil {
|
|
||||||
// qb.Abort(qb.ReplayChangesFailed, err)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// logNumber = recipe.LogNumber
|
|
||||||
// }
|
|
||||||
|
|
||||||
// s.storage, err = storage.NewWriter(storage.WriterOptions{
|
|
||||||
// Dir: s.dir,
|
|
||||||
// LogNumber: logNumber,
|
|
||||||
// AppendToWorkerQueue: s.appendJobToWorkerQueue,
|
|
||||||
// FreeList: s.freeList,
|
|
||||||
// Atree: s.atree,
|
|
||||||
// ExitCh: s.exitCh,
|
|
||||||
// WaitGroup: s.waitGroup,
|
|
||||||
// })
|
|
||||||
// if err != nil {
|
|
||||||
// qb.Abort(qb.CreateChangesWriterFailed, err)
|
|
||||||
|
|
||||||
// }
|
|
||||||
// go s.storage.Run()
|
|
||||||
|
|
||||||
// // fileNames, err := s.searchREDOFiles()
|
|
||||||
// // if err != nil {
|
|
||||||
// // qb.Abort(qb.SearchREDOFilesFailed, err)
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// // if len(fileNames) > 0 {
|
|
||||||
// // for _, fileName := range fileNames {
|
|
||||||
// // err = s.replayREDOFile(fileName)
|
|
||||||
// // if err != nil {
|
|
||||||
// // qb.Abort(qb.ReplayREDOFileFailed, err)
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// // for _, fileName := range fileNames {
|
|
||||||
// // err = os.Remove(fileName)
|
|
||||||
// // if err != nil {
|
|
||||||
// // qb.Abort(qb.RemoveREDOFileFailed, err)
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// if recipe != nil {
|
|
||||||
// if recipe.CompleteSnapshot {
|
|
||||||
// err = s.dumpSnapshot(logNumber)
|
|
||||||
// if err != nil {
|
|
||||||
// qb.Abort(qb.DumpSnapshotFailed, err)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for _, fileName := range recipe.ToDelete {
|
|
||||||
// err = os.Remove(fileName)
|
|
||||||
// if err != nil {
|
|
||||||
// qb.Abort(qb.RemoveRecipeFileFailed, err)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
// зробити object?
|
// зробити object?
|
||||||
@@ -283,9 +217,9 @@ func (s *Database) replayChanges(snapshotNumber int) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) relayMetricsToMetrics(replayMetrics map[uint32]*ReplayMetric) {
|
func (s *Database) relayMetricsToMetrics(replayMetrics map[uint32]*ReplayMetric) {
|
||||||
for metricID, x := range replayMetrics {
|
//for metricID, x := range replayMetrics {
|
||||||
s.metrics[metricID] = x.ToMetric()
|
//s.metrics[metricID] = x.ToMetric() FIX
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
//func (s *Database) verifySnapshot(fileName string) (_ bool, err error) {
|
//func (s *Database) verifySnapshot(fileName string) (_ bool, err error) {
|
||||||
|
|||||||
@@ -44,28 +44,6 @@ func isFileExist(fileName string) (bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) appendJobToWorkerQueue(job any) {
|
|
||||||
s.mutex.Lock()
|
|
||||||
s.workerQueue = append(s.workerQueue, job)
|
|
||||||
s.mutex.Unlock()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case s.workerSignalCh <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Database) metricRUnlock(metricID uint32) {
|
|
||||||
s.mutex.Lock()
|
|
||||||
s.rLocksToRelease = append(s.rLocksToRelease, metricID)
|
|
||||||
s.mutex.Unlock()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case s.workerSignalCh <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func correctToFHD(since, until uint32, firstHourOfDay int) (uint32, uint32) {
|
func correctToFHD(since, until uint32, firstHourOfDay int) (uint32, uint32) {
|
||||||
duration := time.Duration(firstHourOfDay) * time.Hour
|
duration := time.Duration(firstHourOfDay) * time.Hour
|
||||||
since = uint32(time.Unix(int64(since), 0).Add(duration).Unix())
|
since = uint32(time.Unix(int64(since), 0).Add(duration).Unix())
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func (s *_metric) OnMeasuresDeleteCommited(rec storage.MeasuresDeleteCommited) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// func (s *_metric) startAppendMeasures(req tryAppendMeasuresReq, sendToStorage func(storage.AppendedMeasures)) {
|
// func (s *_metric) startAppendMeasures(req tryAppendMeasuresReq, sendToStorage func(storage.AppendedMeasures)) {
|
||||||
func (s *_metric) StartAppendMeasures(req tryAppendMeasuresReq, tmp []byte, sendToStorage func(any)) {
|
func (s *_metric) StartAppendMeasures(req tryAppendMeasuresReq, tmp []byte, saveToStorage func(any)) {
|
||||||
if s.capturedState != nil {
|
if s.capturedState != nil {
|
||||||
s.WaitQueue = append(s.WaitQueue, req)
|
s.WaitQueue = append(s.WaitQueue, req)
|
||||||
}
|
}
|
||||||
@@ -174,7 +174,7 @@ func (s *_metric) StartAppendMeasures(req tryAppendMeasuresReq, tmp []byte, send
|
|||||||
|
|
||||||
if len(pages) > 0 {
|
if len(pages) > 0 {
|
||||||
// пишу в storage довгим шляхом через redo файл і запис в data файл
|
// пишу в storage довгим шляхом через redo файл і запис в data файл
|
||||||
sendToStorage(storage.MeasuresAppendWithGrow{
|
saveToStorage(storage.MeasuresAppendWithGrow{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
LastPageNo: s.lastPageNo,
|
LastPageNo: s.lastPageNo,
|
||||||
TimestampsRewindOffset: timestampsRewindOffset,
|
TimestampsRewindOffset: timestampsRewindOffset,
|
||||||
@@ -191,7 +191,7 @@ func (s *_metric) StartAppendMeasures(req tryAppendMeasuresReq, tmp []byte, send
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// короткий шлях - запис лише в storage
|
// короткий шлях - запис лише в storage
|
||||||
sendToStorage(storage.MeasuresAppend{
|
saveToStorage(storage.MeasuresAppend{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
TimestampsRewindOffset: timestampsRewindOffset,
|
TimestampsRewindOffset: timestampsRewindOffset,
|
||||||
ValuesRewindOffset: valuesRewindOffset,
|
ValuesRewindOffset: valuesRewindOffset,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package database
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
qb "gordenko.dev/dima/qb"
|
qb "gordenko.dev/dima/qb"
|
||||||
"gordenko.dev/dima/qb/atree"
|
"gordenko.dev/dima/qb/atree"
|
||||||
@@ -35,50 +36,109 @@ const (
|
|||||||
DeleteFromAtreeRequired = 14
|
DeleteFromAtreeRequired = 14
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Database) worker() {
|
type Worker struct {
|
||||||
|
mutex sync.Mutex
|
||||||
|
queue []any
|
||||||
|
signalCh chan struct{}
|
||||||
|
dir string
|
||||||
|
snapshotNumber int
|
||||||
|
tmp []byte
|
||||||
|
metrics map[uint32]*_metric
|
||||||
|
saveToStorage func(any)
|
||||||
|
exitCh chan struct{}
|
||||||
|
waitGroup *sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkerOptions struct {
|
||||||
|
SaveToStorage func(any)
|
||||||
|
Metrics map[uint32]*_metric
|
||||||
|
Dir string
|
||||||
|
ExitCh chan struct{}
|
||||||
|
WaitGroup *sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWorker(opt WorkerOptions) *Worker {
|
||||||
|
return &Worker{
|
||||||
|
signalCh: make(chan struct{}, 1),
|
||||||
|
dir: opt.Dir,
|
||||||
|
tmp: make([]byte, 26),
|
||||||
|
metrics: opt.Metrics,
|
||||||
|
saveToStorage: opt.SaveToStorage,
|
||||||
|
exitCh: opt.ExitCh,
|
||||||
|
waitGroup: opt.WaitGroup,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Worker) AddJobToQueue(job any) {
|
||||||
|
s.queue = append(s.queue, job)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case s.signalCh <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Worker) ReleaseRLock(metricID uint32) {
|
||||||
|
s.mutex.Lock()
|
||||||
|
//s.rLocksToRelease = append(s.rLocksToRelease, metricID)
|
||||||
|
s.mutex.Unlock()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case s.signalCh <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Worker) Run() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-s.workerSignalCh:
|
case <-s.signalCh:
|
||||||
s.DoWork()
|
s.doWork()
|
||||||
|
|
||||||
|
case <-s.exitCh:
|
||||||
|
fmt.Println("worker done")
|
||||||
|
s.waitGroup.Done()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) DoWork() {
|
func (s *Worker) doWork() {
|
||||||
|
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
rLocksToRelease := s.rLocksToRelease
|
//rLocksToRelease := s.rLocksToRelease
|
||||||
workerQueue := s.workerQueue
|
queue := s.queue
|
||||||
s.rLocksToRelease = nil
|
//s.rLocksToRelease = nil
|
||||||
s.workerQueue = nil
|
s.queue = nil
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
|
|
||||||
for _, metricID := range rLocksToRelease {
|
// for _, metricID := range rLocksToRelease {
|
||||||
metric, ok := s.metrics[metricID]
|
// metric, ok := s.metrics[metricID]
|
||||||
if !ok {
|
// if !ok {
|
||||||
qb.Abort(qb.NoMetricBug,
|
// qb.Abort(qb.NoMetricBug,
|
||||||
fmt.Errorf("drainQueues: metric %d not found", metricID))
|
// fmt.Errorf("drainQueues: metric %d not found", metricID))
|
||||||
}
|
// }
|
||||||
|
|
||||||
if metric.XLock {
|
// if metric.XLock {
|
||||||
qb.Abort(qb.XLockBug,
|
// qb.Abort(qb.XLockBug,
|
||||||
fmt.Errorf("drainQueues: xlock is set for the metric %d",
|
// fmt.Errorf("drainQueues: xlock is set for the metric %d",
|
||||||
metricID))
|
// metricID))
|
||||||
}
|
// }
|
||||||
|
|
||||||
if metric.RLocks <= 0 {
|
// if metric.RLocks <= 0 {
|
||||||
qb.Abort(qb.NoRLockBug,
|
// qb.Abort(qb.NoRLockBug,
|
||||||
fmt.Errorf("drainQueues: rlock not set for the metric %d",
|
// fmt.Errorf("drainQueues: rlock not set for the metric %d",
|
||||||
metricID))
|
// metricID))
|
||||||
}
|
// }
|
||||||
|
|
||||||
metric.RLocks--
|
// metric.RLocks--
|
||||||
|
|
||||||
if len(metric.WaitQueue) > 0 {
|
// if len(metric.WaitQueue) > 0 {
|
||||||
s.processMetricQueue(metricID, metric)
|
// s.processMetricQueue(metricID, metric)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
for _, untyped := range workerQueue {
|
for _, untyped := range queue {
|
||||||
switch req := untyped.(type) {
|
switch req := untyped.(type) {
|
||||||
case tryAppendMeasuresReq:
|
case tryAppendMeasuresReq:
|
||||||
s.tryAppendMeasures(req)
|
s.tryAppendMeasures(req)
|
||||||
@@ -115,7 +175,7 @@ func (s *Database) DoWork() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// суть у тому що треба запускати запити, пока не зустріну XLock
|
// суть у тому що треба запускати запити, пока не зустріну XLock
|
||||||
func (s *Database) processMetricQueue(metricID uint32, metric *_metric) {
|
func (s *Worker) processMetricQueue(metricID uint32, metric *_metric, tmp []byte) {
|
||||||
if len(metric.WaitQueue) == 0 {
|
if len(metric.WaitQueue) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -132,7 +192,7 @@ func (s *Database) processMetricQueue(metricID uint32, metric *_metric) {
|
|||||||
s.tryGetMetric(req)
|
s.tryGetMetric(req)
|
||||||
|
|
||||||
case tryAppendMeasuresReq:
|
case tryAppendMeasuresReq:
|
||||||
//metric.StartAppendMeasures(req, s.storage.Append) FIX
|
metric.StartAppendMeasures(req, tmp, s.saveToStorage)
|
||||||
|
|
||||||
case tryDeleteMetricReq:
|
case tryDeleteMetricReq:
|
||||||
s.startDeleteMetric(metric, req)
|
s.startDeleteMetric(metric, req)
|
||||||
@@ -152,12 +212,13 @@ type tryAddMetricReq struct {
|
|||||||
ResultCh chan byte
|
ResultCh chan byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) tryAddMetric(req tryAddMetricReq) {
|
func (s *Worker) tryAddMetric(req tryAddMetricReq) {
|
||||||
_, ok := s.metrics[req.MetricID]
|
_, ok := s.metrics[req.MetricID]
|
||||||
if ok {
|
if ok {
|
||||||
req.ResultCh <- MetricDuplicate
|
req.ResultCh <- MetricDuplicate
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
fmt.Println("metric not found. Success")
|
||||||
req.ResultCh <- Succeed // new
|
req.ResultCh <- Succeed // new
|
||||||
|
|
||||||
// lockEntry, ok := s.metricLockEntries[req.MetricID]
|
// lockEntry, ok := s.metricLockEntries[req.MetricID]
|
||||||
@@ -171,7 +232,7 @@ func (s *Database) tryAddMetric(req tryAddMetricReq) {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) processTryAddMetricReqsImmediatelyAfterDelete(reqs []tryAddMetricReq) {
|
func (s *Worker) processTryAddMetricReqsImmediatelyAfterDelete(reqs []tryAddMetricReq) {
|
||||||
if len(reqs) == 0 {
|
if len(reqs) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -197,7 +258,7 @@ type tryGetMetricReq struct {
|
|||||||
ResultCh chan Metric
|
ResultCh chan Metric
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) tryGetMetric(req tryGetMetricReq) {
|
func (s *Worker) tryGetMetric(req tryGetMetricReq) {
|
||||||
metric, ok := s.metrics[req.MetricID]
|
metric, ok := s.metrics[req.MetricID]
|
||||||
if ok {
|
if ok {
|
||||||
req.ResultCh <- Metric{
|
req.ResultCh <- Metric{
|
||||||
@@ -217,7 +278,7 @@ type tryDeleteMetricReq struct {
|
|||||||
ResultCh chan tryDeleteMetricResult
|
ResultCh chan tryDeleteMetricResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) tryDeleteMetric(req tryDeleteMetricReq) {
|
func (s *Worker) tryDeleteMetric(req tryDeleteMetricReq) {
|
||||||
// FIX
|
// FIX
|
||||||
// metric, ok := s.metrics[req.MetricID]
|
// metric, ok := s.metrics[req.MetricID]
|
||||||
// if !ok {
|
// if !ok {
|
||||||
@@ -235,7 +296,7 @@ func (s *Database) tryDeleteMetric(req tryDeleteMetricReq) {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) startDeleteMetric(metric *_metric, req tryDeleteMetricReq) {
|
func (s *Worker) startDeleteMetric(metric *_metric, req tryDeleteMetricReq) {
|
||||||
// FIX
|
// FIX
|
||||||
// s.metricLockEntries[req.MetricID] = &metricLockEntry{
|
// s.metricLockEntries[req.MetricID] = &metricLockEntry{
|
||||||
// XLock: true,
|
// XLock: true,
|
||||||
@@ -252,7 +313,7 @@ type tryDeleteMeasuresReq struct {
|
|||||||
ResultCh chan tryDeleteMeasuresResult
|
ResultCh chan tryDeleteMeasuresResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) tryDeleteMeasures(req tryDeleteMeasuresReq) {
|
func (s *Worker) tryDeleteMeasures(req tryDeleteMeasuresReq) {
|
||||||
// FIX
|
// FIX
|
||||||
// metric, ok := s.metrics[req.MetricID]
|
// metric, ok := s.metrics[req.MetricID]
|
||||||
// if !ok {
|
// if !ok {
|
||||||
@@ -276,7 +337,7 @@ func (s *Database) tryDeleteMeasures(req tryDeleteMeasuresReq) {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) startDeleteMeasures(metric *_metric, req tryDeleteMeasuresReq) {
|
func (s *Worker) startDeleteMeasures(metric *_metric, req tryDeleteMeasuresReq) {
|
||||||
// FIX
|
// FIX
|
||||||
// s.metricLockEntries[req.MetricID] = &metricLockEntry{
|
// s.metricLockEntries[req.MetricID] = &metricLockEntry{
|
||||||
// XLock: true,
|
// XLock: true,
|
||||||
@@ -294,7 +355,7 @@ func (s *Database) startDeleteMeasures(metric *_metric, req tryDeleteMeasuresReq
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) onMeasuresAppendCommited(rec storage.MeasuresAppendCommited) {
|
func (s *Worker) onMeasuresAppendCommited(rec storage.MeasuresAppendCommited) {
|
||||||
metric, ok := s.metrics[rec.MetricID]
|
metric, ok := s.metrics[rec.MetricID]
|
||||||
if !ok {
|
if !ok {
|
||||||
qb.Abort(qb.NoMetricBug,
|
qb.Abort(qb.NoMetricBug,
|
||||||
@@ -304,7 +365,7 @@ func (s *Database) onMeasuresAppendCommited(rec storage.MeasuresAppendCommited)
|
|||||||
metric.OnMeasuresAppendCommited(rec)
|
metric.OnMeasuresAppendCommited(rec)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) onMeasuresAppendWithGrowCommited(rec storage.MeasuresAppendWithGrowCommited) {
|
func (s *Worker) onMeasuresAppendWithGrowCommited(rec storage.MeasuresAppendWithGrowCommited) {
|
||||||
metric, ok := s.metrics[rec.MetricID]
|
metric, ok := s.metrics[rec.MetricID]
|
||||||
if !ok {
|
if !ok {
|
||||||
qb.Abort(qb.NoMetricBug,
|
qb.Abort(qb.NoMetricBug,
|
||||||
@@ -320,7 +381,7 @@ type tryAppendMeasuresReq struct {
|
|||||||
ResultCh chan tryAppendMeasuresResult
|
ResultCh chan tryAppendMeasuresResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) tryAppendMeasures(req tryAppendMeasuresReq) {
|
func (s *Worker) tryAppendMeasures(req tryAppendMeasuresReq) {
|
||||||
metric, ok := s.metrics[req.MetricID]
|
metric, ok := s.metrics[req.MetricID]
|
||||||
if !ok {
|
if !ok {
|
||||||
req.ResultCh <- tryAppendMeasuresResult{
|
req.ResultCh <- tryAppendMeasuresResult{
|
||||||
@@ -332,7 +393,7 @@ func (s *Database) tryAppendMeasures(req tryAppendMeasuresReq) {
|
|||||||
metric.WaitQueue = append(metric.WaitQueue, req)
|
metric.WaitQueue = append(metric.WaitQueue, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//metric.StartAppendMeasures(req, s.storage.Append) FIX
|
metric.StartAppendMeasures(req, s.tmp, s.saveToStorage)
|
||||||
}
|
}
|
||||||
|
|
||||||
type tryRangeScanReq struct {
|
type tryRangeScanReq struct {
|
||||||
@@ -344,7 +405,7 @@ type tryRangeScanReq struct {
|
|||||||
ResultCh chan rangeScanResult
|
ResultCh chan rangeScanResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) tryRangeScan(req tryRangeScanReq) {
|
func (s *Worker) tryRangeScan(req tryRangeScanReq) {
|
||||||
metric, ok := s.metrics[req.MetricID]
|
metric, ok := s.metrics[req.MetricID]
|
||||||
if !ok {
|
if !ok {
|
||||||
req.ResultCh <- rangeScanResult{
|
req.ResultCh <- rangeScanResult{
|
||||||
@@ -374,7 +435,7 @@ type tryFullScanReq struct {
|
|||||||
ResultCh chan fullScanResult
|
ResultCh chan fullScanResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) tryFullScan(req tryFullScanReq) {
|
func (s *Worker) tryFullScan(req tryFullScanReq) {
|
||||||
metric, ok := s.metrics[req.MetricID]
|
metric, ok := s.metrics[req.MetricID]
|
||||||
if !ok {
|
if !ok {
|
||||||
req.ResultCh <- fullScanResult{
|
req.ResultCh <- fullScanResult{
|
||||||
@@ -402,7 +463,7 @@ type tryListCurrentValuesReq struct {
|
|||||||
ResultCh chan struct{}
|
ResultCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) tryListCurrentValues(req tryListCurrentValuesReq) {
|
func (s *Worker) tryListCurrentValues(req tryListCurrentValuesReq) {
|
||||||
for _, metricID := range req.MetricIDs {
|
for _, metricID := range req.MetricIDs {
|
||||||
metric, ok := s.metrics[metricID]
|
metric, ok := s.metrics[metricID]
|
||||||
if ok {
|
if ok {
|
||||||
@@ -418,7 +479,7 @@ func (s *Database) tryListCurrentValues(req tryListCurrentValuesReq) {
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
|
|
||||||
func (s *Database) applyCommits(req storage.Changes) {
|
func (s *Worker) applyCommits(req storage.Changes) {
|
||||||
for _, untyped := range req.Commits {
|
for _, untyped := range req.Commits {
|
||||||
switch rec := untyped.(type) {
|
switch rec := untyped.(type) {
|
||||||
case storage.MetricAddCommited:
|
case storage.MetricAddCommited:
|
||||||
@@ -458,7 +519,7 @@ func (s *Database) applyCommits(req storage.Changes) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) onMetricAddCommited(rec storage.MetricAddCommited) {
|
func (s *Worker) onMetricAddCommited(rec storage.MetricAddCommited) {
|
||||||
// fix lock
|
// fix lock
|
||||||
_, ok := s.metrics[rec.MetricID]
|
_, ok := s.metrics[rec.MetricID]
|
||||||
if ok {
|
if ok {
|
||||||
@@ -477,7 +538,7 @@ func (s *Database) onMetricAddCommited(rec storage.MetricAddCommited) {
|
|||||||
// delete(s.metricLockEntries, rec.MetricID)
|
// delete(s.metricLockEntries, rec.MetricID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) addMetric(rec storage.MetricAddRecord) {
|
func (s *Worker) addMetric(rec storage.MetricAddRecord) {
|
||||||
var (
|
var (
|
||||||
buf = make([]byte, storage.DataPageSize)
|
buf = make([]byte, storage.DataPageSize)
|
||||||
databuf = buf[:storage.DataPagePayloadSize]
|
databuf = buf[:storage.DataPagePayloadSize]
|
||||||
@@ -491,7 +552,7 @@ func (s *Database) addMetric(rec storage.MetricAddRecord) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) onMetricDeleteCommited(rec storage.MetricDeleteCommited) {
|
func (s *Worker) onMetricDeleteCommited(rec storage.MetricDeleteCommited) {
|
||||||
metric, ok := s.metrics[rec.MetricID]
|
metric, ok := s.metrics[rec.MetricID]
|
||||||
if !ok {
|
if !ok {
|
||||||
qb.Abort(qb.NoMetricBug,
|
qb.Abort(qb.NoMetricBug,
|
||||||
@@ -562,7 +623,7 @@ func (s *Database) onMetricDeleteCommited(rec storage.MetricDeleteCommited) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) onMeasuresDeleteCommited(rec storage.MeasuresDeleteCommited) {
|
func (s *Worker) onMeasuresDeleteCommited(rec storage.MeasuresDeleteCommited) {
|
||||||
metric, ok := s.metrics[rec.MetricID]
|
metric, ok := s.metrics[rec.MetricID]
|
||||||
if !ok {
|
if !ok {
|
||||||
qb.Abort(qb.NoMetricBug,
|
qb.Abort(qb.NoMetricBug,
|
||||||
@@ -581,11 +642,97 @@ func (s *Database) onMeasuresDeleteCommited(rec storage.MeasuresDeleteCommited)
|
|||||||
// if len(rec.FreePageNumbers) > 0 {
|
// if len(rec.FreePageNumbers) > 0 {
|
||||||
// s.freeList.AddPages(rec.FreePageNumbers)
|
// s.freeList.AddPages(rec.FreePageNumbers)
|
||||||
// }
|
// }
|
||||||
s.doAfterReleaseXLock(rec.MetricID, metric)
|
//s.doAfterReleaseXLock(rec.MetricID, metric)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) doAfterReleaseXLock(metricID uint32, metric *_metric) {
|
// func (s *Database) doAfterReleaseXLock(metricID uint32, metric *_metric) {
|
||||||
if len(metric.WaitQueue) > 0 {
|
// if len(metric.WaitQueue) > 0 {
|
||||||
s.processMetricQueue(metricID, metric)
|
// s.processMetricQueue(metricID, metric)
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (s *Worker) recovery() {
|
||||||
|
// FIX
|
||||||
|
// advisor, err := recovery.NewRecoveryAdvisor(recovery.RecoveryAdvisorOptions{
|
||||||
|
// Dir: s.dir,
|
||||||
|
// VerifySnapshot: s.verifySnapshot,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// recipe, err := advisor.GetRecipe()
|
||||||
|
// if err != nil {
|
||||||
|
// qb.Abort(qb.GetRecoveryRecipeFailed, err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var logNumber int
|
||||||
|
|
||||||
|
// if recipe != nil {
|
||||||
|
// if recipe.Snapshot != "" {
|
||||||
|
// err = s.loadSnapshot(recipe.Snapshot)
|
||||||
|
// if err != nil {
|
||||||
|
// qb.Abort(qb.LoadSnapshotFailed, err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// for _, changesFileName := range recipe.Changes {
|
||||||
|
// err = s.replayChanges(changesFileName)
|
||||||
|
// if err != nil {
|
||||||
|
// qb.Abort(qb.ReplayChangesFailed, err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// logNumber = recipe.LogNumber
|
||||||
|
// }
|
||||||
|
|
||||||
|
// s.storage, err = storage.NewWriter(storage.WriterOptions{
|
||||||
|
// Dir: s.dir,
|
||||||
|
// LogNumber: logNumber,
|
||||||
|
// AppendToWorkerQueue: s.appendJobToWorkerQueue,
|
||||||
|
// FreeList: s.freeList,
|
||||||
|
// Atree: s.atree,
|
||||||
|
// ExitCh: s.exitCh,
|
||||||
|
// WaitGroup: s.waitGroup,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// qb.Abort(qb.CreateChangesWriterFailed, err)
|
||||||
|
|
||||||
|
// }
|
||||||
|
// go s.storage.Run()
|
||||||
|
|
||||||
|
// // fileNames, err := s.searchREDOFiles()
|
||||||
|
// // if err != nil {
|
||||||
|
// // qb.Abort(qb.SearchREDOFilesFailed, err)
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // if len(fileNames) > 0 {
|
||||||
|
// // for _, fileName := range fileNames {
|
||||||
|
// // err = s.replayREDOFile(fileName)
|
||||||
|
// // if err != nil {
|
||||||
|
// // qb.Abort(qb.ReplayREDOFileFailed, err)
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // for _, fileName := range fileNames {
|
||||||
|
// // err = os.Remove(fileName)
|
||||||
|
// // if err != nil {
|
||||||
|
// // qb.Abort(qb.RemoveREDOFileFailed, err)
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// if recipe != nil {
|
||||||
|
// if recipe.CompleteSnapshot {
|
||||||
|
// err = s.dumpSnapshot(logNumber)
|
||||||
|
// if err != nil {
|
||||||
|
// qb.Abort(qb.DumpSnapshotFailed, err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for _, fileName := range recipe.ToDelete {
|
||||||
|
// err = os.Remove(fileName)
|
||||||
|
// if err != nil {
|
||||||
|
// qb.Abort(qb.RemoveRecipeFileFailed, err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
BIN
database_linux
Executable file
BIN
database_linux
Executable file
Binary file not shown.
@@ -58,7 +58,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
wg.Add(1)
|
//wg.Add(1)
|
||||||
|
|
||||||
fmt.Fprintf(logfile, "database %q started on port %d.\n",
|
fmt.Fprintf(logfile, "database %q started on port %d.\n",
|
||||||
config.DatabaseName, config.TcpPort)
|
config.DatabaseName, config.TcpPort)
|
||||||
|
|||||||
567
examples/play/main.go
Normal file
567
examples/play/main.go
Normal file
@@ -0,0 +1,567 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/rand/v2"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
|
"gordenko.dev/dima/qb"
|
||||||
|
"gordenko.dev/dima/qb/client"
|
||||||
|
"gordenko.dev/dima/qb/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
metricTypeToName = []string{
|
||||||
|
"",
|
||||||
|
"cumulative",
|
||||||
|
"instant",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
iniFileName string
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Fprint(flag.CommandLine.Output(), helpMessage)
|
||||||
|
fmt.Fprint(flag.CommandLine.Output(), configExample)
|
||||||
|
fmt.Fprintf(flag.CommandLine.Output(), mainUsage, os.Args[0])
|
||||||
|
flag.PrintDefaults()
|
||||||
|
}
|
||||||
|
flag.StringVar(&iniFileName, "c", "requests.ini", "path to *.ini config file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
config, err := loadConfig(iniFileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := client.Connect(config.DatabaseAddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("client.Connect(%s): %s\n", config.DatabaseAddr, err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Connected to database")
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRequests(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONFIG FILE
|
||||||
|
|
||||||
|
const mainUsage = `Usage:
|
||||||
|
%s -c path/to/config.ini
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
const helpMessage = `Gordenko project. Example requests. Version: 1.0
|
||||||
|
created by Dmytro Gordenko, 1.e4.kc6@gmail.com
|
||||||
|
`
|
||||||
|
|
||||||
|
const configExample = `
|
||||||
|
requests.ini example:
|
||||||
|
|
||||||
|
databaseAddr = :12345
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
DatabaseAddr string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Config) String() string {
|
||||||
|
return fmt.Sprintf(`starting options:
|
||||||
|
databaseAddr = %s
|
||||||
|
`,
|
||||||
|
s.DatabaseAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadConfig(iniFileName string) (_ Config, err error) {
|
||||||
|
file, err := ini.Load(iniFileName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conf := Config{}
|
||||||
|
top := file.Section("")
|
||||||
|
|
||||||
|
conf.DatabaseAddr = top.Key("databaseAddr").String()
|
||||||
|
return conf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendRequests(conn *client.Connection) {
|
||||||
|
var (
|
||||||
|
//instantMetricID uint32 = 10000
|
||||||
|
cumulativeMetricID uint32 = 10001
|
||||||
|
fracDigits byte = 2
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
//conn.DeleteMetric(instantMetricID)
|
||||||
|
//conn.DeleteMetric(cumulativeMetricID)
|
||||||
|
|
||||||
|
// ADD INSTANT METRIC
|
||||||
|
|
||||||
|
err = conn.AddMetric(proto.AddMetricReq{
|
||||||
|
MetricID: cumulativeMetricID,
|
||||||
|
MetricType: qb.Instant,
|
||||||
|
FracDigits: fracDigits,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("conn.AddMetric: %s\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\nInstant metric %d added\n", cumulativeMetricID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET INSTANT METRIC
|
||||||
|
|
||||||
|
// iMetric, err := conn.GetMetric(cumulativeMetricID)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.GetMetric: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf(`
|
||||||
|
// GetMetric:
|
||||||
|
// metricID: %d
|
||||||
|
// metricType: %s
|
||||||
|
// fracDigits: %d
|
||||||
|
// `,
|
||||||
|
// iMetric.MetricID, metricTypeToName[iMetric.MetricType], fracDigits)
|
||||||
|
//}
|
||||||
|
|
||||||
|
// APPEND MEASURES
|
||||||
|
|
||||||
|
// instantMeasures := GenerateInstantMeasures(62, 220)
|
||||||
|
|
||||||
|
// err = conn.AppendMeasures(proto.AppendMeasuresReq{
|
||||||
|
// MetricID: instantMetricID,
|
||||||
|
// Measures: instantMeasures,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.AppendMeasures: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nAppended %d measures for the metric %d\n",
|
||||||
|
// len(instantMeasures), instantMetricID)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // LIST INSTANT MEASURES
|
||||||
|
|
||||||
|
// lastTimestamp := instantMeasures[len(instantMeasures)-1].Timestamp
|
||||||
|
// until := time.Unix(int64(lastTimestamp), 0)
|
||||||
|
// since := until.Add(-5 * time.Hour)
|
||||||
|
|
||||||
|
// instantList, err := conn.ListInstantMeasures(proto.ListInstantMeasuresReq{
|
||||||
|
// MetricID: instantMetricID,
|
||||||
|
// Since: uint32(since.Unix()),
|
||||||
|
// Until: uint32(until.Unix()),
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.ListInstantMeasures: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nListInstantMeasures %s - %s:\n",
|
||||||
|
// formatTime(uint32(since.Unix())), formatTime(uint32(until.Unix())))
|
||||||
|
// for _, item := range instantList {
|
||||||
|
// fmt.Printf(" %s => %.2f\n", formatTime(item.Timestamp), item.Value)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // LIST ALL INSTANT MEASURES
|
||||||
|
|
||||||
|
// instantList, err = conn.ListAllInstantMeasures(instantMetricID)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.ListAllInstantMeasures: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nListAllInstantMeasures (last 15 items):\n")
|
||||||
|
// for _, item := range instantList[:15] {
|
||||||
|
// fmt.Printf(" %s => %.2f\n", formatTime(item.Timestamp), item.Value)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // LIST INSTANT PERIODS (group by hour)
|
||||||
|
|
||||||
|
// until = time.Unix(int64(lastTimestamp+1), 0)
|
||||||
|
// since = until.Add(-24 * time.Hour)
|
||||||
|
|
||||||
|
// instantPeriods, err := conn.ListInstantPeriods(proto.ListInstantPeriodsReq{
|
||||||
|
// MetricID: instantMetricID,
|
||||||
|
// 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: qb.GroupByHour,
|
||||||
|
// AggregateFuncs: qb.AggregateMin | qb.AggregateMax | qb.AggregateAvg,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.ListInstantPeriods: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nListInstantPeriods (1 day, group by hour):\n")
|
||||||
|
// for _, item := range instantPeriods {
|
||||||
|
// fmt.Printf(" %s => min %.2f, max %.2f, avg %.2f\n", formatHourPeriod(item.Period), item.Min, item.Max, item.Avg)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // LIST INSTANT PERIODS (group by day)
|
||||||
|
|
||||||
|
// until = time.Unix(int64(lastTimestamp+1), 0)
|
||||||
|
// since = until.AddDate(0, 0, -7)
|
||||||
|
|
||||||
|
// instantPeriods, err = conn.ListInstantPeriods(proto.ListInstantPeriodsReq{
|
||||||
|
// MetricID: instantMetricID,
|
||||||
|
// 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: qb.GroupByDay,
|
||||||
|
// AggregateFuncs: qb.AggregateMin | qb.AggregateMax | qb.AggregateAvg,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.ListInstantPeriods: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nListInstantPeriods (7 days, group by day):\n")
|
||||||
|
// for _, item := range instantPeriods {
|
||||||
|
// fmt.Printf(" %s => min %.2f, max %.2f, avg %.2f\n", formatDayPeriod(item.Period), item.Min, item.Max, item.Avg)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // LIST INSTANT PERIODS (group by month)
|
||||||
|
|
||||||
|
// until = time.Unix(int64(lastTimestamp+1), 0)
|
||||||
|
// since = until.AddDate(0, 0, -62)
|
||||||
|
|
||||||
|
// instantPeriods, err = conn.ListInstantPeriods(proto.ListInstantPeriodsReq{
|
||||||
|
// MetricID: instantMetricID,
|
||||||
|
// 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: qb.GroupByMonth,
|
||||||
|
// AggregateFuncs: qb.AggregateMin | qb.AggregateMax | qb.AggregateAvg,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.ListInstantPeriods: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nListInstantPeriods (62 days, group by month):\n")
|
||||||
|
// for _, item := range instantPeriods {
|
||||||
|
// fmt.Printf(" %s => min %.2f, max %.2f, avg %.2f\n", formatMonthPeriod(item.Period), item.Min, item.Max, item.Avg)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // DELETE INSTANT METRIC MEASURES
|
||||||
|
|
||||||
|
// err = conn.DeleteMeasures(proto.DeleteMeasuresReq{
|
||||||
|
// MetricID: instantMetricID,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.DeleteMeasures: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nInstant metric %d measures deleted\n", instantMetricID)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // DELETE INSTANT METRIC
|
||||||
|
|
||||||
|
// err = conn.DeleteMetric(instantMetricID)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.DeleteMetric: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nInstant metric %d deleted\n", instantMetricID)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ADD CUMULATIVE METRIC
|
||||||
|
|
||||||
|
// err = conn.AddMetric(proto.AddMetricReq{
|
||||||
|
// MetricID: cumulativeMetricID,
|
||||||
|
// MetricType: qb.Cumulative,
|
||||||
|
// FracDigits: fracDigits,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.AddMetric: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nCumulative metric %d added\n", cumulativeMetricID)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // GET CUMULATIVE METRIC
|
||||||
|
|
||||||
|
// cMetric, err := conn.GetMetric(cumulativeMetricID)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.GetMetric: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf(`
|
||||||
|
// GetMetric:
|
||||||
|
// metricID: %d
|
||||||
|
// metricType: %s
|
||||||
|
// fracDigits: %d
|
||||||
|
// `,
|
||||||
|
// cMetric.MetricID, metricTypeToName[cMetric.MetricType], fracDigits)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // APPEND MEASURES
|
||||||
|
|
||||||
|
// cumulativeMeasures := GenerateCumulativeMeasures(62)
|
||||||
|
|
||||||
|
// err = conn.AppendMeasures(proto.AppendMeasuresReq{
|
||||||
|
// MetricID: cumulativeMetricID,
|
||||||
|
// Measures: cumulativeMeasures,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.AppendMeasures: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nAppended %d measures for the metric %d\n",
|
||||||
|
// len(cumulativeMeasures), cumulativeMetricID)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // LIST CUMULATIVE MEASURES
|
||||||
|
|
||||||
|
// lastTimestamp = cumulativeMeasures[len(cumulativeMeasures)-1].Timestamp
|
||||||
|
// until = time.Unix(int64(lastTimestamp), 0)
|
||||||
|
// since = until.Add(-5 * time.Hour)
|
||||||
|
|
||||||
|
// cumulativeList, err := conn.ListCumulativeMeasures(proto.ListCumulativeMeasuresReq{
|
||||||
|
// MetricID: cumulativeMetricID,
|
||||||
|
// Since: uint32(since.Unix()),
|
||||||
|
// Until: uint32(until.Unix()),
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.ListCumulativeMeasures: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nListCumulativeMeasures %s - %s:\n",
|
||||||
|
// formatTime(uint32(since.Unix())), formatTime(uint32(until.Unix())))
|
||||||
|
|
||||||
|
// for _, item := range cumulativeList {
|
||||||
|
// fmt.Printf(" %s => %.2f\n", formatTime(item.Timestamp), item.Value)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // LIST ALL CUMULATIVE MEASURES
|
||||||
|
|
||||||
|
// cumulativeList, err = conn.ListAllCumulativeMeasures(cumulativeMetricID)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.ListAllCumulativeMeasures: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nListAllCumulativeMeasures (last 15 items):\n")
|
||||||
|
// for _, item := range cumulativeList[:15] {
|
||||||
|
// fmt.Printf(" %s => %.2f\n", formatTime(item.Timestamp), item.Value)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // LIST CUMULATIVE PERIODS (group by hour)
|
||||||
|
|
||||||
|
// until = time.Unix(int64(lastTimestamp+1), 0)
|
||||||
|
// since = until.Add(-24 * time.Hour)
|
||||||
|
|
||||||
|
// cumulativePeriods, err := conn.ListCumulativePeriods(proto.ListCumulativePeriodsReq{
|
||||||
|
// MetricID: cumulativeMetricID,
|
||||||
|
// 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: qb.GroupByHour,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.ListCumulativePeriods: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nListCumulativePeriods (1 day, group by hour):\n")
|
||||||
|
// for _, item := range cumulativePeriods {
|
||||||
|
// fmt.Printf(" %s => end value %.2f, total %.2f\n", formatHourPeriod(item.Period), item.EndValue, item.Total)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // LIST CUMULATIVE PERIODS (group by day)
|
||||||
|
|
||||||
|
// until = time.Unix(int64(lastTimestamp+1), 0)
|
||||||
|
// since = until.AddDate(0, 0, -7)
|
||||||
|
|
||||||
|
// cumulativePeriods, err = conn.ListCumulativePeriods(proto.ListCumulativePeriodsReq{
|
||||||
|
// MetricID: cumulativeMetricID,
|
||||||
|
// 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: qb.GroupByDay,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.ListCumulativePeriods: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nListCumulativePeriods (7 days, group by day):\n")
|
||||||
|
// for _, item := range cumulativePeriods {
|
||||||
|
// fmt.Printf(" %s => end value %.2f, total %.2f\n", formatDayPeriod(item.Period), item.EndValue, item.Total)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // LIST CUMULATIVE PERIODS (group by day)
|
||||||
|
|
||||||
|
// until = time.Unix(int64(lastTimestamp+1), 0)
|
||||||
|
// since = until.AddDate(0, 0, -62)
|
||||||
|
|
||||||
|
// cumulativePeriods, err = conn.ListCumulativePeriods(proto.ListCumulativePeriodsReq{
|
||||||
|
// MetricID: cumulativeMetricID,
|
||||||
|
// 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: qb.GroupByMonth,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.ListCumulativePeriods: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nListCumulativePeriods (62 days, group by month):\n")
|
||||||
|
// for _, item := range cumulativePeriods {
|
||||||
|
// fmt.Printf(" %s => end value %.2f, total %.2f\n", formatMonthPeriod(item.Period), item.EndValue, item.Total)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // DELETE CUMULATIVE METRIC MEASURES
|
||||||
|
|
||||||
|
// err = conn.DeleteMeasures(proto.DeleteMeasuresReq{
|
||||||
|
// MetricID: cumulativeMetricID,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.DeleteMeasures: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("\nCumulative metric %d measures deleted\n", cumulativeMetricID)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // DELETE CUMULATIVE METRIC
|
||||||
|
|
||||||
|
// err = conn.DeleteMetric(cumulativeMetricID)
|
||||||
|
//
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("conn.DeleteMetric: %s\n", err)
|
||||||
|
// } else {
|
||||||
|
//
|
||||||
|
// fmt.Printf("\nCumulative metric %d deleted\n", cumulativeMetricID)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
const datetimeLayout = "2006-01-02 15:04:05"
|
||||||
|
|
||||||
|
func formatTime(timestamp uint32) string {
|
||||||
|
tm := time.Unix(int64(timestamp), 0)
|
||||||
|
return tm.Format(datetimeLayout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatHourPeriod(period uint32) string {
|
||||||
|
tm := time.Unix(int64(period), 0)
|
||||||
|
return tm.Format("2006-01-02 15:00 - 15") + ":59"
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatDayPeriod(period uint32) string {
|
||||||
|
tm := time.Unix(int64(period), 0)
|
||||||
|
return tm.Format("2006-01-02")
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatMonthPeriod(period uint32) string {
|
||||||
|
tm := time.Unix(int64(period), 0)
|
||||||
|
return tm.Format("2006-01")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateCumulativeMeasures(days int) []proto.Measure {
|
||||||
|
var (
|
||||||
|
measures []proto.Measure
|
||||||
|
minutes = []int{14, 29, 44, 59}
|
||||||
|
hoursPerDay = 24
|
||||||
|
totalHours = days * hoursPerDay
|
||||||
|
since = time.Now().AddDate(0, 0, -days)
|
||||||
|
totalValue float64
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := range totalHours {
|
||||||
|
hourTime := since.Add(time.Duration(i) * time.Hour)
|
||||||
|
for _, m := range minutes {
|
||||||
|
measureTime := time.Date(
|
||||||
|
hourTime.Year(),
|
||||||
|
hourTime.Month(),
|
||||||
|
hourTime.Day(),
|
||||||
|
hourTime.Hour(),
|
||||||
|
m, // minutes
|
||||||
|
0, // seconds
|
||||||
|
0, // nanoseconds
|
||||||
|
time.Local,
|
||||||
|
)
|
||||||
|
|
||||||
|
measure := proto.Measure{
|
||||||
|
Timestamp: uint32(measureTime.Unix()),
|
||||||
|
Value: totalValue,
|
||||||
|
}
|
||||||
|
measures = append(measures, measure)
|
||||||
|
|
||||||
|
totalValue += rand.Float64()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return measures
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateInstantMeasures(days int, baseValue float64) []proto.Measure {
|
||||||
|
var (
|
||||||
|
measures []proto.Measure
|
||||||
|
minutes = []int{14, 29, 44, 59}
|
||||||
|
hoursPerDay = 24
|
||||||
|
totalHours = days * hoursPerDay
|
||||||
|
since = time.Now().AddDate(0, 0, -days)
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := range totalHours {
|
||||||
|
hourTime := since.Add(time.Duration(i) * time.Hour)
|
||||||
|
for _, m := range minutes {
|
||||||
|
measureTime := time.Date(
|
||||||
|
hourTime.Year(),
|
||||||
|
hourTime.Month(),
|
||||||
|
hourTime.Day(),
|
||||||
|
hourTime.Hour(),
|
||||||
|
m, // minutes
|
||||||
|
0, // seconds
|
||||||
|
0, // nanoseconds
|
||||||
|
time.Local,
|
||||||
|
)
|
||||||
|
|
||||||
|
// value = +-10% from base value
|
||||||
|
fluctuation := baseValue * 0.1
|
||||||
|
value := baseValue + (rand.Float64()*2-1)*fluctuation
|
||||||
|
|
||||||
|
measure := proto.Measure{
|
||||||
|
Timestamp: uint32(measureTime.Unix()),
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
measures = append(measures, measure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return measures
|
||||||
|
}
|
||||||
BIN
examples/play/play
Executable file
BIN
examples/play/play
Executable file
Binary file not shown.
1
examples/play/requests.ini
Normal file
1
examples/play/requests.ini
Normal file
@@ -0,0 +1 @@
|
|||||||
|
databaseAddr = :12345
|
||||||
@@ -2,10 +2,10 @@ cd examples/database
|
|||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../database_linux
|
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../database_linux
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
cd examples/loadtest
|
# cd examples/loadtest
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../loadtest_linux
|
# env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../loadtest_linux
|
||||||
cd -
|
# cd -
|
||||||
|
|
||||||
cd examples/requests
|
# cd examples/requests
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../requests_linux
|
# env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../../requests_linux
|
||||||
cd -
|
# cd -
|
||||||
@@ -11,18 +11,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
reChanges = regexp.MustCompile(`(\d+)\.changes`)
|
reChanges = regexp.MustCompile(`(\d+)\.wal`)
|
||||||
reSnapshot = regexp.MustCompile(`(\d+)\.snapshot`)
|
reSnapshot = regexp.MustCompile(`(\d+)\.snapshot`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func joinChangesFileName(dir string, logNumber int) string {
|
func joinWALFileName(dir string, snapshotNumber int) string {
|
||||||
return filepath.Join(dir, fmt.Sprintf("%d.changes", logNumber))
|
return filepath.Join(dir, fmt.Sprintf("%d.wal", snapshotNumber))
|
||||||
}
|
}
|
||||||
|
|
||||||
type RecoveryRecipe struct {
|
type RecoveryRecipe struct {
|
||||||
Snapshot string
|
Snapshot string
|
||||||
Changes []string
|
Changes []string
|
||||||
LogNumber int
|
SnapshotNumber int
|
||||||
ToDelete []string
|
ToDelete []string
|
||||||
CompleteSnapshot bool // флаг - что нужно завершить создание снапшота
|
CompleteSnapshot bool // флаг - что нужно завершить создание снапшота
|
||||||
}
|
}
|
||||||
@@ -52,8 +52,8 @@ func NewRecoveryAdvisor(opt RecoveryAdvisorOptions) (*RecoveryAdvisor, error) {
|
|||||||
|
|
||||||
type SnapshotChangesPair struct {
|
type SnapshotChangesPair struct {
|
||||||
SnapshotFileName string
|
SnapshotFileName string
|
||||||
ChangesFileName string
|
WALFileName string
|
||||||
LogNumber int
|
SnapshotNumber int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RecoveryAdvisor) getSnapshotChangesPairs() (*SnapshotChangesPair, *SnapshotChangesPair, error) {
|
func (s *RecoveryAdvisor) getSnapshotChangesPairs() (*SnapshotChangesPair, *SnapshotChangesPair, error) {
|
||||||
@@ -89,21 +89,21 @@ func (s *RecoveryAdvisor) getSnapshotChangesPairs() (*SnapshotChangesPair, *Snap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for logNumber := range numSet {
|
for snapshotNumber := range numSet {
|
||||||
var (
|
var (
|
||||||
snapshotFileName string
|
snapshotFileName string
|
||||||
changesFileName string
|
changesFileName string
|
||||||
)
|
)
|
||||||
if changesSet[logNumber] {
|
if changesSet[snapshotNumber] {
|
||||||
changesFileName = joinChangesFileName(s.dir, logNumber)
|
changesFileName = joinWALFileName(s.dir, snapshotNumber)
|
||||||
}
|
}
|
||||||
if snapshotsSet[logNumber] {
|
if snapshotsSet[snapshotNumber] {
|
||||||
snapshotFileName = filepath.Join(s.dir, fmt.Sprintf("%d.snapshot", logNumber))
|
snapshotFileName = filepath.Join(s.dir, fmt.Sprintf("%d.snapshot", snapshotNumber))
|
||||||
}
|
}
|
||||||
pairs = append(pairs, SnapshotChangesPair{
|
pairs = append(pairs, SnapshotChangesPair{
|
||||||
ChangesFileName: changesFileName,
|
WALFileName: changesFileName,
|
||||||
SnapshotFileName: snapshotFileName,
|
SnapshotFileName: snapshotFileName,
|
||||||
LogNumber: logNumber,
|
SnapshotNumber: snapshotNumber,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,22 +112,22 @@ func (s *RecoveryAdvisor) getSnapshotChangesPairs() (*SnapshotChangesPair, *Snap
|
|||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(pairs, func(i, j int) bool {
|
sort.Slice(pairs, func(i, j int) bool {
|
||||||
return pairs[i].LogNumber > pairs[j].LogNumber
|
return pairs[i].SnapshotNumber > pairs[j].SnapshotNumber
|
||||||
})
|
})
|
||||||
|
|
||||||
pair := pairs[0]
|
pair := pairs[0]
|
||||||
if pair.ChangesFileName == "" {
|
if pair.WALFileName == "" {
|
||||||
return nil, nil, fmt.Errorf("has %d.shapshot file, but %d.changes file not found",
|
return nil, nil, fmt.Errorf("has %d.shapshot file, but %d.changes file not found",
|
||||||
pair.LogNumber, pair.LogNumber)
|
pair.SnapshotNumber, pair.SnapshotNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(pairs) > 1 {
|
if len(pairs) > 1 {
|
||||||
prevPair := pairs[1]
|
prevPair := pairs[1]
|
||||||
if prevPair.SnapshotFileName == "" && prevPair.LogNumber != 1 {
|
if prevPair.SnapshotFileName == "" && prevPair.SnapshotNumber != 1 {
|
||||||
return &pair, nil, nil
|
return &pair, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if prevPair.ChangesFileName == "" && pair.SnapshotFileName == "" {
|
if prevPair.WALFileName == "" && pair.SnapshotFileName == "" {
|
||||||
return &pair, nil, nil
|
return &pair, nil, nil
|
||||||
}
|
}
|
||||||
return &pair, &prevPair, nil
|
return &pair, &prevPair, nil
|
||||||
@@ -157,14 +157,14 @@ func (s *RecoveryAdvisor) GetRecipe() (*RecoveryRecipe, error) {
|
|||||||
recipe := &RecoveryRecipe{
|
recipe := &RecoveryRecipe{
|
||||||
Snapshot: pair.SnapshotFileName,
|
Snapshot: pair.SnapshotFileName,
|
||||||
Changes: []string{
|
Changes: []string{
|
||||||
pair.ChangesFileName,
|
pair.WALFileName,
|
||||||
},
|
},
|
||||||
LogNumber: pair.LogNumber,
|
SnapshotNumber: pair.SnapshotNumber,
|
||||||
}
|
}
|
||||||
|
|
||||||
if prevPair != nil {
|
if prevPair != nil {
|
||||||
if prevPair.ChangesFileName != "" {
|
if prevPair.WALFileName != "" {
|
||||||
recipe.ToDelete = append(recipe.ToDelete, prevPair.ChangesFileName)
|
recipe.ToDelete = append(recipe.ToDelete, prevPair.WALFileName)
|
||||||
}
|
}
|
||||||
if prevPair.SnapshotFileName != "" {
|
if prevPair.SnapshotFileName != "" {
|
||||||
recipe.ToDelete = append(recipe.ToDelete, prevPair.SnapshotFileName)
|
recipe.ToDelete = append(recipe.ToDelete, prevPair.SnapshotFileName)
|
||||||
@@ -175,55 +175,55 @@ func (s *RecoveryAdvisor) GetRecipe() (*RecoveryRecipe, error) {
|
|||||||
if prevPair != nil {
|
if prevPair != nil {
|
||||||
return s.tryPrevPair(pair, prevPair)
|
return s.tryPrevPair(pair, prevPair)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("%d.shapshot is corrupted", pair.LogNumber)
|
return nil, fmt.Errorf("%d.shapshot is corrupted", pair.SnapshotNumber)
|
||||||
} else {
|
} else {
|
||||||
if prevPair != nil {
|
if prevPair != nil {
|
||||||
return s.tryPrevPair(pair, prevPair)
|
return s.tryPrevPair(pair, prevPair)
|
||||||
} else {
|
} else {
|
||||||
if pair.LogNumber == 1 {
|
if pair.SnapshotNumber == 1 {
|
||||||
return &RecoveryRecipe{
|
return &RecoveryRecipe{
|
||||||
Changes: []string{
|
Changes: []string{
|
||||||
pair.ChangesFileName,
|
pair.WALFileName,
|
||||||
},
|
},
|
||||||
LogNumber: pair.LogNumber,
|
SnapshotNumber: pair.SnapshotNumber,
|
||||||
}, nil
|
}, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("%d.snapshot not found", pair.LogNumber)
|
return nil, fmt.Errorf("%d.snapshot not found", pair.SnapshotNumber)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RecoveryAdvisor) tryPrevPair(pair, prevPair *SnapshotChangesPair) (*RecoveryRecipe, error) {
|
func (s *RecoveryAdvisor) tryPrevPair(pair, prevPair *SnapshotChangesPair) (*RecoveryRecipe, error) {
|
||||||
if prevPair.ChangesFileName == "" {
|
if prevPair.WALFileName == "" {
|
||||||
if pair.SnapshotFileName != "" {
|
if pair.SnapshotFileName != "" {
|
||||||
return nil, fmt.Errorf("%d.shapshot is corrupted and %d.changes not found",
|
return nil, fmt.Errorf("%d.shapshot is corrupted and %d.changes not found",
|
||||||
pair.LogNumber, prevPair.LogNumber)
|
pair.SnapshotNumber, prevPair.SnapshotNumber)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("%d.changes not found", prevPair.LogNumber)
|
return nil, fmt.Errorf("%d.changes not found", prevPair.SnapshotNumber)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if prevPair.SnapshotFileName == "" {
|
if prevPair.SnapshotFileName == "" {
|
||||||
if prevPair.LogNumber == 1 {
|
if prevPair.SnapshotNumber == 1 {
|
||||||
recipe := &RecoveryRecipe{
|
recipe := &RecoveryRecipe{
|
||||||
Changes: []string{
|
Changes: []string{
|
||||||
prevPair.ChangesFileName,
|
prevPair.WALFileName,
|
||||||
pair.ChangesFileName,
|
pair.WALFileName,
|
||||||
},
|
},
|
||||||
LogNumber: pair.LogNumber,
|
SnapshotNumber: pair.SnapshotNumber,
|
||||||
CompleteSnapshot: true,
|
CompleteSnapshot: true,
|
||||||
ToDelete: []string{
|
ToDelete: []string{
|
||||||
prevPair.ChangesFileName,
|
prevPair.WALFileName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return recipe, nil
|
return recipe, nil
|
||||||
} else {
|
} else {
|
||||||
if pair.SnapshotFileName != "" {
|
if pair.SnapshotFileName != "" {
|
||||||
return nil, fmt.Errorf("%d.shapshot is corrupted and %d.snapshot not found",
|
return nil, fmt.Errorf("%d.shapshot is corrupted and %d.snapshot not found",
|
||||||
pair.LogNumber, prevPair.LogNumber)
|
pair.SnapshotNumber, prevPair.SnapshotNumber)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("%d.snapshot not found", pair.LogNumber)
|
return nil, fmt.Errorf("%d.snapshot not found", pair.SnapshotNumber)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,19 +235,19 @@ func (s *RecoveryAdvisor) tryPrevPair(pair, prevPair *SnapshotChangesPair) (*Rec
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !isVerified {
|
if !isVerified {
|
||||||
return nil, fmt.Errorf("%d.shapshot is corrupted", prevPair.LogNumber)
|
return nil, fmt.Errorf("%d.shapshot is corrupted", prevPair.SnapshotNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
recipe := &RecoveryRecipe{
|
recipe := &RecoveryRecipe{
|
||||||
Snapshot: prevPair.SnapshotFileName,
|
Snapshot: prevPair.SnapshotFileName,
|
||||||
Changes: []string{
|
Changes: []string{
|
||||||
prevPair.ChangesFileName,
|
prevPair.WALFileName,
|
||||||
pair.ChangesFileName,
|
pair.WALFileName,
|
||||||
},
|
},
|
||||||
LogNumber: pair.LogNumber,
|
SnapshotNumber: pair.SnapshotNumber,
|
||||||
CompleteSnapshot: true,
|
CompleteSnapshot: true,
|
||||||
ToDelete: []string{
|
ToDelete: []string{
|
||||||
prevPair.ChangesFileName,
|
prevPair.WALFileName,
|
||||||
prevPair.SnapshotFileName,
|
prevPair.SnapshotFileName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gordenko.dev/dima/qb"
|
"gordenko.dev/dima/qb"
|
||||||
"gordenko.dev/dima/qb/atree"
|
|
||||||
"gordenko.dev/dima/qb/freelist"
|
"gordenko.dev/dima/qb/freelist"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,21 +62,19 @@ type Changes struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
dataPagesCount uint32
|
dataPagesCount uint32
|
||||||
indexPagesCount uint32
|
indexPagesCount uint32
|
||||||
dataFreeList *freelist.FreeList
|
dataFreeList *freelist.FreeList
|
||||||
indexFreeList *freelist.FreeList
|
indexFreeList *freelist.FreeList
|
||||||
atree *atree.Atree
|
//atree *atree.Atree
|
||||||
dir string
|
dir string
|
||||||
w *bytes.Buffer // wal буфер для упаковки даних перед записом на диск
|
w *bytes.Buffer // wal буфер для упаковки даних перед записом на диск
|
||||||
wal *os.File
|
wal *os.File
|
||||||
dataFile *os.File
|
dataFile *os.File
|
||||||
indexFile *os.File
|
indexFile *os.File
|
||||||
indexPageSize int
|
|
||||||
dataPageSize int
|
|
||||||
input []any
|
input []any
|
||||||
dataPreparer *WritePreparer
|
writePreparer *WritePreparer
|
||||||
appendToWorkerQueue func(any)
|
appendToWorkerQueue func(any)
|
||||||
written int64
|
written int64
|
||||||
isExited bool
|
isExited bool
|
||||||
@@ -87,25 +84,23 @@ type Writer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WriterOptions struct {
|
type WriterOptions struct {
|
||||||
IndexPageSize int
|
Dir string
|
||||||
DataPageSize int
|
//SnapshotNumber int // номер журнала
|
||||||
Dir string
|
|
||||||
SnapshotNumber int // номер журнала
|
|
||||||
AppendToWorkerQueue func(any)
|
AppendToWorkerQueue func(any)
|
||||||
DataFreeList *freelist.FreeList
|
DataFreeList *freelist.FreeList
|
||||||
IndexFreeList *freelist.FreeList
|
IndexFreeList *freelist.FreeList
|
||||||
Atree *atree.Atree
|
//Atree *atree.Atree
|
||||||
ExitCh chan struct{}
|
ExitCh chan struct{}
|
||||||
WaitGroup *sync.WaitGroup
|
WaitGroup *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWriter(opt WriterOptions) (*Writer, error) {
|
func NewWriter(opt WriterOptions) (*Writer, error) {
|
||||||
if (opt.IndexPageSize % 2) != 0 {
|
// if (opt.IndexPageSize % 2) != 0 {
|
||||||
return nil, errors.New("IndexPageSize must be multiple of 2")
|
// return nil, errors.New("IndexPageSize must be multiple of 2")
|
||||||
}
|
// }
|
||||||
if (opt.DataPageSize % 2) != 0 {
|
// if (opt.DataPageSize % 2) != 0 {
|
||||||
return nil, errors.New("DataPageSize must be multiple of 2")
|
// return nil, errors.New("DataPageSize must be multiple of 2")
|
||||||
}
|
// }
|
||||||
if opt.Dir == "" {
|
if opt.Dir == "" {
|
||||||
return nil, errors.New("Dir option is required")
|
return nil, errors.New("Dir option is required")
|
||||||
}
|
}
|
||||||
@@ -118,9 +113,9 @@ func NewWriter(opt WriterOptions) (*Writer, error) {
|
|||||||
if opt.IndexFreeList == nil {
|
if opt.IndexFreeList == nil {
|
||||||
return nil, errors.New("IndexFreeList option is required")
|
return nil, errors.New("IndexFreeList option is required")
|
||||||
}
|
}
|
||||||
if opt.Atree == nil {
|
// if opt.Atree == nil {
|
||||||
return nil, errors.New("Atree option is required")
|
// return nil, errors.New("Atree option is required")
|
||||||
}
|
// }
|
||||||
if opt.ExitCh == nil {
|
if opt.ExitCh == nil {
|
||||||
return nil, errors.New("ExitCh option is required")
|
return nil, errors.New("ExitCh option is required")
|
||||||
}
|
}
|
||||||
@@ -128,21 +123,31 @@ func NewWriter(opt WriterOptions) (*Writer, error) {
|
|||||||
return nil, errors.New("WaitGroup option is required")
|
return nil, errors.New("WaitGroup option is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pageIngester, err := NewPageIngester(PageIngesterOptions{
|
||||||
|
GetIndexPageNumber: s.getIndexPageNumber,
|
||||||
|
GetDataPageNumber: s.getDataPageNumber,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
writePreparer := NewWritePreparer(WritePreparerOptions{
|
||||||
|
//MinWALBufferSize: 128,
|
||||||
|
PageIngester: pageIngester,
|
||||||
|
})
|
||||||
|
|
||||||
s := &Writer{
|
s := &Writer{
|
||||||
indexPageSize: opt.IndexPageSize,
|
|
||||||
dataPageSize: opt.DataPageSize,
|
|
||||||
dir: opt.Dir,
|
dir: opt.Dir,
|
||||||
appendToWorkerQueue: opt.AppendToWorkerQueue,
|
appendToWorkerQueue: opt.AppendToWorkerQueue,
|
||||||
dataFreeList: opt.DataFreeList,
|
dataFreeList: opt.DataFreeList,
|
||||||
indexFreeList: opt.IndexFreeList,
|
indexFreeList: opt.IndexFreeList,
|
||||||
atree: opt.Atree,
|
writePreparer: writePreparer,
|
||||||
exitCh: opt.ExitCh,
|
//atree: opt.Atree,
|
||||||
waitGroup: opt.WaitGroup,
|
exitCh: opt.ExitCh,
|
||||||
signalCh: make(chan struct{}, 1),
|
waitGroup: opt.WaitGroup,
|
||||||
|
signalCh: make(chan struct{}, 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if opt.SnapshotNumber <= 0 {
|
if opt.SnapshotNumber <= 0 {
|
||||||
log.Fatalln("FIX FUCK NUMBER")
|
log.Fatalln("FIX FUCK NUMBER")
|
||||||
}
|
}
|
||||||
@@ -214,7 +219,7 @@ func (s *Writer) packAndWrite() (err error) {
|
|||||||
// }
|
// }
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
|
|
||||||
prepared := s.dataPreparer.Prepare(input)
|
prepared := s.writePreparer.Prepare(input)
|
||||||
|
|
||||||
// 3. Пишу на диск WAL (append)
|
// 3. Пишу на диск WAL (append)
|
||||||
n, err := s.wal.Write(prepared.Packet)
|
n, err := s.wal.Write(prepared.Packet)
|
||||||
@@ -348,35 +353,35 @@ type PageToWrite struct {
|
|||||||
// writePagesToAtree - записує сторінки в .data та .index файли
|
// writePagesToAtree - записує сторінки в .data та .index файли
|
||||||
func (s *Writer) WritePagesToAtree(indexPages []PageToWrite, dataPages []PageToWrite) (err error) {
|
func (s *Writer) WritePagesToAtree(indexPages []PageToWrite, dataPages []PageToWrite) (err error) {
|
||||||
for _, p := range dataPages {
|
for _, p := range dataPages {
|
||||||
if len(p.Content) != s.dataPageSize {
|
if len(p.Content) != DataPageSize {
|
||||||
return fmt.Errorf("wrong data page size: %d", len(p.Content))
|
return fmt.Errorf("wrong data page size: %d", len(p.Content))
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
off = int(p.PageNo-1) * s.dataPageSize
|
off = int(p.PageNo-1) * DataPageSize
|
||||||
n int
|
n int
|
||||||
)
|
)
|
||||||
n, err = s.dataFile.WriteAt(p.Content, int64(off))
|
n, err = s.dataFile.WriteAt(p.Content, int64(off))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n != s.dataPageSize {
|
if n != DataPageSize {
|
||||||
return fmt.Errorf("write %d instead of %d", n, s.dataPageSize)
|
return fmt.Errorf("write %d instead of %d", n, DataPageSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, p := range indexPages {
|
for _, p := range indexPages {
|
||||||
if len(p.Content) != s.indexPageSize {
|
if len(p.Content) != IndexPageSize {
|
||||||
return fmt.Errorf("wrong index page size: %d", len(p.Content))
|
return fmt.Errorf("wrong index page size: %d", len(p.Content))
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
off = int(p.PageNo-1) * s.indexPageSize
|
off = int(p.PageNo-1) * IndexPageSize
|
||||||
n int
|
n int
|
||||||
)
|
)
|
||||||
n, err = s.indexFile.WriteAt(p.Content, int64(off))
|
n, err = s.indexFile.WriteAt(p.Content, int64(off))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n != s.indexPageSize {
|
if n != IndexPageSize {
|
||||||
return fmt.Errorf("write %d instead of %d", n, s.indexPageSize)
|
return fmt.Errorf("write %d instead of %d", n, IndexPageSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
0
testdir/test.free_data
Normal file
0
testdir/test.free_data
Normal file
0
testdir/test.free_data_delta
Normal file
0
testdir/test.free_data_delta
Normal file
0
testdir/test.free_index
Normal file
0
testdir/test.free_index
Normal file
0
testdir/test.free_index_delta
Normal file
0
testdir/test.free_index_delta
Normal file
Reference in New Issue
Block a user