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

[TRACKING] Async Transport and Protocol #77

Closed
6 tasks done
Byron opened this issue May 6, 2021 · 0 comments
Closed
6 tasks done

[TRACKING] Async Transport and Protocol #77

Byron opened this issue May 6, 2021 · 0 comments

Comments

@Byron
Copy link
Member

Byron commented May 6, 2021

It becomes clear that in order to be more useful for application developers, it's best to support async operation in the transport and protocol layers. This issue tracks the overall progress in making this conversion from blocking code to non-blocking/async code.

  • git-packetline
  • git-transport
  • git-protocol
  • gitoxide-core + gitoxide
  • create discussion in blocking to figure out if we can have blocking::spawn(future) to avoid clumsy code and overhead.

Potential improvements

  • get rid of unsafe pointer magic WithSidebands (cost: high)
    • What needs to be done is to transform the &mut StreamingPeekableIter into a child future, and when exhausted, it must be transformed back into the &mut _ that created it. That way, only a single mutable reference to said Iter is present at any time. Unfortunately the generated futures (using async) don't support that as we would have to keep both the future and the parent that created it inside of our own struct. Instead of hiding this using pointers, one could implement the magical part by hand, a custom future, which happily dissolves into its mutable parent iter ref. That would be quite some work though.

Requirements

  • git-transport via quinn - how would that work? Maybe async http isn't even necessary?
  • must support tokio 1.0 (quinn needs it).
    • surf via hyper seems to be locked to tokio 0.2 (even though async-std supports tokio 1.0 as well.

Research

  • maybe-async - conditional compilation of both sync and async APIs
    • Useful to keep the sync version which compiles quickly thanks to libcurl used under the hood. This might allow to write the async version in smaller increments.
  • async-http(s) client
    • tokio
    • hyper::client - probably too low level
    • reqwest
    • non-tokio
      • surf - **supports multiple backends, some of which are tokio, like hyper which relies on their traits interestingly.
  • Abstracting over the runtime can be done by…knowing which one to use. Sync and async selection can be done with maybe-async.
  • tokio
    • comes with its own compatibility layer to adapt to futures::io related traits.
    • has its own non-blocking process implementation.

Maybe_async

As it's async by default which requires more dependencies than sync code it won't be used in crates to avoid pulling in unnecessary dependencies for builds that don't use it.

This also means definitive duplicates here and there for IO code that otherwise could be deduplicated with maybe_async.

However, tests can and should benefit from maybe_async as minimal dependencies are less important there. Thus we deduplicate tests with it to assure there is no drift in code. This rightfully assumes decent test coverage.

Related

Out of Scope

  • async server
Archive
  • Figure out how git-transport could be used with quinn and other custom transports.
    • quinn streams support both tokio and futures AsyncReadWrite traits, so going with the more general trait at first should be good. Probably it's a good idea to have a redirection for traits so replacing them with the tokio version is easier should it ever be required.
  • Navigate the crate ecosystem and find an http client that can work.
    • Surf with lib-curl as backend seems to do it, as it uses future::io traits which are actually most compatible.
  • Realize that what's really needed is a 'custom async transport' and not do all of the following (as originally intended) unless this comes up again.
    • async client
      • maybe::async transport
        • surf based http backend with feature toggles to allow backend selection. This allows choosing the runtime.
        • assure enough tests for these new feature toggles.
      • maybe::async protocol
      • maybe::async gixp clone path - this represents the application and is a good example on how the API feels, alongside the blocking implementation.
  • Research what it would mean to have an async transport trait and how it could fit
    • goal put everything that's currently availabe into client::blocking and start working with maybe_async to pull the git::Connection into async land, step by step, also using maybe_async. This has a feature-toggle-able ripple effect and hides all other blocking implementations (as the trait is either blocking or async) until these are ported. Generally the git-transport crate will only support one mode at a time.
  • git-packetline
    • the grand refactor - it always felt a little off
    • feature toggles for 'blocking' mode, with default to async operation like in git-transport/protocol
    • refactor packet line provider
    • feature toggle for turning on async-io - otherwise blocking io MUST pull in futures-io for no reason. This will propagate but it's OK.
    • async encode needed for Writer
    • async low level line writer (new)
    • async higher level line writer
    • try to unify encode tests using maybe_async as test-utility only
    • async encode
    • immutable packet line IO
    • async packet line provider (easy mode)
    • async sideband reader (no easy mode)
      • figure out how to use easy mode packet line provider from no-easy-mode (aka async-futures from poll based
  • git-transport
    • async test case - empty, but at least we can compile something
    • async capabilities
    • remove maybe_async from library code and add async-client trait behind feature toggle
    • move everything without IO ties into shared space
    • Capabilities::from_lines_with_version_detection(…)
    • Handshake V1 tests
    • decode an actual pack from an async-read - show how to translate it into the blocking world to combine async and sync
    • TransportExt trait
    • Handshake V2 tests
    • Show how to read a pack while keeping everything async
      • unblock handshake v2 test when traversing entries.
  • git-protocol
    • separate common (no feature toggles needed) tests and those who need feature toggles
    • git-credentials - keep it blocking as async programs aren't really a thing and the blocking crate exists.
    • start out moving everything into 'blocking' module to be able to take on asyncification step-by-step
    • async arguments and command
    • Refs parsing in async
    • Response in async
    • async Delegate
    • use maybe_async and disallow --all-features as it would misconfigure maybe-async
    • async fetch(…)
    • remove #[allow(dead_code)] in async mode
    • crate-features.md update
  • refactor
    • disallow all client features enabled in git-transport
    • disallow all client features enabled in git-packetline
  • gitoxide-core + gitoxide
    • feature toggle for blocking client, none set means no networking
    • feature toggle for async client
    • don't let blocking client take precedence in gitoxide-core
    • async transport creation
    • async LsRemote delegate impl + journey test
    • async PackReceive delegate impl
    • Is there an easy way to unblock the pack writing?
    • journey test for async client receiving a pack from a git daemon using async io + fix TODO
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

No branches or pull requests

1 participant