package oltp
import (
"errors"
"time"
)
// MirrorBackoff is the wait after the nth failed push in a row, for
// either kind of mirror: a minute, doubling, at most an hour. The
// runners decide with it and the settings page and the API report with
// it, so what a person is told is what the runner will do.
func MirrorBackoff(fails int) time.Duration {
d := time.Minute
for i := 1; i < fails && d < time.Hour; i++ {
d *= 2
}
return min(d, time.Hour)
}
// MirrorNextTry is when a mirror whose last attempt failed at errAt
// (the stored RFC 3339 stamp) is next tried, after fails failures in a
// row. False when there is no failure to wait out. The retry tick adds
// up to its own interval to this: it is the earliest, not a promise.
func MirrorNextTry(errAt string, fails int) (time.Time, bool) {
if errAt == "" {
return time.Time{}, false
}
failed, err := time.Parse(time.RFC3339, errAt)
if err != nil {
return time.Time{}, false
}
return failed.Add(MirrorBackoff(fails)), true
}
// FossilLoginRefusedWords is the stored failure when the Fossil server
// refused the login. It is named here so the runner that writes it and
// the page that answers it with a password field mean the same words.
const FossilLoginRefusedWords = "the server refused the login: check the user and password, and that the remote is a copy of this repository (the same project code)"
// mirrorRefusal is a setting the forge will not keep, naming the input
// it is about, so a page can mark that field and not only say a thing
// went wrong. It answers to ErrMirrorRefused and reads as it always did.
type mirrorRefusal struct{ field, why string }
func (e *mirrorRefusal) Error() string { return ErrMirrorRefused.Error() + ": " + e.why }
func (e *mirrorRefusal) Is(target error) bool { return target == ErrMirrorRefused }
// refuseMirror is a refusal about one input ("remote", "branch", "user",
// "password"), or about the setting as a whole when field is "".
func refuseMirror(field, why string) error { return &mirrorRefusal{field: field, why: why} }
// MirrorRefusalField is the input a refusal is about, "" when it is
// about none or is not a mirror refusal.
func MirrorRefusalField(err error) string {
var r *mirrorRefusal
if errors.As(err, &r) {
return r.field
}
return ""
}