twitch-manager/storage.go
Knut Ahlers 54224d2118
Add subs, increase concurrency safety
Signed-off-by: Knut Ahlers <knut@ahlers.me>
2020-12-30 16:40:23 +01:00

108 lines
2.0 KiB
Go

package main
import (
"compress/gzip"
"encoding/json"
"os"
"sync"
"github.com/pkg/errors"
)
const storeMaxRecent = 25
type subscriber struct {
Name string `json:"name"`
Months int64 `json:"months"`
}
type storage struct {
Donations struct {
LastDonator *string `json:"last_donator"`
LastAmount float64 `json:"last_amount"`
TotalAmount float64 `json:"total_amount"`
} `json:"donations"`
Followers struct {
Last *string `json:"last"`
Seen []string `json:"seen"`
Count int64 `json:"count"`
} `json:"followers"`
Subs struct {
Last *string `json:"last"`
LastDuration int64 `json:"last_duration"`
Count int64 `json:"count"`
Recent []subscriber `json:"recent"`
} `json:"subs"`
modLock sync.RWMutex
saveLock sync.Mutex
}
func newStorage() *storage { return &storage{} }
func (s *storage) Load(from string) error {
f, err := os.Open(from)
if err != nil {
if os.IsNotExist(err) {
return err
}
return errors.Wrap(err, "opening storage file")
}
defer f.Close()
gf, err := gzip.NewReader(f)
if err != nil {
return errors.Wrap(err, "create gzip reader")
}
defer gf.Close()
return errors.Wrap(
json.NewDecoder(gf).Decode(s),
"decode json",
)
}
func (s *storage) Save(to string) error {
s.modLock.RLock()
defer s.modLock.RUnlock()
s.saveLock.Lock()
defer s.saveLock.Unlock()
if len(s.Followers.Seen) > storeMaxRecent {
s.Followers.Seen = s.Followers.Seen[:storeMaxRecent]
}
if len(s.Subs.Recent) > storeMaxRecent {
s.Subs.Recent = s.Subs.Recent[:storeMaxRecent]
}
f, err := os.Create(to)
if err != nil {
return errors.Wrap(err, "create file")
}
defer f.Close()
gf := gzip.NewWriter(f)
defer gf.Close()
return errors.Wrap(
json.NewEncoder(gf).Encode(s),
"encode json",
)
}
func (s *storage) WithModLock(fn func() error) error {
s.modLock.Lock()
defer s.modLock.Unlock()
return fn()
}
func (s *storage) WithModRLock(fn func() error) error {
s.modLock.RLock()
defer s.modLock.RUnlock()
return fn()
}