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

Support cancellation during client connection establishment #721

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

DerGuteMoritz
Copy link
Collaborator

@DerGuteMoritz DerGuteMoritz commented Apr 5, 2024

With #714 we added support for cancelling in-flight HTTP requests by putting the response deferred into an error state. However, this only worked once the underlying TCP connection was established. With this patch, it is now possible to cancel requests even while the connection is still being established (possible since Netty 4.1.108.Final via netty/netty#13849). This also works for aleph.tcp/client.

Diff best viewed without whitespace changes.

Completes #712.

With #714 we added support for cancelling in-flight HTTP requests by putting the response deferred
into an error state. However, this only worked once the underlying TCP connection was
established. With this patch, it is now possible to cancel requests even while the connection is
still being established (possible since Netty 4.1.108.Final via
netty/netty#13849). This also works for `aleph.tcp/client`.
@DerGuteMoritz DerGuteMoritz force-pushed the client-connection-establishment-cancellation branch from 4923350 to 9a66ceb Compare April 5, 2024 20:41
src/aleph/http.clj Outdated Show resolved Hide resolved
src/aleph/http.clj Outdated Show resolved Hide resolved
src/aleph/http/client.clj Outdated Show resolved Hide resolved
src/aleph/netty.clj Outdated Show resolved Hide resolved
src/aleph/tcp.clj Outdated Show resolved Hide resolved
Copy link
Collaborator

@KingMob KingMob left a comment

Choose a reason for hiding this comment

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

I've reviewed half of it. It seems ok, but there's some weird uses of catch that needs some clarification.

@DerGuteMoritz
Copy link
Collaborator Author

I've reviewed half of it. It seems ok, but there's some weird uses of catch that needs some clarification.

Thank you for your feedback so far! I've addressed it all but am not yet satisfied with the current state. I'll see if I can somehow make the error propagation a bit easier to grok, possibly by extracting a util function as discussed above. Stay tuned.

The new namespace `aleph.util` introduces two new helpers:

* `on-error` which is like `d/on-realized` but only for the error case (success case uses
  `identity`).
* `propagate-error` which propagates error states from a source deferred to a destination deferred
  and optinally accepts a callback which is only run when the propagation has indeed occurred.

These are now used in all places where we propagate error states back to upstream deferreds for the
purpose of cancellation which hopefully makes it a bit more obvious what's going on.
See code comment for background
@DerGuteMoritz
Copy link
Collaborator Author

OK just pushed an attempt at making the error back-propagation more self-describing. @KingMob @arnaudgeiser WDYT?

@DerGuteMoritz
Copy link
Collaborator Author

BTW if you're wondering about these weird "fixup!" commit messages, see e.g. this post for an explanation. I've adopted that workflow now instead of amending commits & force pushing while working on a branch. That should be less confusing during review hopefully 🙏 I'll rebase & autosquash these before merge then!

@KingMob
Copy link
Collaborator

KingMob commented Apr 19, 2024

BTW if you're wondering about these weird "fixup!" commit messages, see e.g. this post for an explanation. I've adopted that workflow now instead of amending commits & force pushing while working on a branch. That should be less confusing during review hopefully 🙏 I'll rebase & autosquash these before merge then!

If you like doing that, you'll love Jujutsu.

Want to squash the current revision into some older one? jj can do this in a single command: jj squash -r some-older-revision. It's less likely to have rebase conflicts than git, too.

Want to just edit that older revision without worry? jj edit -r some-older-revision will make that revision the current one. Then, all your file changes will immediately update it, and everything downstream will be rebased, all while you type.

Not ready to just edit that older rev in-place, or need to see the diffs of your new change more clearly? jj new -r some-older-revision will immediately make a child revision of some-older-revision, and make it the current one. When you're done, jj squash puts your changes in some-older-revision and immediately rebases everything downstream.

Want to insert a new blank revision between a parent and child? jj new --insert-after parent

Here's a wild one. Need to work on three branches simultaneously, but don't want to rebase them off each other? jj new branch1 branch2 branch3 create a 3-way merged commit. You can use jj squash --into branchX to pick which branch to place changes into, and you can get rid of the merge commit when you're all done with jj abandon.

Notice I mention editing "revisions", not commits. A commit is immutable, right? Well, in jj, a revision is a stable, semantically-related change that has a history of different commits underneath the hood. If you rebase a branch, the commit IDs all change in git, yes? That's true for jj too, but the revisions stay the same no matter what.

Plus, it's all git-compatible, so your coworkers don't even have to know.

Comment on lines +4 to +6
(defn on-error
[d f]
(d/on-realized d identity f))
Copy link
Collaborator

Choose a reason for hiding this comment

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

OK for now, but this should be in Manifold

Copy link
Collaborator

@KingMob KingMob left a comment

Choose a reason for hiding this comment

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

Looking good, but I think these utility fns should be in Manifold, not aleph.

Comment on lines +8 to +20
(defn propagate-error
"Registers an error callback with source which will attempt to propagate the error to destination.

If the error was propagated (i.e. destination wasn't yet realized), on-propagate is invoked with
the error value.

Returns source."
([source destination]
(propagate-error source destination identity))
([source destination on-propagate]
(on-error source (fn [e]
(when (d/error! destination e)
(on-propagate e))))))
Copy link
Collaborator

Choose a reason for hiding this comment

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

source is stream terminology, not deferred terminology. We should call it something else to avoid confusion. I made that exact mistake on the first read-through.

Comment on lines +12 to +13
(d/error! src :boom)
(is (d/realized? dst))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't this a race condition? Might need to put them on same executor and thread.

Comment on lines -1491 to +1497
(is (= true (deref conn-established 1000 :timeout)))
(is (= true (deref @conn-established 1000 :timeout)))
(http/cancel-request! rsp)
(is (= true (deref conn-closed 1000 :timeout)))
(is (= true (deref @conn-closed 1000 :timeout)))
Copy link
Collaborator

Choose a reason for hiding this comment

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

(deref @ ... looks weird. I suggest switching to unwrap/unwrap'.

(ns aleph.util
(:require [manifold.deferred :as d]))

(defn on-error
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it should be documented why you would use this over chain or on-realized.

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.

None yet

2 participants