184 lines
3.7 KiB
Go
184 lines
3.7 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/dgraph-io/badger"
|
|
)
|
|
|
|
// Badger db implementation
|
|
type Badger struct {
|
|
db *badger.DB
|
|
cancelGC func()
|
|
wg sync.WaitGroup
|
|
}
|
|
|
|
// NewBadger creates a new badger instance for the given path
|
|
func NewBadger(dbPath string) (*Badger, error) {
|
|
db, err := badger.Open(badger.DefaultOptions(dbPath).WithLogger(nil))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open badger db at path %s: %w", dbPath, err)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
b := &Badger{
|
|
db: db,
|
|
cancelGC: cancel,
|
|
}
|
|
|
|
b.wg.Add(1)
|
|
go func() {
|
|
defer b.wg.Done()
|
|
|
|
ticker := time.NewTicker(time.Hour)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
for b.db.RunValueLogGC(0.5) == nil && ctx.Err() == nil {
|
|
}
|
|
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
return b, nil
|
|
}
|
|
|
|
// Close the database
|
|
func (b *Badger) Close() error {
|
|
b.cancelGC()
|
|
b.wg.Wait()
|
|
|
|
return b.db.Close()
|
|
}
|
|
|
|
// AddUser to the database
|
|
func (b *Badger) AddUser(user *User) error {
|
|
return b.db.Update(func(tx *badger.Txn) error {
|
|
data, err := json.Marshal(user)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to JSON marshal user: %w", err)
|
|
}
|
|
|
|
key := user.badgerKey()
|
|
if _, err = tx.Get(key); err == nil {
|
|
return fmt.Errorf("user %s already exists", user.Name)
|
|
}
|
|
|
|
return tx.Set(key, data)
|
|
})
|
|
}
|
|
|
|
// GetUser from the database
|
|
func (b *Badger) GetUser(username string) (user *User, err error) {
|
|
err = b.db.View(func(tx *badger.Txn) error {
|
|
item, err := tx.Get(badgerKeyForUsername(username))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get user value for username %s: %w", username, err)
|
|
}
|
|
|
|
user = &User{}
|
|
|
|
return item.Value(func(val []byte) error {
|
|
err = json.Unmarshal(val, user)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to unmarshal user value for username %s: %w", username, err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// ListUsers from the database
|
|
func (b *Badger) ListUsers() (users []*User, err error) {
|
|
err = b.db.View(func(tx *badger.Txn) error {
|
|
opts := badger.DefaultIteratorOptions
|
|
opts.Prefix = []byte("user:")
|
|
|
|
it := tx.NewIterator(opts)
|
|
defer it.Close()
|
|
|
|
for it.Rewind(); it.Valid(); it.Next() {
|
|
item := it.Item()
|
|
|
|
err := item.Value(func(val []byte) error {
|
|
user := &User{}
|
|
err := json.Unmarshal(val, user)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to unmarshal user value for user key %s: %w", string(item.Key()), err)
|
|
}
|
|
|
|
users = append(users, user)
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// AddMedication to the database
|
|
func (b *Badger) AddMedication(medication *Medication) error {
|
|
return b.db.Update(func(tx *badger.Txn) error {
|
|
data, err := json.Marshal(medication)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to JSON marshal medication: %w", err)
|
|
}
|
|
|
|
return tx.Set(medication.badgerKey(), data)
|
|
})
|
|
}
|
|
|
|
// ListMedicationsForUser from the database
|
|
func (b *Badger) ListMedicationsForUser(user *User) (medications []*Medication, err error) {
|
|
err = b.db.View(func(tx *badger.Txn) error {
|
|
opts := badger.DefaultIteratorOptions
|
|
opts.Prefix = badgerPrefixKeyForMedicationUser(user)
|
|
|
|
it := tx.NewIterator(opts)
|
|
defer it.Close()
|
|
|
|
for it.Rewind(); it.Valid(); it.Next() {
|
|
item := it.Item()
|
|
|
|
err := item.Value(func(val []byte) error {
|
|
medication := &Medication{}
|
|
err := json.Unmarshal(val, medication)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to unmarshal medication value for medication key %s: %w", string(item.Key()), err)
|
|
}
|
|
|
|
medications = append(medications, medication)
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return
|
|
}
|