Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Cap concurrent calls to PESU Academy and add a circuit breaker

Aperta
#183 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
35/100
Tipo di issue
Funzionalità
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
fastapi, openapi, python

Direzione di ricerca

Read CONTRIBUTING.md first, then inspect _upstream_call and authenticate in app/pesu.py, plus the existing exception and metric patterns. Define and test the breaker in app/resilience.py with an injected clock, then cover bulkhead and refresh behavior before updating README and app/docs/authenticate.py; tests/unit/test_openapi_docs.py must pass and the live credentialed suite must succeed.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

api enhancement pesuacademy

🔄 Updated for v5.0.0 (2026-10-08). pesu-auth now signs in through PESU Academy's mobile API (#241): a login is at most two upstream calls (login and profile_fetch), and the CSRF prefetch and refresh loop are gone. The bulkhead and breaker are still wanted; the parts that relied on background calls (the refresh as a free health probe) are removed, and what counts as a failure is restated for the new responses.

⚠️ PES University students only. This change sits in the login path and has to be tested end to end against PESU Academy with real PESU credentials (see "Tests that need credentials" in CONTRIBUTING.md). Run the whole suite with your credentials in .env and paste the summary line into your PR. Every run makes real logins to PESU Academy, so run it when you need it, not in a loop.

Summary

When PESU Academy is slow or down, pesu-auth keeps sending it every request, and every caller waits for the full timeout before failing. Add two standard resilience patterns around the upstream calls:

  1. A bulkhead: a cap on how many logins talk to PESU Academy at once, so a traffic spike can't pile up hundreds of slow requests (and hundreds of open clients) in one process.
  2. A circuit breaker: after repeated upstream failures, fail fast with 503 and Retry-After for a short cool-down instead of waiting on a service that is known to be down, then let a trial request through to check whether it has recovered.

Background

  • Every upstream request goes through _upstream_call (app/pesu.py), which already classifies each call as success, error or cancelled. It treats "no exception" as success, so a 5xx response counts as success there; see #170 for the same distinction.
  • The client timeout is 10 s per call (UPSTREAM_TIMEOUT_SECONDS in app/pesu.py), and a login makes up to 2 calls: login, then profile_fetch when the profile is requested. Each login gets its own httpx2.AsyncClient, closed before it returns.
  • pesu-auth is one process on Render's free tier, so all state here is in memory and per process. Say so in comments.

Proposed change

Bulkhead
  • An asyncio.Semaphore in PESUAcademy, sized by a constant (for example 20) with a comment explaining the choice, acquired for the whole of authenticate.
  • If no permit is free within a short wait (for example 2 s), fail with 503 and Retry-After, rather than queueing indefinitely.
  • Since 5.0.0 there is no background work (the CSRF prefetch and refresh loop are gone), so every upstream call belongs to a request and goes through the bulkhead.
Circuit breaker
  • States: closed (normal), open (fail fast), and half-open (one trial request).
  • Opening: N consecutive upstream failures (for example 5) open the circuit for a cool-down (for example 30 s). A failure is an exception or a 5xx response from either operation (login, profile_fetch). A 4xx and a wrong password (PESU's 401) are not failures; cancelled is ignored. Neither is an HTTP 200 carrying PESU's error envelope, such as the profile call's {"status": 403, "message": "Access Denied"} for a non-student account: PESU answered.
  • While open, authenticate raises a new PESUAcademyError subclass straight away (status 503, header Retry-After: <remaining seconds>, message "PESU Academy is currently unavailable. Please try again later."). Put the header on the exception like MetricsAuthorizationError does, and the existing handler will pass it through.
  • Half-open: after the cool-down, exactly one request is let through. A success closes the circuit; a failure opens it again.
  • Nothing calls PESU in the background any more, so while the circuit is open nothing probes it; the half-open trial request is the only probe. Say so in a comment.
  • Keep the breaker in a small class of its own (for example in app/resilience.py), with its own unit tests, and inject a clock so the tests don't sleep.
Observability and docs
  • A gauge for breaker state (0 closed, 1 half-open, 2 open) and a counter of rejections, following the add-metric skill. The exception class already lands in errors_total{type}.
  • The README's /authenticate responses table gets a 503 row, and app/docs/authenticate.py gets a 503 example (tests/unit/test_openapi_docs.py enforces that the examples match).

Tests

  • The breaker class: opens after N failures, rejects while open, lets exactly one trial through when half-open, closes on success, re-opens on failure; 4xx and cancellation don't count.
  • authenticate returns 503 with Retry-After while open.
  • The bulkhead rejects with 503 when saturated, and releases permits on every path, including exceptions and cancellation.
  • A wrong password (401) and an HTTP 200 error envelope never count as failures.
  • The live suite passes.

Acceptance criteria

  • With PESU down, callers get a fast 503 with Retry-After instead of waiting 10–20 s
  • The service recovers on its own when PESU does
  • No more than the configured number of logins talk to PESU at once
  • Metrics, README and OpenAPI examples are updated

Related

  • #184 (504 for timeouts; since 5.0.0 a transport error is already a 502, UpstreamError or ProfileFetchError) and #185 (timeouts) define the per-request failures this breaker counts.
  • #170 records when PESU last succeeded and failed; share the definition of "failure".

How to contribute

  • Wait until you're assigned. Comment to ask for this issue, then wait for a maintainer to assign it to you before writing any code. PRs for unassigned issues are closed without review.
  • Read CONTRIBUTING.md first. It covers forking, setting up with uv, and running the tests.
  • Branch from dev in your fork, and open the PR against dev with a Conventional Commit title and Closes #183 in the description.
  • Bump the minor version in pyproject.toml and run uv lock (CI checks this). uv run pre-commit run --all-files must pass, and coverage must stay at 100%.
  • Update the README, app/docs/ and comments in the same PR as the behaviour they describe.
  • Never log, print, store or echo a password or token, including in tests, PR descriptions and screenshots.
Lingua principale
Python
Stelle
22
Fork
29
Merge medio
1g 3h
PR unite (30g)
19

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di pesu-dev/auth

Tutte le issue di pesu-dev/auth

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.