Skip to content

Switched resolver and execution logic to voption - #614

Merged
xperiandri merged 7 commits into
devfrom
voption-execution
Sep 19, 2026
Merged

xperiandri merged 7 commits into
devfrom
voption-execution

Conversation

@xperiandri

@xperiandri xperiandri commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Continues the ongoing struct-optionals effort by moving the resolver and execution pipeline from reference option to struct voption, removing allocations on the hottest path of query execution.

This is a focused follow-up to the already-merged active-pattern conversion (#610) and is orthogonal to the optional-parameter work in #604 — reviewers of those PRs do not need to re-review that ground here.

What changed

Execution pipeline (src/FSharp.Data.GraphQL.Server/Execution.fs)

  • ResolverResult<'T> now carries IObservable<GQLDeferredResponseContent> voption instead of option.
  • executeResolvers accepts AsyncVal<obj voption> instead of AsyncVal<obj option>.
  • Pattern matches and call sites updated to ValueSome / ValueNone.
  • Deferred/streamed resolver plumbing threads voption end-to-end, so no boxing round-trip remains between the resolver and the response writer.

Shared helpers

  • Extensions.fs: new ValueOption.mergeWith and ValueOption.unwrap, mirroring the existing Option counterparts.
  • ObjAndStructConversions.fs: added Array.vtryPick.
  • Reflection.fs: objectOptionCast is now accessible and documented; new toValueOption lifts an obj into obj voption unless it is already an option; the now-unused optionCast wrapper was dropped.

Client

  • BaseTypes.fs, GraphQLClient.fs, ReflectionPatterns.fs, Serialization.fs updated to the voption-based helpers, plus assorted string-formatting and XML-doc cleanups.

Compatibility notes

  • ResolverResult<'T> is public and its shape changed (option -> voption). Code that destructures it directly needs a mechanical Some/None -> ValueSome/ValueNone update.
  • Reflection.optionCast was removed; objectOptionCast (returning voption) is the replacement, and ValueOption.toOption recovers the old shape if needed.

Validation

  • dotnet build FSharp.Data.GraphQL.slnx0 errors, 0 warnings.
  • dotnet test tests/FSharp.Data.GraphQL.Tests667 passed, 0 failed, 5 skipped.

Related

* Refactored codebase to use F# `voption` (value option) instead of option for improved performance and clarity, especially in resolver and execution logic.
* Replaced `Option` functions with `ValueOption` equivalents, added `vtryPick` for arrays and extended `vtryFind` for lists.
* Updated pattern matching and function signatures to use `ValueSome`/`ValueNone`.
* Changed resolver result types to use `IObservable<_> voption`.
* Improved error handling, string formatting, and added `objectOptionCast`/`toValueOption` helpers in `Reflection.fs`.
* Updated code comments and XML docs.
* Ensured backward compatibility and better nullability handling.
@github-actions

github-actions Bot commented Sep 19, 2026

Copy link
Copy Markdown

Test Results

    9 files      9 suites   13m 5s ⏱️
  777 tests   772 ✅  5 💤 0 ❌
2 331 runs  2 316 ✅ 15 💤 0 ❌

Results for commit a965a9e.

♻️ This comment has been updated with latest results.

* Refactored and reorganized collection and option helpers from `Extensions.fs` and `ObjAndStructConversions.fs` into `Helpers/CollectionExtensions.fs`.
* Moved `kvp`, `kvpObj`, and `IDictionary` extension methods to the new file.
* Removed redundant implementations from the original files.
* Updated usages to reference the new module and clarified documentation comments.
* Ensured helpers are internal or auto-opened for seamless usage.
@xperiandri xperiandri changed the title Switch resolver and execution logic to voption Switched resolver and execution logic to voption Sep 19, 2026
@xperiandri
xperiandri requested a lite review from Copilot September 19, 2026 21:58

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.

Copilot review overview

🟡 Changes recommended

toValueOption incorrectly wraps boxed ValueNone values as present values, breaking nullable voption list elements.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
What changed in this PR

Moves resolver execution and client serialization from reference option to struct voption.

Changes:

  • Adds shared ValueOption and collection helpers.
  • Updates server execution, deferred, streamed, and live resolver paths.
  • Adapts client reflection, serialization, and upload handling.
File Description
Validation.fs Uses struct-option lookup helpers.
Reflection.fs Adds object-to-voption conversion.
ObjAndStructConversions.fs Consolidates option helpers.
Extensions.fs Removes relocated helpers.
CollectionExtensions.fs Adds collection voption helpers.
FSharp.Data.GraphQL.Shared.fsproj Includes the new helper file.
Execution.fs Threads voption through execution.
Serialization.fs Handles struct-option client values.
ReflectionPatterns.fs Adds struct-option reflection patterns.
GraphQLClient.fs Updates upload traversal.
BaseTypes.fs Uses struct-option conversions.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/FSharp.Data.GraphQL.Shared/Helpers/Reflection.fs Outdated
Copilot AI and others added 4 commits September 19, 2026 22:10
Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com>
Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com>
Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com>
Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com>

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.

Copilot review overview

🔵 Needs a closer look

The list lookup helper introduces sequence/enumerator allocations on an execution path targeted for allocation reduction.

Review effort: Lite
Findings: None

Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Avoid lazy Seq allocation in voption list lookup

src/​FSharp.Data.GraphQL.Shared/​Helpers/​CollectionExtensions.fs:78

This helper is now used by the execution path (Execution.fs:44), but implementing it through Seq.where and Seq.vtryHead creates a lazy sequence/enumerator for every list lookup. That can allocate more than the previous List.tryFind and undermines the PR's allocation-reduction goal; implement the voption lookup with a direct tail-recursive loop over the list instead.

@xperiandri
xperiandri merged commit fc0e9a9 into dev Sep 19, 2026
4 checks passed
@xperiandri
xperiandri deleted the voption-execution branch September 19, 2026 22:35
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.

3 participants