From 4996c00bea465640ff081077ff8fd8ea591e4d99 Mon Sep 17 00:00:00 2001 From: Trevor Turk Date: Tue, 9 Mar 2021 11:21:00 -0600 Subject: [PATCH 1/6] lazy-resolve simple --- .../.ruby-version | 1 + .../Gemfile | 8 ++ .../Gemfile.lock | 78 +++++++++++++++++++ .../README.md | 17 ++++ .../app.rb | 13 ++++ .../config.ru | 7 ++ .../query.rb | 28 +++++++ .../schema.rb | 6 ++ 8 files changed, 158 insertions(+) create mode 100644 rack-async-http-falcon-graphql-lazy-resolve/.ruby-version create mode 100644 rack-async-http-falcon-graphql-lazy-resolve/Gemfile create mode 100644 rack-async-http-falcon-graphql-lazy-resolve/Gemfile.lock create mode 100644 rack-async-http-falcon-graphql-lazy-resolve/README.md create mode 100644 rack-async-http-falcon-graphql-lazy-resolve/app.rb create mode 100644 rack-async-http-falcon-graphql-lazy-resolve/config.ru create mode 100644 rack-async-http-falcon-graphql-lazy-resolve/query.rb create mode 100644 rack-async-http-falcon-graphql-lazy-resolve/schema.rb diff --git a/rack-async-http-falcon-graphql-lazy-resolve/.ruby-version b/rack-async-http-falcon-graphql-lazy-resolve/.ruby-version new file mode 100644 index 0000000..37c2961 --- /dev/null +++ b/rack-async-http-falcon-graphql-lazy-resolve/.ruby-version @@ -0,0 +1 @@ +2.7.2 diff --git a/rack-async-http-falcon-graphql-lazy-resolve/Gemfile b/rack-async-http-falcon-graphql-lazy-resolve/Gemfile new file mode 100644 index 0000000..2fc9c13 --- /dev/null +++ b/rack-async-http-falcon-graphql-lazy-resolve/Gemfile @@ -0,0 +1,8 @@ +source "https://rubygems.org" + +ruby "2.7.2" + +gem "rack" +gem "falcon" +gem "async-http" +gem "graphql" diff --git a/rack-async-http-falcon-graphql-lazy-resolve/Gemfile.lock b/rack-async-http-falcon-graphql-lazy-resolve/Gemfile.lock new file mode 100644 index 0000000..eb211e5 --- /dev/null +++ b/rack-async-http-falcon-graphql-lazy-resolve/Gemfile.lock @@ -0,0 +1,78 @@ +GEM + remote: https://rubygems.org/ + specs: + async (1.28.7) + console (~> 1.10) + nio4r (~> 2.3) + timers (~> 4.1) + async-container (0.16.8) + async (~> 1.0) + async-io (~> 1.26) + async-http (0.54.1) + async (~> 1.25) + async-io (~> 1.28) + async-pool (~> 0.2) + protocol-http (~> 0.21.0) + protocol-http1 (~> 0.13.0) + protocol-http2 (~> 0.14.0) + async-http-cache (0.3.0) + async-http (~> 0.53) + async-io (1.30.2) + async (~> 1.14) + async-pool (0.3.4) + async (~> 1.25) + build-environment (1.13.0) + console (1.10.1) + fiber-local + falcon (0.37.2) + async (~> 1.13) + async-container (~> 0.16.0) + async-http (~> 0.54.0) + async-http-cache (~> 0.3.0) + async-io (~> 1.22) + build-environment (~> 1.13) + bundler + localhost (~> 1.1) + process-metrics (~> 0.2.0) + rack (>= 1.0) + samovar (~> 2.1) + fiber-local (1.0.0) + graphql (1.12.4) + graphql-batch (0.4.3) + graphql (>= 1.3, < 2) + promise.rb (~> 0.7.2) + localhost (1.1.6) + mapping (1.1.1) + nio4r (2.5.5) + process-metrics (0.2.1) + console (~> 1.8) + samovar (~> 2.1) + promise.rb (0.7.4) + protocol-hpack (1.4.2) + protocol-http (0.21.0) + protocol-http1 (0.13.2) + protocol-http (~> 0.19) + protocol-http2 (0.14.2) + protocol-hpack (~> 1.4) + protocol-http (~> 0.18) + rack (2.2.3) + samovar (2.1.4) + console (~> 1.0) + mapping (~> 1.0) + timers (4.3.2) + +PLATFORMS + ruby + +DEPENDENCIES + async-http + falcon + graphql + graphql-batch + rack + +RUBY VERSION + ruby 2.7.2p137 + +BUNDLED WITH + 2.1.4 diff --git a/rack-async-http-falcon-graphql-lazy-resolve/README.md b/rack-async-http-falcon-graphql-lazy-resolve/README.md new file mode 100644 index 0000000..c0e29d2 --- /dev/null +++ b/rack-async-http-falcon-graphql-lazy-resolve/README.md @@ -0,0 +1,17 @@ +Example app using: + * rack + * falcon + * async-http + * graphql + +Notes: + * The same idea as rack-async-http-falcon-graphql, but... + * Using lazy_resolve instead of a batch loader + * Re: https://github.com/trevorturk/async-examples/issues/2 + +Running and benchmarking: + + falcon serve --count 1 + ab -n 100 -c 100 https://localhost:9292/ + +Benchmark results: diff --git a/rack-async-http-falcon-graphql-lazy-resolve/app.rb b/rack-async-http-falcon-graphql-lazy-resolve/app.rb new file mode 100644 index 0000000..34a9104 --- /dev/null +++ b/rack-async-http-falcon-graphql-lazy-resolve/app.rb @@ -0,0 +1,13 @@ +require_relative "schema" + +class App + def self.call(env) + puts "Request start" + + result = Schema.execute("query { one two three }") + + puts "Request finish" + + [200, {}, [result.to_json]] + end +end diff --git a/rack-async-http-falcon-graphql-lazy-resolve/config.ru b/rack-async-http-falcon-graphql-lazy-resolve/config.ru new file mode 100644 index 0000000..b929d04 --- /dev/null +++ b/rack-async-http-falcon-graphql-lazy-resolve/config.ru @@ -0,0 +1,7 @@ +require "rubygems" +require "bundler" +Bundler.require + +require_relative "app" + +run App diff --git a/rack-async-http-falcon-graphql-lazy-resolve/query.rb b/rack-async-http-falcon-graphql-lazy-resolve/query.rb new file mode 100644 index 0000000..f5309e2 --- /dev/null +++ b/rack-async-http-falcon-graphql-lazy-resolve/query.rb @@ -0,0 +1,28 @@ +require "async/http/internet" + +class Query < GraphQL::Schema::Object + field :one, String, null: false + field :two, String, null: false + field :three, String, null: false + + def one + Async do + response = Async::HTTP::Internet.new.get("https://httpbin.org/delay/1").read + JSON.parse(response)["url"] + end + end + + def two + Async do + response = Async::HTTP::Internet.new.get("https://httpbin.org/delay/2").read + JSON.parse(response)["url"] + end + end + + def three + Async do + response = Async::HTTP::Internet.new.get("https://httpbin.org/delay/2").read + JSON.parse(response)["url"] + end + end +end diff --git a/rack-async-http-falcon-graphql-lazy-resolve/schema.rb b/rack-async-http-falcon-graphql-lazy-resolve/schema.rb new file mode 100644 index 0000000..4cbcafc --- /dev/null +++ b/rack-async-http-falcon-graphql-lazy-resolve/schema.rb @@ -0,0 +1,6 @@ +require_relative "query" + +class Schema < GraphQL::Schema + query Query + lazy_resolve Async::Task, :wait +end From 7be7c5e917a2ed092747403082a5cc6282ff3f87 Mon Sep 17 00:00:00 2001 From: Trevor Turk Date: Tue, 9 Mar 2021 11:31:53 -0600 Subject: [PATCH 2/6] try to improve it, but break it --- .../query.rb | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/rack-async-http-falcon-graphql-lazy-resolve/query.rb b/rack-async-http-falcon-graphql-lazy-resolve/query.rb index f5309e2..38509e5 100644 --- a/rack-async-http-falcon-graphql-lazy-resolve/query.rb +++ b/rack-async-http-falcon-graphql-lazy-resolve/query.rb @@ -6,23 +6,32 @@ class Query < GraphQL::Schema::Object field :three, String, null: false def one - Async do - response = Async::HTTP::Internet.new.get("https://httpbin.org/delay/1").read - JSON.parse(response)["url"] - end + delay_1_data["url"] end def two - Async do - response = Async::HTTP::Internet.new.get("https://httpbin.org/delay/2").read - JSON.parse(response)["url"] - end + delay_2_data["url"] end def three - Async do - response = Async::HTTP::Internet.new.get("https://httpbin.org/delay/2").read - JSON.parse(response)["url"] + delay_2_data["url"] + end + + def internet + @_internet ||= Async::HTTP::Internet.new + end + + def delay_1_data + @_delay_1_data ||= Async do + puts "getting delay_1_data" + JSON.parse(internet.get("https://httpbin.org/delay/1").read) + end + end + + def delay_2_data + @_delay_2_data ||= Async do + puts "getting delay_2_data" + JSON.parse(internet.get("https://httpbin.org/delay/2").read) end end end From 70a3529f3edbe5d7820a50c730144dbacc4cd8cf Mon Sep 17 00:00:00 2001 From: Trevor Turk Date: Tue, 9 Mar 2021 11:33:31 -0600 Subject: [PATCH 3/6] yield fixes it, but why --- .../query.rb | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/rack-async-http-falcon-graphql-lazy-resolve/query.rb b/rack-async-http-falcon-graphql-lazy-resolve/query.rb index 38509e5..72b20a3 100644 --- a/rack-async-http-falcon-graphql-lazy-resolve/query.rb +++ b/rack-async-http-falcon-graphql-lazy-resolve/query.rb @@ -6,32 +6,38 @@ class Query < GraphQL::Schema::Object field :three, String, null: false def one - delay_1_data["url"] + delay_1_data do |data| + data["url"] + end end def two - delay_2_data["url"] + delay_2_data do |data| + data["url"] + end end def three - delay_2_data["url"] + delay_2_data do |data| + data["url"] + end end def internet @_internet ||= Async::HTTP::Internet.new end - def delay_1_data + def delay_1_data(&block) @_delay_1_data ||= Async do puts "getting delay_1_data" - JSON.parse(internet.get("https://httpbin.org/delay/1").read) + yield JSON.parse(internet.get("https://httpbin.org/delay/1").read) end end - def delay_2_data + def delay_2_data(&block) @_delay_2_data ||= Async do puts "getting delay_2_data" - JSON.parse(internet.get("https://httpbin.org/delay/2").read) + yield JSON.parse(internet.get("https://httpbin.org/delay/2").read) end end end From 8857ab82057a3e9b46302e2201405a061d89a3d9 Mon Sep 17 00:00:00 2001 From: Trevor Turk Date: Tue, 9 Mar 2021 21:43:36 -0600 Subject: [PATCH 4/6] make sure to return an Async::Task --- .../query.rb | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/rack-async-http-falcon-graphql-lazy-resolve/query.rb b/rack-async-http-falcon-graphql-lazy-resolve/query.rb index 72b20a3..0868bd4 100644 --- a/rack-async-http-falcon-graphql-lazy-resolve/query.rb +++ b/rack-async-http-falcon-graphql-lazy-resolve/query.rb @@ -6,38 +6,32 @@ class Query < GraphQL::Schema::Object field :three, String, null: false def one - delay_1_data do |data| - data["url"] - end + Async { delay_1_data["url"] } end def two - delay_2_data do |data| - data["url"] - end + Async { delay_2_data["url"] } end def three - delay_2_data do |data| - data["url"] - end + Async { delay_2_data["url"] } end def internet @_internet ||= Async::HTTP::Internet.new end - def delay_1_data(&block) - @_delay_1_data ||= Async do + def delay_1_data + @_delay_1_data ||= begin puts "getting delay_1_data" - yield JSON.parse(internet.get("https://httpbin.org/delay/1").read) + JSON.parse(internet.get("https://httpbin.org/delay/1").read) end end - def delay_2_data(&block) - @_delay_2_data ||= Async do + def delay_2_data + @_delay_2_data ||= begin puts "getting delay_2_data" - yield JSON.parse(internet.get("https://httpbin.org/delay/2").read) + JSON.parse(internet.get("https://httpbin.org/delay/2").read) end end end From ea4241b75ff5b39ff40dc6feb2c649e8231e89d2 Mon Sep 17 00:00:00 2001 From: Trevor Turk Date: Wed, 10 Mar 2021 10:26:04 -0600 Subject: [PATCH 5/6] use semaphores so memoizing works --- .../query.rb | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/rack-async-http-falcon-graphql-lazy-resolve/query.rb b/rack-async-http-falcon-graphql-lazy-resolve/query.rb index 0868bd4..4751a35 100644 --- a/rack-async-http-falcon-graphql-lazy-resolve/query.rb +++ b/rack-async-http-falcon-graphql-lazy-resolve/query.rb @@ -21,17 +21,33 @@ def internet @_internet ||= Async::HTTP::Internet.new end + def delay_1_semaphore + @_delay_1_semaphore ||= Async::Semaphore.new + end + + def delay_2_semaphore + @_delay_2_semaphore ||= Async::Semaphore.new + end + def delay_1_data - @_delay_1_data ||= begin - puts "getting delay_1_data" - JSON.parse(internet.get("https://httpbin.org/delay/1").read) - end + delay_1_semaphore.async do |task| + @_delay_1_data ||= begin + puts "-> delay_1_data" + data = JSON.parse(internet.get("https://httpbin.org/delay/1").read) + puts "<- delay_1_data" + data + end + end.result end def delay_2_data - @_delay_2_data ||= begin - puts "getting delay_2_data" - JSON.parse(internet.get("https://httpbin.org/delay/2").read) - end + delay_2_semaphore.async do |task| + @_delay_2_data ||= begin + puts "-> delay_2_data" + data = JSON.parse(internet.get("https://httpbin.org/delay/2").read) + puts "<- delay_2_data" + data + end + end.result end end From 6e50566429832919043de60d40d3100c028dfbe0 Mon Sep 17 00:00:00 2001 From: Trevor Turk Date: Wed, 10 Mar 2021 11:13:12 -0600 Subject: [PATCH 6/6] benchmark --- .../README.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/rack-async-http-falcon-graphql-lazy-resolve/README.md b/rack-async-http-falcon-graphql-lazy-resolve/README.md index c0e29d2..b5bb2fc 100644 --- a/rack-async-http-falcon-graphql-lazy-resolve/README.md +++ b/rack-async-http-falcon-graphql-lazy-resolve/README.md @@ -15,3 +15,42 @@ Running and benchmarking: ab -n 100 -c 100 https://localhost:9292/ Benchmark results: + + Server Software: + Server Hostname: localhost + Server Port: 9292 + SSL/TLS Protocol: TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256 + Server Temp Key: ECDH P-256 256 bits + TLS Server Name: localhost + + Document Path: / + Document Length: 120 bytes + + Concurrency Level: 100 + Time taken for tests: 5.140 seconds + Complete requests: 100 + Failed requests: 0 + Total transferred: 18300 bytes + HTML transferred: 12000 bytes + Requests per second: 19.45 [#/sec] (mean) + Time per request: 5140.459 [ms] (mean) + Time per request: 51.405 [ms] (mean, across all concurrent requests) + Transfer rate: 3.48 [Kbytes/sec] received + + Connection Times (ms) + min mean[+/-sd] median max + Connect: 45 219 64.6 270 273 + Processing: 2213 2287 67.8 2258 2570 + Waiting: 2213 2285 68.2 2258 2567 + Total: 2324 2506 71.4 2521 2821 + + Percentage of the requests served within a certain time (ms) + 50% 2521 + 66% 2527 + 75% 2528 + 80% 2530 + 90% 2605 + 95% 2681 + 98% 2687 + 99% 2821 + 100% 2821 (longest request)