Skip to content
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

Merged
merged 4 commits into from
Jul 13, 2024
Merged

[GraphQL][RFC] Introduce UInt53 scalar #18552

merged 4 commits into from
Jul 13, 2024

Conversation

amnn
Copy link
Member

@amnn amnn commented Jul 8, 2024

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 u64s 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:
  • 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 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.
  • CLI:
  • Rust SDK:

@amnn amnn requested a review from hayes-mysten July 8, 2024 10:42
@amnn amnn self-assigned this Jul 8, 2024
Copy link

vercel bot commented Jul 8, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
sui-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 13, 2024 0:51am
3 Skipped Deployments
Name Status Preview Comments Updated (UTC)
multisig-toolkit ⬜️ Ignored (Inspect) Visit Preview Jul 13, 2024 0:51am
sui-kiosk ⬜️ Ignored (Inspect) Visit Preview Jul 13, 2024 0:51am
sui-typescript-docs ⬜️ Ignored (Inspect) Visit Preview Jul 13, 2024 0:51am

@amnn amnn changed the title [GraphQL][RFC] Introduce UInt scalar [GraphQL][RFC] Introduce UInt53 scalar Jul 12, 2024
_ => None,
};
}

let GqlValue::Number(value) = &value?.node else {
return None;
};
value.as_u64()
value.as_u64().map(|v| v as u32)
Copy link
Contributor

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

Copy link
Member Author

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!

Copy link
Member Author

@amnn amnn left a 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!

amnn added 4 commits July 13, 2024 13:46
## 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
```
@amnn amnn merged commit c2ef24a into main Jul 13, 2024
48 checks passed
@amnn amnn deleted the amnn/gql-uint branch July 13, 2024 13:45
tx-tomcat pushed a commit to tx-tomcat/sui-network that referenced this pull request Jul 29, 2024
## 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:
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.

2 participants