-
Notifications
You must be signed in to change notification settings - Fork 11.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[GraphQL][RFC] Introduce UInt53 scalar #18552
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
3 Skipped Deployments
|
_ => None, | ||
}; | ||
} | ||
|
||
let GqlValue::Number(value) = &value?.node else { | ||
return None; | ||
}; | ||
value.as_u64() | ||
value.as_u64().map(|v| v as u32) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took me a second to wrap my head around this but I think it's fine because u64 maps to Int which on graphQL is capped at u32, so it shouldn't be possible for a graphQL client to provide > u32
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The query limits checker needs a deeper overhaul in any case so your confusion is warranted -- this should be clearer!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review!
## Description Whilst working on #18337, I noticed that we were over-using the `Int` scalar -- using it to represent values that could exceed 2^31 - 1 -- when the GraphQL spec states that `Int` must be a 32-bit signed integer. We made this decision at the time (a) because `async-graphql` allowed converting `u64`s to `Int` and we were primarily concerned with the fact that although JSON doesn't specify a precision for its numeric types, JS (among other languages), assumes it is an IEEE double-precision floating point number, so can only represent integral values precisely below 2^53. `cynic` (a Rust GraphQL client library) is (correctly) stricter, however, and maps an `Int` to an `i32`, always. There may be other similarly strict client libraries for other languages. This PR introduces a new scalar, `UInt`, that maps to a JSON number literal, just like `Int`, but allows us to ascribe our own meaning (in this case, it will be an unsigned number, and it can be as large as 2^53). This scalar has been used in many cases where we had previously used `Int`: sequence numbers, counts of objects, checkpoints, transactions, etc. While other uses continue to use `Int` (pagination limits, service limits, values bounded by the number of validators). In some cases, we have switched from `BigInt` to using this scalar notably: - the db cost estimate, which was previously a `BigInt` because we were unsure of its scale, but in hindsight, after benchmarking, it is unlikely that we would want to set a limit greater than 2^31 - 1. - the number of checkpoints in an epoch, as the number of transactions in an epoch (a number that is guaranteed to be greater) is being represented using an `Int` at the moment (and soon a `UInt`). This will be a breaking change, so will only go out with the new major version. Hopefully, this change will be minimal as the format of this scalar over the wire is the same as for `Int`, but it will require existing clients to register a new scalar in most cases. ## Test plan Existing tests: ``` sui-graphql-rpc$ cargo nextest run sui-graphql-e2e-tests$ cargo nextest run --features pg_integration ```
## Description Whilst working on MystenLabs#18337, I noticed that we were over-using the `Int` scalar -- using it to represent values that could exceed 2^31 - 1 -- when the GraphQL spec states that `Int` must be a 32-bit signed integer. We made this decision at the time (a) because `async-graphql` allowed converting `u64`s to `Int` and we were primarily concerned with the fact that although JSON doesn't specify a precision for its numeric types, JS (among other languages), assumes it is an IEEE double-precision floating point number, so can only represent integral values precisely below 2^53. `cynic` (a Rust GraphQL client library) is (correctly) stricter, however, and maps an `Int` to an `i32`, always. There may be other similarly strict client libraries for other languages. This PR introduces a new scalar, `UInt`, that maps to a JSON number literal, just like `Int`, but allows us to ascribe our own meaning (in this case, it will be an unsigned number, and it can be as large as 2^53). This scalar has been used in many cases where we had previously used `Int`: sequence numbers, counts of objects, checkpoints, transactions, etc. While other uses continue to use `Int` (pagination limits, service limits, values bounded by the number of validators). In some cases, we have switched from `BigInt` to using this scalar notably: - the db cost estimate, which was previously a `BigInt` because we were unsure of its scale, but in hindsight, after benchmarking, it is unlikely that we would want to set a limit greater than 2^31 - 1. - the number of checkpoints in an epoch, as the number of transactions in an epoch (a number that is guaranteed to be greater) is being represented using an `Int` at the moment (and soon a `UInt53`). This will be a breaking change, so will only go out with the new major version. Hopefully, this change will be minimal as the format of this scalar over the wire is the same as for `Int`, but it will require existing clients to register a new scalar in most cases. ## Test plan Existing tests: ``` sui-graphql-rpc$ cargo nextest run sui-graphql-e2e-tests$ cargo nextest run --features pg_integration ``` --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: Introduces a new scalar -- `UInt53` -- to represent unsigned 53 bit integer values. Some uses of `Int` in the existing schema have been replaced with `UInt53`. All clients will need to register the new scalar and clients for statically typed languages will also need to use a wider (e.g. 64 bit), unsigned type to hold the value. - [ ] CLI: - [ ] Rust SDK:
Description
Whilst working on #18337, I noticed that we were over-using the
Int
scalar -- using it to represent values that could exceed 2^31 - 1 -- when the GraphQL spec states thatInt
must be a 32-bit signed integer.We made this decision at the time (a) because
async-graphql
allowed convertingu64
s toInt
and we were primarily concerned with the fact that although JSON doesn't specify a precision for its numeric types, JS (among other languages), assumes it is an IEEE double-precision floating point number, so can only represent integral values precisely below 2^53.cynic
(a Rust GraphQL client library) is (correctly) stricter, however, and maps anInt
to ani32
, always. There may be other similarly strict client libraries for other languages.This PR introduces a new scalar,
UInt
, that maps to a JSON number literal, just likeInt
, but allows us to ascribe our own meaning (in this case, it will be an unsigned number, and it can be as large as 2^53).This scalar has been used in many cases where we had previously used
Int
: sequence numbers, counts of objects, checkpoints, transactions, etc. While other uses continue to useInt
(pagination limits, service limits, values bounded by the number of validators).In some cases, we have switched from
BigInt
to using this scalar notably:the db cost estimate, which was previously a
BigInt
because we were unsure of its scale, but in hindsight, after benchmarking, it is unlikely that we would want to set a limit greater than 2^31 - 1.the number of checkpoints in an epoch, as the number of transactions in an epoch (a number that is guaranteed to be greater) is being represented using an
Int
at the moment (and soon aUInt53
).This will be a breaking change, so will only go out with the new major version. Hopefully, this change will be minimal as the format of this scalar over the wire is the same as for
Int
, but it will require existing clients to register a new scalar in most cases.Test plan
Existing tests:
Release notes
Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required.
For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates.
UInt53
-- to represent unsigned 53 bit integer values. Some uses ofInt
in the existing schema have been replaced withUInt53
. All clients need to register the new scalar, and clients for statically typed languages also need to use a wider (64 bit, for instance), unsigned type to hold the value.