Skip to content

feat(core): add operation-level sample rates to the usage plugin#8181

Open
ChaoyangS wants to merge 2 commits into
graphql-hive:mainfrom
ChaoyangS:feat/operation-level-sample-rates
Open

feat(core): add operation-level sample rates to the usage plugin#8181
ChaoyangS wants to merge 2 commits into
graphql-hive:mainfrom
ChaoyangS:feat/operation-level-sample-rates

Conversation

@ChaoyangS

Copy link
Copy Markdown

Background

Hive's usage plugin currently supports a single global sampleRate, applied
uniformly to every operation. For systems that mix very high-volume and
low-volume operations this forces a bad trade-off: a low global rate to control
quota cost causes low-volume operations to be missed entirely, while a high rate
to keep visibility into rare operations overpays by reporting all the
high-volume traffic.

This PR adds operation-level sample rates so high-volume operations can be
sampled aggressively while low-volume operations stay at (or near) 100%
visibility.

Resolves #3556.

Description

Adds a new declarative sampleRates option to HiveUsagePluginOptions in
@graphql-hive/core (the client/agent library used by the integrations).

  • packages/libraries/core/src/client/types.ts — new OperationSampleRate
    type (name or regex + sampleRate) and the sampleRates?: OperationSampleRate[]
    option.
  • packages/libraries/core/src/client/sampling.ts — new operationSampling()
    factory returning a shouldInclude(context) function: first matching rule wins,
    unmatched operations fall back to the global sampleRate. Rules are validated
    eagerly (exactly one of name/regex; rate within 0..1).
  • packages/libraries/core/src/client/usage.ts — wires sampleRates into the
    existing shouldInclude selection, preserving precedence:
    exclude > sampler > sampleRates > global sampleRate. Fully backward
    compatible (option is optional; default behavior unchanged).
  • Tests: unit tests (tests/sampling.spec.ts) and an end-to-end usage test
    (tests/usage.spec.ts), plus a runnable demo (demo/operation-sample-rates.ts).
  • Changeset: included (@graphql-hive/core, minor).
usage: {
  sampleRate: 1.0, // fallback for unmatched operations
  sampleRates: [
    { name: 'SampledQuery', sampleRate: 0.1 },
    { regex: /^HighVolume/, sampleRate: 0.1 },
  ],
}
Note: the issue's design sketch targets the Apollo Router (Rust) config block
operation_sample_rates; this PR implements the underlying capability in the
shared core library that the integrations build on.

Checklist
 Input validation  sample rates and rule shapes are validated eagerly, throwing clear errors on misconfiguration (out-of-range rate, missing or ambiguous name/regex).
 Error handling and logging  validation fails fast at setup; the existing per-operation error handling/logging in the usage collector is unchanged.
 Testing  added unit + integration tests and a runnable demo; typecheck passes and the core suite is green.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces operation-level sample rates to the usage plugin via the new sampleRates option. The review feedback suggests avoiding the non-null assertion operator (!) in sampling.ts by destructuring properties and leveraging TypeScript's control flow analysis to narrow the type of regex automatically.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +32 to +53
const rules = config.rates.map(rate => {
const hasName = typeof rate.name === 'string';
const hasRegex = rate.regex instanceof RegExp;

if (hasName === hasRegex) {
throw new Error(
'Expected usage.sampleRates entry to define exactly one of "name" or "regex".',
);
}

if (rate.sampleRate > 1 || rate.sampleRate < 0) {
throw new Error(
`Expected usage.sampleRates sampleRate to be 0 <= x <= 1, received ${rate.sampleRate}`,
);
}

const matches = hasRegex
? (operationName: string) => rate.regex!.test(operationName)
: (operationName: string) => operationName === rate.name;

return { matches, sampleRate: rate.sampleRate };
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Avoid using the non-null assertion operator (!) outside of test files, as it can lead to subtle bugs. We can destructure the properties from rate and let TypeScript's control flow analysis narrow the type of regex automatically when checking regex instanceof RegExp.

  const rules = config.rates.map(rate => {
    const { name, regex, sampleRate } = rate;
    const hasName = typeof name === 'string';
    const hasRegex = regex instanceof RegExp;

    if (hasName === hasRegex) {
      throw new Error(
        'Expected usage.sampleRates entry to define exactly one of "name" or "regex".',
      );
    }

    if (sampleRate > 1 || sampleRate < 0) {
      throw new Error(
        'Expected usage.sampleRates sampleRate to be 0 <= x <= 1, received ' + sampleRate,
      );
    }

    const matches = regex instanceof RegExp
      ? (operationName: string) => regex.test(operationName)
      : (operationName: string) => operationName === name;

    return { matches, sampleRate };
  });
References
  1. Avoid using the non-null assertion operator (!) outside of test files, as it can lead to subtle bugs.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed.

@ChaoyangS ChaoyangS marked this pull request as ready for review June 29, 2026 02:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Add support for operation-level sample rates

1 participant