Monorepo for Tangled

appview/oauth: store and reuse the same app password session

We seem to be hammering bsky's PDS, which explains the 429s. Perhaps
re-using the session alleviates the pressure. It's hard to test this
locally since their rate limits vanish (different host/IP).

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>

authored by

Anirudh Oppiliappan and committed by
Seongmin Lee
7b28c489 2e78be8f

+43 -4
+38 -3
appview/oauth/handler.go
··· 6 "encoding/json" 7 "errors" 8 "fmt" 9 "net/http" 10 "slices" 11 "time" ··· 130 } 131 132 l.Debug("adding to default spindle") 133 - session, err := CreateAppPasswordSession(o.IdResolver, o.Config.Core.AppPassword, consts.TangledDid, o.Config.Core.RateLimitBypass) 134 if err != nil { 135 l.Error("failed to create session", "err", err) 136 return ··· 144 } 145 146 if err := session.putRecord(record, tangled.SpindleMemberNSID); err != nil { 147 l.Error("failed to add to default spindle", "err", err) 148 return 149 } ··· 169 } 170 171 l.Debug("adding to default knot") 172 - session, err := CreateAppPasswordSession(o.IdResolver, o.Config.Core.AppPassword, consts.TangledDid, o.Config.Core.RateLimitBypass) 173 if err != nil { 174 l.Error("failed to create session", "err", err) 175 return ··· 183 } 184 185 if err := session.putRecord(record, tangled.KnotMemberNSID); err != nil { 186 l.Error("failed to add to default knot", "err", err) 187 return 188 } ··· 248 PdsEndpoint string 249 Did string 250 RateLimitBypass string 251 } 252 253 - func CreateAppPasswordSession(res *idresolver.Resolver, appPassword, did, rateLimitBypass string) (*AppPasswordSession, error) { 254 if appPassword == "" { 255 return nil, fmt.Errorf("no app password configured") 256 } ··· 284 sessionReq.Header.Set("x-ratelimit-bypass", rateLimitBypass) 285 } 286 287 client := &http.Client{Timeout: 30 * time.Second} 288 sessionResp, err := client.Do(sessionReq) 289 if err != nil { ··· 303 session.PdsEndpoint = pdsEndpoint 304 session.Did = did 305 session.RateLimitBypass = rateLimitBypass 306 307 return &session, nil 308 } ··· 337 req.Header.Set("x-ratelimit-bypass", s.RateLimitBypass) 338 } 339 340 client := &http.Client{Timeout: 30 * time.Second} 341 resp, err := client.Do(req) 342 if err != nil { ··· 350 351 return nil 352 }
··· 6 "encoding/json" 7 "errors" 8 "fmt" 9 + "log/slog" 10 "net/http" 11 "slices" 12 "time" ··· 131 } 132 133 l.Debug("adding to default spindle") 134 + session, err := o.getAppPasswordSession() 135 if err != nil { 136 l.Error("failed to create session", "err", err) 137 return ··· 145 } 146 147 if err := session.putRecord(record, tangled.SpindleMemberNSID); err != nil { 148 + o.invalidateAppPasswordSession() 149 l.Error("failed to add to default spindle", "err", err) 150 return 151 } ··· 171 } 172 173 l.Debug("adding to default knot") 174 + session, err := o.getAppPasswordSession() 175 if err != nil { 176 l.Error("failed to create session", "err", err) 177 return ··· 185 } 186 187 if err := session.putRecord(record, tangled.KnotMemberNSID); err != nil { 188 + o.invalidateAppPasswordSession() 189 l.Error("failed to add to default knot", "err", err) 190 return 191 } ··· 251 PdsEndpoint string 252 Did string 253 RateLimitBypass string 254 + Logger *slog.Logger 255 } 256 257 + func CreateAppPasswordSession(res *idresolver.Resolver, appPassword, did, rateLimitBypass string, logger *slog.Logger) (*AppPasswordSession, error) { 258 if appPassword == "" { 259 return nil, fmt.Errorf("no app password configured") 260 } ··· 288 sessionReq.Header.Set("x-ratelimit-bypass", rateLimitBypass) 289 } 290 291 + logger.Debug("creating app password session", "url", sessionURL, "headers", sessionReq.Header) 292 + 293 client := &http.Client{Timeout: 30 * time.Second} 294 sessionResp, err := client.Do(sessionReq) 295 if err != nil { ··· 309 session.PdsEndpoint = pdsEndpoint 310 session.Did = did 311 session.RateLimitBypass = rateLimitBypass 312 + session.Logger = logger 313 314 return &session, nil 315 } ··· 344 req.Header.Set("x-ratelimit-bypass", s.RateLimitBypass) 345 } 346 347 + s.Logger.Debug("putting record", "url", url, "collection", collection, "headers", req.Header) 348 + 349 client := &http.Client{Timeout: 30 * time.Second} 350 resp, err := client.Do(req) 351 if err != nil { ··· 359 360 return nil 361 } 362 + 363 + // getAppPasswordSession returns a cached AppPasswordSession, creating one if needed. 364 + func (o *OAuth) getAppPasswordSession() (*AppPasswordSession, error) { 365 + o.appPasswordSessionMu.Lock() 366 + defer o.appPasswordSessionMu.Unlock() 367 + 368 + if o.appPasswordSession != nil { 369 + return o.appPasswordSession, nil 370 + } 371 + 372 + session, err := CreateAppPasswordSession(o.IdResolver, o.Config.Core.AppPassword, consts.TangledDid, o.Config.Core.RateLimitBypass, o.Logger) 373 + if err != nil { 374 + return nil, err 375 + } 376 + 377 + o.appPasswordSession = session 378 + return session, nil 379 + } 380 + 381 + // invalidateAppPasswordSession clears the cached session so the next call to 382 + // getAppPasswordSession will create a fresh one. 383 + func (o *OAuth) invalidateAppPasswordSession() { 384 + o.appPasswordSessionMu.Lock() 385 + defer o.appPasswordSessionMu.Unlock() 386 + o.appPasswordSession = nil 387 + }
+4
appview/oauth/oauth.go
··· 5 "fmt" 6 "log/slog" 7 "net/http" 8 "time" 9 10 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 33 Enforcer *rbac.Enforcer 34 IdResolver *idresolver.Resolver 35 Logger *slog.Logger 36 } 37 38 func New(config *config.Config, ph posthog.Client, db *db.DB, enforcer *rbac.Enforcer, res *idresolver.Resolver, logger *slog.Logger) (*OAuth, error) {
··· 5 "fmt" 6 "log/slog" 7 "net/http" 8 + "sync" 9 "time" 10 11 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 34 Enforcer *rbac.Enforcer 35 IdResolver *idresolver.Resolver 36 Logger *slog.Logger 37 + 38 + appPasswordSession *AppPasswordSession 39 + appPasswordSessionMu sync.Mutex 40 } 41 42 func New(config *config.Config, ph posthog.Client, db *db.DB, enforcer *rbac.Enforcer, res *idresolver.Resolver, logger *slog.Logger) (*OAuth, error) {
+1 -1
appview/state/state.go
··· 622 return 623 } 624 625 - session, err := oauth.CreateAppPasswordSession(res, config.Core.AppPassword, consts.TangledDid, config.Core.RateLimitBypass) 626 if err != nil { 627 logger.Error("failed to create appassword session... skipping fetch", "err", err) 628 return
··· 622 return 623 } 624 625 + session, err := oauth.CreateAppPasswordSession(res, config.Core.AppPassword, consts.TangledDid, config.Core.RateLimitBypass, logger) 626 if err != nil { 627 logger.Error("failed to create appassword session... skipping fetch", "err", err) 628 return