this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Cache failed handle resolutions with exponentially increasing TTLs (#300)

authored by

Whyrusleeping and committed by
GitHub
e4f0da26 f934d626

+62 -4
+54 -2
api/extra.go
··· 14 14 15 15 "github.com/bluesky-social/indigo/did" 16 16 "github.com/bluesky-social/indigo/xrpc" 17 + arc "github.com/hashicorp/golang-lru/arc/v2" 17 18 logging "github.com/ipfs/go-log" 18 19 "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" 19 20 otel "go.opentelemetry.io/otel" ··· 75 76 ResolveHandleToDid(ctx context.Context, handle string) (string, error) 76 77 } 77 78 79 + type failCacheItem struct { 80 + err error 81 + count int 82 + expiresAt time.Time 83 + } 84 + 78 85 type ProdHandleResolver struct { 79 - ReqMod func(*http.Request, string) error 86 + ReqMod func(*http.Request, string) error 87 + FailCache *arc.ARCCache[string, *failCacheItem] 88 + } 89 + 90 + func NewProdHandleResolver(failureCacheSize int) (*ProdHandleResolver, error) { 91 + failureCache, err := arc.NewARC[string, *failCacheItem](failureCacheSize) 92 + if err != nil { 93 + return nil, err 94 + } 95 + 96 + return &ProdHandleResolver{ 97 + FailCache: failureCache, 98 + }, nil 80 99 } 81 100 82 101 func (dr *ProdHandleResolver) ResolveHandleToDid(ctx context.Context, handle string) (string, error) { ··· 86 105 ctx, span := otel.Tracer("resolver").Start(ctx, "ResolveHandleToDid") 87 106 defer span.End() 88 107 108 + var cachedFailureCount int 109 + 110 + if dr.FailCache != nil { 111 + if item, ok := dr.FailCache.Get(handle); ok { 112 + cachedFailureCount = item.count 113 + if item.expiresAt.After(time.Now()) { 114 + return "", item.err 115 + } 116 + dr.FailCache.Remove(handle) 117 + } 118 + } 119 + 89 120 var wkres, dnsres string 90 121 var wkerr, dnserr error 91 122 ··· 117 148 return wkres, nil 118 149 } 119 150 120 - return "", errors.Join(fmt.Errorf("no did record found for handle %q", handle), dnserr, wkerr) 151 + err := errors.Join(fmt.Errorf("no did record found for handle %q", handle), dnserr, wkerr) 152 + 153 + if dr.FailCache != nil { 154 + cachedFailureCount++ 155 + expireAt := time.Now().Add(time.Millisecond * 100) 156 + if cachedFailureCount > 1 { 157 + // exponential backoff 158 + expireAt = time.Now().Add(time.Millisecond * 100 * time.Duration(cachedFailureCount*cachedFailureCount)) 159 + // Clamp to one hour 160 + if expireAt.After(time.Now().Add(time.Hour)) { 161 + expireAt = time.Now().Add(time.Hour) 162 + } 163 + } 164 + 165 + dr.FailCache.Add(handle, &failCacheItem{ 166 + err: err, 167 + expiresAt: expireAt, 168 + count: cachedFailureCount, 169 + }) 170 + } 171 + 172 + return "", err 121 173 } 122 174 123 175 func (dr *ProdHandleResolver) resolveWellKnown(ctx context.Context, handle string) (string, error) {
+5 -2
cmd/bigsky/main.go
··· 297 297 blobstore = &blobs.DiskBlobStore{bsdir} 298 298 } 299 299 300 - prodHR := api.ProdHandleResolver{} 300 + prodHR, err := api.NewProdHandleResolver(100_000) 301 + if err != nil { 302 + return fmt.Errorf("failed to set up handle resolver: %w", err) 303 + } 301 304 if rlskip != "" { 302 305 prodHR.ReqMod = func(req *http.Request, host string) error { 303 306 if strings.HasSuffix(host, ".bsky.social") { ··· 307 310 } 308 311 } 309 312 310 - var hr api.HandleResolver = &prodHR 313 + var hr api.HandleResolver = prodHR 311 314 if cctx.StringSlice("handle-resolver-hosts") != nil { 312 315 hr = &api.TestHandleResolver{ 313 316 TrialHosts: cctx.StringSlice("handle-resolver-hosts"),
+1
go.mod
··· 12 12 github.com/gorilla/websocket v1.5.0 13 13 github.com/hashicorp/go-retryablehttp v0.7.2 14 14 github.com/hashicorp/golang-lru v0.5.4 15 + github.com/hashicorp/golang-lru/arc/v2 v2.0.6 15 16 github.com/hashicorp/golang-lru/v2 v2.0.6 16 17 github.com/icrowley/fake v0.0.0-20221112152111-d7b7e2276db2 17 18 github.com/ipfs/go-block-format v0.1.2
+2
go.sum
··· 235 235 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 236 236 github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= 237 237 github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 238 + github.com/hashicorp/golang-lru/arc/v2 v2.0.6 h1:4NU7uP5vSoK6TbaMj3NtY478TTAWLso/vL1gpNrInHg= 239 + github.com/hashicorp/golang-lru/arc/v2 v2.0.6/go.mod h1:cfdDIX05DWvYV6/shsxDfa/OVcRieOt+q4FnM8x+Xno= 238 240 github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM= 239 241 github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 240 242 github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ=