Пример #1
0
        /// <remarks>метод при обращении к бд использует индекс <see cref="MongoIndexCreator.CompoundIndexName"/></remarks>
        private ReservedRange ReserveRangeInternal(string dpuId, ReserveRangeOptions options, CancellationToken cancellationToken)
        {
            var packetsParts = this.ReservePackets(dpuId, options, cancellationToken);

            return(new ReservedRange
            {
                DataNodeToFinalParts = new Dictionary <DataNodeDefinition, List <AgentPacketPartInfo> >()
                {
                    { this.nodeDefinition, packetsParts }
                }
            });
        }
Пример #2
0
        private List <AgentPacketPartInfo> ReservePackets(
            string dpuId, ReserveRangeOptions options, CancellationToken cancellationToken)
        {
            var needReserveRange = options.NeedReserveRange;
            var packetsParts     = new List <AgentPacketPartInfo>();
            var update           = Builders <PacketPart> .Update
                                   .Set(x => x.ProcessingUnitId, dpuId)
                                   // $currentDate
                                   .CurrentDate(x => x.StartTime);

            var isFullScanQuery = (Interlocked.Increment(ref this.fullRangeScanCounter) % options.FullScanAfterNQueries) == 0;
            // в mongo нет аналога select for update, поэтому пока используется такая реализация
            // из альтенатив можно использовать транзакции, но они не атомарны для read+update, ещё есть вариант с $isolated
            // при запуске
            // при оптимизации сортировки может быть интересно
            // https://docs.mongodb.com/manual/reference/operator/aggregation/sort/index.html#sort-operator-and-memory
            // https://stackoverflow.com/questions/52019790/sort-makes-my-query-too-slow-in-mongodb
            var filters = this.GetFiltersForReserveQuery(isFullScanQuery, dpuId, options, cancellationToken);

            // в mongo
            var stopWatch          = Stopwatch.StartNew();
            var findedForAssigment = this.packetsPartsCollection
                                     .Find(filters.ForReservationFilter)
                                     .Sort(reservePacketsSortDef)
                                     .Limit(needReserveRange.Max)
                                     // проекция иногда снижает производительность ??? (был странный момент когда производительность падала в десятки раз, но воспроизвести я это не смог)
                                     .Project(reservePacketsProjection)
                                     .ToList(cancellationToken);

            stopWatch.Stop();

            if (!isFullScanQuery && stopWatch.Elapsed > warnLimitForSelecting)
            {
                // TODO instrumentation
                Debug.WriteLine($"find not processed query time - {stopWatch.Elapsed}");
            }

            // TODO эту часть стоит улучшить\изменить (Min не учитывается, хотя по сути должен)!
            while (findedForAssigment.Any() && needReserveRange.Min > packetsParts.Count)
            {
                var idsFilter = Builders <PacketPart> .Filter.In(x => x.Id, findedForAssigment.Select(x => x.Id));

                var toUpdateFilter = filters.ForReservationFilter & idsFilter;
                var updateResult   = this.packetsPartsCollection.UpdateMany(toUpdateFilter, update, null, cancellationToken);

                if (findedForAssigment.Count == updateResult.ModifiedCount || updateResult.MatchedCount == 0)
                {
                    packetsParts = findedForAssigment;
                    break;
                }
                else
                {
                    findedForAssigment = this.packetsPartsCollection
                                         .Find(idsFilter & filters.AlreadyAssignedOnCurrentFilter)
                                         .Sort(reservePacketsSortDef)
                                         .Limit(needReserveRange.Max)
                                         .Project(reservePacketsProjection)
                                         .ToList(cancellationToken);
                    //break;
                }
            }

            var newVal = findedForAssigment.Max(x => x.FinalPartTransferTime);

            if (newVal != null)
            {
                InterlockedExtension.AssignIfNewValueBigger(ref this.lastFinishTransferDateTimeTicksForQuery, newVal.Value.Ticks);
            }

            return(findedForAssigment);
        }
Пример #3
0
        private ReserveQueryFilters GetFiltersForReserveQuery(bool isFullScanQuery, string dpuId, ReserveRangeOptions options, CancellationToken cancellationToken)
        {
            FilterDefinition <PacketPart> assignedOnCurrentDpuFilter = Builders <PacketPart> .Filter
                                                                       .Eq(x => x.ProcessingUnitId, dpuId);

            var update = Builders <PacketPart> .Update
                         .Set(x => x.ProcessingUnitId, dpuId)
                         // $currentDate
                         .CurrentDate(x => x.StartTime);

            FilterDefinition <PacketPart> alreadyAssignedOnCurrentFilter = assignedOnCurrentDpuFilter & notEndedFilter;

            // Пока DateTime.UtcNow отсутствует для запросов фильтрации (есть CurrentDate для Update и $$NOW для aggregation pipeline)
            // https://jira.mongodb.org/browse/SERVER-23656
            // https://stackoverflow.com/questions/20620368/is-there-any-equivalent-of-now-in-mongodb
            // т.к. lease у нас имеет относительно большое время жизни здесь мы указываем фиксированную дату для условия, но по сути, её следует обновлять при выполнении запросов (а лучше заменить на аналог NOW() и тп когда он появится)
            var leaseExpairedFilter = Builders <PacketPart> .Filter
                                      .Lt(x => x.StartTime, (timeService.GetCurrentUtcTime(cancellationToken) - options.LeaseTimeout).ToUniversalTime());

            // есть дополнительные условия для использования индексов
            var canBeReassignedFilter = assigned & leaseExpairedFilter;

            var lastFinishTransferDateTimeTicks = this.lastFinishTransferDateTimeTicksForQuery;

            // FinalPartTransferTime != null - ищем только среди последних частей
            // addGteFinishTime - в случае когда записей много и нужно выбрать только те которые нужны "следующие" для обработки, без этого условия (котрое добавляется при addGteFinishTime==true) мы получаем слишком большое время выполнения запроса
            var addGteFinishTime = !isFullScanQuery && lastFinishTransferDateTimeTicks != 0;
            var commonFilter     = addGteFinishTime
                    ? hasFinishTimeAndFinal & Builders <PacketPart> .Filter
                                   .Gte(x => x.FinalPartTransferTime, new DateTime(lastFinishTransferDateTimeTicks))
                    : hasFinishTimeAndFinal;

            return(new ReserveQueryFilters(commonFilter &
                                           (
                                               alreadyAssignedOnCurrentFilter
                                               | canBeReassignedFilter
                                               // next to process
                                               | notStartedFilter
                                           ), alreadyAssignedOnCurrentFilter));
        }
Пример #4
0
 public ReservedRange ReserveRangeAsync(string dpuId, ReserveRangeOptions options, CancellationToken cancellationToken)
 {
     return(this.RunMongoOps((token) => this.ReserveRangeInternal(dpuId, options, token), cancellationToken));
 }