예제 #1
0
    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
예제 #2
0
    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,
                )
예제 #3
0
    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()