Skip to content

cache: background stale-while-revalidate subrequest fails for HTTP/2 downstream sessions #1033

Open

Description

@stillnovice

What is the problem your feature solves, or the need that your feature fulfills?

When a proxy enables stale-while-revalidate via ProxyHttp::should_serve_stale(..., None), a downstream HTTP/2 request that holds the cache write lock spawns a background subrequest that always fails:

  1. SubrequestSpawner::spawn_background_subrequest serializes the downstream request as HTTP/1 text via to_h1_raw(). For an HTTP/2 session, pseudo_raw_h1_request_header writes HTTP/2 as the version in the request line, and httparse rejects it with Error::Version.
  2. process_subrequest then hits the None => return, // bad request branch, so cache_write_lock() is never taken — the write permit is dropped while still held.

Observed with pingora 0.9.0; main has the same code:

  • debug builds panic with debug_assert!: Dangling cache lock started! (pingora-cache lock drop);
  • release builds release the permit as Dangling, the origin never sees the revalidation, and every subsequent stale request on the key repeats the failure.

HTTP/1.1 downstream sessions work correctly: stale response served immediately, background subrequest refreshes the entry.

Describe the solution

Build the background subrequest from a synthesized HTTP/1.1-compatible request line (e.g. force HTTP/1.1 version in pseudo_raw_h1_request_header when producing the raw subrequest header), or parse subrequests without requiring a valid h1 version field.

Describe alternatives you have considered

Proxy-side workaround: in should_serve_stale, return false when session.is_http2() && session.cache.is_cache_lock_writer(), so the HTTP/2 lock holder revalidates in the foreground while other requests still get served stale. This is what downstreams (e.g. vicanso/pingap#233) currently do.

Additional context

Found while testing RFC 5861 support in a pingora-based reverse proxy: vicanso/pingap#233

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions