Example #1
0
        private async Task <List <UserExerciseSubmission> > TryGetExerciseSubmissions(int count)
        {
            var notSoLongAgo = DateTime.Now - TimeSpan.FromMinutes(15);
            List <UserExerciseSubmission> submissions;

            using (var transaction = db.Database.BeginTransaction(IsolationLevel.Serializable))
            {
                submissions = db.UserExerciseSubmissions
                              .AsNoTracking()
                              .Where(s =>
                                     s.Timestamp > notSoLongAgo &&
                                     s.AutomaticChecking.Status == AutomaticExerciseCheckingStatus.Waiting)
                              .OrderByDescending(s => s.Timestamp)
                              .Take(count)
                              .ToList();
                foreach (var submission in submissions)
                {
                    submission.AutomaticChecking.Status = AutomaticExerciseCheckingStatus.Running;
                }
                await SaveAll(submissions.Select(s => s.AutomaticChecking));

                transaction.Commit();

                db.ObjectContext().AcceptAllChanges();
            }

            foreach (var submission in submissions)
            {
                unhandledSubmissions.TryRemove(submission.Id, out _);
            }

            return(submissions);
        }
Example #2
0
        private async Task <UserExerciseSubmission> TryGetExerciseSubmission(string agentName, SubmissionLanguage language)
        {
            var notSoLongAgo = DateTime.Now - TimeSpan.FromMinutes(15);
            UserExerciseSubmission submission;

            using (var transaction = db.Database.BeginTransaction(IsolationLevel.Serializable))
            {
                var submissionsQueryable = db.UserExerciseSubmissions
                                           .AsNoTracking()
                                           .Where(s =>
                                                  s.Timestamp > notSoLongAgo &&
                                                  s.AutomaticChecking.Status == AutomaticExerciseCheckingStatus.Waiting &&
                                                  s.Language == language);

                if (!submissionsQueryable.Any())
                {
                    return(null);
                }

                var maxId = submissionsQueryable.Select(s => s.Id).DefaultIfEmpty(-1).Max();
                submission = submissionsQueryable.FirstOrDefault(s => s.Id == maxId);
                if (submission == null)
                {
                    return(null);
                }

                /* Mark submission as "running" */
                submission.AutomaticChecking.Status            = AutomaticExerciseCheckingStatus.Running;
                submission.AutomaticChecking.CheckingAgentName = agentName;

                await SaveAll(new List <AutomaticExerciseChecking> {
                    submission.AutomaticChecking
                });

                transaction.Commit();

                db.ObjectContext().AcceptAllChanges();
            }

            unhandledSubmissions.TryRemove(submission.Id, out _);

            return(submission);
        }
Example #3
0
        protected async Task SaveAll(IEnumerable <AutomaticExerciseChecking> checkings)
        {
            foreach (var checking in checkings)
            {
                log.Info($"Обновляю статус автоматической проверки #{checking.Id}: {checking.Status}");
                db.AutomaticExerciseCheckings.AddOrUpdate(checking);
                UpdateIsRightAnswerForSubmission(checking);
            }

            try
            {
                await db.ObjectContext().SaveChangesAsync(SaveOptions.DetectChangesBeforeSave).ConfigureAwait(false);
            }
            catch (DbEntityValidationException e)
            {
                throw new Exception(
                          string.Join("\r\n",
                                      e.EntityValidationErrors.SelectMany(v => v.ValidationErrors).Select(err => err.PropertyName + " " + err.ErrorMessage)));
            }
        }
Example #4
0
        // ReSharper disable once MemberCanBePrivate.Global
        public IQueryable <UserIdWrapper> GetUsersByNamePrefix(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                return(db.Users.Where(u => !u.IsDeleted).Select(u => new UserIdWrapper(u.Id)));
            }

            var splittedName  = name.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
            var nameQuery     = string.Join(" & ", splittedName.Select(s => "\"" + s.Trim().Replace("\"", "\\\"") + "*\""));
            var nameParameter = new ObjectParameter("name", nameQuery);

            return(db.ObjectContext().CreateQuery <UserIdWrapper>($"[{nameof(GetUsersByNamePrefix)}](@name)", nameParameter));
        }
Example #5
0
        // ReSharper disable once MemberCanBePrivate.Global
        public IQueryable <SubmissionIdWrapper> GetGraderSolutionByClientUserId(string clientUserId)
        {
            if (string.IsNullOrEmpty(clientUserId))
            {
                return(db.ExerciseSolutionsByGrader.Select(s => new SubmissionIdWrapper(s.Id)));
            }

            var splittedUserId = clientUserId.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
            var query          = string.Join(" & ", splittedUserId.Select(s => "\"" + s.Trim().Replace("\"", "\\\"") + "*\""));
            var parameter      = new ObjectParameter("userId", query);

            return(db.ObjectContext().CreateQuery <SubmissionIdWrapper>($"[{nameof(GetGraderSolutionByClientUserId)}](@userId)", parameter));
        }
Example #6
0
        private async Task TryUpdate(string userId, Guid slideId, string ltiRequestJson)
        {
            var ltiRequestModel = FindElement(userId, slideId);

            if (ltiRequestModel == null)
            {
                ltiRequestModel = new LtiSlideRequest
                {
                    UserId  = userId,
                    SlideId = slideId,
                    Request = ltiRequestJson
                };
            }
            else
            {
                ltiRequestModel.Request = ltiRequestJson;
            }

            db.LtiRequests.AddOrUpdate(ltiRequestModel);
            await db.ObjectContext().SaveChangesAsync(SaveOptions.DetectChangesBeforeSave);

            db.ObjectContext().AcceptAllChanges();
        }
Example #7
0
		private async Task<UserExerciseSubmission> TryGetExerciseSubmission(string agentName, IEnumerable<Language> languages)
		{
			var notSoLongAgo = DateTime.Now - TimeSpan.FromMinutes(15);

			var submissionsQueryable = db.UserExerciseSubmissions
				.AsNoTracking()
				.Where(s =>
					s.Timestamp > notSoLongAgo
					&& s.AutomaticChecking.Status == AutomaticExerciseCheckingStatus.Waiting
					&& languages.Contains(s.Language));

			var maxId = submissionsQueryable.Select(s => s.Id).DefaultIfEmpty(-1).Max();
			if (maxId == -1)
				return null;

			// NOTE: Если транзакция здесь, а не в начале метода, может возникнуть ситуация, что maxId только что кто-то взял, и мы тоже взяли.
			// То, что мы не обработаем дважды, защищает проверка на Waiting внутри транзакции ниже.
			// Мы можем не взять из-за этого другой solution. Не стращно, попробуем снова сразу же с помощью WaitAnyUnhandledSubmissions (см. RunnerController.GetSubmissions).
			// RepeatableRead блокирует от изменения те строки, которые видел.
			// Serializable отличается от него только тем, что другая транзакция не добавит другую строку, которая тоже будет подходить под запрос, даже после того, как запрос совершен.
			// Нам важно только, чтобы не менялись виденные в транзакции строки, поэтому подходит RepeatableRead.
			// Хотя, здесь делается запрос просто по Id, поэтому в любом случае заблокированных строк мало.
			// Малое количество затронутых строк должно уменьшить возможность дедлоков. Теоретически, если обе прочитают одно и то же и заходят записать, должна сработать одна транзакция и одна откатиться.
			// Дедлоки всё-таки есть в большом количестве, поэтому поставил Semaphore
			log.Debug("GetUnhandledSubmission(): trying to acquire semaphore");
			var semaphoreLocked = await getSubmissionSemaphore.WaitAsync(TimeSpan.FromSeconds(2));
			if (!semaphoreLocked)
			{
				log.Error("TryGetExerciseSubmission(): Can't lock semaphore for 2 seconds");
				return null;
			}

			log.Debug("GetUnhandledSubmission(): semaphore acquired!");
			try
			{
				UserExerciseSubmission submission;
				using (var transaction = db.Database.BeginTransaction(IsolationLevel.RepeatableRead))
				{
					submission = db.UserExerciseSubmissions.AsNoTracking().FirstOrDefault(s => s.Id == maxId);
					if (submission == null)
						return null;

					if (submission.AutomaticChecking.Status != AutomaticExerciseCheckingStatus.Waiting)
						return null;

					/* Mark submission as "running" */
					submission.AutomaticChecking.Status = AutomaticExerciseCheckingStatus.Running;
					submission.AutomaticChecking.CheckingAgentName = agentName;

					await SaveAll(new List<AutomaticExerciseChecking> { submission.AutomaticChecking }).ConfigureAwait(false);

					transaction.Commit();

					db.ObjectContext().AcceptAllChanges();
				}

				unhandledSubmissions.TryRemove(submission.Id, out _);

				return submission;
			}
			catch (Exception e)
			{
				log.Error("TryGetExerciseSubmission() error", e);
				return null;
			}
			finally
			{
				log.Debug("GetUnhandledSubmission(): trying to release semaphore");
				getSubmissionSemaphore.Release();
				log.Debug("GetUnhandledSubmission(): semaphore released");
			}
		}