R-1261: the line hold holds every running seeder, drops the record of an app removed while held, and puts back the qBittorrent slot it held (alternative speed). Red-proved. Commit only.
gates / gates (push) Successful in 1m29s

This commit is contained in:
2026-10-25 20:38:28 +01:00
parent fe9837708a
commit a79a292d72
7 changed files with 475 additions and 88 deletions
+14 -10
View File
@@ -14,8 +14,10 @@ import (
type State struct {
Line []LineRecord `json:"line,omitempty"`
QBLimit *QBLimitState `json:"qb_limit,omitempty"`
// Hold is a torrent app held low for a line test on request (R-1250, linehold.go); nil when none.
// Hold is the ONE held app of controller 0.343.0 (R-1250); read for a record that version left, never written.
Hold *LineHold `json:"line_test_hold,omitempty"`
// Holds are the torrent apps held low for a line test on request — every running seeder (R-1261, linehold.go).
Holds []LineHold `json:"line_test_holds,omitempty"`
}
// Store is the settings half the service needs (settings.Settings satisfies it).
@@ -46,14 +48,16 @@ type Service struct {
// to ep0 inside the tunnel. nil or ok=false = not measured. Read every 15 minutes (RefreshMeasured), never per
// minute.
Measured func(ctx context.Context) (mbps float64, at time.Time, ok bool)
// The line test for a box that seeds all day (R-1250, linehold.go). Torrents are the apps that can be held, in
// order of preference; TorrentRunning says whether one runs now; LineWaiting is the agent's waiting_for_quiet;
// LineRun is its test on request. Any nil = no hold.
Torrents []Holdable
TorrentRunning func(stack string) bool
LineWaiting func(ctx context.Context) bool
LineRun func(ctx context.Context) (LineRunResult, error)
Log *log.Logger
// The line test for a box that seeds all day (R-1250, linehold.go). Torrents are the apps that can be held;
// TorrentRunning says whether one runs now; TorrentInstalled whether it is installed at all (a hold of an app
// removed while held is dropped — R-1261; nil = assume installed); LineWaiting is the agent's waiting_for_quiet;
// LineRun is its test on request. Any of Torrents/TorrentRunning/LineWaiting/LineRun nil = no hold.
Torrents []Holdable
TorrentRunning func(stack string) bool
TorrentInstalled func(stack string) bool
LineWaiting func(ctx context.Context) bool
LineRun func(ctx context.Context) (LineRunResult, error)
Log *log.Logger
mu sync.Mutex
measured float64
@@ -146,7 +150,7 @@ func (s *Service) QBLimitNow(ctx context.Context) {
s.RefreshMeasured(ctx)
line, known := s.MeasuredLine()
cur := decode(s.Store.GetUpwatch())
if cur.Hold != nil {
if len(heldList(cur)) > 0 {
// A line-test hold is in place or was left behind: qBittorrent holds the test value, which the rule would
// read as the household's change and then never touch the limit again (linehold.go).
logx.Debugf(s.Log, "[upwatch] qBittorrent limit: a line-test hold record exists — rule skipped until it is cleared")