You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reserve runners for the whole duration of their use
This commit changes how runners are reserved and released. Previously, a runner was reserved only for the actual time it was actively used and the aim was to reserve it for a time as short as possible. The previous mechanism worked, but had some drawbacks:
For example, a learner could start a submission scoring, which would perform (1) a file copy, (2) an execution of the first test file, (3) an execution of the second test file, etc. For each of these actions, the runner would be reserved and released. Now, it could happen that another run was triggered between the execution of both test files, causing issues with the remaining scoring.
In the refactored version, a runner is reserved once before initially copying files and only released after the last command finished. This ensures that no one can interrupt an execution started once, hopefully making it more robust (and faster, since less locks are required).
Rails.logger.debug{"#{Time.zone.now.getutc.inspect}: Copying files to Runner #{runner.id} for #{contributor_type}#{contributor_id} and Submission #{id}."}
236
-
runner.copy_files(files)
237
+
# We don't want `copy_files` to be exclusive since we reserve runners for the whole `prepared_runner` block.
238
+
runner.copy_files(files,exclusive: false)
237
239
rescueRunner::Error=>e
238
240
e.waiting_duration=Time.zone.now - request_time
239
241
raise
240
242
end
241
243
waiting_duration=Time.zone.now - request_time
242
-
yield(runner,waiting_duration)
244
+
yield(runner,waiting_duration)ifblock_given?
245
+
ensure
246
+
runner&.release!ifexclusive
243
247
end
244
248
245
-
defswap_runner(old_runner)
246
-
old_runner.update(runner_id: nil)
247
-
new_runner=nil
248
-
new_waiting_duration=nil
249
-
# We request a new runner that will also include all files of the current submission
250
-
prepared_runnerdo |runner,waiting_duration|
251
-
new_runner=runner
252
-
new_waiting_duration=waiting_duration
249
+
defswap_runner(runner,exclusive: true)
250
+
# We use a transaction to ensure that the runner is swapped atomically.
251
+
transactiondo
252
+
# Due to the `before_validation` callback in the `Runner` model,
253
+
# the following line will immediately request a new runner.
254
+
runner.update(runner_id: nil)
255
+
256
+
# With the new runner being ready, we only need to prepare it (by copying the files).
257
+
# Since no actual execution is performed, we don't need to reserve the runner.
0 commit comments