Пример #1
0
        // perform a query
        public override async Task QueryAsync(PartitionQueryEvent queryEvent, EffectTracker effectTracker)
        {
            try
            {
                var instanceQuery = queryEvent.InstanceQuery;

#if FASTER_SUPPORTS_PSF
                IAsyncEnumerable <OrchestrationState> queryPSFsAsync(ClientSession <Key, Value, EffectTracker, TrackedObject, PartitionReadEvent, Functions> session)
                {
                    // Issue the PSF query. Note that pending operations will be completed before this returns.
                    var querySpec = new List <(IPSF, IEnumerable <PSFKey>)>();

                    if (instanceQuery.HasRuntimeStatus)
                    {
                        querySpec.Add((this.RuntimeStatusPsf, instanceQuery.RuntimeStatus.Select(s => new PSFKey(s))));
                    }
                    if (instanceQuery.CreatedTimeFrom.HasValue || instanceQuery.CreatedTimeTo.HasValue)
                    {
                        IEnumerable <PSFKey> enumerateDateBinKeys()
                        {
                            var to   = instanceQuery.CreatedTimeTo ?? DateTime.UtcNow;
                            var from = instanceQuery.CreatedTimeFrom ?? to.AddDays(-7);   // TODO Some default so we don't have to iterate from the first possible date

                            for (var dt = from; dt <= to; dt += PSFKey.DateBinInterval)
                            {
                                yield return(new PSFKey(dt));
                            }
                        }
                        querySpec.Add((this.CreatedTimePsf, enumerateDateBinKeys()));
                    }
                    if (!string.IsNullOrWhiteSpace(instanceQuery.InstanceIdPrefix))
                    {
                        querySpec.Add((this.InstanceIdPrefixPsf, new[] { new PSFKey(instanceQuery.InstanceIdPrefix) }));
                    }
                    var querySettings = new PSFQuerySettings
                    {
                        // This is a match-all-PSFs enumeration so do not continue after any PSF has hit EOS
                        OnStreamEnded = (unusedPsf, unusedIndex) => false
                    };

                    OrchestrationState getOrchestrationState(ref Value v)
                    {
                        if (v.Val is byte[] serialized)
                        {
                            var result = ((InstanceState)Serializer.DeserializeTrackedObject(serialized))?.OrchestrationState;
                            if (result != null && !instanceQuery.FetchInput)
                            {
                                result.Input = null;
                            }
                            return(result);
                        }
                        else
                        {
                            var state  = ((InstanceState)((TrackedObject)v))?.OrchestrationState;
                            var result = state?.ClearFieldsImmutably(instanceQuery.FetchInput, true);
                            return(result);
                        }
                    }

                    return(session.QueryPSFAsync(querySpec, matches => matches.All(b => b), querySettings)
                           .Select(providerData => getOrchestrationState(ref providerData.GetValue()))
                           .Where(orchestrationState => orchestrationState != null));
                }
#else
                IAsyncEnumerable <OrchestrationState> queryPSFsAsync(ClientSession <Key, Value, EffectTracker, TrackedObject, object, IFunctions <Key, Value, EffectTracker, TrackedObject, object> > session)
                => this.ScanOrchestrationStates(effectTracker, queryEvent);
#endif
                // create an individual session for this query so the main session can be used
                // while the query is progressing.
                using (var session = this.CreateASession())
                {
                    var orchestrationStates = (this.partition.Settings.UsePSFQueries && instanceQuery.IsSet)
                        ? queryPSFsAsync(session)
                        : this.ScanOrchestrationStates(effectTracker, queryEvent);

                    await effectTracker.ProcessQueryResultAsync(queryEvent, orchestrationStates);
                }
            }
            catch (Exception exception)
                when(this.terminationToken.IsCancellationRequested && !Utils.IsFatal(exception))
                {
                    throw new OperationCanceledException("Partition was terminated.", exception, this.terminationToken);
                }
        }
Пример #2
0
        IAsyncEnumerable <OrchestrationState> ScanOrchestrationStates(
            EffectTracker effectTracker,
            PartitionQueryEvent queryEvent)
        {
            var    instanceQuery = queryEvent.InstanceQuery;
            string queryId       = queryEvent.EventIdString;

            this.partition.EventDetailTracer?.TraceEventProcessingDetail($"starting query {queryId}");

            // we use a separate thread to iterate, since Faster can iterate synchronously only at the moment
            // and we don't want it to block thread pool worker threads
            var channel    = Channel.CreateBounded <OrchestrationState>(500);
            var scanThread = new Thread(RunScan)
            {
                Name = $"QueryScan-{queryId}"
            };

            scanThread.Start();
            return(channel.Reader.ReadAllAsync());

            void RunScan()
            {
                using var _ = EventTraceContext.MakeContext(0, queryId);

                // get the unique set of keys appearing in the log and emit them
                using var iter1 = this.fht.Iterate();

                Stopwatch stopwatch = new Stopwatch();

                stopwatch.Start();
                long scanned      = 0;
                long deserialized = 0;
                long matched      = 0;
                long lastReport;

                void ReportProgress()
                {
                    this.partition.EventDetailTracer?.TraceEventProcessingDetail(
                        $"query {queryId} scan position={iter1.CurrentAddress} elapsed={stopwatch.Elapsed.TotalSeconds:F2}s scanned={scanned} deserialized={deserialized} matched={matched}");
                    lastReport = stopwatch.ElapsedMilliseconds;
                }

                ReportProgress();

                while (iter1.GetNext(out RecordInfo recordInfo) && !recordInfo.Tombstone)
                {
                    if (stopwatch.ElapsedMilliseconds - lastReport > 5000)
                    {
                        ReportProgress();
                    }

                    TrackedObjectKey key = iter1.GetKey().Val;
                    if (key.ObjectType == TrackedObjectKey.TrackedObjectType.Instance)
                    {
                        scanned++;
                        //this.partition.EventDetailTracer?.TraceEventProcessingDetail($"found instance {key.InstanceId}");

                        if (string.IsNullOrEmpty(instanceQuery?.InstanceIdPrefix) ||
                            key.InstanceId.StartsWith(instanceQuery.InstanceIdPrefix))
                        {
                            //this.partition.EventDetailTracer?.TraceEventProcessingDetail($"reading instance {key.InstanceId}");

                            object val = iter1.GetValue().Val;

                            //this.partition.EventDetailTracer?.TraceEventProcessingDetail($"read instance {key.InstanceId}, is {(val == null ? "null" : val.GetType().Name)}");

                            InstanceState instanceState;

                            if (val is byte[] bytes)
                            {
                                instanceState = (InstanceState)Serializer.DeserializeTrackedObject(bytes);
                                deserialized++;
                            }
                            else
                            {
                                instanceState = (InstanceState)val;
                            }

                            // reading the orchestrationState may race with updating the orchestration state
                            // but it is benign because the OrchestrationState object is immutable
                            var orchestrationState = instanceState?.OrchestrationState;

                            if (orchestrationState != null &&
                                instanceQuery.Matches(orchestrationState))
                            {
                                matched++;

                                this.partition.EventDetailTracer?.TraceEventProcessingDetail($"match instance {key.InstanceId}");

                                var value = orchestrationState.ClearFieldsImmutably(!instanceQuery.FetchInput, false);

                                var task = channel.Writer.WriteAsync(value);

                                if (!task.IsCompleted)
                                {
                                    task.AsTask().Wait();
                                }
                            }
                        }
                    }
                }

                ReportProgress();

                channel.Writer.Complete();

                this.partition.EventDetailTracer?.TraceEventProcessingDetail($"finished query {queryId}");
            }
        }
 // perform a query
 public abstract Task QueryAsync(PartitionQueryEvent queryEvent, EffectTracker effectTracker);