Skip to content

Add runtime.graphql.cost options to configure GraphQL cost analysis - #3876

Open
Marcus Rangell (marcusrangell) wants to merge 4 commits into
Azure:mainfrom
marcusrangell:feature/graphql-cost-options
Open

Marcus Rangell (marcusrangell) wants to merge 4 commits into
Azure:mainfrom
marcusrangell:feature/graphql-cost-options

Conversation

@marcusrangell

Copy link
Copy Markdown

Why make this change?

What is this change?

Adds a cost section to runtime.graphql, configurable from the config file and from dab configure:

"graphql": {
  "cost": {
    "enabled": true,
    "max-field-cost": 5000,
    "max-type-cost": 10000
  }
}
  • enabled (default false): whether cost limits are enforced.
  • max-field-cost / max-type-cost: maximum allowed field / type cost of a request. Allowed values: 1 to 2147483647.

Behaviour to review

  • Default behaviour change. With no cost section, or enabled: false, DAB no longer enforces cost limits. Previously Hot Chocolate's built-in default limit was always enforced. This matches how depth-limit works (off unless configured), but it removes a protection that 2.x had, so I'd like maintainers to confirm this default.
  • enabled is explicit. Setting a maximum on its own does not turn enforcement on, in line with other sections such as runtime.cache.
  • Naming follows DAB's config conventions, not Hot Chocolate's. The switch is enabled (like runtime.cache, runtime.health and the other sections), not Hot Chocolate's EnforceCostLimits, and the limits are kebab-case max-* properties like max-page-size. This keeps the config consistent and independent of the engine's property names, which are mapped to Hot Chocolate in Startup.cs.
  • A maximum that isn't set uses Hot Chocolate's default when enabled is true. Setting only max-type-cost, for example, still applies the default field-cost limit.
  • The range is DAB's own (an int, like depth-limit). Hot Chocolate accepts larger values.
  • Configs with a released $schema URL fail validation until the schema is published. Validation (dab validate, and the validation done on hot reload) downloads the schema named in $schema. A config pointing at an already released schema, e.g. .../releases/download/v0.10.23/dab.draft.schema.json, doesn't know cost, so it is rejected, and on hot reload DAB keeps the previous configuration. This is the same for other options added after that release. It resolves once a release publishes the updated dab.draft.schema.json.

Implementation

  • GraphQLCostOptions record in Config/ObjectModel, exposed as GraphQLRuntimeOptions.Cost.
  • Dedicated GraphQLCostOptionsConverter, registered in RuntimeConfigLoader and used by the GraphQL options converter, the same way as MultipleMutationOptionsConverter.
  • Startup.AddGraphQLService applies the settings with Hot Chocolate's ModifyCostOptions, next to the existing depth-limit rule.
  • dab configure: --runtime.graphql.cost.enabled, --runtime.graphql.cost.max-field-cost, --runtime.graphql.cost.max-type-cost. Like the cache and health options, values below 1 are rejected and there is no way to remove a value from the CLI.
  • JSON schema (dab.draft.schema.json) and docs/design/dab-configure.md updated.

How was this tested?

  • Integration Tests

  • Unit Tests

  • Converter tests (round trip, defaults, enabled not implied by a maximum, all combinations of the three properties, invalid values).

  • dab configure tests (add, update, enabled not implied, all combinations, existing values kept, invalid limits rejected).

  • Host-level test against SQL Server: with enabled: true a low max-field-cost or max-type-cost rejects the request with HC0047, a high maximum allows it, and enabled: false or no cost section allows it.

  • Manually verified on a large real-world config: a low max-field-cost rejects a request that previously failed with the default limit, and a higher value lets it through.

  • Hot reload of the cost settings is not covered by a test and was not verified; a restart applies them reliably.

Sample Request(s)

CLI, setting all three options:

dab configure --runtime.graphql.cost.enabled true --runtime.graphql.cost.max-field-cost 5000 --runtime.graphql.cost.max-type-cost 10000

This writes the following to the config file:

"graphql": {
  "cost": {
    "enabled": true,
    "max-field-cost": 5000,
    "max-type-cost": 10000
  }
}

With enabled: true and a maximum below the request's estimated cost, GraphQL requests are rejected with HC0047 ("The maximum allowed field cost was exceeded").

Follow-up

  • Learn documentation for runtime.graphql.cost (configuration/runtime.md and command-line/dab-configure.md in MicrosoftDocs/data-api-builder-docs) should be done in a separate PR once this is released.

Adds a cost section to runtime.graphql (enabled, max-field-cost, max-type-cost), configurable in the config file and with dab configure. Cost limits are only enforced when enabled is true. Closes Azure#3748.
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI 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.

🟡 Changes recommended

Late-configured deployments ignore enabled cost limits, and the schema and converter disagree on accepted values.

3 open issues
What changed in this PR

Adds configurable GraphQL cost limits to DAB, addressing #3748 by allowing users to adjust or disable limits that reject valid queries with HC0047.

Changes:

  • Adds runtime.graphql.cost settings and corresponding dab configure options.
  • Maps settings to Hot Chocolate, making enforcement opt-in while preserving engine defaults for unspecified maximums.
  • Adds converter, CLI, and host-level tests, plus schema and CLI documentation updates.
File Description
src/​Service/​Startup.cs Applies cost settings to Hot Chocolate.
src/​Service.Tests/​UnitTests/​RuntimeOptionsConverterCoverageTests.cs Tests serialization, defaults, and invalid values.
src/​Service.Tests/​Configuration/​ConfigurationTests.cs Tests cost enforcement through the GraphQL endpoint.
src/​Config/​RuntimeConfigLoader.cs Registers the cost-options converter.
src/​Config/​ObjectModel/​GraphQLRuntimeOptions.cs Exposes the optional cost configuration.
src/​Config/​ObjectModel/​GraphQLCostOptions.cs Defines enforcement and maximum-cost settings.
src/​Config/​Converters/​GraphQLRuntimeOptionsConverterFactory.cs Reads and writes the nested cost section.
src/​Config/​Converters/​GraphQLCostOptionsConverter.cs Converts and validates cost settings.
src/​Cli/​ConfigGenerator.cs Updates cost settings while preserving unspecified values.
src/​Cli/​Commands/​ConfigureOptions.cs Declares the three cost-related CLI options.
src/​Cli.Tests/​ConfigureOptionsTests.cs Tests CLI updates, defaults, and invalid limits.
schemas/​dab.draft.schema.json Defines the cost configuration schema.
docs/​design/​dab-configure.md Lists the new CLI options.

Review effort: Balanced

Comment thread src/Service/Startup.cs Outdated
Comment thread schemas/dab.draft.schema.json
Comment thread src/Config/Converters/GraphQLCostOptionsConverter.cs Outdated
@marcusrangell

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: 2.0 rejects filter-variable queries with HC0047; Hot Chocolate cost limits are not configurable

2 participants