[WIP] add tkrzw

This commit is contained in:
iacore 2024-07-07 03:30:12 +00:00
parent 1052a35271
commit e53554365b
Signed by: iacore
GPG key ID: F8C16E5157A63006
5 changed files with 160 additions and 95 deletions

94
core/db_interface.go Normal file
View file

@ -0,0 +1,94 @@
package core
import (
"time"
. "git.exozy.me/exozyme/status/scanner"
)
// change implementation here
func OpenDatabase(dir string) (Database, error) {
return OpenPrototypeDb(dir)
}
type Database interface {
// dump data to disk
Persist() error
// get all records after some time (`cutoff`)
//
// Usage: (get past 7 days of history)
// db.QueryAllAfter(time.Now().Add(-7 * 24 * time.Hour))
//
// Then, parse the result with `GetServiceStatusSpans(rows, service_url)`
QueryAllAfter(cutoff time.Time) ([]Row, error)
// store service status records with timestamp
// returns non-duplicate rows
StoreServiceStatusBatch(entries ServiceStatusBatch, when time.Time) ([]ServiceStatus, error)
// store self up/down event with timestamp
//
// `status` is EventSelfUp or EventSelfDown
StoreSelfStatus(status EventType, when time.Time) error
}
type Row struct {
time time.Time
kind EventType
data ServiceStatus
}
type EventType int
const (
// represents when a servire's status text changes
// start and stop events are represented with EventSelfUp and EventSelfDown
EventService EventType = iota
// represents when this program starts
EventSelfUp
// represents when this program stops
EventSelfDown
)
type Span struct {
start time.Time
stop time.Time
status ServiceStatus
}
func append_close_span(res *[]Span, rows []Row, start_i, stop_i int, status ServiceStatus) {
var stop_time time.Time
if stop_i < 0 {
stop_time = time.Now()
} else {
stop_time = rows[stop_i].time
}
start_time := rows[start_i].time
*res = append(*res, Span{start_time, stop_time, status})
}
// Find when the service is up, and when the service is down
// returns timespans. There might be gaps between two spans, meaning that we don't know the state of that service
func CalculateServiceStatusSpans(rows []Row, service_url string) []Span {
res := []Span{}
start_i := -1
var status ServiceStatus
for i, v := range rows {
switch v.kind {
case EventSelfDown:
append_close_span(&res, rows, start_i, i, status)
start_i = -1
case EventService:
if v.data.Url == service_url {
append_close_span(&res, rows, start_i, i, status)
start_i = i
status = v.data
}
}
}
append_close_span(&res, rows, start_i, -1, status)
return res
}

View file

@ -13,99 +13,17 @@ import (
"github.com/oklog/ulid/v2"
. "git.exozy.me/exozyme/status/scanner"
. "git.exozy.me/exozyme/status/scanner"
)
type Row struct {
time time.Time
kind EventType
data ServiceStatus
}
type EventType int
const (
// represents when a servire's status text changes
// start and stop events are represented with EventSelfUp and EventSelfDown
EventService EventType = iota
// represents when this program starts
EventSelfUp
// represents when this program stops
EventSelfDown
)
type Database interface {
// dump data to disk
Persist() error
// get all records after some time (`cutoff`)
//
// Usage: (get past 7 days of history)
// db.QueryAllAfter(time.Now().Add(-7 * 24 * time.Hour))
//
// Then, parse the result with `GetServiceStatusSpans(rows, service_url)`
QueryAllAfter(cutoff time.Time) ([]Row, error)
// store service status records with timestamp
// returns non-duplicate rows
StoreServiceStatusBatch(entries ServiceStatusBatch, when time.Time) ([]ServiceStatus, error)
// store self up/down event with timestamp
//
// `status` is EventSelfUp or EventSelfDown
StoreSelfStatus(status EventType, when time.Time) error
}
type Span struct {
start time.Time
stop time.Time
status ServiceStatus
}
func append_close_span(res *[]Span, rows []Row, start_i, stop_i int, status ServiceStatus) {
var stop_time time.Time
if stop_i < 0 {
stop_time = time.Now()
} else {
stop_time = rows[stop_i].time
}
start_time := rows[start_i].time
*res = append(*res, Span{start_time, stop_time, status})
}
// Find when the service is up, and when the service is down
// returns timespans. There might be gaps between two spans, meaning that we don't know the state of that service
func CalculateServiceStatusSpans(rows []Row, service_url string) []Span {
res := []Span{}
start_i := -1
var status ServiceStatus
for i, v := range rows {
switch v.kind {
case EventSelfDown:
append_close_span(&res, rows, start_i, i, status)
start_i = -1
case EventService:
if v.data.Url == service_url {
append_close_span(&res, rows, start_i, i, status)
start_i = i
status = v.data
}
}
}
append_close_span(&res, rows, start_i, -1, status)
return res
}
type dumbDatabase struct {
type PrototypeDb struct {
rootDir string
rows []Row
mutex sync.Mutex
}
// Load the newest backup, or start anew
func OpenDatabase(dir string) (Database, error) {
func OpenPrototypeDb(dir string) (Database, error) {
if dir == "" {
return nil, fmt.Errorf("database path is empty. did you set it in config file?")
}
@ -141,13 +59,13 @@ func OpenDatabase(dir string) (Database, error) {
}
log.Printf("Loaded %d rows from: %v", len(rows), fullpath)
return &dumbDatabase{rootDir: dir, rows: rows}, nil
return &PrototypeDb{rootDir: dir, rows: rows}, nil
}
return &dumbDatabase{rootDir: dir, rows: []Row{}}, nil
return &PrototypeDb{rootDir: dir, rows: []Row{}}, nil
}
func (db *dumbDatabase) sortRows() {
func (db *PrototypeDb) sortRows() {
sort.Slice(db.rows, func(i, j int) bool {
return db.rows[i].time.Compare(db.rows[j].time) == -1
})
@ -157,7 +75,7 @@ func (db *dumbDatabase) sortRows() {
//
// Steps:
// create -> write -> fsync -> rename
func (db *dumbDatabase) Persist() error {
func (db *PrototypeDb) Persist() error {
db.mutex.Lock()
defer db.mutex.Unlock()
@ -215,7 +133,7 @@ func (db *dumbDatabase) Persist() error {
return nil
}
func (db *dumbDatabase) QueryAllAfter(cutoff time.Time) ([]Row, error) {
func (db *PrototypeDb) QueryAllAfter(cutoff time.Time) ([]Row, error) {
db.mutex.Lock()
defer db.mutex.Unlock()
@ -235,7 +153,7 @@ func (db *dumbDatabase) QueryAllAfter(cutoff time.Time) ([]Row, error) {
return db.rows[start:], nil
}
func (db *dumbDatabase) StoreServiceStatusBatch(entries ServiceStatusBatch, when time.Time) ([]ServiceStatus, error) {
func (db *PrototypeDb) StoreServiceStatusBatch(entries ServiceStatusBatch, when time.Time) ([]ServiceStatus, error) {
non_duplicates := []ServiceStatus{}
db.mutex.Lock()
@ -264,7 +182,7 @@ func (db *dumbDatabase) StoreServiceStatusBatch(entries ServiceStatusBatch, when
return non_duplicates, nil
}
func (db *dumbDatabase) StoreSelfStatus(status EventType, when time.Time) error {
func (db *PrototypeDb) StoreSelfStatus(status EventType, when time.Time) error {
db.mutex.Lock()
defer db.mutex.Unlock()

53
core/db_tkrzw.go Normal file
View file

@ -0,0 +1,53 @@
package core
import (
"bytes"
"encoding/json"
"fmt"
"log"
"os"
"path"
"sort"
"sync"
"time"
"github.com/oklog/ulid/v2"
"github.com/estraier/tkrzw-go"
. "git.exozy.me/exozyme/status/scanner"
)
type Tkrzw struct {
*tkrzw.DBM
}
func OpenTkrzw(dir string) (Database, error) {
dbm := tkrzw.NewDBM()
status := dbm.Open(path.Join(dir, "casket.tks"), true, map[string]string{})
if !status.IsOK() {
return nil, status
}
return &Tkrzw{DBM: dbm}, nil
// dbm.Rebuild()
}
func (dbm *Tkrzw) Persist() error {
status := dbm.Synchronize(true, map[string]string{})
if !status.IsOK() {
return status
}
return nil
}
func (dbm *Tkrzw) QueryAllAfter(cutoff time.Time) ([]Row, error) {
panic("not implemented") // TODO: Implement
}
func (dbm *Tkrzw) StoreServiceStatusBatch(entries ServiceStatusBatch, when time.Time) ([]ServiceStatus, error) {
panic("not implemented") // TODO: Implement
}
func (dbm *Tkrzw) StoreSelfStatus(status EventType, when time.Time) error {
panic("not implemented") // TODO: Implement
}

2
go.mod
View file

@ -4,6 +4,7 @@ go 1.21.1
require (
github.com/cbroglie/mustache v1.4.0
github.com/estraier/tkrzw-go v0.0.0-20240603165139-89707b45d5ab
github.com/oklog/ulid/v2 v2.1.0
github.com/pelletier/go-toml/v2 v2.1.0
github.com/prometheus-community/pro-bing v0.3.0
@ -11,7 +12,6 @@ require (
)
require (
github.com/elnormous/contenttype v1.0.4
github.com/google/uuid v1.3.1 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect

4
go.sum
View file

@ -4,8 +4,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elnormous/contenttype v1.0.4 h1:FjmVNkvQOGqSX70yvocph7keC8DtmJaLzTTq6ZOQCI8=
github.com/elnormous/contenttype v1.0.4/go.mod h1:5KTOW8m1kdX1dLMiUJeN9szzR2xkngiv2K+RVZwWBbI=
github.com/estraier/tkrzw-go v0.0.0-20240603165139-89707b45d5ab h1:yQbaZGVW+i4XGqOCmngDuuusApVBBqIDPaLMI6JOyXU=
github.com/estraier/tkrzw-go v0.0.0-20240603165139-89707b45d5ab/go.mod h1:vUOsQ39pa/SuhSmt/Nj+FiNFEHhGf58xM61e0EfZQcc=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=