tstor/src/sources/torrent/stats.go

208 lines
4.4 KiB
Go
Raw Normal View History

2024-05-19 21:24:09 +00:00
package torrent
import (
2024-07-16 20:58:06 +00:00
"context"
"encoding/json"
"path"
"slices"
"time"
2024-07-16 20:58:06 +00:00
"git.kmsign.ru/royalcat/tstor/src/log"
"github.com/anacrolix/torrent/types/infohash"
"github.com/dgraph-io/badger/v4"
)
2024-07-16 20:58:06 +00:00
func newStatsStore(metaDir string, lifetime time.Duration) (*statsStore, error) {
db, err := badger.OpenManaged(
badger.
DefaultOptions(path.Join(metaDir, "stats")).
WithNumVersionsToKeep(int(^uint(0) >> 1)).
WithLogger(log.BadgerLogger("stats")), // Infinity
)
if err != nil {
return nil, err
}
2024-07-16 20:58:06 +00:00
go func() {
for n := range time.NewTimer(lifetime / 2).C {
db.SetDiscardTs(uint64(n.Add(-lifetime).Unix()))
}
}()
return &statsStore{
db: db,
}, nil
}
2024-07-16 20:58:06 +00:00
type statsStore struct {
db *badger.DB
}
2024-07-16 20:58:06 +00:00
type TorrentStats struct {
Timestamp time.Time
DownloadedBytes uint64
UploadedBytes uint64
TotalPeers uint16
ActivePeers uint16
ConnectedSeeders uint16
}
2024-07-16 20:58:06 +00:00
func (s TorrentStats) Same(o TorrentStats) bool {
return s.DownloadedBytes == o.DownloadedBytes &&
s.UploadedBytes == o.UploadedBytes &&
s.TotalPeers == o.TotalPeers &&
s.ActivePeers == o.ActivePeers &&
s.ConnectedSeeders == o.ConnectedSeeders
}
2024-07-16 20:58:06 +00:00
func (r *statsStore) addStats(key []byte, stat TorrentStats) error {
ts := uint64(stat.Timestamp.Unix())
2021-01-02 19:09:05 +00:00
2024-07-16 20:58:06 +00:00
txn := r.db.NewTransactionAt(ts, true)
defer txn.Discard()
2024-07-16 20:58:06 +00:00
item, err := txn.Get(key)
if err != nil && err != badger.ErrKeyNotFound {
return err
}
2024-07-16 20:58:06 +00:00
if err != badger.ErrKeyNotFound {
var prevStats TorrentStats
err = item.Value(func(val []byte) error {
return json.Unmarshal(val, &prevStats)
})
if err != nil {
return err
}
2024-07-16 20:58:06 +00:00
if prevStats.Same(stat) {
return nil
}
}
2024-07-16 20:58:06 +00:00
data, err := json.Marshal(stat)
if err != nil {
return err
}
err = txn.Set(key, data)
if err != nil {
return err
}
2024-07-16 20:58:06 +00:00
return txn.CommitAt(ts, nil)
}
2024-07-16 20:58:06 +00:00
func (r *statsStore) AddTorrentStats(ih infohash.T, stat TorrentStats) error {
return r.addStats(ih.Bytes(), stat)
}
2024-07-16 20:58:06 +00:00
const totalKey = "total"
2024-07-16 20:58:06 +00:00
func (r *statsStore) AddTotalStats(stat TorrentStats) error {
return r.addStats([]byte(totalKey), stat)
}
2024-07-16 20:58:06 +00:00
func (r *statsStore) ReadTotalStatsHistory(ctx context.Context, since time.Time) ([]TorrentStats, error) {
stats := []TorrentStats{}
err := r.db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.AllVersions = true
opts.SinceTs = uint64(since.Unix())
it := txn.NewKeyIterator([]byte(totalKey), opts)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
var stat TorrentStats
err := item.Value(func(v []byte) error {
return json.Unmarshal(v, &stat)
})
if err != nil {
return err
}
2024-07-16 20:58:06 +00:00
stats = append(stats, stat)
}
return nil
})
if err != nil {
return nil, err
}
2024-07-16 20:58:06 +00:00
slices.SortFunc(stats, func(a, b TorrentStats) int {
return a.Timestamp.Compare(b.Timestamp)
})
stats = slices.Compact(stats)
return stats, nil
}
2024-07-16 20:58:06 +00:00
func (r *statsStore) ReadTorrentStatsHistory(ctx context.Context, since time.Time, ih infohash.T) ([]TorrentStats, error) {
stats := []TorrentStats{}
err := r.db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.AllVersions = true
opts.SinceTs = uint64(since.Unix())
it := txn.NewKeyIterator(ih.Bytes(), opts)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
var stat TorrentStats
err := item.Value(func(v []byte) error {
return json.Unmarshal(v, &stat)
})
if err != nil {
return err
}
2024-07-16 20:58:06 +00:00
stats = append(stats, stat)
}
2024-07-16 20:58:06 +00:00
return nil
})
if err != nil {
return nil, err
}
2024-07-16 20:58:06 +00:00
slices.SortFunc(stats, func(a, b TorrentStats) int {
return a.Timestamp.Compare(b.Timestamp)
})
stats = slices.Compact(stats)
return stats, nil
}
2024-07-16 20:58:06 +00:00
func (r *statsStore) ReadStatsHistory(ctx context.Context, since time.Time) ([]TorrentStats, error) {
stats := []TorrentStats{}
err := r.db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.AllVersions = true
opts.SinceTs = uint64(since.Unix())
it := txn.NewIterator(opts)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
var stat TorrentStats
err := item.Value(func(v []byte) error {
return json.Unmarshal(v, &stat)
})
if err != nil {
return err
}
stats = append(stats, stat)
}
return nil
})
if err != nil {
return nil, err
}
2024-07-16 20:58:06 +00:00
slices.SortFunc(stats, func(a, b TorrentStats) int {
return a.Timestamp.Compare(b.Timestamp)
})
stats = slices.Compact(stats)
return stats, nil
}