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

Implement the Happy Eye Balls RFC's #196

Merged
merged 1 commit into from
Oct 23, 2019

Conversation

WyriHaximus
Copy link
Member

@WyriHaximus WyriHaximus commented Feb 27, 2019

By using the happy eye balls algorythm as descripted in RFC6555 and
RFC8305 it will connect to the quickest responding server with a
preference for IPv6.

@WyriHaximus WyriHaximus added this to the v1.3.0 milestone Feb 27, 2019
@WyriHaximus WyriHaximus requested review from jsor and clue February 27, 2019 21:32
@WyriHaximus WyriHaximus force-pushed the happy-eye-balls branch 6 times, most recently from 4662be9 to ac92884 Compare March 5, 2019 21:46
@WyriHaximus WyriHaximus marked this pull request as ready for review March 5, 2019 21:46
@WyriHaximus
Copy link
Member Author

This PR is now ready for review. A follow up will be filed to replace the DnsConnector with the HappyEyeBallsConnector in the Connector an upcoming PR.

@WyriHaximus WyriHaximus force-pushed the happy-eye-balls branch 2 times, most recently from 89c9769 to e4676ea Compare March 6, 2019 16:49
@WyriHaximus WyriHaximus force-pushed the happy-eye-balls branch 2 times, most recently from 46a9866 to 25c1ecd Compare May 2, 2019 06:25
@WyriHaximus
Copy link
Member Author

Ping @clue && @jsor did some major refactoring on this PR and it is now ready few review

Copy link
Member

@clue clue left a comment

Choose a reason for hiding this comment

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

@WyriHaximus Thank you very much for working on this, this is one of the features that is very high on my personal wishlist! ❤️

However, I realize that the implementation is anything but trivial. I've added some remarks to some places that look odd to me, perhaps you can take a look?

That being said, once all implementation issues are resolved, I wonder what's the best path forward to ensure a safe rollout of this feature in the future? Perhaps it makes sense to first enable this without IPv6 support to see the initial implementation does not break any existing implementations? I can see some (legacy) use cases where it may be desirable to explicitly disable IPv6 because the downstream application might not be IPv6-ready? (think database fields storing an address in a fixed length field)

src/HappyEyeBallsConnectionBuilder.php Outdated Show resolved Hide resolved
src/HappyEyeBallsConnectionBuilder.php Outdated Show resolved Hide resolved
@WyriHaximus WyriHaximus force-pushed the happy-eye-balls branch 2 times, most recently from 3117a07 to 8fa1d3e Compare May 18, 2019 22:43
@WyriHaximus
Copy link
Member Author

@WyriHaximus Thank you very much for working on this, this is one of the features that is very high on my personal wishlist!

🎉 !!!

However, I realize that the implementation is anything but trivial. I've added some remarks to some places that look odd to me, perhaps you can take a look?

Thanks for the review, I've addressed those points.

That being said, once all implementation issues are resolved, I wonder what's the best path forward to ensure a safe rollout of this feature in the future? Perhaps it makes sense to first enable this without IPv6 support to see the initial implementation does not break any existing implementations? I can see some (legacy) use cases where it may be desirable to explicitly disable IPv6 because the downstream application might not be IPv6-ready? (think database fields storing an address in a fixed length field)

Not entirely sure yet how to do that. What I want to do is write a follow up PR that initially will go full in on happy-eyeballs. And then we'll comes up during the discussion on that PR how we're shaping that. Because I'm pretty sure we can solve this with a flag on the Connector class 😄 .

Copy link
Member

@clue clue left a comment

Choose a reason for hiding this comment

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

@WyriHaximus Thanks for the update! This PR/implementation is not exactly trivial, so I've added some remarks to see if we can avoid some of its complexity. Let me know what you think about it.

src/HappyEyeBallsConnector.php Outdated Show resolved Hide resolved
src/HappyEyeBallsConnectionBuilder.php Show resolved Hide resolved
src/HappyEyeBallsConnectionBuilder.php Outdated Show resolved Hide resolved
src/HappyEyeBallsConnectionBuilder.php Outdated Show resolved Hide resolved
src/HappyEyeBallsConnectionBuilder.php Show resolved Hide resolved
src/HappyEyeBallsConnectionBuilder.php Outdated Show resolved Hide resolved
@WyriHaximus
Copy link
Member Author

@WyriHaximus Thanks for the update! This PR/implementation is not exactly trivial, so I've added some remarks to see if we can avoid some of its complexity. Let me know what you think about it.

It isn't trivial at all. I'll be adding links to certain implementation details linking to the RFC('s) where applicable to understand the why for those things.

@WyriHaximus WyriHaximus force-pushed the happy-eye-balls branch 2 times, most recently from 1326b88 to 335cdcb Compare June 27, 2019 15:36
@WyriHaximus
Copy link
Member Author

@clue oki updated to after your comments

Copy link
Member

@clue clue left a comment

Choose a reason for hiding this comment

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

@WyriHaximus Thank you very much, the additional comments really help, I think we're slowly getting there! :shipit: I've noticed this code currently has ~85% coverage and I've added some remarks to perhaps improve this.

Consider my structural concerns resolved, I'd love to get this in once we've added some additional tests, given the inherent complexity of this algorithm 👍

src/HappyEyeBallsConnectionBuilder.php Outdated Show resolved Hide resolved
src/HappyEyeBallsConnectionBuilder.php Show resolved Hide resolved
src/HappyEyeBallsConnector.php Outdated Show resolved Hide resolved
@WyriHaximus
Copy link
Member Author

@clue working on the tests now, noticed the same thing this morning. 15% coverage to go 🎉

@WyriHaximus WyriHaximus force-pushed the happy-eye-balls branch 4 times, most recently from c886e32 to d227b9c Compare August 25, 2019 19:43
@WyriHaximus
Copy link
Member Author

We should assert that a second connection will not be attempted while the first one is pending for less than 0.1 seconds.

We should assert that this "next connection" timer is started.

We should assert that a second connection attempt is started when the first attempt is still pending, but the timer fired.

Likewise, add similar tests to check variations of IPv4/IPv6 attempts depending on DNS resolution etc.

❌ (working on it)

@WyriHaximus WyriHaximus force-pushed the happy-eye-balls branch 4 times, most recently from dcc19c6 to 22d2f6b Compare August 29, 2019 19:37
@WyriHaximus
Copy link
Member Author

Likewise, add similar tests to check variations of IPv4/IPv6 attempts depending on DNS resolution etc.

Copy link
Member

@clue clue left a comment

Choose a reason for hiding this comment

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

@WyriHaximus Thanks for the update! All my local tests have succeeded with the updated version 👍

Unfortunately, the test suite now takes ~1.8 minutes(!) to run, whereas the old version took ~8 seconds. Perhaps rebase on #207 which should further improve this and then take a look at why the tests are so slow?

The test suite should probably use a mocked look (unless it's an integration test) and use $promise->then($this->expectCallableOneWith($expectedValue)) instead of running and awaiting the loop. In particular, all timers should be avoided unless absolutely required.


if ($that->timer instanceof TimerInterface) {
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

Can you take a look at this again? I can't reproduce this locally anymore, but I don't see any change here?

$ipv4Deferred = new Promise\Deferred();
$deferred = new Promise\Deferred();

$timer = $that->loop->addTimer($that::RESOLVE_WAIT, function () use ($deferred, $ips) {
Copy link
Member

Choose a reason for hiding this comment

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

Promise cancellation doesn't seem to cancel this timer?

Copy link
Member Author

@WyriHaximus WyriHaximus Sep 20, 2019

Choose a reason for hiding this comment

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

Addressed that

if (count($ipv4) > 0) {
$this->tcp->expects($this->at($i++))->method('connect')->with($this->equalTo('scheme://' . array_shift($ipv4) . ':80/?hostname=google.com'))->will($this->returnValue(Promise\resolve()));
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Not a big fan of having this logic in the test suite. It's my understanding this should be rather static and/or multiple independent tests. After all, where are the tests for this test suite? 👀 I'm okay with this for now.

Copy link
Member Author

Choose a reason for hiding this comment

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

Not a big fan of it either. But this lets us test different lenghth IPvX responses against several tests. This uncovered certain edge cases in the past and help me first the issue you commented about on line 60.

@WyriHaximus WyriHaximus force-pushed the happy-eye-balls branch 3 times, most recently from e73fa33 to 888941f Compare September 20, 2019 15:46
@WyriHaximus
Copy link
Member Author

@clue decreased tests time, they're still slower then before but that's due to the timings involved.

@clue
Copy link
Member

clue commented Sep 21, 2019

@WyriHaximus Can confirm the test time is now down to ~50s on my machine. There still seem to be plenty of timers, so I would still suggest mocking the loop and explicitly invoking the timer callback like this:

$loop = $this->createMock(LoopInterface::class);
$timer = null;
$loop->expects($this->once())->method('addTimer')->with(2.0, $this->callback(function ($cb) use (&$timer) {
    $timer = $cb;
    return true;
}));

// continue with test

$this->assertNotNull($timer);
$timer();

@WyriHaximus
Copy link
Member Author

@clue yeah this is going to be fun 🤐

@WyriHaximus
Copy link
Member Author

@clue went for a slightly different approach that still does a full run with an actual event loop and timers, but I've speed it up instead by a factor of 10.

Copy link
Member

@clue clue left a comment

Choose a reason for hiding this comment

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

@WyriHaximus Thanks for keeping up with this, I can confirm that test time changed to ~13s (~8s prior to this patch) 👍 Now let's get this feature shipped! :shipit:

README.md Outdated
It will then replace the hostname in the destination URI with this IP's and
append a `hostname` query parameter and pass this updated URI to the underlying
connector.
The Happy Eye Balls algorythm describes looking the IPv6 and IPv4 address for
Copy link
Member Author

Choose a reason for hiding this comment

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

algorithm

README.md Outdated
The `HappyEyeBallsConnector` class implements the
[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext
TCP/IP connections to any hostname-port-combination. Internally it implements the
happy eyeballs algorythm from [`RFC6555`](https://tools.ietf.org/html/rfc6555) and
Copy link
Member Author

Choose a reason for hiding this comment

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

algorithm

@WyriHaximus
Copy link
Member Author

So @jsor spotted two typo's after approving, will fix those before merging 👍

By using the happy eye balls algorithm as described in RFC6555 and
RFC8305 it will connect to the quickest responding server with a
preference for IPv6.
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.

3 participants