wip: migrate to mono-repo. SPN has already been moved to spn/
This commit is contained in:
202
spn/hub/database.go
Normal file
202
spn/hub/database.go
Normal file
@@ -0,0 +1,202 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/iterator"
|
||||
"github.com/safing/portbase/database/query"
|
||||
"github.com/safing/portbase/database/record"
|
||||
)
|
||||
|
||||
var (
|
||||
db = database.NewInterface(&database.Options{
|
||||
Local: true,
|
||||
Internal: true,
|
||||
})
|
||||
|
||||
getFromNavigator func(mapName, hubID string) *Hub
|
||||
)
|
||||
|
||||
// MakeHubDBKey makes a hub db key.
|
||||
func MakeHubDBKey(mapName, hubID string) string {
|
||||
return fmt.Sprintf("cache:spn/hubs/%s/%s", mapName, hubID)
|
||||
}
|
||||
|
||||
// MakeHubMsgDBKey makes a hub msg db key.
|
||||
func MakeHubMsgDBKey(mapName string, msgType MsgType, hubID string) string {
|
||||
return fmt.Sprintf("cache:spn/msgs/%s/%s/%s", mapName, msgType, hubID)
|
||||
}
|
||||
|
||||
// SetNavigatorAccess sets a shortcut function to access hubs from the navigator instead of having go through the database.
|
||||
// This also reduces the number of object in RAM and better caches parsed attributes.
|
||||
func SetNavigatorAccess(fn func(mapName, hubID string) *Hub) {
|
||||
if getFromNavigator == nil {
|
||||
getFromNavigator = fn
|
||||
}
|
||||
}
|
||||
|
||||
// GetHub get a Hub from the database - or the navigator, if configured.
|
||||
func GetHub(mapName string, hubID string) (*Hub, error) {
|
||||
if getFromNavigator != nil {
|
||||
hub := getFromNavigator(mapName, hubID)
|
||||
if hub != nil {
|
||||
return hub, nil
|
||||
}
|
||||
}
|
||||
|
||||
return GetHubByKey(MakeHubDBKey(mapName, hubID))
|
||||
}
|
||||
|
||||
// GetHubByKey returns a hub by its raw DB key.
|
||||
func GetHubByKey(key string) (*Hub, error) {
|
||||
r, err := db.Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hub, err := EnsureHub(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hub, nil
|
||||
}
|
||||
|
||||
// EnsureHub makes sure a database record is a Hub.
|
||||
func EnsureHub(r record.Record) (*Hub, error) {
|
||||
// unwrap
|
||||
if r.IsWrapped() {
|
||||
// only allocate a new struct, if we need it
|
||||
newHub := &Hub{}
|
||||
err := record.Unwrap(r, newHub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newHub = prepHub(newHub)
|
||||
|
||||
// Fully validate when getting from database.
|
||||
if err := newHub.Info.validateFormatting(); err != nil {
|
||||
return nil, fmt.Errorf("announcement failed format validation: %w", err)
|
||||
}
|
||||
if err := newHub.Status.validateFormatting(); err != nil {
|
||||
return nil, fmt.Errorf("status failed format validation: %w", err)
|
||||
}
|
||||
if err := newHub.Info.prepare(false); err != nil {
|
||||
return nil, fmt.Errorf("failed to prepare announcement: %w", err)
|
||||
}
|
||||
|
||||
return newHub, nil
|
||||
}
|
||||
|
||||
// or adjust type
|
||||
newHub, ok := r.(*Hub)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("record not of type *Hub, but %T", r)
|
||||
}
|
||||
newHub = prepHub(newHub)
|
||||
|
||||
// Prepare only when already parsed.
|
||||
if err := newHub.Info.prepare(false); err != nil {
|
||||
return nil, fmt.Errorf("failed to prepare announcement: %w", err)
|
||||
}
|
||||
|
||||
// ensure status
|
||||
return newHub, nil
|
||||
}
|
||||
|
||||
func prepHub(h *Hub) *Hub {
|
||||
if h.Status == nil {
|
||||
h.Status = &Status{}
|
||||
}
|
||||
h.Measurements = getSharedMeasurements(h.ID, h.Measurements)
|
||||
return h
|
||||
}
|
||||
|
||||
// Save saves to Hub to the correct scope in the database.
|
||||
func (h *Hub) Save() error {
|
||||
if !h.KeyIsSet() {
|
||||
h.SetKey(MakeHubDBKey(h.Map, h.ID))
|
||||
}
|
||||
|
||||
return db.Put(h)
|
||||
}
|
||||
|
||||
// RemoveHubAndMsgs deletes a Hub and it's saved messages from the database.
|
||||
func RemoveHubAndMsgs(mapName string, hubID string) (err error) {
|
||||
err = db.Delete(MakeHubDBKey(mapName, hubID))
|
||||
if err != nil && !errors.Is(err, database.ErrNotFound) {
|
||||
return fmt.Errorf("failed to delete main hub entry: %w", err)
|
||||
}
|
||||
|
||||
err = db.Delete(MakeHubMsgDBKey(mapName, MsgTypeAnnouncement, hubID))
|
||||
if err != nil && !errors.Is(err, database.ErrNotFound) {
|
||||
return fmt.Errorf("failed to delete hub announcement data: %w", err)
|
||||
}
|
||||
|
||||
err = db.Delete(MakeHubMsgDBKey(mapName, MsgTypeStatus, hubID))
|
||||
if err != nil && !errors.Is(err, database.ErrNotFound) {
|
||||
return fmt.Errorf("failed to delete hub status data: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HubMsg stores raw Hub messages.
|
||||
type HubMsg struct { //nolint:golint
|
||||
record.Base
|
||||
sync.Mutex
|
||||
|
||||
ID string
|
||||
Map string
|
||||
Type MsgType
|
||||
Data []byte
|
||||
|
||||
Received int64
|
||||
}
|
||||
|
||||
// SaveHubMsg saves a raw (and signed) message received by another Hub.
|
||||
func SaveHubMsg(id string, mapName string, msgType MsgType, data []byte) error {
|
||||
// create wrapper record
|
||||
msg := &HubMsg{
|
||||
ID: id,
|
||||
Map: mapName,
|
||||
Type: msgType,
|
||||
Data: data,
|
||||
Received: time.Now().Unix(),
|
||||
}
|
||||
// set key
|
||||
msg.SetKey(MakeHubMsgDBKey(msg.Map, msg.Type, msg.ID))
|
||||
// save
|
||||
return db.PutNew(msg)
|
||||
}
|
||||
|
||||
// QueryRawGossipMsgs queries the database for raw gossip messages.
|
||||
func QueryRawGossipMsgs(mapName string, msgType MsgType) (it *iterator.Iterator, err error) {
|
||||
it, err = db.Query(query.New(MakeHubMsgDBKey(mapName, msgType, "")))
|
||||
return
|
||||
}
|
||||
|
||||
// EnsureHubMsg makes sure a database record is a HubMsg.
|
||||
func EnsureHubMsg(r record.Record) (*HubMsg, error) {
|
||||
// unwrap
|
||||
if r.IsWrapped() {
|
||||
// only allocate a new struct, if we need it
|
||||
newHubMsg := &HubMsg{}
|
||||
err := record.Unwrap(r, newHubMsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newHubMsg, nil
|
||||
}
|
||||
|
||||
// or adjust type
|
||||
newHubMsg, ok := r.(*HubMsg)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("record not of type *Hub, but %T", r)
|
||||
}
|
||||
return newHubMsg, nil
|
||||
}
|
||||
Reference in New Issue
Block a user