wamp-worker 0.1.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.
Files changed (129) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +5 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +204 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/bin/wamp-worker +46 -0
  13. data/lib/wamp/worker.rb +132 -0
  14. data/lib/wamp/worker/config.rb +184 -0
  15. data/lib/wamp/worker/handler.rb +196 -0
  16. data/lib/wamp/worker/proxy/backgrounder.rb +38 -0
  17. data/lib/wamp/worker/proxy/base.rb +101 -0
  18. data/lib/wamp/worker/proxy/dispatcher.rb +115 -0
  19. data/lib/wamp/worker/proxy/requestor.rb +91 -0
  20. data/lib/wamp/worker/queue.rb +135 -0
  21. data/lib/wamp/worker/rails.rb +28 -0
  22. data/lib/wamp/worker/runner.rb +240 -0
  23. data/lib/wamp/worker/ticker.rb +30 -0
  24. data/lib/wamp/worker/version.rb +5 -0
  25. data/spec/spec_helper.rb +29 -0
  26. data/spec/support/client_stub.rb +47 -0
  27. data/spec/support/handler_stub.rb +105 -0
  28. data/spec/support/redis_stub.rb +89 -0
  29. data/spec/support/session_stub.rb +101 -0
  30. data/spec/wamp/worker/config_spec.rb +90 -0
  31. data/spec/wamp/worker/handler_spec.rb +162 -0
  32. data/spec/wamp/worker/proxy_spec.rb +153 -0
  33. data/spec/wamp/worker/queue_spec.rb +49 -0
  34. data/spec/wamp/worker/runner_spec.rb +108 -0
  35. data/spec/wamp/worker_spec.rb +8 -0
  36. data/test/app_test.rb +124 -0
  37. data/test/hello.py +50 -0
  38. data/test/sidekiq.yml +5 -0
  39. data/test/wamp_test/.generators +8 -0
  40. data/test/wamp_test/.ruby-version +1 -0
  41. data/test/wamp_test/Gemfile +65 -0
  42. data/test/wamp_test/Gemfile.lock +246 -0
  43. data/test/wamp_test/README.md +24 -0
  44. data/test/wamp_test/Rakefile +6 -0
  45. data/test/wamp_test/app/assets/config/manifest.js +3 -0
  46. data/test/wamp_test/app/assets/images/.keep +0 -0
  47. data/test/wamp_test/app/assets/javascripts/application.js +16 -0
  48. data/test/wamp_test/app/assets/javascripts/cable.js +13 -0
  49. data/test/wamp_test/app/assets/javascripts/channels/.keep +0 -0
  50. data/test/wamp_test/app/assets/stylesheets/application.css +15 -0
  51. data/test/wamp_test/app/channels/application_cable/channel.rb +4 -0
  52. data/test/wamp_test/app/channels/application_cable/connection.rb +4 -0
  53. data/test/wamp_test/app/controllers/add_controller.rb +11 -0
  54. data/test/wamp_test/app/controllers/application_controller.rb +3 -0
  55. data/test/wamp_test/app/controllers/concerns/.keep +0 -0
  56. data/test/wamp_test/app/controllers/ping_controller.rb +7 -0
  57. data/test/wamp_test/app/handlers/add_handler.rb +9 -0
  58. data/test/wamp_test/app/handlers/back_add_handler.rb +26 -0
  59. data/test/wamp_test/app/handlers/back_ping_handler.rb +10 -0
  60. data/test/wamp_test/app/handlers/ping_handler.rb +10 -0
  61. data/test/wamp_test/app/helpers/application_helper.rb +2 -0
  62. data/test/wamp_test/app/jobs/application_job.rb +2 -0
  63. data/test/wamp_test/app/mailers/application_mailer.rb +4 -0
  64. data/test/wamp_test/app/models/application_record.rb +3 -0
  65. data/test/wamp_test/app/models/concerns/.keep +0 -0
  66. data/test/wamp_test/app/views/layouts/application.html.erb +15 -0
  67. data/test/wamp_test/app/views/layouts/mailer.html.erb +13 -0
  68. data/test/wamp_test/app/views/layouts/mailer.text.erb +1 -0
  69. data/test/wamp_test/bin/bundle +3 -0
  70. data/test/wamp_test/bin/rails +9 -0
  71. data/test/wamp_test/bin/rake +9 -0
  72. data/test/wamp_test/bin/setup +36 -0
  73. data/test/wamp_test/bin/spring +17 -0
  74. data/test/wamp_test/bin/update +31 -0
  75. data/test/wamp_test/bin/yarn +11 -0
  76. data/test/wamp_test/config.ru +5 -0
  77. data/test/wamp_test/config/application.rb +19 -0
  78. data/test/wamp_test/config/boot.rb +4 -0
  79. data/test/wamp_test/config/cable.yml +10 -0
  80. data/test/wamp_test/config/credentials.yml.enc +1 -0
  81. data/test/wamp_test/config/database.yml +25 -0
  82. data/test/wamp_test/config/environment.rb +5 -0
  83. data/test/wamp_test/config/environments/development.rb +61 -0
  84. data/test/wamp_test/config/environments/production.rb +94 -0
  85. data/test/wamp_test/config/environments/test.rb +46 -0
  86. data/test/wamp_test/config/initializers/application_controller_renderer.rb +8 -0
  87. data/test/wamp_test/config/initializers/assets.rb +14 -0
  88. data/test/wamp_test/config/initializers/backtrace_silencers.rb +7 -0
  89. data/test/wamp_test/config/initializers/content_security_policy.rb +25 -0
  90. data/test/wamp_test/config/initializers/cookies_serializer.rb +5 -0
  91. data/test/wamp_test/config/initializers/filter_parameter_logging.rb +4 -0
  92. data/test/wamp_test/config/initializers/inflections.rb +16 -0
  93. data/test/wamp_test/config/initializers/mime_types.rb +4 -0
  94. data/test/wamp_test/config/initializers/wamp-worker.rb +8 -0
  95. data/test/wamp_test/config/initializers/wrap_parameters.rb +14 -0
  96. data/test/wamp_test/config/locales/en.yml +33 -0
  97. data/test/wamp_test/config/master.key +1 -0
  98. data/test/wamp_test/config/puma.rb +34 -0
  99. data/test/wamp_test/config/routes.rb +4 -0
  100. data/test/wamp_test/config/sidekiq.yml +6 -0
  101. data/test/wamp_test/config/spring.rb +6 -0
  102. data/test/wamp_test/config/storage.yml +34 -0
  103. data/test/wamp_test/db/development.sqlite3 +0 -0
  104. data/test/wamp_test/db/seeds.rb +7 -0
  105. data/test/wamp_test/lib/assets/.keep +0 -0
  106. data/test/wamp_test/lib/tasks/.keep +0 -0
  107. data/test/wamp_test/package.json +5 -0
  108. data/test/wamp_test/public/404.html +67 -0
  109. data/test/wamp_test/public/422.html +67 -0
  110. data/test/wamp_test/public/500.html +66 -0
  111. data/test/wamp_test/public/apple-touch-icon-precomposed.png +0 -0
  112. data/test/wamp_test/public/apple-touch-icon.png +0 -0
  113. data/test/wamp_test/public/favicon.ico +0 -0
  114. data/test/wamp_test/public/robots.txt +1 -0
  115. data/test/wamp_test/storage/.keep +0 -0
  116. data/test/wamp_test/test/application_system_test_case.rb +5 -0
  117. data/test/wamp_test/test/controllers/.keep +0 -0
  118. data/test/wamp_test/test/fixtures/.keep +0 -0
  119. data/test/wamp_test/test/fixtures/files/.keep +0 -0
  120. data/test/wamp_test/test/helpers/.keep +0 -0
  121. data/test/wamp_test/test/integration/.keep +0 -0
  122. data/test/wamp_test/test/mailers/.keep +0 -0
  123. data/test/wamp_test/test/models/.keep +0 -0
  124. data/test/wamp_test/test/system/.keep +0 -0
  125. data/test/wamp_test/test/test_helper.rb +10 -0
  126. data/test/wamp_test/vendor/.keep +0 -0
  127. data/test/web/index.html +101 -0
  128. data/wamp-worker.gemspec +32 -0
  129. metadata +395 -0
@@ -0,0 +1,184 @@
1
+ require "redis"
2
+
3
+ module Wamp
4
+ module Worker
5
+ DEFAULT = :default
6
+
7
+ #region Storage Objects
8
+ class Handle
9
+ attr_reader :klass, :method, :options
10
+
11
+ def initialize(klass, method, options)
12
+ @klass = klass
13
+ @method = method
14
+ @options = options
15
+
16
+ unless klass.ancestors.include? BaseHandler
17
+ raise(ArgumentError, "'klass' must be a Wamp::Worker::Handler type")
18
+ end
19
+ end
20
+ end
21
+
22
+ class Registration < Handle
23
+ attr_reader :procedure
24
+
25
+ def initialize(procedure, klass, method, options)
26
+ super klass, method, options
27
+ @procedure = procedure
28
+ end
29
+ end
30
+
31
+ class Subscription < Handle
32
+ attr_reader :topic
33
+
34
+ def initialize(topic, klass, method, options)
35
+ super klass, method, options
36
+ @topic = topic
37
+ end
38
+ end
39
+ #endregion
40
+
41
+ # This class is a config proxy that lets you specify the name globally
42
+ #
43
+ class ConfigProxy
44
+ attr_reader :name, :config
45
+
46
+ def initialize(config, name=nil)
47
+ @name = name || DEFAULT
48
+ @config = config
49
+ end
50
+
51
+ # Sets the timeout value
52
+ #
53
+ def timeout(seconds)
54
+ self[:timeout] = seconds
55
+ end
56
+
57
+ # Sets the Redis connection
58
+ #
59
+ def redis(connection)
60
+ self[:redis] = connection
61
+ end
62
+
63
+ # Connection options
64
+ #
65
+ def connection(**options)
66
+ self[:connection] = options
67
+ end
68
+
69
+ # Subscribe the handler to a topic
70
+ #
71
+ # @param topic [String] - The topic to subscribe to
72
+ # @param klass [Wamp::Worker::Handler] - The class to use
73
+ # @param method [Symbol] - The name of the method to execute
74
+ # @param options [Hash] - Options for the subscription
75
+ def subscribe(topic, klass, method, **options)
76
+ subscriptions = self[:subscriptions] || []
77
+ subscriptions << Subscription.new(topic, klass, method, options)
78
+ self[:subscriptions] = subscriptions
79
+ end
80
+
81
+ # Register the handler for a procedure
82
+ #
83
+ # @param procedure [String] - The procedure to register for
84
+ # @param klass [Wamp::Worker::Handler] - The class to use
85
+ # @param method [Symbol] - The name of the method to execute
86
+ # @param options [Hash] - Options for the subscription
87
+ def register(procedure, klass, method, **options)
88
+ registrations = self[:registrations] || []
89
+ registrations << Registration.new(procedure, klass, method, options)
90
+ self[:registrations] = registrations
91
+ end
92
+
93
+ # Allows the user to configure without typing "config."
94
+ #
95
+ def configure(&callback)
96
+ self.instance_eval(&callback)
97
+ end
98
+
99
+ # Sets the attribute using the name
100
+ #
101
+ # @param attribute [Symbol] - The attribute
102
+ # @param value - The value for the attribute
103
+ def []=(attribute, value)
104
+ self.config[self.name][attribute] = value
105
+ end
106
+
107
+ # Gets the attribute using the name
108
+ #
109
+ # @param attribute [Symbol] - The attribute
110
+ def [](attribute)
111
+ self.config[self.name][attribute]
112
+ end
113
+ end
114
+
115
+ # This class is used to store the configuration of the worker
116
+ #
117
+ class Config
118
+ attr_reader :settings
119
+
120
+ def initialize
121
+ @settings = {}
122
+ end
123
+
124
+ # Returns the connection options
125
+ #
126
+ # @param name [Symbol] - The name of the connection
127
+ def connection(name=nil)
128
+ name ||= DEFAULT
129
+ self[name][:connection] || {}
130
+ end
131
+
132
+ # Returns the timeout value
133
+ #
134
+ # @param name [Symbol] - The name of the connection
135
+ def timeout(name=nil)
136
+ name ||= DEFAULT
137
+ self[name][:timeout] || 60
138
+ end
139
+
140
+ # Returns the redis value
141
+ #
142
+ # @param name [Symbol] - The name of the connection
143
+ def redis(name=nil)
144
+ name ||= DEFAULT
145
+ redis = self[name][:redis]
146
+
147
+ # If it is not a redis object, create one using it as the options
148
+ if redis == nil
149
+ redis = ::Redis.new
150
+ elsif not redis.is_a? ::Redis
151
+ redis = ::Redis.new(redis)
152
+ end
153
+
154
+ redis
155
+ end
156
+
157
+ # Returns the subscriptions
158
+ #
159
+ # @param name [Symbol] - The name of the connection
160
+ def subscriptions(name=nil)
161
+ name ||= DEFAULT
162
+ self[name][:subscriptions] || []
163
+ end
164
+
165
+ # Returns the registrations
166
+ #
167
+ # @param name [Symbol] - The name of the connection
168
+ def registrations(name=nil)
169
+ name ||= DEFAULT
170
+ self[name][:registrations] || []
171
+ end
172
+
173
+ # Returns the settings for a particular connection
174
+ #
175
+ # @param name [Symbol] - The name of the connection
176
+ def [](name)
177
+ settings = self.settings[name] || {}
178
+ self.settings[name] = settings
179
+ settings
180
+ end
181
+ end
182
+
183
+ end
184
+ end
@@ -0,0 +1,196 @@
1
+ require_relative "proxy/backgrounder"
2
+ require 'wamp/client/response'
3
+ require 'json'
4
+
5
+ module Wamp
6
+ module Worker
7
+
8
+ module BaseHandler
9
+ def self.included(base)
10
+ attr_reader :proxy, :command, :args, :kwargs, :details, :background
11
+
12
+ base.extend(ClassMethods)
13
+ end
14
+
15
+ module ClassMethods
16
+
17
+ # Instantiates the object
18
+ #
19
+ def create(proxy, command, args, kwargs, details)
20
+ handler = self.new
21
+ handler.configure(proxy, command, args, kwargs, details)
22
+ handler
23
+ end
24
+
25
+ # Subscribe the handler to a topic
26
+ #
27
+ # @param topic [String] - The topic to subscribe to
28
+ # @param method [Symbol] - The name of the method to execute
29
+ # @param options [Hash] - Options for the subscription
30
+ def subscribe(topic, method, name: nil, **options)
31
+ klass = self
32
+ Wamp::Worker::configure name do
33
+ subscribe topic, klass, method, **options
34
+ end
35
+ end
36
+
37
+ # Register the handler for a procedure
38
+ #
39
+ # @param procedure [String] - The procedure to register for
40
+ # @param method [Symbol] - The name of the method to execute
41
+ # @param options [Hash] - Options for the subscription
42
+ def register(procedure, method, name: nil, **options)
43
+ klass = self
44
+ Wamp::Worker::configure name do
45
+ register procedure, klass, method, **options
46
+ end
47
+ end
48
+ end
49
+
50
+ # Configures the handler
51
+ #
52
+ def configure(proxy, command, args, kwargs, details, background=false)
53
+ @proxy = proxy
54
+ @command = command
55
+ @args = args || []
56
+ @kwargs = kwargs || {}
57
+ @details = details || {}
58
+ @background = background
59
+ end
60
+
61
+ # This method will send progress of the call to the caller
62
+ #
63
+ # @param result - The value you would like to send to the caller for progress
64
+ def progress(result)
65
+
66
+ # Only allow progress if it is a procedure and the client set "receive_progress"
67
+ if command.to_sym == :procedure and self.details[:receive_progress]
68
+
69
+ # Get the request ID
70
+ request = self.details[:request]
71
+
72
+ # Send the data back to the
73
+ self.session.yield request, result, { progress: true }, self.background
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+
80
+ module Handler
81
+
82
+ def self.included(base)
83
+ base.class_eval do
84
+ include BaseHandler
85
+ end
86
+ end
87
+
88
+ # Returns the session for the call
89
+ #
90
+ # @return [Wamp::Client::Session, Wamp::Worker::Proxy::Requestor]
91
+ def session
92
+ self.proxy.session
93
+ end
94
+
95
+ # Method that invokes the handler
96
+ #
97
+ def invoke(method)
98
+ self.send(method)
99
+ end
100
+
101
+ end
102
+
103
+ module BackgroundHandler
104
+
105
+ def self.included(base)
106
+ base.class_eval do
107
+ include BaseHandler
108
+
109
+ # Use Sidekiq
110
+ require 'sidekiq'
111
+ include ::Sidekiq::Worker
112
+ end
113
+ end
114
+
115
+ # Returns the session for the call
116
+ #
117
+ # @return [Wamp::Client::Session, Wamp::Worker::Proxy::Requestor]
118
+ def session
119
+ self.proxy
120
+ end
121
+
122
+ # Override the invoke method to push the process to the background
123
+ #
124
+ def invoke(method)
125
+
126
+ # Also need to remove the session since it is not serializable.
127
+ # Will add a new one in the background handler
128
+ self.details.delete(:session)
129
+
130
+ # Send the task to Sidekiq
131
+ #
132
+ # Note: We are explicitly serializing the args, kwargs, details
133
+ # so that we can deserialize and have them appear as symbols in
134
+ # the handler.
135
+ self.class.perform_async(
136
+ method,
137
+ self.proxy.name,
138
+ self.proxy.background_res_queue,
139
+ self.command,
140
+ self.args.to_json,
141
+ self.kwargs.to_json,
142
+ details.to_json)
143
+
144
+ # If it is a procedure, return a defer
145
+ if self.command.to_sym == :procedure
146
+ Wamp::Client::Response::CallDefer.new
147
+ else
148
+ nil
149
+ end
150
+
151
+ end
152
+
153
+ # Method that is run when the process is invoked on the worker
154
+ #
155
+ # @param method [Symbol] - The name of the method to execute
156
+ # @param command [Symbol] - The command that is being backgrounded
157
+ # @param args [Array] - The arguments for the handler
158
+ # @param kwargs [Hash] - The keyword arguments for the handler
159
+ # @param details [Hash] - Other details about the call
160
+ def perform(method, proxy_name, proxy_handle, command, args, kwargs, details)
161
+
162
+ # Create a proxy to act like the session. Use a backgrounder so we also
163
+ # get the "yield" method
164
+ proxy = Proxy::Backgrounder.new(proxy_name, proxy_handle)
165
+
166
+ # Deserialize the arguments as symbols
167
+ args = JSON.parse(args, :symbolize_names => true)
168
+ kwargs = JSON.parse(kwargs, :symbolize_names => true)
169
+ details = JSON.parse(details, :symbolize_names => true)
170
+
171
+ # Get the request ID
172
+ request = details[:request]
173
+
174
+ # Add the proxy to the details as a "session"
175
+ details[:session] = self.session
176
+
177
+ # Configure the handler
178
+ self.configure(proxy, command, args, kwargs, details, true)
179
+
180
+ # Call the user code and make sure to catch exceptions
181
+ result = Wamp::Client::Response.invoke_handler do
182
+ self.send(method)
183
+ end
184
+
185
+ # Only return the response if it is a procedure
186
+ if command.to_sym == :procedure
187
+
188
+ # Send the data back to the
189
+ self.session.yield request, result, {}, true
190
+
191
+ end
192
+ end
193
+
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,38 @@
1
+ require_relative "requestor"
2
+
3
+ module Wamp
4
+ module Worker
5
+ module Proxy
6
+
7
+ class Backgrounder < Requestor
8
+ attr_reader :handle
9
+
10
+ # Constructor
11
+ #
12
+ def initialize(name, handle, uuid: nil)
13
+ super name, uuid: uuid
14
+ @handle = handle
15
+ end
16
+
17
+ # Returns the response to the dispatcher
18
+ #
19
+ # @param request [Int] - The ID of the request
20
+ # @param result [CallResult,CallError] - The result or error for us to serialize
21
+ # @param options [Hash] - Options for the yield
22
+ # @param check_defer [Bool] - 'true' is this is linked to a defer call
23
+ def yield(request, result, options={}, check_defer=false)
24
+
25
+ # Create the response object
26
+ result = Wamp::Client::Response::CallResult.ensure(result, allow_error: true)
27
+
28
+ # Create the params
29
+ params = { request: request, result: result.to_hash, options: options, check_defer: check_defer }
30
+
31
+ # Push to the worker who requested the result
32
+ self.queue.push self.handle, :yield, params
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,101 @@
1
+ require "wamp/client/response"
2
+ require_relative "../ticker"
3
+ require_relative "../queue"
4
+
5
+ module Wamp
6
+ module Worker
7
+ module Proxy
8
+
9
+ class Base
10
+ attr_reader :queue, :ticker, :name, :uuid
11
+
12
+ # Constructor
13
+ #
14
+ # @param name [Symbol] - The name of the connection
15
+ # @param uuid [String] - The uuid for the proxy. This can be passed in as
16
+ # well to allow it to be initialized for hte entire worker
17
+ def initialize(name, uuid: nil)
18
+ @name = name
19
+ @queue = Wamp::Worker::Queue.new(name)
20
+ @ticker = Wamp::Worker::Ticker.new(name)
21
+ @uuid = uuid || ENV['DYNO'] || SecureRandom.hex(12)
22
+ end
23
+
24
+ #region Command/Response
25
+ # ----------------
26
+ # This workflow is used by a "Requestor" to make a "call"
27
+ # or "publish" request to the "Dispatcher". This would for example be in
28
+ # your rails app where a service or controller needs to make a WAMP call
29
+ #
30
+ # The flow is as follows
31
+ #
32
+ # - Requestor performs a "push" operation with the following parameters
33
+ # - queue_name - The "command queue"
34
+ # - command - The command ("call" or "publish")
35
+ # - params - The parameters for the command (args/kwargs/etc.)
36
+ # - handle - A unique "response queue" name that the Dispatcher will
37
+ # provide the resposne on
38
+ # - Requestor then blocks (with timeout) awaiting the response
39
+ # - Dispatcher performs a "pop" operation and executes the command
40
+ # - Dispatcher "pushes" the response to the "handle" queue
41
+ # - Requestor "pops" the response and deletes the temporary "handle" queue
42
+
43
+ # Returns the commands queue key for the worker
44
+ #
45
+ # @return [String] - The key for the commands list
46
+ def command_req_queue
47
+ "wamp:#{self.name}:command"
48
+ end
49
+
50
+ # Returns a new handle
51
+ #
52
+ # @return [String] - The key for the new handle
53
+ def unique_command_resp_queue
54
+ "wamp:#{self.name}:response:#{SecureRandom.hex(12)}"
55
+ end
56
+
57
+ #endregion
58
+
59
+ #region Dispatcher/Backgrounder
60
+ # ----------------
61
+ # This workflow is used by a "Dispatcher" to execute a "topic" or "procedure"
62
+ # on a background thread. This is used by a "BackgroundHandler" to push a
63
+ # handler to Sidekiq and get the response from the background job. This frees
64
+ # up the Event Machine to process other requests
65
+ #
66
+ # The flow is as follows
67
+ #
68
+ # - Dispatcher pushes the task to the background sidekiq worker providing
69
+ # the "handle" it will respond on
70
+ # - Backgrounder performs the operation
71
+ # - Backgrounder performs a "push" with response back to the Dispatcher
72
+ # - Dispatcher performs a "pop" and sends the response (if it is a call)
73
+
74
+ # Returns the response queue name for the backgrounder
75
+ #
76
+ # @return [String] - The key for the worker
77
+ def background_res_queue
78
+ "wamp:#{self.name}:background:#{self.uuid}"
79
+ end
80
+
81
+ #endregion
82
+
83
+ #region Requestor/Dispatcher Ticker
84
+ # ----------------
85
+ # This workflow is used to sense when a worker is no longer running
86
+ #
87
+ # The flow is as follows
88
+ #
89
+ # - Dispatcher periodically increments the ticker
90
+ # - Requestor does the following when performing a pop
91
+ # - stores the value of the ticker
92
+ # - blocks waiting for a "pop"
93
+ # - if the pop comes back "nil", it means we timed out
94
+ # - if the value of the ticker is that same as before, then th worker is not running
95
+
96
+ #endregion
97
+
98
+ end
99
+ end
100
+ end
101
+ end