Skip to content

GraphQL: add a query to fetch specific pending snark work #16244

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

Merged
merged 11 commits into from
Dec 9, 2024

Conversation

anne-laure-s
Copy link
Member

This PR adds the possibility to fetch a specific range of work in the pending snark work list with GraphQL.

(included). If not specified, only the work at index \
[startingIndex] will be returned. An empty list will be \
returned if startingIndex > endingIndex"
~typ:Types.Input.UInt32.arg_typ ~default:Unsigned.UInt32.max_int
Copy link
Member

Choose a reason for hiding this comment

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

My personal preference, would for this to also be non-null.

Copy link
Member Author

Choose a reason for hiding this comment

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

I would expect this working if startingIndex = endingIndex = 0, so I would not be in favor to expect endingIndex ≠ 0

Copy link
Member

@svv232 svv232 Oct 15, 2024

Choose a reason for hiding this comment

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

What if the upper bound was exclusive instead? i.e 0-1 gets the first index. Also If I am calling an API end point that returns a range, I would expect n-null to return the list from the index n to the end.

Copy link
Member

@deepthiskumar deepthiskumar Oct 15, 2024

Choose a reason for hiding this comment

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

I agree, it is counter intuitive for me when for the range n to max-int it returns only the element at index n

Copy link
Member Author

Choose a reason for hiding this comment

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

I changed the default behavior. Now

  • if endingIndex ≥ length(pending_work), everything from startingIndex is returned. (max_int is still the default)
  • endingIndex is excluded
  • [] is returned when startingIndex ≥ endingIndex or startingIndex ≥ length(pending_work)

Copy link
Member

@svv232 svv232 Oct 16, 2024

Choose a reason for hiding this comment

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

I'm still thinking having max-int is unnecessary logic. I think we can come up with a solution that removes this and enforces the invariants we know to be true. For example

  let pending_snark_work_range =
    field "pendingSnarkWorkRange"
      ~doc:
        "Find any sequence of pending snark works between two indexes in the \
         pending snark work pool"
      ~args:
        Arg.
          [ arg "startingIndex"
              ~doc:
                "The first index to be taken from the pending snark work pool"
              ~typ:(non_null Types.Input.UInt32.arg_typ)
          ; arg "endingIndex"
              ~doc:
                "The last index to be taken from the pending snark work pool \
                 (excluded). If not specified or greater than the pending \
                 snark work list, all elements from index [startingIndex] will \
                 be returned. An empty list will be returned if \
                 [startingIndex, endingIndex] is not a valid range of the \
                 pending snark work list."
              ~typ:Types.Input.UInt32.arg_typ 
          ]
      ~typ:(non_null @@ list @@ non_null Types.pending_work)
      ~resolve:(fun { ctx = mina; _ } () start_idx end_idx ->
        let pending_work = get_pending_work mina in
        let pending_work_size = pending_work |> List.length |> Unsigned.UInt32.of_int in
        let less_than uint1 uint2 = Unsigned.UInt32.compare uint1 uint2 < 0  in 
        match end_idx with 
        | None ->
            (* drop handles case when start_idx is greater than pending work and is O(start_idx)*)
            let start = Unsigned.UInt32.to_int start_idx in
            List.drop pending_work start
        | Some end_idx when  less_than start_idx  end_idx  && less_than end_idx pending_work_size ->
          let pos = Unsigned.UInt32.to_int start_idx in
          let len = Unsigned.UInt32.(sub end_idx start_idx |> to_int) in
          List.sub ~pos ~len pending_work
        | _ ->
            [] )

Copy link
Member Author

Choose a reason for hiding this comment

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

Is there a reason for prefering UInt32 manipulation over to_int conversion from the start ?

Copy link
Member

@svv232 svv232 Oct 18, 2024

Choose a reason for hiding this comment

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

I didn't want to run any transformations on user controlled data before checking, and it was more convenient to write with the match case. You can do uint to int conversions safely, but I wasn't sure how the js one was implemented. I just took the safe route without reading the implementation. Maybe it's also worth having some error checking in the None case as well for start idx.

Copy link
Member Author

Choose a reason for hiding this comment

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

I pushed a new version with your suggestion but I modified some match cases to match the spec.

in
Work_selector.pending_work_statements ~snark_pool ~fee_opt
snark_job_state )
try List.sub ~pos:start_idx ~len @@ get_pending_work mina with _ -> []
Copy link
Member

@svv232 svv232 Oct 15, 2024

Choose a reason for hiding this comment

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

I am fine with a slice here because the Janestreet runtime is O(n), and we won't actually save on significant ram usage because work selection stores state.available jobs anyway in work_lib. Since state.availabe jobs is a super set of the pending work, we wouldn't save much space overall i.e worst case O(2n)

Copy link
Member

Choose a reason for hiding this comment

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

this is also currently a low frequency graphql call

Comment on lines 1074 to 1079
let pending_snark_work_range =
Command.async
~summary:
"Range of snark works in JSON format that are not available in the pool \
yet"
pending_snark_work_list
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't this also splice with a start and end?

Copy link
Member Author

@anne-laure-s anne-laure-s Oct 15, 2024

Choose a reason for hiding this comment

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

You are right, & this is actually unrelated, I’ll remove it from these commits

Copy link
Member

Choose a reason for hiding this comment

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

that makes sense, I thought the client and graphql changes could be split up but i didn't really mind.

@anne-laure-s anne-laure-s force-pushed the al/snark-work-graphql branch from 65f30d1 to bed927c Compare October 15, 2024 15:33
Comment on lines 130 to 158
module Pending_snark_work_range =
[%graphql
{|
query pendingSnarkWorkRange {
pendingSnarkWork {
workBundle {
source_first_pass_ledger_hash: sourceFirstPassLedgerHash
target_first_pass_ledger_hash: targetFirstPassLedgerHash
source_second_pass_ledger_hash: sourceSecondPassLedgerHash
target_second_pass_ledger_hash: targetSecondPassLedgerHash
fee_excess: feeExcess {
feeTokenLeft
feeExcessLeft {
sign
feeMagnitude
}
feeTokenRight
feeExcessRight {
sign
feeMagnitude
}
}
supply_increase : supplyIncrease
work_id: workId
}
}
}
|}]

Copy link
Member

Choose a reason for hiding this comment

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

i think should also be deleted for now, until/if the client command is added back

Copy link
Member Author

Choose a reason for hiding this comment

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

Right, deleted

@anne-laure-s anne-laure-s force-pushed the al/snark-work-graphql branch from bed927c to 2d977f5 Compare October 16, 2024 10:30
@anne-laure-s anne-laure-s force-pushed the al/snark-work-graphql branch from 2d977f5 to d5882ba Compare October 21, 2024 14:58
Comment on lines 2246 to 2249
| None when less_than start_idx pending_work_size ->
(* drop handles case when start_idx is greater than pending work and is O(start_idx)*)
let start = Unsigned.UInt32.to_int start_idx in
List.drop pending_work start
Copy link
Member

Choose a reason for hiding this comment

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

nit
drop returns empty list when the start index is greater than the list size, so you don't really need the check, but it is fine to leave it.

Copy link
Member Author

Choose a reason for hiding this comment

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

That’s good to know !

I’m in favor to leave it to make the behavior explicit, but I don’t have a strong opinion about it.

Copy link
Member

@svv232 svv232 Oct 22, 2024

Choose a reason for hiding this comment

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

ya that is fine, i mentioned it in the comment to clarify as well on line 2247

@svv232
Copy link
Member

svv232 commented Oct 22, 2024

!ci-build-me

@svv232
Copy link
Member

svv232 commented Oct 22, 2024

!ci-nightly-me

@coveralls
Copy link

Coverage Status

coverage: 61.0% (+0.01%) from 60.989%
when pulling d5882ba on al/snark-work-graphql
into 4f78a48 on compatible.

@deepthiskumar
Copy link
Member

!ci-build-me

@deepthiskumar deepthiskumar requested a review from svv232 November 20, 2024 00:50
@@ -2202,19 +2202,69 @@ module Queries = struct
Mina_lib.snark_pool mina |> Network_pool.Snark_pool.resource_pool
|> Network_pool.Snark_pool.Resource_pool.all_completed_work )

(* Auxiliary function to fetch all pending work *)
let get_pending_work mina =
Copy link
Member

Choose a reason for hiding this comment

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

I feel this is slightly misnamed now. The design has changed from the previous intention. Previously we obtained all pending work without fees, where as now it seems we obtain all work, including work that was previously completed in case another snark worker want to provide a better bid. I feel this change in intention should be documented and explained in this doc comment.

perhaps something like:
get_pending_and_completed_work

Copy link
Member

@svv232 svv232 left a comment

Choose a reason for hiding this comment

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

The logic looks good, I just wanted you to change the pending_snark_work graphql name to something more apt. It seems the goal now is to get all work indexes in the case an external snark work script want to re-do work at a better price.

anne-laure-s and others added 3 commits December 2, 2024 21:03
This function was initially added to unify code. At this point this is no longer relevant, as it is now used only in one place.
@svv232
Copy link
Member

svv232 commented Dec 9, 2024

!ci-build-me

@svv232
Copy link
Member

svv232 commented Dec 9, 2024

!ci-build-me

@svv232 svv232 merged commit c1634c3 into compatible Dec 9, 2024
44 checks passed
@svv232 svv232 deleted the al/snark-work-graphql branch December 9, 2024 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants