grumlin 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -3
- data/bin/console +1 -1
- data/grumlin.gemspec +1 -0
- data/lib/grumlin.rb +17 -7
- data/lib/grumlin/client.rb +65 -84
- data/lib/grumlin/request_dispatcher.rb +84 -0
- data/lib/grumlin/step.rb +9 -5
- data/lib/grumlin/test/rspec/gremlin_context.rb +1 -2
- data/lib/grumlin/transport.rb +78 -0
- data/lib/grumlin/traversal.rb +3 -7
- data/lib/grumlin/version.rb +1 -1
- metadata +18 -3
- data/lib/grumlin/transport/async.rb +0 -95
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e532964db5d5afd11d89978a132a5f4f7d1a4d98c740f639acadac260c93be89
|
4
|
+
data.tar.gz: 9015cb71bbab17895d8809dc7ff514b90e9d1ceb81ed6a9eec446a0db7ec0994
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4295b9e6041726c36c05cc0aed94a645e8d7103c99351dfadef7077f14037b81daf87a4ffb1fd48105d8c1fa888e96ce173aa3be42c9b35ec58de3e289c01ed
|
7
|
+
data.tar.gz: 51824ca159be63be91fc798b40777a25d55f0ce423b07a9ce4ce9880002e94b4e4196bc0d92b4ca84bee86356f17fd90f3cc2dd9daa594f906fc2e93f11be422
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
grumlin (0.
|
4
|
+
grumlin (0.3.0)
|
5
|
+
async-pool (~> 0.3)
|
5
6
|
async-websocket (~> 0.19)
|
6
7
|
|
7
8
|
GEM
|
@@ -14,7 +15,7 @@ GEM
|
|
14
15
|
tzinfo (~> 2.0)
|
15
16
|
zeitwerk (~> 2.3)
|
16
17
|
ast (2.4.2)
|
17
|
-
async (1.30.
|
18
|
+
async (1.30.1)
|
18
19
|
console (~> 1.10)
|
19
20
|
nio4r (~> 2.3)
|
20
21
|
timers (~> 4.1)
|
@@ -124,7 +125,7 @@ GEM
|
|
124
125
|
simplecov_json_formatter (~> 0.1)
|
125
126
|
simplecov-html (0.12.3)
|
126
127
|
simplecov_json_formatter (0.1.3)
|
127
|
-
solargraph (0.
|
128
|
+
solargraph (0.43.0)
|
128
129
|
backport (~> 1.2)
|
129
130
|
benchmark
|
130
131
|
bundler (>= 1.17.2)
|
data/bin/console
CHANGED
data/grumlin.gemspec
CHANGED
data/lib/grumlin.rb
CHANGED
@@ -4,6 +4,9 @@ require "securerandom"
|
|
4
4
|
require "json"
|
5
5
|
|
6
6
|
require "async"
|
7
|
+
require "async/pool"
|
8
|
+
require "async/pool/resource"
|
9
|
+
require "async/pool/controller"
|
7
10
|
require "async/queue"
|
8
11
|
require "async/barrier"
|
9
12
|
require "async/http/endpoint"
|
@@ -12,19 +15,20 @@ require "async/websocket/client"
|
|
12
15
|
require_relative "grumlin/version"
|
13
16
|
require_relative "grumlin/exceptions"
|
14
17
|
|
15
|
-
require_relative "grumlin/transport
|
18
|
+
require_relative "grumlin/transport"
|
19
|
+
require_relative "grumlin/client"
|
16
20
|
|
17
21
|
require_relative "grumlin/vertex"
|
18
22
|
require_relative "grumlin/edge"
|
19
23
|
require_relative "grumlin/path"
|
20
24
|
require_relative "grumlin/typing"
|
21
|
-
require_relative "grumlin/client"
|
22
25
|
require_relative "grumlin/traversal"
|
26
|
+
require_relative "grumlin/request_dispatcher"
|
27
|
+
require_relative "grumlin/translator"
|
23
28
|
|
24
29
|
require_relative "grumlin/anonymous_step"
|
25
30
|
require_relative "grumlin/step"
|
26
31
|
|
27
|
-
require_relative "grumlin/translator"
|
28
32
|
require_relative "grumlin/t"
|
29
33
|
require_relative "grumlin/order"
|
30
34
|
require_relative "grumlin/u"
|
@@ -34,14 +38,20 @@ require_relative "grumlin/sugar"
|
|
34
38
|
|
35
39
|
module Grumlin
|
36
40
|
class Config
|
37
|
-
attr_accessor :url
|
41
|
+
attr_accessor :url, :pool_size, :client_concurrency
|
42
|
+
|
43
|
+
# For some reason, client_concurrency must be greather pool_size
|
44
|
+
def initialize
|
45
|
+
@pool_size = 10
|
46
|
+
@client_concurrency = 20
|
47
|
+
end
|
38
48
|
|
39
|
-
def
|
40
|
-
@
|
49
|
+
def default_pool
|
50
|
+
@default_pool ||= Async::Pool::Controller.new(Grumlin::Client::PoolResource, limit: pool_size)
|
41
51
|
end
|
42
52
|
|
43
53
|
def reset!
|
44
|
-
@
|
54
|
+
@default_pool = nil
|
45
55
|
end
|
46
56
|
end
|
47
57
|
|
data/lib/grumlin/client.rb
CHANGED
@@ -2,120 +2,101 @@
|
|
2
2
|
|
3
3
|
module Grumlin
|
4
4
|
class Client
|
5
|
-
|
6
|
-
|
7
|
-
SUCCESS = {
|
8
|
-
200 => :success,
|
9
|
-
204 => :no_content,
|
10
|
-
206 => :partial_content
|
11
|
-
}.freeze
|
12
|
-
|
13
|
-
ERRORS = {
|
14
|
-
499 => InvalidRequestArgumentsError,
|
15
|
-
500 => ServerError,
|
16
|
-
597 => ScriptEvaluationError,
|
17
|
-
599 => ServerSerializationError,
|
18
|
-
598 => ServerTimeoutError,
|
19
|
-
|
20
|
-
401 => ClientSideError,
|
21
|
-
407 => ClientSideError,
|
22
|
-
498 => ClientSideError
|
23
|
-
}.freeze
|
24
|
-
|
25
|
-
def initialize(url, autoconnect: true)
|
26
|
-
@url = url
|
27
|
-
@transport = Transport::Async.new(url)
|
28
|
-
connect if autoconnect
|
29
|
-
end
|
5
|
+
class PoolResource < self
|
6
|
+
attr :concurrency, :count
|
30
7
|
|
31
|
-
|
8
|
+
def self.call
|
9
|
+
new(Grumlin.config.url, concurrency: Grumlin.config.client_concurrency).tap(&:connect)
|
10
|
+
end
|
32
11
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
ensure
|
39
|
-
@transport.close_request(request_id)
|
40
|
-
end
|
12
|
+
def initialize(url, concurrency: 1, parent: Async::Task.current)
|
13
|
+
super(url, parent: parent)
|
14
|
+
@concurrency = concurrency
|
15
|
+
@count = 0
|
16
|
+
end
|
41
17
|
|
42
|
-
|
43
|
-
|
44
|
-
|
18
|
+
def viable?
|
19
|
+
connected?
|
20
|
+
end
|
45
21
|
|
46
|
-
|
22
|
+
def closed?
|
23
|
+
connected?
|
24
|
+
end
|
47
25
|
|
48
|
-
|
26
|
+
def reusable?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
49
30
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
31
|
+
def initialize(url, parent: Async::Task.current)
|
32
|
+
@parent = parent
|
33
|
+
@transport = Transport.new(url)
|
34
|
+
reset!
|
35
|
+
end
|
53
36
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
37
|
+
def connect
|
38
|
+
response_queue = @transport.connect
|
39
|
+
@request_dispatcher = RequestDispatcher.new
|
40
|
+
@parent.async do
|
41
|
+
response_queue.each do |response|
|
42
|
+
@request_dispatcher.add_response(response)
|
60
43
|
end
|
61
44
|
end
|
62
|
-
rescue ::Async::Stop
|
63
|
-
retry if @transport.ongoing_request?(request_id)
|
64
|
-
raise UnknownRequestStopped, "#{request_id} is not in the ongoing requests list"
|
65
45
|
end
|
66
46
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
bytecode_query_message(request_id, Translator.to_bytecode_query(message))
|
73
|
-
end
|
47
|
+
def close
|
48
|
+
@transport.close
|
49
|
+
raise ResourceLeakError, "Request list is not empty: #{requests}" if @request_dispatcher.requests.any?
|
50
|
+
|
51
|
+
reset!
|
74
52
|
end
|
75
53
|
|
76
|
-
def
|
77
|
-
|
54
|
+
def connected?
|
55
|
+
@transport.connected?
|
56
|
+
end
|
78
57
|
|
79
|
-
|
58
|
+
# TODO: support yielding
|
59
|
+
def write(*args) # rubocop:disable Metrics/MethodLength
|
60
|
+
request_id = SecureRandom.uuid
|
61
|
+
request = to_query(request_id, args)
|
62
|
+
queue = @request_dispatcher.add_request(request)
|
63
|
+
@transport.write(request)
|
80
64
|
|
81
|
-
|
82
|
-
|
83
|
-
|
65
|
+
begin
|
66
|
+
msg, response = queue.dequeue
|
67
|
+
raise response if msg == :error
|
84
68
|
|
85
|
-
|
69
|
+
return response.flat_map { |item| Typing.cast(item) } if msg == :result
|
86
70
|
|
87
|
-
|
71
|
+
raise "ERROR"
|
72
|
+
rescue Async::Stop
|
73
|
+
retry if @request_dispatcher.ongoing_request?(request_id)
|
74
|
+
raise UnknownRequestStopped, "#{request_id} is not in the ongoing requests list"
|
75
|
+
end
|
88
76
|
end
|
89
77
|
|
90
|
-
def
|
91
|
-
|
92
|
-
rescue StandardError
|
93
|
-
raise UnknownError
|
78
|
+
def inspect
|
79
|
+
"<#{self.class} url=#{@transport.url}>"
|
94
80
|
end
|
95
81
|
|
96
|
-
|
97
|
-
{
|
98
|
-
requestId: request_id,
|
99
|
-
op: "eval",
|
100
|
-
processor: "",
|
101
|
-
args: {
|
102
|
-
gremlin: query,
|
103
|
-
bindings: bindings,
|
104
|
-
language: "gremlin-groovy"
|
105
|
-
}
|
106
|
-
}
|
107
|
-
end
|
82
|
+
alias to_s inspect
|
108
83
|
|
109
|
-
|
84
|
+
private
|
85
|
+
|
86
|
+
def to_query(request_id, message)
|
110
87
|
{
|
111
88
|
requestId: request_id,
|
112
89
|
op: "bytecode",
|
113
90
|
processor: "traversal",
|
114
91
|
args: {
|
115
|
-
gremlin: Typing.to_bytecode(
|
92
|
+
gremlin: Typing.to_bytecode(Translator.to_bytecode_query(message)),
|
116
93
|
aliases: { g: :g }
|
117
94
|
}
|
118
95
|
}
|
119
96
|
end
|
97
|
+
|
98
|
+
def reset!
|
99
|
+
@request_dispatcher = nil
|
100
|
+
end
|
120
101
|
end
|
121
102
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
class RequestDispatcher
|
5
|
+
attr_reader :requests
|
6
|
+
|
7
|
+
SUCCESS = {
|
8
|
+
200 => :success,
|
9
|
+
204 => :no_content,
|
10
|
+
206 => :partial_content
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
ERRORS = {
|
14
|
+
499 => InvalidRequestArgumentsError,
|
15
|
+
500 => ServerError,
|
16
|
+
597 => ScriptEvaluationError,
|
17
|
+
599 => ServerSerializationError,
|
18
|
+
598 => ServerTimeoutError,
|
19
|
+
|
20
|
+
401 => ClientSideError,
|
21
|
+
407 => ClientSideError,
|
22
|
+
498 => ClientSideError
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@requests = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_request(request)
|
30
|
+
raise "ERROR" if @requests.key?(request[:requestId])
|
31
|
+
|
32
|
+
Async::Queue.new.tap do |queue|
|
33
|
+
@requests[request[:requestId]] = { request: request, result: [], queue: queue }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# builds a response object, when it's ready sends it to the client via a queue
|
38
|
+
# TODO: sometimes response does not include requestID, no idea how to handle it so far.
|
39
|
+
def add_response(response) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
40
|
+
request_id = response[:requestId]
|
41
|
+
raise "ERROR" unless ongoing_request?(request_id)
|
42
|
+
|
43
|
+
request = @requests[request_id]
|
44
|
+
|
45
|
+
check_errors!(response[:status])
|
46
|
+
|
47
|
+
case SUCCESS[response.dig(:status, :code)]
|
48
|
+
when :success
|
49
|
+
request[:queue] << [:result, request[:result] + [response.dig(:result, :data)]]
|
50
|
+
close_request(request_id)
|
51
|
+
when :partial_content then request[:result] << response.dig(:result, :data)
|
52
|
+
when :no_content
|
53
|
+
request[:queue] << [:result, []]
|
54
|
+
close_request(request_id)
|
55
|
+
end
|
56
|
+
rescue StandardError => e
|
57
|
+
request[:queue] << [:error, e]
|
58
|
+
close_request(request_id)
|
59
|
+
end
|
60
|
+
|
61
|
+
def close_request(request_id)
|
62
|
+
raise "ERROR" unless ongoing_request?(request_id)
|
63
|
+
|
64
|
+
request = @requests.delete(request_id)
|
65
|
+
request[:queue] << nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def ongoing_request?(request_id)
|
69
|
+
@requests.key?(request_id)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def check_errors!(status)
|
75
|
+
if (error = ERRORS[status[:code]])
|
76
|
+
raise(error, status)
|
77
|
+
end
|
78
|
+
|
79
|
+
return unless SUCCESS[status[:code]].nil?
|
80
|
+
|
81
|
+
raise(UnknownResponseStatus, status)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/grumlin/step.rb
CHANGED
@@ -4,9 +4,9 @@ module Grumlin
|
|
4
4
|
class Step < AnonymousStep
|
5
5
|
attr_reader :client
|
6
6
|
|
7
|
-
def initialize(
|
7
|
+
def initialize(pool, name, *args, previous_steps: [])
|
8
8
|
super(name, *args, previous_steps: previous_steps)
|
9
|
-
@
|
9
|
+
@pool = pool
|
10
10
|
end
|
11
11
|
|
12
12
|
def next
|
@@ -15,17 +15,21 @@ module Grumlin
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def toList # rubocop:disable Naming/MethodName
|
18
|
-
@
|
18
|
+
@pool.acquire do |client|
|
19
|
+
client.write(*steps)
|
20
|
+
end
|
19
21
|
end
|
20
22
|
|
21
23
|
def iterate
|
22
|
-
@
|
24
|
+
@pool.acquire do |client|
|
25
|
+
client.write(*(steps + [nil]))
|
26
|
+
end
|
23
27
|
end
|
24
28
|
|
25
29
|
private
|
26
30
|
|
27
31
|
def add_step(step_name, args, previous_steps:)
|
28
|
-
self.class.new(@
|
32
|
+
self.class.new(@pool, step_name, *args, previous_steps: previous_steps)
|
29
33
|
end
|
30
34
|
end
|
31
35
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
class Transport
|
5
|
+
# A transport based on https://github.com/socketry/async
|
6
|
+
# and https://github.com/socketry/async-websocket
|
7
|
+
def initialize(url, parent: Async::Task.current)
|
8
|
+
@endpoint = Async::HTTP::Endpoint.parse(url)
|
9
|
+
@parent = parent
|
10
|
+
@request_queue = Async::Queue.new
|
11
|
+
@response_queue = Async::Queue.new
|
12
|
+
reset!
|
13
|
+
end
|
14
|
+
|
15
|
+
def url
|
16
|
+
@endpoint.url
|
17
|
+
end
|
18
|
+
|
19
|
+
def connected?
|
20
|
+
@connected
|
21
|
+
end
|
22
|
+
|
23
|
+
def connect # rubocop:disable Metrics/MethodLength
|
24
|
+
raise AlreadyConnectedError if connected?
|
25
|
+
|
26
|
+
@connection = Async::WebSocket::Client.connect(@endpoint)
|
27
|
+
|
28
|
+
@response_task = @parent.async do
|
29
|
+
loop do
|
30
|
+
data = @connection.read
|
31
|
+
@response_queue << data
|
32
|
+
end
|
33
|
+
rescue Async::Stop
|
34
|
+
@response_queue << nil
|
35
|
+
end
|
36
|
+
|
37
|
+
@request_task = @parent.async do
|
38
|
+
@request_queue.each do |message|
|
39
|
+
@connection.write(message)
|
40
|
+
@connection.flush
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@connected = true
|
45
|
+
|
46
|
+
@response_queue
|
47
|
+
end
|
48
|
+
|
49
|
+
def write(message)
|
50
|
+
raise NotConnectedError unless connected?
|
51
|
+
|
52
|
+
@request_queue << message
|
53
|
+
end
|
54
|
+
|
55
|
+
def close
|
56
|
+
raise NotConnectedError unless connected?
|
57
|
+
|
58
|
+
@request_queue << nil
|
59
|
+
@request_task.wait
|
60
|
+
|
61
|
+
@response_task.stop
|
62
|
+
@response_task.wait
|
63
|
+
|
64
|
+
@connection.close
|
65
|
+
|
66
|
+
reset!
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def reset!
|
72
|
+
@connected = false
|
73
|
+
@connection = nil
|
74
|
+
@response_task = nil
|
75
|
+
@request_task = nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/grumlin/traversal.rb
CHANGED
@@ -4,18 +4,14 @@ module Grumlin
|
|
4
4
|
class Traversal
|
5
5
|
attr_reader :connection
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
9
|
-
Grumlin::Client.new(client_or_url)
|
10
|
-
else
|
11
|
-
client_or_url
|
12
|
-
end
|
7
|
+
def initialize(pool = Grumlin.config.default_pool)
|
8
|
+
@pool = pool
|
13
9
|
end
|
14
10
|
|
15
11
|
# TODO: add other start steps
|
16
12
|
%w[addV addE V E].each do |step|
|
17
13
|
define_method step do |*args|
|
18
|
-
Step.new(@
|
14
|
+
Step.new(@pool, step, *args)
|
19
15
|
end
|
20
16
|
end
|
21
17
|
|
data/lib/grumlin/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grumlin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gleb Sinyavskiy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-07-
|
11
|
+
date: 2021-07-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: async-pool
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.3'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: async-websocket
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -57,6 +71,7 @@ files:
|
|
57
71
|
- lib/grumlin/p.rb
|
58
72
|
- lib/grumlin/path.rb
|
59
73
|
- lib/grumlin/pop.rb
|
74
|
+
- lib/grumlin/request_dispatcher.rb
|
60
75
|
- lib/grumlin/step.rb
|
61
76
|
- lib/grumlin/sugar.rb
|
62
77
|
- lib/grumlin/t.rb
|
@@ -64,7 +79,7 @@ files:
|
|
64
79
|
- lib/grumlin/test/rspec/db_cleaner_context.rb
|
65
80
|
- lib/grumlin/test/rspec/gremlin_context.rb
|
66
81
|
- lib/grumlin/translator.rb
|
67
|
-
- lib/grumlin/transport
|
82
|
+
- lib/grumlin/transport.rb
|
68
83
|
- lib/grumlin/traversal.rb
|
69
84
|
- lib/grumlin/typing.rb
|
70
85
|
- lib/grumlin/u.rb
|
@@ -1,95 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Grumlin
|
4
|
-
module Transport
|
5
|
-
# A transport based on https://github.com/socketry/async
|
6
|
-
# and https://github.com/socketry/async-websocket
|
7
|
-
class Async
|
8
|
-
attr_reader :requests
|
9
|
-
|
10
|
-
def initialize(url, task: ::Async::Task.current)
|
11
|
-
@task = task
|
12
|
-
@endpoint = ::Async::HTTP::Endpoint.parse(url)
|
13
|
-
|
14
|
-
@requests = {}
|
15
|
-
@query_queue = ::Async::Queue.new
|
16
|
-
end
|
17
|
-
|
18
|
-
def connect
|
19
|
-
raise AlreadyConnectedError if connected?
|
20
|
-
|
21
|
-
@client = ::Async::WebSocket::Client.open(@endpoint)
|
22
|
-
@connection = @client.connect(@endpoint.authority, @endpoint.path)
|
23
|
-
|
24
|
-
@tasks_barrier = ::Async::Barrier.new(parent: @task)
|
25
|
-
|
26
|
-
@tasks_barrier.async { query_task }
|
27
|
-
@tasks_barrier.async { response_task }
|
28
|
-
rescue StandardError
|
29
|
-
raise ConnectionError
|
30
|
-
end
|
31
|
-
|
32
|
-
def disconnect
|
33
|
-
raise NotConnectedError unless connected?
|
34
|
-
|
35
|
-
@tasks_barrier.tasks.each(&:stop)
|
36
|
-
@tasks_barrier.wait
|
37
|
-
|
38
|
-
@connection.close
|
39
|
-
@client.close
|
40
|
-
|
41
|
-
@client = nil
|
42
|
-
@connection = nil
|
43
|
-
@tasks_barrier = nil
|
44
|
-
|
45
|
-
raise ResourceLeakError, "ongoing requests list is not empty: #{@requests.count} items" unless @requests.empty?
|
46
|
-
raise ResourceLeakError, "query queue empty: #{@query.count} items" unless @query_queue.empty?
|
47
|
-
end
|
48
|
-
|
49
|
-
# Raw message
|
50
|
-
def submit(message)
|
51
|
-
raise NotConnectedError unless connected?
|
52
|
-
|
53
|
-
uuid = message[:requestId]
|
54
|
-
::Async::Queue.new.tap do |queue|
|
55
|
-
@requests[uuid] = queue
|
56
|
-
@query_queue << message
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def close_request(request_id)
|
61
|
-
@requests.delete(request_id)
|
62
|
-
end
|
63
|
-
|
64
|
-
def ongoing_request?(request_id)
|
65
|
-
@requests.key?(request_id)
|
66
|
-
end
|
67
|
-
|
68
|
-
def connected?
|
69
|
-
!@connection.nil?
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def query_task
|
75
|
-
@query_queue.each do |query|
|
76
|
-
@connection.write(query)
|
77
|
-
@connection.flush
|
78
|
-
end
|
79
|
-
rescue StandardError
|
80
|
-
raise DisconnectError
|
81
|
-
end
|
82
|
-
|
83
|
-
def response_task
|
84
|
-
loop do
|
85
|
-
response = @connection.read
|
86
|
-
# TODO: sometimes response does not include requestID, no idea how to handle it so far.
|
87
|
-
response_queue = @requests[response[:requestId]]
|
88
|
-
response_queue << [:response, response]
|
89
|
-
end
|
90
|
-
rescue StandardError
|
91
|
-
raise DisconnectError
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|