public async Task <CreateReindexResponse> Handle(CreateReindexRequest request, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(request, nameof(request));

            if (await _authorizationService.CheckAccess(DataActions.Reindex, cancellationToken) != DataActions.Reindex)
            {
                throw new UnauthorizedFhirActionException();
            }

            (var activeReindexJobs, var reindexJobId) = await _fhirOperationDataStore.CheckActiveReindexJobsAsync(cancellationToken);

            if (activeReindexJobs)
            {
                throw new JobConflictException(string.Format(Resources.OnlyOneResourceJobAllowed, reindexJobId));
            }

            // We need to pull in latest search parameter updates from the data store before creating a reindex job.
            // There could be a potential delay of <see cref="ReindexJobConfiguration.JobPollingFrequency"/> before
            // search parameter updates on one instance propagates to other instances. If we store the reindex
            // job with the old hash value in _searchParameterDefinitionManager.SearchParameterHashMap, then we will
            // not detect the resources that need to be reindexed.
            await _searchParameterOperations.GetAndApplySearchParameterUpdates(cancellationToken);

            var jobRecord = new ReindexJobRecord(
                _searchParameterDefinitionManager.SearchParameterHashMap,
                request.MaximumConcurrency ?? _reindexJobConfiguration.DefaultMaximumThreadsPerReindexJob,
                request.MaximumResourcesPerQuery ?? _reindexJobConfiguration.MaximumNumberOfResourcesPerQuery,
                request.QueryDelayIntervalInMilliseconds ?? _reindexJobConfiguration.QueryDelayIntervalInMilliseconds,
                request.TargetDataStoreUsagePercentage);
            var outcome = await _fhirOperationDataStore.CreateReindexJobAsync(jobRecord, cancellationToken);

            return(new CreateReindexResponse(outcome));
        }
        public async Task <ReindexSingleResourceResponse> Handle(ReindexSingleResourceRequest request, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(request, nameof(request));

            if (await _authorizationService.CheckAccess(DataActions.Reindex, cancellationToken) != DataActions.Reindex)
            {
                throw new UnauthorizedFhirActionException();
            }

            var             key            = new ResourceKey(request.ResourceType, request.ResourceId);
            ResourceWrapper storedResource = await _fhirDataStore.GetAsync(key, cancellationToken);

            if (storedResource == null)
            {
                throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundById, request.ResourceType, request.ResourceId));
            }

            await _searchParameterOperations.GetAndApplySearchParameterUpdates(cancellationToken);

            // We need to extract the "new" search indices since the assumption is that
            // a new search parameter has been added to the fhir server.
            ResourceElement resourceElement = _resourceDeserializer.Deserialize(storedResource);
            IReadOnlyCollection <SearchIndexEntry> newIndices = _searchIndexer.Extract(resourceElement);

            // If it's a post request we need to go update the resource in the database.
            if (request.HttpMethod == HttpPostName)
            {
                await ProcessPostReindexSingleResourceRequest(storedResource, newIndices);
            }

            // Create a new parameter resource and include the new search indices and the corresponding values.
            var parametersResource = new Parameters
            {
                Id        = Guid.NewGuid().ToString(),
                VersionId = "1",
                Parameter = new List <Parameters.ParameterComponent>(),
            };

            foreach (SearchIndexEntry searchIndex in newIndices)
            {
                parametersResource.Parameter.Add(new Parameters.ParameterComponent()
                {
                    Name = searchIndex.SearchParameter.Code.ToString(), Value = new FhirString(searchIndex.Value.ToString())
                });
            }

            return(new ReindexSingleResourceResponse(parametersResource.ToResourceElement()));
        }
        public async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            var runningTasks = new List <Task>();

            while (!cancellationToken.IsCancellationRequested)
            {
                if (_searchParametersInitialized)
                {
                    // Check for any changes to Search Parameters
                    try
                    {
                        await _searchParameterOperations.GetAndApplySearchParameterUpdates(cancellationToken);
                    }
                    catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested)
                    {
                        _logger.LogDebug("Reindex job worker canceled.");
                    }
                    catch (Exception ex)
                    {
                        // The job failed.
                        _logger.LogError(ex, "Error querying latest SearchParameterStatus updates");
                    }

                    // Check for any new Reindex Jobs
                    try
                    {
                        // Remove all completed tasks.
                        runningTasks.RemoveAll(task => task.IsCompleted);

                        // Get list of available jobs.
                        if (runningTasks.Count < _reindexJobConfiguration.MaximumNumberOfConcurrentJobsAllowed)
                        {
                            using (IScoped <IFhirOperationDataStore> store = _fhirOperationDataStoreFactory.Invoke())
                            {
                                _logger.LogTrace("Querying datastore for reindex jobs.");

                                IReadOnlyCollection <ReindexJobWrapper> jobs = await store.Value.AcquireReindexJobsAsync(
                                    _reindexJobConfiguration.MaximumNumberOfConcurrentJobsAllowed,
                                    _reindexJobConfiguration.JobHeartbeatTimeoutThreshold,
                                    cancellationToken);

                                foreach (ReindexJobWrapper job in jobs)
                                {
                                    _logger.LogTrace("Picked up reindex job: {jobId}.", job.JobRecord.Id);

                                    runningTasks.Add(_reindexJobTaskFactory().ExecuteAsync(job.JobRecord, job.ETag, cancellationToken));
                                }
                            }
                        }
                    }
                    catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested)
                    {
                        // End the execution of the task
                    }
                    catch (Exception ex)
                    {
                        // The job failed.
                        _logger.LogError(ex, "Error polling Reindex jobs.");
                    }
                }

                try
                {
                    await Task.Delay(_reindexJobConfiguration.JobPollingFrequency, cancellationToken);
                }
                catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested)
                {
                    // End the execution of the task
                }
            }
        }