Implement runner#wait in a generic way#179
Conversation
| queue = Queue.new | ||
| wait_threads = | ||
| Runner.runners.map do |runner| | ||
| next unless runner.respond_to?(:wait) |
There was a problem hiding this comment.
@kbrock if we don't have a wait thread what is going to unblock the queue.pop
# Block until an event is raised
event, runner_context = queue.pop
There was a problem hiding this comment.
@agrare That thread is only needed to handle the async nature of the "docker" runner. I commented out the "docker" thread and was able to run a workflow just fine:
{
"StartAt": "a",
"States": {
"a":{
"Type": "Pass",
"Next": "b"
},
"b": {
"Type": "Wait",
"Seconds": 1,
"Next": "c"
},
"c": {
"Type": "Succeed"
}
}
}ASIDE: was also able to call with an awesome:// Task without this thread
| private def resolve_scheme(scheme) | ||
| runner = @runners[scheme] | ||
| runner = @runners[scheme] = @runners[scheme].call if runner.is_a?(Proc) | ||
| runner = @runners[scheme] = @runners[scheme].call if runner.kind_of?(Proc) |
There was a problem hiding this comment.
this is a lazy eval, but it caches the result on the first call.
I do wonder if this is not thread safe. Ruby's GPL probably takes care of it.
| def runners | ||
| @runners.each_value.map do |runner| | ||
| runner = runner.call if runner.kind_of?(Proc) | ||
| runner | ||
| end | ||
| end |
There was a problem hiding this comment.
This is essentially @runners.values resolving the procs.
I'm realizing that on startup 100% of runners are procs.
But after 1 task is run, 0% of runners are procs.
Wonder if we can do a resolve up front so we don't have to handle this case during regular runtime activity.
I feel supporting multiple schemes by a single runner is currently far fetched.
Ignoring that use case allows us to keep to our current simple runner registration process: Floe::Runner.register_scheme.
In the future, if we come up with a case where there are multiple schemes, we can make our registration process more complicated.
|
@Fryguy re: use case I had implemented #178 using a synchronous/blocking implementation (first commit) and an async/event implementation (second commit) The sync/blocking implementation works fine without this PR. This PR provides the thread for any runner that needs one. |
| def wait(timeout: nil, events: %i[create update delete]) | ||
| raise NotImplementedError, "Must be implemented in a subclass" | ||
| end | ||
| # Implement if the runner needs a watch for async events | ||
| # def wait(timeout: nil, events: %i[create update delete]) | ||
| # raise NotImplementedError, "Must be implemented in a subclass" | ||
| # end |
There was a problem hiding this comment.
some runners do not have async events.
Putting an empty method into wait calls an empty method in a tight loop.
So having a switch that says a thread is not needed seems the best route (i.e.: not defining wait.
53db854 to
82f320d
Compare
|
Checked commit kbrock@7e419b0 with ruby 2.7.8, rubocop 1.56.3, haml-lint 0.51.0, and yamllint |
|
reducing branches: handle this with awesome_spawn |
pulled out of #178
Depend upon:
Overview
The
builtinrunner does not need awaitmethod since it does not listen to completed tasks. But other runners may need a await.So the solution is to make wait optional, and setup a thread for all the runners that implement that interface.
Before
Run a thread to support
waitfrom"docker"runner, but no others.After
Run a thread for every runner that requires
wait.Developer speak
aka:
handling multiple runners:
Aside
I had wanted to add something with
exe/floe's interface, since that is also hard coded, but this addresses my primary concerns.