dht store and stats query
This commit is contained in:
parent
93892a6f1d
commit
d5aa78cb39
13 changed files with 1114 additions and 74 deletions
|
@ -1,5 +1,6 @@
|
||||||
type TorrentDaemonQuery {
|
type TorrentDaemonQuery {
|
||||||
torrents(filter: TorrentsFilter): [Torrent!]! @resolver
|
torrents(filter: TorrentsFilter): [Torrent!]! @resolver
|
||||||
|
stats: TorrentStats! @resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
input TorrentsFilter {
|
input TorrentsFilter {
|
||||||
|
|
|
@ -32,3 +32,22 @@ enum TorrentPriority {
|
||||||
READAHEAD
|
READAHEAD
|
||||||
NOW
|
NOW
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TorrentStats {
|
||||||
|
bytesWritten: Int!
|
||||||
|
bytesWrittenData: Int!
|
||||||
|
bytesRead: Int!
|
||||||
|
bytesReadData: Int!
|
||||||
|
bytesReadUsefulData: Int!
|
||||||
|
bytesReadUsefulIntendedData: Int!
|
||||||
|
|
||||||
|
chunksWritten: Int!
|
||||||
|
chunksRead: Int!
|
||||||
|
chunksReadUseful: Int!
|
||||||
|
chunksReadWasted: Int!
|
||||||
|
|
||||||
|
metadataChunksRead: Int!
|
||||||
|
|
||||||
|
piecesDirtiedGood: Int!
|
||||||
|
piecesDirtiedBad: Int!
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -192,6 +192,7 @@ type TorrentDaemonMutation struct {
|
||||||
|
|
||||||
type TorrentDaemonQuery struct {
|
type TorrentDaemonQuery struct {
|
||||||
Torrents []*Torrent `json:"torrents"`
|
Torrents []*Torrent `json:"torrents"`
|
||||||
|
Stats *TorrentStats `json:"stats"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TorrentFs struct {
|
type TorrentFs struct {
|
||||||
|
@ -269,6 +270,22 @@ func (TorrentProgress) IsProgress() {}
|
||||||
func (this TorrentProgress) GetCurrent() int64 { return this.Current }
|
func (this TorrentProgress) GetCurrent() int64 { return this.Current }
|
||||||
func (this TorrentProgress) GetTotal() int64 { return this.Total }
|
func (this TorrentProgress) GetTotal() int64 { return this.Total }
|
||||||
|
|
||||||
|
type TorrentStats struct {
|
||||||
|
BytesWritten int64 `json:"bytesWritten"`
|
||||||
|
BytesWrittenData int64 `json:"bytesWrittenData"`
|
||||||
|
BytesRead int64 `json:"bytesRead"`
|
||||||
|
BytesReadData int64 `json:"bytesReadData"`
|
||||||
|
BytesReadUsefulData int64 `json:"bytesReadUsefulData"`
|
||||||
|
BytesReadUsefulIntendedData int64 `json:"bytesReadUsefulIntendedData"`
|
||||||
|
ChunksWritten int64 `json:"chunksWritten"`
|
||||||
|
ChunksRead int64 `json:"chunksRead"`
|
||||||
|
ChunksReadUseful int64 `json:"chunksReadUseful"`
|
||||||
|
ChunksReadWasted int64 `json:"chunksReadWasted"`
|
||||||
|
MetadataChunksRead int64 `json:"metadataChunksRead"`
|
||||||
|
PiecesDirtiedGood int64 `json:"piecesDirtiedGood"`
|
||||||
|
PiecesDirtiedBad int64 `json:"piecesDirtiedBad"`
|
||||||
|
}
|
||||||
|
|
||||||
type TorrentsFilter struct {
|
type TorrentsFilter struct {
|
||||||
Infohash *StringFilter `json:"infohash,omitempty"`
|
Infohash *StringFilter `json:"infohash,omitempty"`
|
||||||
Name *StringFilter `json:"name,omitempty"`
|
Name *StringFilter `json:"name,omitempty"`
|
||||||
|
|
|
@ -86,6 +86,26 @@ func (r *torrentDaemonQueryResolver) Torrents(ctx context.Context, obj *model.To
|
||||||
return tr, nil
|
return tr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stats is the resolver for the stats field.
|
||||||
|
func (r *torrentDaemonQueryResolver) Stats(ctx context.Context, obj *model.TorrentDaemonQuery) (*model.TorrentStats, error) {
|
||||||
|
stats := r.Service.Stats()
|
||||||
|
return &model.TorrentStats{
|
||||||
|
BytesWritten: stats.BytesWritten.Int64(),
|
||||||
|
BytesRead: stats.BytesRead.Int64(),
|
||||||
|
BytesWrittenData: stats.BytesWrittenData.Int64(),
|
||||||
|
BytesReadData: stats.BytesReadData.Int64(),
|
||||||
|
BytesReadUsefulData: stats.BytesReadUsefulData.Int64(),
|
||||||
|
BytesReadUsefulIntendedData: stats.BytesReadUsefulIntendedData.Int64(),
|
||||||
|
ChunksWritten: stats.ChunksWritten.Int64(),
|
||||||
|
ChunksRead: stats.ChunksRead.Int64(),
|
||||||
|
ChunksReadUseful: stats.ChunksReadUseful.Int64(),
|
||||||
|
ChunksReadWasted: stats.ChunksReadWasted.Int64(),
|
||||||
|
MetadataChunksRead: stats.MetadataChunksRead.Int64(),
|
||||||
|
PiecesDirtiedGood: stats.PiecesDirtiedGood.Int64(),
|
||||||
|
PiecesDirtiedBad: stats.PiecesDirtiedBad.Int64(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TorrentDaemonQuery returns graph.TorrentDaemonQueryResolver implementation.
|
// TorrentDaemonQuery returns graph.TorrentDaemonQueryResolver implementation.
|
||||||
func (r *Resolver) TorrentDaemonQuery() graph.TorrentDaemonQueryResolver {
|
func (r *Resolver) TorrentDaemonQuery() graph.TorrentDaemonQueryResolver {
|
||||||
return &torrentDaemonQueryResolver{r}
|
return &torrentDaemonQueryResolver{r}
|
||||||
|
|
|
@ -23,7 +23,7 @@ func New(fc *filecache.Cache, s *torrent.Daemon, vfs vfs.Filesystem, logPath str
|
||||||
// middleware.Recover(),
|
// middleware.Recover(),
|
||||||
middleware.Gzip(),
|
middleware.Gzip(),
|
||||||
middleware.Decompress(),
|
middleware.Decompress(),
|
||||||
Logger(),
|
// Logger(),
|
||||||
)
|
)
|
||||||
|
|
||||||
echopprof.Register(r)
|
echopprof.Register(r)
|
||||||
|
|
|
@ -22,15 +22,16 @@ func newClient(st storage.ClientImpl, fis bep44.Store, cfg *config.TorrentClient
|
||||||
torrentCfg.PeerID = string(id[:])
|
torrentCfg.PeerID = string(id[:])
|
||||||
torrentCfg.DefaultStorage = st
|
torrentCfg.DefaultStorage = st
|
||||||
torrentCfg.AlwaysWantConns = true
|
torrentCfg.AlwaysWantConns = true
|
||||||
|
torrentCfg.DropMutuallyCompletePeers = true
|
||||||
|
torrentCfg.TorrentPeersLowWater = 100
|
||||||
|
torrentCfg.TorrentPeersHighWater = 1000
|
||||||
torrentCfg.AcceptPeerConnections = true
|
torrentCfg.AcceptPeerConnections = true
|
||||||
torrentCfg.DisableAggressiveUpload = false
|
|
||||||
|
|
||||||
torrentCfg.Seed = true
|
torrentCfg.Seed = true
|
||||||
// torrentCfg.DownloadRateLimiter = rate.NewLimiter(rate.Inf, 0)
|
torrentCfg.DisableAggressiveUpload = false
|
||||||
// torrentCfg
|
|
||||||
|
|
||||||
tl := tlog.NewLogger()
|
tl := tlog.NewLogger()
|
||||||
tl.SetHandlers(&dlog.Torrent{L: l})
|
tl.SetHandlers(&dlog.Torrent{L: l})
|
||||||
|
|
||||||
torrentCfg.Logger = tl
|
torrentCfg.Logger = tl
|
||||||
torrentCfg.Callbacks.NewPeer = append(torrentCfg.Callbacks.NewPeer, func(p *torrent.Peer) {
|
torrentCfg.Callbacks.NewPeer = append(torrentCfg.Callbacks.NewPeer, func(p *torrent.Peer) {
|
||||||
l := l.With("ip", p.RemoteAddr.String())
|
l := l.With("ip", p.RemoteAddr.String())
|
||||||
|
@ -55,6 +56,7 @@ func newClient(st storage.ClientImpl, fis bep44.Store, cfg *config.TorrentClient
|
||||||
// l.Debug("peer closed", "ip", c.RemoteAddr.String())
|
// l.Debug("peer closed", "ip", c.RemoteAddr.String())
|
||||||
// })
|
// })
|
||||||
|
|
||||||
|
torrentCfg.PeriodicallyAnnounceTorrentsToDht = true
|
||||||
torrentCfg.ConfigureAnacrolixDhtServer = func(cfg *dht.ServerConfig) {
|
torrentCfg.ConfigureAnacrolixDhtServer = func(cfg *dht.ServerConfig) {
|
||||||
cfg.Store = fis
|
cfg.Store = fis
|
||||||
cfg.Exp = 2 * time.Hour
|
cfg.Exp = 2 * time.Hour
|
||||||
|
|
|
@ -29,12 +29,12 @@ type Controller struct {
|
||||||
log *rlog.Logger
|
log *rlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func newController(t *torrent.Torrent, torrentFileProperties kv.Store[string, FileProperties], storage TorrentFileDeleter) *Controller {
|
func newController(t *torrent.Torrent, torrentFileProperties kv.Store[string, FileProperties], storage TorrentFileDeleter, log *rlog.Logger) *Controller {
|
||||||
return &Controller{
|
return &Controller{
|
||||||
t: t,
|
t: t,
|
||||||
storage: storage,
|
storage: storage,
|
||||||
fileProperties: torrentFileProperties,
|
fileProperties: torrentFileProperties,
|
||||||
log: rlog.Component("torrent-client", "controller").With(slog.String("infohash", t.InfoHash().HexString())),
|
log: log.WithComponent("controller").With(slog.String("infohash", t.InfoHash().HexString())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ func (s *Controller) Files(ctx context.Context) ([]*FileController, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
props := kvsingle.New(s.fileProperties, v.Path())
|
props := kvsingle.New(s.fileProperties, v.Path())
|
||||||
ctl := NewFileController(v, props)
|
ctl := NewFileController(v, props, s.log)
|
||||||
files = append(files, ctl)
|
files = append(files, ctl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,15 +179,25 @@ func (s *Controller) ValidateTorrent(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) SetPriority(ctx context.Context, priority types.PiecePriority) error {
|
func (c *Controller) SetPriority(ctx context.Context, priority types.PiecePriority) error {
|
||||||
// log := c.log.With(slog.Int("priority", int(priority)))
|
log := c.log.With(slog.Int("priority", int(priority)))
|
||||||
|
files, err := c.Files(ctx)
|
||||||
for _, f := range c.t.Files() {
|
|
||||||
err := c.setFilePriority(ctx, f, priority)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for _, f := range files {
|
||||||
|
excluded, err := f.Excluded(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(ctx, "failed to get file exclusion status", rlog.Error(err))
|
||||||
|
}
|
||||||
|
if excluded {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = f.SetPriority(ctx, priority)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(ctx, "failed to set file priority", rlog.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,37 +210,35 @@ func (c *Controller) Priority(ctx context.Context) (types.PiecePriority, error)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
for _, v := range files {
|
for _, v := range files {
|
||||||
props, err := v.Properties(ctx)
|
filePriority := v.Priority()
|
||||||
if err != nil {
|
if filePriority > prio {
|
||||||
return 0, err
|
prio = filePriority
|
||||||
}
|
|
||||||
if props.Priority > prio {
|
|
||||||
prio = props.Priority
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return prio, nil
|
return prio, nil
|
||||||
}
|
}
|
||||||
func (c *Controller) setFilePriority(ctx context.Context, file *torrent.File, priority types.PiecePriority) error {
|
|
||||||
err := c.fileProperties.Edit(ctx, file.Path(), func(ctx context.Context, v FileProperties) (FileProperties, error) {
|
|
||||||
v.Priority = priority
|
|
||||||
return v, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err == kv.ErrKeyNotFound {
|
// func (c *Controller) setFilePriority(ctx context.Context, file *torrent.File, priority types.PiecePriority) error {
|
||||||
seterr := c.fileProperties.Set(ctx, file.Path(), FileProperties{Priority: priority})
|
// err := c.fileProperties.Edit(ctx, file.Path(), func(ctx context.Context, v FileProperties) (FileProperties, error) {
|
||||||
if seterr != nil {
|
// v.Priority = priority
|
||||||
return seterr
|
// return v, nil
|
||||||
}
|
// })
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
// if err == kv.ErrKeyNotFound {
|
||||||
return err
|
// seterr := c.fileProperties.Set(ctx, file.Path(), FileProperties{Priority: priority})
|
||||||
}
|
// if seterr != nil {
|
||||||
file.SetPriority(priority)
|
// return seterr
|
||||||
return nil
|
// }
|
||||||
}
|
// err = nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// file.SetPriority(priority)
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
func (c *Controller) initializeTorrentPriories(ctx context.Context) error {
|
func (c *Controller) initializeTorrentPriories(ctx context.Context) error {
|
||||||
ctx, span := tracer.Start(ctx, "initializeTorrentPriories")
|
ctx, span := tracer.Start(ctx, "initializeTorrentPriories")
|
||||||
|
@ -248,12 +256,10 @@ func (c *Controller) initializeTorrentPriories(ctx context.Context) error {
|
||||||
log.Error(ctx, "failed to get file properties", rlog.Error(err))
|
log.Error(ctx, "failed to get file properties", rlog.Error(err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log = log.With(slog.Int("priority", int(props.Priority)))
|
|
||||||
|
|
||||||
file.file.SetPriority(props.Priority)
|
file.file.SetPriority(props.Priority)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info(ctx, "torrent initialization complete", slog.String("infohash", c.InfoHash()), slog.String("torrent_name", c.Name()))
|
log.Debug(ctx, "torrent initialization complete", slog.String("infohash", c.InfoHash()), slog.String("torrent_name", c.Name()))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ type Daemon struct {
|
||||||
client *torrent.Client
|
client *torrent.Client
|
||||||
infoBytes *infoBytesStore
|
infoBytes *infoBytesStore
|
||||||
Storage *fileStorage
|
Storage *fileStorage
|
||||||
fis *fileItemStore
|
fis *dhtFileItemStore
|
||||||
dirsAquire kv.Store[string, DirAquire]
|
dirsAquire kv.Store[string, DirAquire]
|
||||||
fileProperties kv.Store[string, FileProperties]
|
fileProperties kv.Store[string, FileProperties]
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ func NewService(sourceFs billy.Filesystem, conf config.TorrentClient) (*Daemon,
|
||||||
return nil, fmt.Errorf("error creating metadata folder: %w", err)
|
return nil, fmt.Errorf("error creating metadata folder: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.fis, err = newFileItemStore(filepath.Join(conf.MetadataFolder, "items"), 2*time.Hour)
|
s.fis, err = newDHTStore(filepath.Join(conf.MetadataFolder, "dht-item-store"), 3*time.Hour)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error starting item store: %w", err)
|
return nil, fmt.Errorf("error starting item store: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -149,9 +149,11 @@ func (s *Daemon) loadTorrent(ctx context.Context, f vfs.File) (*Controller, erro
|
||||||
return nil, fmt.Errorf("loading torrent metadata from file %s, error: %w", stat.Name(), err)
|
return nil, fmt.Errorf("loading torrent metadata from file %s, error: %w", stat.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ctl *Controller
|
||||||
t, ok := s.client.Torrent(mi.HashInfoBytes())
|
t, ok := s.client.Torrent(mi.HashInfoBytes())
|
||||||
if !ok {
|
if ok {
|
||||||
|
ctl = s.newController(t)
|
||||||
|
} else {
|
||||||
span.AddEvent("torrent not found, loading from file")
|
span.AddEvent("torrent not found, loading from file")
|
||||||
log.Info(ctx, "torrent not found, loading from file")
|
log.Info(ctx, "torrent not found, loading from file")
|
||||||
|
|
||||||
|
@ -176,10 +178,12 @@ func (s *Daemon) loadTorrent(ctx context.Context, f vfs.File) (*Controller, erro
|
||||||
|
|
||||||
t, _ = s.client.AddTorrentOpt(torrent.AddTorrentOpts{
|
t, _ = s.client.AddTorrentOpt(torrent.AddTorrentOpts{
|
||||||
InfoHash: spec.InfoHash,
|
InfoHash: spec.InfoHash,
|
||||||
|
InfoHashV2: spec.InfoHashV2,
|
||||||
Storage: s.Storage,
|
Storage: s.Storage,
|
||||||
InfoBytes: infoBytes,
|
InfoBytes: infoBytes,
|
||||||
ChunkSize: spec.ChunkSize,
|
ChunkSize: spec.ChunkSize,
|
||||||
})
|
})
|
||||||
|
|
||||||
t.AllowDataDownload()
|
t.AllowDataDownload()
|
||||||
t.AllowDataUpload()
|
t.AllowDataUpload()
|
||||||
|
|
||||||
|
@ -199,6 +203,13 @@ func (s *Daemon) loadTorrent(ctx context.Context, f vfs.File) (*Controller, erro
|
||||||
}
|
}
|
||||||
span.AddEvent("got info")
|
span.AddEvent("got info")
|
||||||
|
|
||||||
|
ctl = s.newController(t)
|
||||||
|
|
||||||
|
err = ctl.initializeTorrentPriories(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("initialize torrent priorities: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// info := t.Info()
|
// info := t.Info()
|
||||||
// if info == nil {
|
// if info == nil {
|
||||||
// return nil, fmt.Errorf("info is nil")
|
// return nil, fmt.Errorf("info is nil")
|
||||||
|
@ -216,13 +227,6 @@ func (s *Daemon) loadTorrent(ctx context.Context, f vfs.File) (*Controller, erro
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
ctl := s.newController(t)
|
|
||||||
|
|
||||||
err = ctl.initializeTorrentPriories(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(ctx, "error initializing torrent priorities", rlog.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctl, nil
|
return ctl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,11 +236,7 @@ func isValidInfoHashBytes(d []byte) bool {
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Daemon) Stats() (*Stats, error) {
|
func (s *Daemon) Stats() torrent.ConnStats {
|
||||||
return &Stats{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Daemon) GetStats() torrent.ConnStats {
|
|
||||||
return s.client.ConnStats()
|
return s.client.ConnStats()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,6 +316,7 @@ func (s *Daemon) newController(t *torrent.Torrent) *Controller {
|
||||||
return newController(t,
|
return newController(t,
|
||||||
storeByTorrent(s.fileProperties, t.InfoHash()),
|
storeByTorrent(s.fileProperties, t.InfoHash()),
|
||||||
s.Storage,
|
s.Storage,
|
||||||
|
s.log,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,16 +10,16 @@ import (
|
||||||
"github.com/dgraph-io/badger/v4"
|
"github.com/dgraph-io/badger/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ bep44.Store = &fileItemStore{}
|
var _ bep44.Store = &dhtFileItemStore{}
|
||||||
|
|
||||||
type fileItemStore struct {
|
type dhtFileItemStore struct {
|
||||||
ttl time.Duration
|
ttl time.Duration
|
||||||
db *badger.DB
|
db *badger.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFileItemStore(path string, itemsTTL time.Duration) (*fileItemStore, error) {
|
func newDHTStore(path string, itemsTTL time.Duration) (*dhtFileItemStore, error) {
|
||||||
opts := badger.DefaultOptions(path).
|
opts := badger.DefaultOptions(path).
|
||||||
WithLogger(dlog.BadgerLogger("torrent-client", "item-store")).
|
WithLogger(dlog.BadgerLogger("torrent-client", "dht-item-store")).
|
||||||
WithValueLogFileSize(1<<26 - 1)
|
WithValueLogFileSize(1<<26 - 1)
|
||||||
|
|
||||||
db, err := badger.Open(opts)
|
db, err := badger.Open(opts)
|
||||||
|
@ -32,13 +32,13 @@ func newFileItemStore(path string, itemsTTL time.Duration) (*fileItemStore, erro
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &fileItemStore{
|
return &dhtFileItemStore{
|
||||||
db: db,
|
db: db,
|
||||||
ttl: itemsTTL,
|
ttl: itemsTTL,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fis *fileItemStore) Put(i *bep44.Item) error {
|
func (fis *dhtFileItemStore) Put(i *bep44.Item) error {
|
||||||
tx := fis.db.NewTransaction(true)
|
tx := fis.db.NewTransaction(true)
|
||||||
defer tx.Discard()
|
defer tx.Discard()
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ func (fis *fileItemStore) Put(i *bep44.Item) error {
|
||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fis *fileItemStore) Get(t bep44.Target) (*bep44.Item, error) {
|
func (fis *dhtFileItemStore) Get(t bep44.Target) (*bep44.Item, error) {
|
||||||
tx := fis.db.NewTransaction(false)
|
tx := fis.db.NewTransaction(false)
|
||||||
defer tx.Discard()
|
defer tx.Discard()
|
||||||
|
|
||||||
|
@ -84,11 +84,11 @@ func (fis *fileItemStore) Get(t bep44.Target) (*bep44.Item, error) {
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fis *fileItemStore) Del(t bep44.Target) error {
|
func (fis *dhtFileItemStore) Del(t bep44.Target) error {
|
||||||
// ignore this
|
// ignore this
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fis *fileItemStore) Close() error {
|
func (fis *dhtFileItemStore) Close() error {
|
||||||
return fis.db.Close()
|
return fis.db.Close()
|
||||||
}
|
}
|
|
@ -2,8 +2,10 @@ package torrent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
"git.kmsign.ru/royalcat/tstor/pkg/kvsingle"
|
"git.kmsign.ru/royalcat/tstor/pkg/kvsingle"
|
||||||
|
"git.kmsign.ru/royalcat/tstor/pkg/rlog"
|
||||||
"github.com/anacrolix/torrent"
|
"github.com/anacrolix/torrent"
|
||||||
"github.com/anacrolix/torrent/metainfo"
|
"github.com/anacrolix/torrent/metainfo"
|
||||||
"github.com/anacrolix/torrent/types"
|
"github.com/anacrolix/torrent/types"
|
||||||
|
@ -13,12 +15,14 @@ import (
|
||||||
type FileController struct {
|
type FileController struct {
|
||||||
file *torrent.File
|
file *torrent.File
|
||||||
properties *kvsingle.Value[string, FileProperties]
|
properties *kvsingle.Value[string, FileProperties]
|
||||||
|
log *rlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFileController(f *torrent.File, properties *kvsingle.Value[string, FileProperties]) *FileController {
|
func NewFileController(f *torrent.File, properties *kvsingle.Value[string, FileProperties], log *rlog.Logger) *FileController {
|
||||||
return &FileController{
|
return &FileController{
|
||||||
file: f,
|
file: f,
|
||||||
properties: properties,
|
properties: properties,
|
||||||
|
log: log.WithComponent("file-controller").With(slog.String("file", f.Path())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +42,8 @@ func (s *FileController) Properties(ctx context.Context) (FileProperties, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FileController) SetPriority(ctx context.Context, priority types.PiecePriority) error {
|
func (s *FileController) SetPriority(ctx context.Context, priority types.PiecePriority) error {
|
||||||
|
log := s.log.With(slog.Int("priority", int(priority)))
|
||||||
|
|
||||||
err := s.properties.Edit(ctx, func(ctx context.Context, v FileProperties) (FileProperties, error) {
|
err := s.properties.Edit(ctx, func(ctx context.Context, v FileProperties) (FileProperties, error) {
|
||||||
v.Priority = priority
|
v.Priority = priority
|
||||||
return v, nil
|
return v, nil
|
||||||
|
@ -55,6 +61,7 @@ func (s *FileController) SetPriority(ctx context.Context, priority types.PiecePr
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debug(ctx, "file priority set")
|
||||||
s.file.SetPriority(priority)
|
s.file.SetPriority(priority)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -83,6 +90,10 @@ func (s *FileController) Size() int64 {
|
||||||
return s.file.Length()
|
return s.file.Length()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *FileController) Priority() types.PiecePriority {
|
||||||
|
return s.file.Priority()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *FileController) BytesCompleted() int64 {
|
func (s *FileController) BytesCompleted() int64 {
|
||||||
return s.file.BytesCompleted()
|
return s.file.BytesCompleted()
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,7 +124,7 @@ func (fs *LogFS) Open(ctx context.Context, filename string) (file File, err erro
|
||||||
if isLoggableError(err) {
|
if isLoggableError(err) {
|
||||||
fs.log.Error(ctx, "Failed to open file")
|
fs.log.Error(ctx, "Failed to open file")
|
||||||
}
|
}
|
||||||
file = wrapLogFile(file, filename, fs.log, fs.readTimeout, fs.tel)
|
file = WrapLogFile(file, filename, fs.log, fs.readTimeout, fs.tel)
|
||||||
|
|
||||||
if file != nil {
|
if file != nil {
|
||||||
fs.tel.openedFiles.Add(ctx, 1)
|
fs.tel.openedFiles.Add(ctx, 1)
|
||||||
|
@ -221,7 +221,7 @@ func (f *LogFile) Type() fs.FileMode {
|
||||||
|
|
||||||
var _ File = (*LogFile)(nil)
|
var _ File = (*LogFile)(nil)
|
||||||
|
|
||||||
func wrapLogFile(f File, filename string, log *rlog.Logger, timeout time.Duration, tel *fsTelemetry) *LogFile {
|
func WrapLogFile(f File, filename string, log *rlog.Logger, timeout time.Duration, tel *fsTelemetry) *LogFile {
|
||||||
return &LogFile{
|
return &LogFile{
|
||||||
filename: filename,
|
filename: filename,
|
||||||
f: f,
|
f: f,
|
||||||
|
|
|
@ -106,6 +106,7 @@ type TorrentDaemonMutation {
|
||||||
}
|
}
|
||||||
type TorrentDaemonQuery {
|
type TorrentDaemonQuery {
|
||||||
torrents(filter: TorrentsFilter): [Torrent!]! @resolver
|
torrents(filter: TorrentsFilter): [Torrent!]! @resolver
|
||||||
|
stats: TorrentStats! @resolver
|
||||||
}
|
}
|
||||||
type TorrentFS implements Dir & FsEntry {
|
type TorrentFS implements Dir & FsEntry {
|
||||||
name: String!
|
name: String!
|
||||||
|
@ -154,6 +155,21 @@ type TorrentProgress implements Progress {
|
||||||
current: Int!
|
current: Int!
|
||||||
total: Int!
|
total: Int!
|
||||||
}
|
}
|
||||||
|
type TorrentStats {
|
||||||
|
bytesWritten: Int!
|
||||||
|
bytesWrittenData: Int!
|
||||||
|
bytesRead: Int!
|
||||||
|
bytesReadData: Int!
|
||||||
|
bytesReadUsefulData: Int!
|
||||||
|
bytesReadUsefulIntendedData: Int!
|
||||||
|
chunksWritten: Int!
|
||||||
|
chunksRead: Int!
|
||||||
|
chunksReadUseful: Int!
|
||||||
|
chunksReadWasted: Int!
|
||||||
|
metadataChunksRead: Int!
|
||||||
|
piecesDirtiedGood: Int!
|
||||||
|
piecesDirtiedBad: Int!
|
||||||
|
}
|
||||||
input TorrentsFilter {
|
input TorrentsFilter {
|
||||||
infohash: StringFilter
|
infohash: StringFilter
|
||||||
name: StringFilter
|
name: StringFilter
|
||||||
|
|
Loading…
Reference in a new issue