-
Notifications
You must be signed in to change notification settings - Fork 18
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
Add preview, submit tx and tx stream APIs in Rust #95
Conversation
content: | ||
application/json: | ||
schema: | ||
"$ref": "#/components/schemas/ErrorResponse" | ||
'500': |
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.
IMHO it's quite pointless to specify 500
responses, just focus on responses returned by application that serve business purposes (200 and 400 in this case).
500, 501, 502, 503, 401, 402, 100, ... - if customer to our API is not expected to face them not to mention handle them what's the point of documenting them? BTW we can't guarantee response payload format for 500 errors :)
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.
Agreed, I don't see much point in specifying it either. As a side note, I'll also get rid of the leftover polymorphic error types (InvalidTransactionError, PreviewError etc) - it isn't supported by the Rust generator.
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.
Hmh, I take that back, it seems that with the Rust openapi gen it's required to define the 500 error responses if we want to return any meaningful messages for server errors. Otherwise we can't return a custom message (it's hardcoded to a generic "An internal error occurred").
Overall, there are 4 categories of error responses:
- a request that doesn't match the schema: 400 + txt error message (no json) - handled by the generated server
- client error (or any error that can loosely fit into this category, e.g. "mempool full" error) - 400 + json error (code, message) - corresponds to
Ok(SomeResponse::ClientError)
- expected/handled server error (e.g. state manager lock error) - 500 + json error - corresponds to
Ok(SomeResponse::ServerError)
- unhandled server error - 500 + generic txt error message - corresponds to
Err(ApiError(...))
- we shouldn't really use it
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.
It could be that "unhandled server error - 500 + generic txt error message" is also returned by the generated server in some cases
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.
@KrzysztofLabus-RDX - just for context, the Core API (like the Olympia Gateway) was designed to follow "RPC over HTTP" so everything which isn't a 200 is a 500; with a separate internal code.
I'd be potentially happy to change this for Babylon, but it'd need a wider conversation.
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.
@dhedey Ah, right! Forget the question/suggestion then :)
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.
"everything which isn't a 200 is a 500;" is that really the case? I thought that'd be "everything which isn't a 200 is either a 400 or a 500;"
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.
Honestly, I'm happy to use proper HTTP response codes. But paging @talekhinezh in case he wants to argue for keeping 500
for everything
items: | ||
type: string | ||
error_message: | ||
type: string |
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.
Could we please define optional (non-required
) properties as nullable
? There's long reasoning behind that: https://radixdlt.atlassian.net/browse/NG-64?jql=text%20~%20%22nullable%20json%22 tl;dr: changes nothing in practice but makes it possible to define proper backing types (think: POJOs) in virtually any sane language (Java, C#, PHP etc.)
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.
yup, can do
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.
This, on the other hand, kind-of breaks the Rust generation :D Non-required nullable fields become Option<Nullable<T>>
which isn't ideal, but I guess we can live with that.
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.
btw, did you just call PHP a sane language!?
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.
I believe you're using this OAS Generator to automatically generate backing types, right? So it looks like it's capable of producing proper backing types with use of Ruts' Option
. I'll take my request back. What you've been doing was just right. I'll simply try to fix C# OAS Generator generator to produce nullable resource where appropriate. Ideally we could improve it to the point where C# generator also uses Optional<T>
where T
is nullable or non-nullable type but that would be more difficult due to various reasons. CC @dhedey
EDIT: Yeah, I've called PHP sane language, shame on me. :D
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.
If we're not supporting nullable types in the Core API, then, yes my PR here documents two distinct issues with the handling of required/not-required types in the C# generation: OpenAPITools/openapi-generator#11607 - and fixes issue (2) - but hasn't been merged. I think we're talking about issue (1) here.
It's quite important in the C# that both (1) and (2) are fixed in the generated models - or at least it was for Olympia.
I'd possibly recommend either picking up the PR, and/or fixing these in template file overrides locally - see eg https://github.com/radixdlt/radixdlt-python-clients/tree/main/regeneration and https://openapi-generator.tech/docs/templating
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.
Ok, removed "nullable"
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.
Looks good! A few suggestions for minor tweaks. Excited to see this speed us up compared to having this in Java.
Is the plan to rename /state-manager/
to eg /rust
(or something) in a small PR at some point? :)
- notary_signature | ||
properties: | ||
hash: | ||
type: string |
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.
In Olympia, Josh designed the API to return explicit wrapped "identifier" objects, I'd be happy to ditch these though - but just wanted to check this was an intentional direction?
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.
We could also make these a $ref to a string type with docs, in order to de-dupe any docs we want to add :)
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.
It was more accidental than intentional, I kind of like the idea of having those defined as separate types
notary_as_signatory: | ||
type: boolean | ||
cost_unit_limit: | ||
type: string |
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.
Can we document what these mean / represent?
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.
will do 👍
@@ -363,16 +457,13 @@ components: | |||
type: string | |||
cost_unit_limit: | |||
description: A limit of cost units available for execution |
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.
For these three options, can we mention in the documentation that these are integers?
I know in Olympia for integers, we used format: integer
etc, and reserved strings for big integers, or longs where we're likely to go about 2^53 (cf https://stackoverflow.com/users/596689/tom-christie ).
But also potentially happy to just use strings if we feel this is more consistent; but I'd like us to ensure we are consistent, and to document this somewhere. (Inline in each explanation; and the general approach and justification in the overall API description).
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.
good point, wdyt about adding custom types (and use $ref) for UInt*?
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.
ah, but that might not work well with descriptions
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.
Yeah - sadly this doesn't work so well with descriptions :( - otherwise I love it.
.lock() | ||
.map_err(|_| submit_server_error("Internal server error (state manager lock)"))?; | ||
|
||
let result = locked_state_manager.mempool.add_transaction(transaction); |
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.
It's worth us flagging to @talekhinezh that this doesn't go through any code in the Java Ledger/StateManager, suggesting anything like metrics / locking / error handling should move down into the Rust mempool eventually 👍
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.
Yup this is fine.
TransactionSubmitPostResponse::ServerError(ErrorResponse::new(500, message.to_string())) | ||
} | ||
|
||
pub(crate) fn handle_transactions( |
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.
Can we name this slightly more clearly? eg handle_get_committed_transactions
let mut txns = vec![]; | ||
let mut state_version = initial_state_version; | ||
while state_version <= up_to_state_version_inclusive { | ||
if let Some(next_tid) = locked_state_manager.proof_store.get_tid(state_version) { |
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.
When would we expect state version to be missing from the state store?
What do you think about us (A) reading some "max state version", (B) capping up_to_state_version_inclusive to the max, (C) raising error for any state version missing, (D) returning max state version in the response?
(Maybe in a follow up PR so that we can merge for Krzysztof)
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.
I think we're currently missing a tx for state version 0, but this can also be handled; will do in a separate PR
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.
Transactions map to resultant state versions
, so I think it's right that there is no tranasction mapping to resultant state version 0.
The Genesis transaction would map 0 to 1
, so be at resultant state version 1 👍
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.
yup, makes sense, done
0f3afcd
to
4a3eb2e
Compare
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.
Nice! Let's get this merged. Happy for any fixes to follow
@@ -164,8 +165,10 @@ components: | |||
type: array | |||
items: | |||
type: string | |||
description: The engine return data (only present if status is succeeded) |
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.
Is this true? I imagine if failed, they'd still be output, because there's still state changes and a commit?
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.
we might need a better naming :) the state diff is still there, but not the output (as in function return value):
pub enum TransactionOutcome {
Success(Vec<Vec<u8>>),
Failure(RuntimeError),
}
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.
Ahh right, yes. Sorry, got you. This is the "output", which is really the "transaction executor log" I think? I'm not really sure what it is to be honest xD.
Yulong and I were using the term "Outcome" for Success/Failure :)
Kudos, SonarCloud Quality Gate passed! |
No description provided.