grumlin 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6633f34f12f74200b56d3447e7b1cd5c2c4185cd4013a27157d809b2f97f01b2
4
- data.tar.gz: a5b0c1b0f4a8ef813a48144b79b17a71c6385802ff77b46ef0fa703643790ac1
3
+ metadata.gz: 4260297c3d75ec7bc1f987d8c7bad4efd75c81627814fdf984a0aaac0045332b
4
+ data.tar.gz: 1cb521906a08cf5390f058a28f380492a2999e55ef023c473fc6bae64533b53b
5
5
  SHA512:
6
- metadata.gz: 6824448759e26af95f8753a1461fac6b0b66677e1cab723b8e79c76333c4e791b5364da81f87844b624796238a3e02b13065a7afb64d7a5ef7ca83a6e9c3e8be
7
- data.tar.gz: d7ce1a01b2b50f90a317d270ad005561220fcb0f69da5036b97d20c88b2458e5dd7ea285c384cef5d5e9521ff077f2d43650985c1e928942e463cfc29bbdd247
6
+ metadata.gz: 992d2192402f3fe282203a3fd0eff1e6c886470899cb37f00ca2abf1392e4d946c7391be0eb7d6560c9941f7a17a3552682a8a47f56273f4299c06551c1e9ffc
7
+ data.tar.gz: d8695e2485915cb7b4d37a755e42d20b6d7f575d906ae5a0aca5f508c01a104c08f7bb51a2285c9dac48a0091771882afeb405fd26e7802c4d48513274e2895f
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- grumlin (0.6.2)
4
+ grumlin (0.7.0)
5
5
  async-pool (~> 0.3)
6
6
  async-websocket (~> 0.19)
7
7
 
data/lib/async/channel.rb CHANGED
@@ -32,7 +32,7 @@ module Async
32
32
  end
33
33
 
34
34
  def close
35
- raise(ChannelClosedError, "Cannot close a closed channel") if closed?
35
+ return if closed?
36
36
 
37
37
  @queue << [:close]
38
38
  @closed = true
@@ -13,6 +13,7 @@ module Grumlin
13
13
  def initialize(url, client_factory:, concurrency: 1, parent: Async::Task.current)
14
14
  super(concurrency)
15
15
  @client = client_factory.call(url, parent).tap(&:connect)
16
+ @parent = parent
16
17
  end
17
18
 
18
19
  def closed?
@@ -25,6 +26,8 @@ module Grumlin
25
26
 
26
27
  def write(*args)
27
28
  @client.write(*args)
29
+ ensure
30
+ @count += 1
28
31
  end
29
32
 
30
33
  def viable?
@@ -36,33 +39,54 @@ module Grumlin
36
39
  end
37
40
  end
38
41
 
42
+ include Console
43
+
44
+ # Client is not reusable. Once closed should be recreated.
39
45
  def initialize(url, parent: Async::Task.current, **client_options)
40
46
  @url = url
41
47
  @client_options = client_options
42
48
  @parent = parent
43
- reset!
49
+ @request_dispatcher = nil
50
+ @transport = nil
44
51
  end
45
52
 
46
53
  def connect
54
+ raise "ClientClosed" if @closed
55
+
47
56
  @transport = build_transport
48
57
  response_channel = @transport.connect
49
58
  @request_dispatcher = RequestDispatcher.new
50
- @parent.async do
59
+ @response_task = @parent.async do
51
60
  response_channel.each do |response|
52
61
  @request_dispatcher.add_response(response)
53
62
  end
54
- rescue StandardError
55
- close
63
+ rescue Async::Stop, Async::TimeoutError, StandardError
64
+ close(check_requests: false)
56
65
  end
66
+ logger.debug(self, "Connected")
57
67
  end
58
68
 
59
- def close
69
+ # Before calling close the user must ensure that:
70
+ # 1) There are no ongoing requests
71
+ # 2) There will be no new writes after
72
+ def close(check_requests: true) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
73
+ return if @closed
74
+
75
+ @closed = true
76
+
60
77
  @transport&.close
61
- if @request_dispatcher&.requests&.any?
62
- raise ResourceLeakError, "Request list is not empty: #{@request_dispatcher.requests}"
63
- end
78
+ @transport&.wait
64
79
 
65
- reset!
80
+ @response_task&.stop
81
+ @response_task&.wait
82
+
83
+ return if @request_dispatcher&.requests&.empty?
84
+
85
+ @request_dispatcher.clear unless check_requests
86
+
87
+ raise ResourceLeakError, "Request list is not empty: #{@request_dispatcher.requests}" if check_requests
88
+ ensure
89
+ logger.debug(self, "Closed")
66
90
  end
67
91
 
68
92
  def connected?
@@ -80,9 +104,9 @@ module Grumlin
80
104
 
81
105
  begin
82
106
  channel.dequeue.flat_map { |item| Typing.cast(item) }
83
- rescue Async::Stop
84
- retry if @request_dispatcher.ongoing_request?(request_id)
85
- raise Grumlin::UnknownRequestStoppedError, "#{request_id} is not in the ongoing requests list"
107
+ rescue Async::Stop, Async::TimeoutError
108
+ close(check_requests: false)
109
+ raise
86
110
  end
87
111
  end
88
112
 
@@ -106,11 +130,6 @@ module Grumlin
106
130
  }
107
131
  end
108
132
 
109
- def reset!
110
- @request_dispatcher = nil
111
- @transport = nil
112
- end
113
-
114
133
  def build_transport
115
134
  Transport.new(@url, parent: @parent, **@client_options)
116
135
  end
@@ -22,6 +22,8 @@ module Grumlin
22
22
  498 => ClientSideError
23
23
  }.freeze
24
24
 
25
+ include Console
26
+
25
27
  def initialize
26
28
  @requests = {}
27
29
  end
@@ -69,6 +71,10 @@ module Grumlin
69
71
  @requests.key?(request_id)
70
72
  end
71
73
 
74
+ def clear
75
+ @requests.clear
76
+ end
77
+
72
78
  private
73
79
 
74
80
  def check_errors!(status)
@@ -5,47 +5,33 @@ module Grumlin
5
5
  # A transport based on https://github.com/socketry/async
6
6
  # and https://github.com/socketry/async-websocket
7
7
 
8
+ include Console
9
+
8
10
  attr_reader :url
9
11
 
12
+ # Transport is not reusable. Once closed should be recreated.
10
13
  def initialize(url, parent: Async::Task.current, **client_options)
11
14
  @url = url
12
15
  @parent = parent
13
16
  @client_options = client_options
14
17
  @request_channel = Async::Channel.new
15
18
  @response_channel = Async::Channel.new
16
- reset!
17
19
  end
18
20
 
19
21
  def connected?
20
- @connected
22
+ !@connection.nil?
21
23
  end
22
24
 
23
- def connect # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
25
+ def connect
26
+ raise "ClientClosed" if @closed
24
27
  raise AlreadyConnectedError if connected?
25
28
 
26
29
  @connection = Async::WebSocket::Client.connect(Async::HTTP::Endpoint.parse(@url), **@client_options)
30
+ logger.debug(self) { "Connected to #{@url}." }
27
31
 
28
- @response_task = @parent.async do
29
- loop do
30
- data = @connection.read
31
- @response_channel << data
32
- end
33
- rescue Async::Stop
34
- @response_channel.close
35
- rescue StandardError => e
36
- @response_channel.exception(e)
37
- end
32
+ @response_task = @parent.async { run_response_task }
38
33
 
39
- @request_task = @parent.async do
40
- @request_channel.each do |message|
41
- @connection.write(message)
42
- @connection.flush
43
- end
44
- rescue StandardError => e
45
- @response_channel.exception(e)
46
- end
47
-
48
- @connected = true
34
+ @request_task = @parent.async { run_request_task }
49
35
 
50
36
  @response_channel
51
37
  end
@@ -57,30 +43,59 @@ module Grumlin
57
43
  end
58
44
 
59
45
  def close
60
- return unless connected?
46
+ return if @closed
61
47
 
62
- @request_channel.close
63
- @request_task.wait
48
+ @closed = true
64
49
 
65
- @response_task.stop
66
- @response_task.wait
50
+ @request_channel.close
51
+ @response_channel.close
67
52
 
68
53
  begin
69
54
  @connection.close
70
- rescue Errno::EPIPE
55
+ rescue StandardError
71
56
  nil
72
57
  end
58
+ @connection = nil
59
+
60
+ @request_task&.stop(true)
61
+ @response_task&.stop(true)
62
+ end
73
63
 
74
- reset!
64
+ def wait
65
+ @request_task.wait
66
+ @response_task.wait
75
67
  end
76
68
 
77
69
  private
78
70
 
79
- def reset!
80
- @connected = false
81
- @connection = nil
82
- @response_task = nil
83
- @request_task = nil
71
+ def run_response_task
72
+ with_guard do
73
+ loop do
74
+ data = @connection.read
75
+ @response_channel << data
76
+ end
77
+ end
78
+ end
79
+
80
+ def run_request_task
81
+ with_guard do
82
+ @request_channel.each do |message|
83
+ @connection.write(message)
84
+ @connection.flush
85
+ end
86
+ end
87
+ end
88
+
89
+ def with_guard
90
+ yield
91
+ rescue Async::Stop, Async::TimeoutError, StandardError => e
92
+ logger.debug(self) { "Guard error, closing." }
93
+ begin
94
+ @response_channel.exception(e)
95
+ rescue Async::Channel::ChannelClosedError
96
+ nil
97
+ end
98
+ close
84
99
  end
85
100
  end
86
101
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grumlin
4
- VERSION = "0.6.2"
4
+ VERSION = "0.7.0"
5
5
  end
data/lib/grumlin.rb CHANGED
@@ -45,7 +45,7 @@ module Grumlin
45
45
 
46
46
  def initialize
47
47
  @pool_size = 10
48
- @client_concurrency = 2
48
+ @client_concurrency = 5
49
49
  @client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
50
50
  end
51
51
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grumlin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.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-09-03 00:00:00.000000000 Z
11
+ date: 2021-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-pool