Expand description
Conditional GETs: ask GitHub whether a listing changed before paying for
it again.
§Why
A demand poll re-reads the same few listings every interval, and on an idle host they are the same bytes every time. GitHub’s REST documentation (“Best practices for using the REST API”, Use conditional requests if appropriate, https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api#use-conditional-requests-if-appropriate):
Most endpoints return an
etagheader, and many endpoints return alast-modifiedheader. You can use the values of these headers to make conditionalGETrequests. … If the data has not changed, you will receive a304 Not Modifiedresponse.Making a conditional request does not count against your primary rate limit if a
304response is returned and the request was made while correctly authorized with anAuthorizationheader.
So ConditionalCache keeps each listing’s validators and body, sends the
validators back, and on 304 hands the caller the body it already has. The
caller cannot tell the two apart except through Fetched::not_modified,
which is the point: parsing is unchanged, and an idle poll costs nothing
against the hourly quota.
§What was measured before this was relied on
Against api.github.com, with gh api -i, read-only:
GET /repos/{o}/{r}/actions/runs?status=queued|in_progress&per_page=100,GET …/actions/runs/{id}/jobs?filter=latest&per_page=100andGET /repos/{o}/{r}/actions/runners?per_page=100each return a weakETag(W/"…") andCache-Control: private, max-age=60.- For an unchanged listing the
ETagis stable. Five listings were asked twelve times, ten seconds apart: an idle repository’s queued and in-progress runs, a queued-runs listing holding two stuck runs, one of those runs’ jobs, and a repository’s runners. All sixty answers were304, one distinctETagper listing. - A listing that is changing is not: while CI ran, the jobs of an in-progress run changed on 11 of 18 polls ten seconds apart, and the in-progress runs listing on 1 of 18. That is why an active target keeps the longer active interval.
- A
304carries the account’sx-ratelimit-*headers, and twenty304s in a row leftx-ratelimit-usedunchanged apart from other clients’ traffic on the same account — consistent with the documented exemption. - The
ETagdiffers between credentials for the same listing (the response varies onAuthorization), so it is cached per client and is never shared between machines. After a token renewal the first poll of each listing may be a full200; that is one request per listing per eight hours.
§Bounded
A job listing’s URL names a run id, so a busy repository mints new keys all
day. Entries unused for IDLE_ENTRY_TTL are dropped, and the map never
holds more than MAX_ENTRIES.
Structs§
- Conditional
Cache - Validators and bodies of the listings one client polls.
- Fetched
- One response, and whether GitHub said it was unchanged.
Constants§
- IDLE_
ENTRY_ TTL - How long an entry nobody asked for is kept.
- MAX_
ENTRIES - The most entries kept. A ceiling against a pathological number of runs, not a size anyone should reach: ten repositories with a handful of active runs each use a few dozen.