/// <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 } } }); }
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); }
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)); }
public ReservedRange ReserveRangeAsync(string dpuId, ReserveRangeOptions options, CancellationToken cancellationToken) { return(this.RunMongoOps((token) => this.ReserveRangeInternal(dpuId, options, token), cancellationToken)); }