Skip to main content

Crate github_graphql_node_count

Crate github_graphql_node_count 

Source
Expand description

Compute, offline, the two numbers GitHub’s GraphQL API charges a query — the worst-case node count it may return and the rate-limit points one call of it spends — from the query text and its page-size variable bindings alone, before the query is ever sent.

§Two numbers, two limits

GitHub meters two different numbers against two different limits. Both are computed here, from one traversal of the document, and confusing them is the mistake this section exists to prevent.

what it countswhat it is limited againstcomputed by
nodeCountthe maximum number of nodes one query may returnNODE_LIMIT, per querynode_count
costthe rate-limit points one call spendsan hourly budget, per credential, across everything that credential doespoint_cost

They are not two views of one quantity. A cheap query run in a loop exhausts the hourly budget without ever approaching NODE_LIMIT; a single enormous query is rejected outright while costing a handful of points. So a consumer gating on one is not gating on the other, and the two are never renamed into each other here.

§The rules, and where they come from

GitHub publishes both at https://docs.github.com/en/graphql/overview/rate-limits-and-node-limits-for-the-graphql-api.

For the node count: it multiplies down a nested path, sums across sibling paths, every connection supplies a first or a last inside 1..=100, and the limit one query may not reach is 500,000.

For the points: add up the number of requests needed to fulfil each unique connection in the call, assuming every request reaches its page-size limit; divide that aggregate by 100 and round to the nearest whole number; and never answer below GitHub’s stated minimum of 1. A connection is resolved once per parent node, so the requests it needs are the product of the page sizes strictly above it — which is the same quantity the node count multiplies by that connection’s own page size, and why one walk answers both.

Check this crate against that page rather than against our confidence.

§No schema, by design

node_count and point_cost are handed document text and nothing else. They reach no network, read no credential, and consult no GraphQL schema — so they cannot know which fields are connections by type. Instead:

  • a field carrying a first: or a last: argument is a connection: it multiplies the count beneath it, and it is one of the connections whose requests are aggregated;
  • every other field contributes no multiplier, no nodes and no requests of its own.

That is what makes this crate runnable in a fork pull request with no secret, which is the whole reason a consumer can put it in a gate. The price is one blind spot, stated plainly and applying to both answers: a connection that supplies neither first nor last is invalid to GitHub and invisible here. Such a field is treated as an ordinary field, so both the node count and the point cost are an undercount rather than an error. A field supplying both is also invalid to GitHub; the larger of the two is used, so both answers stay a worst case.

§Example

use github_graphql_node_count::{
    node_count, point_cost, NodeCountError, Variables, NODE_LIMIT,
};

let document = r#"
    query($repos: Int!) {
      viewer {
        repositories(first: $repos) {
          edges { node { name issues(first: 10) { edges { node { title } } } } }
        }
      }
    }
"#;
let variables = Variables::from([("repos".to_string(), 50)]);

// 50 repositories + 50 x 10 issues.
assert_eq!(node_count(document, &variables)?, 550);
assert!(node_count(document, &variables)? < NODE_LIMIT);

// The same document against the other limit: `repositories` is resolved
// once and `issues` fifty times, so 51 requests round to one point.
assert_eq!(point_cost(document, &variables)?, 1);

// A page size GitHub would reject comes back as an error, not a number.
let over = Variables::from([("repos".to_string(), 500)]);
assert!(matches!(
    node_count(document, &over),
    Err(NodeCountError::PageSizeOutOfRange { .. })
));

Structs§

Position
Where in the document a problem was found, rendered as line:column.

Enums§

NodeCountError
Why a document’s node count could not be computed.
PageSizeArgument
Which argument a connection took its page size from.

Constants§

NODE_LIMIT
GitHub’s published limit on the number of nodes one query may return.

Functions§

node_count
The worst-case number of nodes the one operation in document may return, under variables, computed by GitHub’s published rules.
point_aggregate
The aggregate GitHub’s step 1 produces for one call of the one operation in document, under variables: the number of requests needed to fulfil every unique connection in the call, assuming each reaches its page-size limit.
point_cost
The rate-limit points one call of the one operation in document spends, under variables, computed by GitHub’s published rules.

Type Aliases§

Variables
The integer bound each page-size variable a document names is given, keyed by variable name without the leading $.