From 9ed331ae3dc32bb1b175239dc1d2f65f6d997826 Mon Sep 17 00:00:00 2001 From: Niklas Keller Date: Fri, 23 Jun 2017 17:48:03 +0200 Subject: [PATCH] Implement request sharing for concurrent requests to the same resource --- lib/BasicResolver.php | 16 +++++++++++++++- test/IntegrationTest.php | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/BasicResolver.php b/lib/BasicResolver.php index 346ede7..455a643 100644 --- a/lib/BasicResolver.php +++ b/lib/BasicResolver.php @@ -30,6 +30,9 @@ class BasicResolver implements Resolver { /** @var array */ private $servers = []; + /** @var array */ + private $pendingQueries = []; + public function __construct(Cache $cache = null, ConfigLoader $configLoader = null) { $this->cache = $cache ?? new ArrayCache; $this->configLoader = $configLoader ?? \stripos(PHP_OS, "win") === 0 @@ -134,7 +137,11 @@ class BasicResolver implements Resolver { /** @inheritdoc */ public function query(string $name, int $type): Promise { - return call(function () use ($name, $type) { + if (isset($this->pendingQueries[$type . " " . $name])) { + return $this->pendingQueries[$type . " " . $name]; + } + + $promise = call(function () use ($name, $type) { if (!$this->config) { $this->config = yield $this->configLoader->loadConfig(); } @@ -209,6 +216,13 @@ class BasicResolver implements Resolver { throw new ResolutionException("No response from any nameserver after {$attempts} attempts"); }); + + $this->pendingQueries[$type . " " . $name] = $promise; + $promise->onResolve(function () use ($name, $type) { + unset($this->pendingQueries[$type . " " . $name]); + }); + + return $promise; } /** diff --git a/test/IntegrationTest.php b/test/IntegrationTest.php index 6559f08..f329fc1 100644 --- a/test/IntegrationTest.php +++ b/test/IntegrationTest.php @@ -38,6 +38,20 @@ class IntegrationTest extends TestCase { }); } + /** + * Test that two concurrent requests to the same resource share the same request and do not result in two requests + * being sent. + */ + public function testRequestSharing() { + Loop::run(function () { + $promise1 = Dns\query("example.com", Record::A); + $promise2 = Dns\query("example.com", Record::A); + + $this->assertSame($promise1, $promise2); + $this->assertSame(yield $promise1, yield $promise2); + }); + } + public function provideHostnames() { return [ ["google.com"],