diff --git a/internal/transport/controller.go b/internal/transport/controller.go index 5a72871d8..4db8ee00e 100644 --- a/internal/transport/controller.go +++ b/internal/transport/controller.go @@ -51,7 +51,8 @@ type controller struct { fedDB federatingdb.DB clock pub.Clock client pub.HttpClient - cache cache.Cache[string, *transport] + trspCache cache.Cache[string, *transport] + badHosts cache.Cache[string, struct{}] userAgent string } @@ -66,13 +67,20 @@ func NewController(db db.DB, federatingDB federatingdb.DB, clock pub.Clock, clie fedDB: federatingDB, clock: clock, client: client, - cache: cache.New[string, *transport](), + trspCache: cache.New[string, *transport](), + badHosts: cache.New[string, struct{}](), userAgent: fmt.Sprintf("%s; %s (gofed/activity gotosocial-%s)", applicationName, host, version), } - // Transport cache has TTL=1hr freq=1m - c.cache.SetTTL(time.Hour, false) - if !c.cache.Start(time.Minute) { + // Transport cache has TTL=1hr freq=1min + c.trspCache.SetTTL(time.Hour, false) + if !c.trspCache.Start(time.Minute) { + log.Panic("failed to start transport controller cache") + } + + // Bad hosts cache has TTL=15min freq=1min + c.badHosts.SetTTL(15*time.Minute, false) + if !c.badHosts.Start(time.Minute) { log.Panic("failed to start transport controller cache") } @@ -89,7 +97,7 @@ func (c *controller) NewTransport(pubKeyID string, privkey *rsa.PrivateKey) (Tra pubStr := privkeyToPublicStr(privkey) // First check for cached transport - transp, ok := c.cache.Get(pubStr) + transp, ok := c.trspCache.Get(pubStr) if ok { return transp, nil } @@ -102,13 +110,13 @@ func (c *controller) NewTransport(pubKeyID string, privkey *rsa.PrivateKey) (Tra } // Cache this transport under pubkey - if !c.cache.Put(pubStr, transp) { + if !c.trspCache.Put(pubStr, transp) { var cached *transport - cached, ok = c.cache.Get(pubStr) + cached, ok = c.trspCache.Get(pubStr) if !ok { // Some ridiculous race cond. - c.cache.Set(pubStr, transp) + c.trspCache.Set(pubStr, transp) } else { // Use already cached transp = cached diff --git a/internal/transport/transport.go b/internal/transport/transport.go index 5af8b738e..94a0cc8ef 100644 --- a/internal/transport/transport.go +++ b/internal/transport/transport.go @@ -88,7 +88,23 @@ func (t *transport) POST(r *http.Request, body []byte, retryOn ...int) (*http.Re func (t *transport) do(r *http.Request, signer func(*http.Request) error, retryOn ...int) (*http.Response, error) { const maxRetries = 5 - backoff := time.Second * 2 + + var ( + // Initial backoff duration + backoff = 2 * time.Second + + // Get request hostname + host = r.URL.Hostname() + ) + + // Check if recently reached max retries for this host + // so we don't need to bother reattempting it. The only + // errors that are retried upon are server failure and + // domain resolution type errors, so this cached result + // indicates this server is likely having issues. + if t.controller.badHosts.Has(host) { + return nil, errors.New("too many failed attempts") + } // Start a log entry for this request l := log.WithFields(kv.Fields{ @@ -155,6 +171,9 @@ func (t *transport) do(r *http.Request, signer func(*http.Request) error, retryO } } + // Add "bad" entry for this host + t.controller.badHosts.Set(host, struct{}{}) + return nil, errors.New("transport reached max retries") }