def acquire_worker(self, job, side_data=None): """Tries to assign a job to an available worker. If no workers are available then this returns None, otherwise this returns the chosen worker. job (job): the job to assign to a worker side_data (object): object to attach to the worker for later use returns (int): None if no workers are available, the worker assigned to the job otherwise """ # We look for an available worker try: shard = self.find_worker(WorkerPool.WORKER_INACTIVE, require_connection=True, random_worker=True) except LookupError: return None # Then we fill the info for future memory self._job[shard] = job self._start_time[shard] = make_datetime() self._side_data[shard] = side_data logger.debug("Worker %s acquired." % shard) # And finally we ask the worker to do the job action, object_id = job timestamp = side_data[1] queue_time = self._start_time[shard] - timestamp logger.info( "Asking worker %s to %s submission/user test %d " " (%s after submission)." % (shard, action, object_id, queue_time) ) with SessionGen(commit=False) as session: if action == EvaluationService.JOB_TYPE_COMPILATION: submission = Submission.get_from_id(object_id, session) job_ = CompilationJob.from_submission(submission) elif action == EvaluationService.JOB_TYPE_EVALUATION: submission = Submission.get_from_id(object_id, session) job_ = EvaluationJob.from_submission(submission) elif action == EvaluationService.JOB_TYPE_TEST_COMPILATION: user_test = UserTest.get_from_id(object_id, session) job_ = CompilationJob.from_user_test(user_test) elif action == EvaluationService.JOB_TYPE_TEST_EVALUATION: user_test = UserTest.get_from_id(object_id, session) job_ = EvaluationJob.from_user_test(user_test) job_.get_output = True job_.only_execution = True self._worker[shard].execute_job( job_dict=job_.export_to_dict(), callback=self._service.action_finished.im_func, plus=(action, object_id, side_data, shard), ) return shard
def new_user_test(self, user_test_id): """This RPC prompts ES of the existence of a new user test. ES takes takes the right countermeasures, i.e., it schedules it for compilation. user_test_id (int): the id of the new user test. returns (bool): True if everything went well. """ with SessionGen(commit=False) as session: user_test = UserTest.get_from_id(user_test_id, session) if user_test is None: logger.error("[new_user_test] Couldn't find user test %d " "in the database." % (user_test_id)) return if user_test_to_compile(user_test): self.push_in_queue( (EvaluationService.JOB_TYPE_TEST_COMPILATION, user_test_id), EvaluationService.JOB_PRIORITY_HIGH, user_test.timestamp, )
def action_finished(self, data, plus, error=None): """Callback from a worker, to signal that is finished some action (compilation or evaluation). data (dict): a dictionary that describes a Job instance. plus (tuple): the tuple (job_type, object_id, side_data=(priority, timestamp), shard_of_worker) """ # TODO - The next two comments are in the wrong place and # really little understandable anyway. # We notify the pool that the worker is free (even if it # replied with an error), but if the pool wants to disable the # worker, it's because it already assigned its job to someone # else, so we discard the data from the worker. job_type, object_id, side_data, shard = plus # If worker was ignored, do nothing. if self.pool.release_worker(shard): return job_success = True if error is not None: logger.error("Received error from Worker: `%s'." % error) job_success = False else: try: job = Job.import_from_dict_with_type(data) except: logger.error("[action_finished] Couldn't build Job for data" " %s." % (data)) job_success = False else: if not job.success: logger.error("Worker %s signaled action " "not successful." % shard) job_success = False _, timestamp = side_data logger.info("Action %s for submission %s completed. Success: %s." % (job_type, object_id, data["success"])) # We get the submission from DB and update it. with SessionGen(commit=False) as session: if job_type == EvaluationService.JOB_TYPE_COMPILATION: submission = Submission.get_from_id(object_id, session) if submission is None: logger.error("[action_finished] Couldn't find " "submission %d in the database." % object_id) return submission.compilation_tries += 1 if job_success: submission.compilation_outcome = "ok" if job.compilation_success else "fail" submission.compilation_text = job.text submission.compilation_shard = job.shard submission.compilation_sandbox = ":".join(job.sandboxes) for executable in job.executables.itervalues(): submission.executables[executable.filename] = executable session.add(executable) self.compilation_ended(submission) elif job_type == EvaluationService.JOB_TYPE_EVALUATION: submission = Submission.get_from_id(object_id, session) if submission is None: logger.error("[action_finished] Couldn't find " "submission %s in the database." % object_id) return submission.evaluation_tries += 1 if job_success: submission.evaluation_outcome = "ok" for test_number, info in job.evaluations.iteritems(): evaluation = Evaluation( text=info["text"], outcome=info["outcome"], num=test_number, memory_used=info["plus"].get("memory_used", None), execution_time=info["plus"].get("execution_time", None), execution_wall_clock_time=info["plus"].get("execution_wall_clock_time", None), evaluation_shard=job.shard, evaluation_sandbox=":".join(info["sandboxes"]), submission=submission, ) session.add(evaluation) self.evaluation_ended(submission) elif job_type == EvaluationService.JOB_TYPE_TEST_COMPILATION: user_test = UserTest.get_from_id(object_id, session) if user_test is None: logger.error("[action_finished] Couldn't find " "user test %d in the database." % object_id) return user_test.compilation_tries += 1 if job_success: user_test.compilation_outcome = "ok" if job.compilation_success else "fail" user_test.compilation_text = job.text user_test.compilation_shard = job.shard user_test.compilation_sandbox = ":".join(job.sandboxes) for executable in job.executables.itervalues(): ut_executable = UserTestExecutable.import_from_dict(executable.export_to_dict()) user_test.executables[ut_executable.filename] = ut_executable session.add(ut_executable) self.user_test_compilation_ended(user_test) elif job_type == EvaluationService.JOB_TYPE_TEST_EVALUATION: user_test = UserTest.get_from_id(object_id, session) if user_test is None: logger.error("[action_finished] Couldn't find " "user test %d in the database." % object_id) return user_test.evaluation_tries += 1 if job_success: try: [evaluation] = job.evaluations.values() except ValueError: logger.error( "[action_finished] I expected the job " "for a user test to contain a single " "evaluation, while instead it has %d." % (len(job.evaluations.values())) ) return user_test.evaluation_outcome = "ok" user_test.evaluation_shard = job.shard user_test.output = evaluation["output"] user_test.evaluation_text = evaluation["text"] user_test.evaluation_sandbox = ":".join(evaluation["sandboxes"]) user_test.memory_used = (evaluation["plus"].get("memory_used", None),) user_test.execution_time = (evaluation["plus"].get("execution_time", None),) self.user_test_evaluation_ended(user_test) else: logger.error("Invalid job type %r." % (job_type)) return session.commit()