R-1271 (P2): „use my kept data” never takes a copy dated in the future — a copy more than 10 minutes past the box's clock never counts as the newest (the newest true date wins, tie local); alone it is still offered, said and not recommended. Red-proved. Commit only.
gates / gates (push) Successful in 1m33s
gates / gates (push) Successful in 1m33s
This commit is contained in:
@@ -44,6 +44,9 @@ type KeptCopy struct {
|
||||
// SnapshotID and UnitPath name the off-site copy (Tier 3 only): the snapshot and the unit's path in it.
|
||||
SnapshotID string
|
||||
UnitPath string
|
||||
// Future (R-1271): the copy's date lies past this box's clock (+ KeptClockSkew) — chosen only when it is the only
|
||||
// copy, and the question then says so and does not recommend it.
|
||||
Future bool
|
||||
}
|
||||
|
||||
// KeptTierOffsite is KeptCopy.Tier for the off-site copy.
|
||||
@@ -112,11 +115,10 @@ func (m *Manager) KeptDBCopy(app, drive string) (KeptCopy, bool) {
|
||||
if app == "" || m.isStackDeployed(app) {
|
||||
return KeptCopy{}, false
|
||||
}
|
||||
var best KeptCopy
|
||||
found := false
|
||||
var cands []KeptCopy
|
||||
consider := func(c KeptCopy, ok bool) {
|
||||
if ok && (!found || c.Time.After(best.Time)) {
|
||||
best, found = c, true
|
||||
if ok {
|
||||
cands = append(cands, c)
|
||||
}
|
||||
}
|
||||
if u, ok := m.RemovedAppUnitFor(app); ok {
|
||||
@@ -127,7 +129,7 @@ func (m *Manager) KeptDBCopy(app, drive string) (KeptCopy, bool) {
|
||||
for _, d := range m.Tier2MirrorDirsForApp(app) {
|
||||
consider(m.KeptCopyAt(tier2UnitDir(d), drive, 2))
|
||||
}
|
||||
return best, found
|
||||
return keptPick(time.Now(), cands...) // R-1271: the newest TRUE date; own unit before the second drive on a tie
|
||||
}
|
||||
|
||||
// KeptOffsiteCopies is the off-site copy kept files of each app could be loaded from: the app's newest
|
||||
@@ -174,21 +176,71 @@ func (m *Manager) KeptOffsiteCopy(ctx context.Context, app string) (KeptCopy, bo
|
||||
return c, ok
|
||||
}
|
||||
|
||||
// KeptNewer chooses between a local copy and the off-site copy: the off-site one only when it is NEWER or
|
||||
// the only one (a tie goes local — no download).
|
||||
func KeptNewer(local KeptCopy, lok bool, off KeptCopy, ook bool) (KeptCopy, bool) {
|
||||
if ook && (!lok || off.Time.After(local.Time)) {
|
||||
return off, true
|
||||
// KeptNewerAt chooses between a local copy and the off-site copy at now: the off-site one only when it is NEWER by a
|
||||
// true date, or the only one (a tie goes local — no download). R-1271: a future-dated copy never counts as newer.
|
||||
func KeptNewerAt(now time.Time, local KeptCopy, lok bool, off KeptCopy, ook bool) (KeptCopy, bool) {
|
||||
var cands []KeptCopy
|
||||
if lok {
|
||||
cands = append(cands, local)
|
||||
}
|
||||
return local, lok
|
||||
if ook {
|
||||
cands = append(cands, off)
|
||||
}
|
||||
return keptPick(now, cands...)
|
||||
}
|
||||
|
||||
// KeptBestCopy is the copy „Use my kept data" and Load use for app's kept files on drive: the newest
|
||||
// usable local copy (KeptDBCopy), or the off-site copy when it is newer or the only one.
|
||||
// usable local copy (KeptDBCopy), or the off-site copy when it is newer by a true date or the only one.
|
||||
func (m *Manager) KeptBestCopy(ctx context.Context, app, drive string) (KeptCopy, bool) {
|
||||
local, lok := m.KeptDBCopy(app, drive)
|
||||
off, ook := m.KeptOffsiteCopy(ctx, app)
|
||||
return KeptNewer(local, lok, off, ook)
|
||||
return KeptNewerAt(time.Now(), local, lok, off, ook)
|
||||
}
|
||||
|
||||
// ── R-1271 (brief 2026-10-25 Part 4): a copy dated in the future is never „the newest" ────────────────────────────
|
||||
//
|
||||
// MEASURED (catalog sweep 2026-10-24, Tester4: Paperless-ngx, Nextcloud, Immich): the off-site store held copies of the
|
||||
// 2026-10-20 rehearsal box, whose clock had been set to 2026-10-28. The off-site copy's date is the snapshot time that
|
||||
// OTHER box wrote — this box recorded no data time to cap it (offsiteDataTime) — so „newer than every local copy" took
|
||||
// it over the family's own copy of the same evening, and „Use my kept data" loaded it. The `07` §6.5 „taken of another
|
||||
// drive" check (KeptCopyAt) compares the unit's HDD_PATH, a mount PATH with the same name on every box: a copy of
|
||||
// another box's /mnt/felhom-drives/hdd_1 matches this box's. It is a path check, not a physical identity, and it stays
|
||||
// one — loading the old box's data on a new box is what off-site exists for.
|
||||
//
|
||||
// THE RULE. A copy dated more than KeptClockSkew past now is FUTURE-DATED and never counts as the newest: the newest
|
||||
// copy with a true date wins (a tie goes to the earlier candidate — local before off-site, own unit before the second
|
||||
// drive). A future-dated copy is chosen only when no other exists; it is then marked Future, and the question says its
|
||||
// date is in the future and does not recommend it. Pinned by kept_pick_test.go.
|
||||
|
||||
// KeptClockSkew is how far a copy's date may lie past this box's clock and still count as a true date: NTP keeps a box
|
||||
// within a second; ten minutes covers a box whose clock synced late after a power cut.
|
||||
const KeptClockSkew = 10 * time.Minute
|
||||
|
||||
// KeptFuture reports whether c's date lies more than KeptClockSkew past now.
|
||||
func KeptFuture(c KeptCopy, now time.Time) bool { return c.Time.After(now.Add(KeptClockSkew)) }
|
||||
|
||||
// keptPick: the newest candidate with a true date (the first one on a tie); only when every candidate is
|
||||
// future-dated, the first one, marked Future.
|
||||
func keptPick(now time.Time, cands ...KeptCopy) (KeptCopy, bool) {
|
||||
var best KeptCopy
|
||||
found := false
|
||||
for _, c := range cands {
|
||||
if KeptFuture(c, now) {
|
||||
continue
|
||||
}
|
||||
if !found || c.Time.After(best.Time) {
|
||||
best, found = c, true
|
||||
}
|
||||
}
|
||||
if found {
|
||||
return best, true
|
||||
}
|
||||
if len(cands) == 0 {
|
||||
return KeptCopy{}, false
|
||||
}
|
||||
c := cands[0]
|
||||
c.Future = true
|
||||
return c, true
|
||||
}
|
||||
|
||||
// Refusals of an off-site load, born as bundle keys. Each is raised BEFORE prepare and before anything
|
||||
|
||||
Reference in New Issue
Block a user