Retry/stall: игнорировать last_activity из будущего (клэмп)

Наблюдение из ревью кластера B: если qBittorrent отдаёт last_activity
впереди now (перекос часов или sentinel «никогда не был активен»),
stallDuration уходил в минус и реально застрявший торрент никогда не
помечался stuck. Теперь значение из будущего трактуется как непригодное
и простой считается от базиса добавления (addedBasis), как при
отсутствующем last_activity. Поведение спеки не меняется — оборонительная
деталь реализации.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-07-08 17:35:33 +03:00
co-authored by Claude Opus 4.8
parent 4cc4de4269
commit 78c61605fd
2 changed files with 29 additions and 4 deletions
+8 -4
View File
@@ -626,12 +626,16 @@ func (w *Worker) torrentAge(d store.Download, t qbt.Torrent) (time.Duration, boo
// stallDuration — длительность простоя торрента для stuck_after: от
// last_activity qBittorrent (момент последнего движения данных), с фолбэком на
// базис добавления, если qBit не отдал last_activity. Базис приподнят до
// retried_at (ручной retry даёт свежее окно). ok=false — базис неизвестен.
// базис добавления, если qBit не отдал пригодного last_activity. Базис приподнят
// до retried_at (ручной retry даёт свежее окно). ok=false — базис неизвестен.
//
// last_activity в будущем (перекос часов, sentinel «никогда не был активен»)
// трактуем как непригодное значение и падаем на addedBasis: иначе простой вышел
// бы отрицательным и реально застрявший торрент никогда бы не пометился stuck.
func (w *Worker) stallDuration(d store.Download, t qbt.Torrent) (time.Duration, bool) {
var basis time.Time
if t.LastActivity > 0 {
basis = time.Unix(t.LastActivity, 0).UTC()
if la := time.Unix(t.LastActivity, 0).UTC(); t.LastActivity > 0 && !la.After(w.now()) {
basis = la
} else {
var ok bool
if basis, ok = w.addedBasis(d, t); !ok {