Search in sources :

Example 16 with ContractDTO

use of gov.cms.ab2d.common.dto.ContractDTO in project ab2d by CMSgov.

the class CoverageDriverImpl method isCoverageAvailable.

/**
 * Determine whether database contains all necessary enrollment for a contract and that no updates to that
 * enrollment are currently occurring.
 *
 * Steps
 *      - Lock coverage so no other workers can modify coverage while this check is occurring
 *      - Create any {@link CoveragePeriod}s that are currently missing
 *      - Check the following to determine whether a job can run (return false if any are not met)
 *          - Look for whether months have failed to update during earlier attempts
 *          - Look for coverage periods that have never been successfully searched and queue them
 *          - Look for coverage periods currently being updated
 *
 * @param job job to check for coverage
 * @throws CoverageDriverException if enrollment state violates assumed preconditions or database lock cannot be retrieved
 * @throws InterruptedException if trying to lock the table is interrupted
 */
@Trace(metricName = "EnrollmentIsAvailable", dispatcher = true)
@Override
public boolean isCoverageAvailable(Job job, ContractDTO contract) throws InterruptedException {
    String contractNumber = job.getContractNumber();
    assert contractNumber.equals(contract.getContractNumber());
    Lock coverageLock = coverageLockWrapper.getCoverageLock();
    // Track whether locked or not to prevent an illegal monitor exception
    boolean locked = false;
    try {
        locked = coverageLock.tryLock(MINUTE, TimeUnit.MINUTES);
        if (!locked) {
            log.warn("Could not retrieve lock after timeout of {} minute(s)." + " Cannot confirm coverage metadata is available", MINUTE);
            return false;
        }
        // Check whether a coverage period is missing for this contract.
        // If so then create those coverage periods.
        discoverCoveragePeriods(mapping.map(contract));
        log.info("queueing never searched coverage metadata periods for {}", contractNumber);
        /*
             * If any relevant coverage period has never been pulled from BFD successfully then automatically fail the
             * search
             */
        List<CoveragePeriod> neverSearched = coverageService.coveragePeriodNeverSearchedSuccessfully().stream().filter(period -> Objects.equals(contract.getContractNumber(), period.getContractNumber())).toList();
        if (!neverSearched.isEmpty()) {
            // Check that we've not submitted and failed these jobs
            neverSearched.forEach(period -> checkCoveragePeriodValidity(job, period));
            // Add all never searched coverage periods to the queue for processing
            neverSearched.forEach(period -> coverageProcessor.queueCoveragePeriod(period, false));
            return false;
        }
        log.info("checking whether any coverage metadata is currently being updated for {}", contractNumber);
        /*
             * If coverage periods are submitted, in progress or null then ignore for the moment.
             *
             * There will always be at least one coverage period returned.
             */
        List<CoveragePeriod> periods = coverageService.findAssociatedCoveragePeriods(contract.getContractNumber());
        if (periods.isEmpty()) {
            log.error("There are no existing coverage periods for this job so no metadata exists");
            throw new CoverageDriverException("There are no existing coverage periods for this job so no ");
        }
        return periods.stream().map(CoveragePeriod::getStatus).noneMatch(status -> status == null || status == CoverageJobStatus.IN_PROGRESS || status == CoverageJobStatus.SUBMITTED);
    } catch (InterruptedException interruptedException) {
        log.error("Interrupted attempting to retrieve lock. Cannot confirm coverage metadata is available");
        throw interruptedException;
    } finally {
        if (locked) {
            coverageLock.unlock();
        }
    }
}
Also used : Trace(com.newrelic.api.agent.Trace) ContractForCoverageDTO(gov.cms.ab2d.coverage.model.ContractForCoverageDTO) ZonedDateTime(java.time.ZonedDateTime) Collectors.groupingBy(java.util.stream.Collectors.groupingBy) Constants(gov.cms.ab2d.common.util.Constants) Scheduled(org.springframework.scheduling.annotation.Scheduled) CoverageUtils.getEndDateTime(gov.cms.ab2d.worker.processor.coverage.CoverageUtils.getEndDateTime) CoverageJobStatus(gov.cms.ab2d.coverage.model.CoverageJobStatus) ArrayList(java.util.ArrayList) CoveragePagingRequest(gov.cms.ab2d.coverage.model.CoveragePagingRequest) CoveragePresentCheck(gov.cms.ab2d.worker.processor.coverage.check.CoveragePresentCheck) CoverageCount(gov.cms.ab2d.coverage.model.CoverageCount) CoverageNoDuplicatesCheck(gov.cms.ab2d.worker.processor.coverage.check.CoverageNoDuplicatesCheck) Service(org.springframework.stereotype.Service) AB2D_ZONE(gov.cms.ab2d.common.util.DateUtil.AB2D_ZONE) Map(java.util.Map) CoverageStableCheck(gov.cms.ab2d.worker.processor.coverage.check.CoverageStableCheck) ContractToContractCoverageMapping(gov.cms.ab2d.worker.config.ContractToContractCoverageMapping) ZoneOffset(java.time.ZoneOffset) ContractDTO(gov.cms.ab2d.common.dto.ContractDTO) LinkedHashSet(java.util.LinkedHashSet) DateUtil(gov.cms.ab2d.common.util.DateUtil) Contract(gov.cms.ab2d.common.model.Contract) Job(gov.cms.ab2d.common.model.Job) CoverageSearchRepository(gov.cms.ab2d.coverage.repository.CoverageSearchRepository) CoveragePagingResult(gov.cms.ab2d.coverage.model.CoveragePagingResult) CoverageUpToDateCheck(gov.cms.ab2d.worker.processor.coverage.check.CoverageUpToDateCheck) Set(java.util.Set) CoverageUtils.getAttestationTime(gov.cms.ab2d.worker.processor.coverage.CoverageUtils.getAttestationTime) CoverageService(gov.cms.ab2d.coverage.service.CoverageService) Objects(java.util.Objects) TimeUnit(java.util.concurrent.TimeUnit) AB2D_EPOCH(gov.cms.ab2d.common.util.DateUtil.AB2D_EPOCH) List(java.util.List) Lock(java.util.concurrent.locks.Lock) Slf4j(lombok.extern.slf4j.Slf4j) OffsetDateTime(java.time.OffsetDateTime) ChronoUnit(java.time.temporal.ChronoUnit) DayOfWeek(java.time.DayOfWeek) PropertiesService(gov.cms.ab2d.common.service.PropertiesService) CoveragePeriodsPresentCheck(gov.cms.ab2d.worker.processor.coverage.check.CoveragePeriodsPresentCheck) PdpClientService(gov.cms.ab2d.common.service.PdpClientService) CoverageMapping(gov.cms.ab2d.coverage.model.CoverageMapping) CoveragePeriod(gov.cms.ab2d.coverage.model.CoveragePeriod) TemporalAdjusters(java.time.temporal.TemporalAdjusters) Optional(java.util.Optional) CoverageSearch(gov.cms.ab2d.coverage.model.CoverageSearch) CoveragePeriod(gov.cms.ab2d.coverage.model.CoveragePeriod) Lock(java.util.concurrent.locks.Lock) Trace(com.newrelic.api.agent.Trace)

Example 17 with ContractDTO

use of gov.cms.ab2d.common.dto.ContractDTO in project ab2d by CMSgov.

the class CoverageProcessorImpl method startJob.

/**
 * Attempt to start a coverage search of BFD
 * @param mapping a mapping job
 */
@Override
public boolean startJob(CoverageMapping mapping) {
    synchronized (inProgressMappings) {
        if (inShutdown.get()) {
            log.warn("cannot start job because service has been shutdown");
            return false;
        }
        ContractDTO contract = contractWorkerClient.getContractByContractNumber(mapping.getContractNumber());
        if (contract == null) {
            log.warn("cannot grab contract using contract number {}", mapping.getContractNumber());
            return false;
        }
        log.info("starting search for {} during {}-{}", mapping.getContractNumber(), mapping.getPeriod().getMonth(), mapping.getPeriod().getYear());
        // Currently, we are using the STU3 version to get patient mappings
        CoverageMappingCallable callable = new CoverageMappingCallable(STU3, mapping, bfdClient, contractCoverageMapping.map(contract).getCorrectedYear(mapping.getPeriod().getYear()));
        executor.submit(callable);
        inProgressMappings.add(callable);
        return true;
    }
}
Also used : ContractDTO(gov.cms.ab2d.common.dto.ContractDTO)

Example 18 with ContractDTO

use of gov.cms.ab2d.common.dto.ContractDTO in project ab2d by CMSgov.

the class ContractProcessorImpl method process.

/**
 * Process the contract - execute an entire {@link Job} from start to finish. Under the hood beneficiaries are
 * loaded from our database, queued for a thread pool to process, and the results processed and written out.
 * <p>
 * Under the hood all of this is done by paging through beneficiaries. We queue {@link #eobJobPatientQueuePageSize}
 * beneficiaries at a time and then check to see what requests have finished before attempting
 * to queue more beneficiaries.
 * <p>
 * Steps
 * - Calculate number of beneficiaries expected to process for the job
 * - Open a stream to the file system to write out results
 * - Page through enrollment queueing requests for each patient, and processing results
 * - Handle remaining requests waiting to be finished
 * - Report all files generated by running the job
 * - Cleanup stream
 * <p>
 * Periodically run at various points in this method
 * - Update the progress tracker with number of beneficiaries and eobs processed
 * - Are too many failures occurring in requests and do we need to shut down?
 * - Has the job been cancelled externally?
 * - Are too many requests waiting to run and do we have to wait for some requests to be processed?
 * - Log progress to database or console
 *
 * @return - the job output records containing the file information
 */
public List<JobOutput> process(Job job) {
    var contractNumber = job.getContractNumber();
    log.info("Beginning to process contract {}", keyValue(CONTRACT_LOG, contractNumber));
    // noinspection OptionalGetWithoutIsPresent
    ContractDTO contract = contractWorkerClient.getContractByContractNumber(contractNumber);
    int numBenes = coverageDriver.numberOfBeneficiariesToProcess(job, contract);
    jobChannelService.sendUpdate(job.getJobUuid(), JobMeasure.PATIENTS_EXPECTED, numBenes);
    log.info("Contract [{}] has [{}] Patients", contractNumber, numBenes);
    // Create the aggregator
    AggregatorCallable aggregator = new AggregatorCallable(searchConfig.getEfsMount(), job.getJobUuid(), contractNumber, ndjsonRollOver, searchConfig.getStreamingDir(), searchConfig.getFinishedDir(), searchConfig.getMultiplier());
    List<JobOutput> jobOutputs = new ArrayList<>();
    try {
        // Let the aggregator create all the necessary directories
        JobHelper.workerSetUpJobDirectories(job.getJobUuid(), searchConfig.getEfsMount(), searchConfig.getStreamingDir(), searchConfig.getFinishedDir());
        // Create the aggregator thread
        Future<Integer> aggregatorFuture = aggregatorThreadPool.submit(aggregator);
        ContractData contractData = new ContractData(contract, job);
        contractData.addAggregatorHandle(aggregatorFuture);
        // Iterate through pages of beneficiary data
        loadEobRequests(contractData);
        // Wait for remaining work to finish before cleaning up after the job
        // This should be at most eobJobPatientQueueMaxSize requests
        processRemainingRequests(contractData);
        log.info("Finished writing {} EOBs for contract {}", jobProgressService.getStatus(job.getJobUuid()).getEobsProcessedCount(), contractNumber);
        // Mark the job as finished for the aggregator (all file data has been written out)
        JobHelper.workerFinishJob(searchConfig.getEfsMount() + "/" + job.getJobUuid() + "/" + searchConfig.getStreamingDir());
        // Wait for the aggregator to finish
        while (!isDone(aggregatorFuture, job.getJobUuid(), true)) {
            Thread.sleep(1000);
        }
        // Retrieve all the job output info
        jobOutputs.addAll(getOutputs(job.getJobUuid(), DATA));
        jobOutputs.addAll(getOutputs(job.getJobUuid(), ERROR));
        log.info("Number of outputs: " + jobOutputs.size());
    } catch (InterruptedException | IOException ex) {
        log.error("interrupted while processing job for contract");
    }
    return jobOutputs;
}
Also used : ArrayList(java.util.ArrayList) IOException(java.io.IOException) ContractDTO(gov.cms.ab2d.common.dto.ContractDTO) AggregatorCallable(gov.cms.ab2d.aggregator.AggregatorCallable) JobOutput(gov.cms.ab2d.common.model.JobOutput)

Example 19 with ContractDTO

use of gov.cms.ab2d.common.dto.ContractDTO in project ab2d by CMSgov.

the class ContractProcessorImpl method loadEobRequests.

/**
 * Load beneficiaries and create an EOB request for each patient. Patients are loaded a page at a time. The page size is
 * configurable. At the end of loading all requests, the number of requests loaded is compared to the expected
 * number and the job immediately failed if not equal.
 * <p>
 * Steps:
 * - load a page of beneficiaries from the database
 * - create a request per patient and queue each request
 * - update the progress tracker with the number of patients added
 * - check if the job has been cancelled
 * - process any requests to BFD that are complete
 *
 * @param contractData job requests record and object for storing in motion requests
 * @throws InterruptedException if job is shut down during a busy wait for space in the queue
 */
private void loadEobRequests(ContractData contractData) throws InterruptedException {
    String jobUuid = contractData.getJob().getJobUuid();
    ContractDTO contract = contractData.getContract();
    // Handle first page of beneficiaries and then enter loop
    CoveragePagingResult current = coverageDriver.pageCoverage(new CoveragePagingRequest(eobJobPatientQueuePageSize, null, mapping.map(contract), contractData.getJob().getCreatedAt()));
    loadRequestBatch(contractData, current, searchConfig.getNumberBenesPerBatch());
    jobChannelService.sendUpdate(jobUuid, JobMeasure.PATIENT_REQUEST_QUEUED, current.size());
    // noinspection WhileLoopReplaceableByForEach
    while (current.getNextRequest().isPresent()) {
        if (eobClaimRequestsQueue.size(jobUuid) > eobJobPatientQueueMaxSize) {
            // Wait for queue to empty out some before adding more
            // noinspection BusyWait
            Thread.sleep(1000);
            continue;
        }
        // Queue a batch of patients
        current = coverageDriver.pageCoverage(current.getNextRequest().get());
        loadRequestBatch(contractData, current, searchConfig.getNumberBenesPerBatch());
        jobChannelService.sendUpdate(jobUuid, JobMeasure.PATIENT_REQUEST_QUEUED, current.size());
        processFinishedRequests(contractData);
    }
    // Verify that the number of benes requested matches the number expected from the database and fail
    // immediately if the two do not match
    ProgressTracker progressTracker = jobProgressService.getStatus(jobUuid);
    int totalQueued = progressTracker.getPatientRequestQueuedCount();
    int totalExpected = progressTracker.getPatientsExpected();
    if (totalQueued != totalExpected) {
        throw new ContractProcessingException("expected " + totalExpected + " patients from database but retrieved " + totalQueued);
    }
}
Also used : CoveragePagingRequest(gov.cms.ab2d.coverage.model.CoveragePagingRequest) CoveragePagingResult(gov.cms.ab2d.coverage.model.CoveragePagingResult) ContractDTO(gov.cms.ab2d.common.dto.ContractDTO)

Example 20 with ContractDTO

use of gov.cms.ab2d.common.dto.ContractDTO in project ab2d by CMSgov.

the class JobPreProcessorImpl method preprocess.

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
public Job preprocess(String jobUuid) {
    Job job = jobRepository.findByJobUuid(jobUuid);
    if (job == null) {
        log.error("Job was not found");
        throw new IllegalArgumentException("Job " + jobUuid + " was not found");
    }
    // validate status is SUBMITTED
    if (!SUBMITTED.equals(job.getStatus())) {
        final String errMsg = String.format("Job %s is not in %s status", jobUuid, SUBMITTED);
        log.error("Job is not in submitted status");
        throw new IllegalArgumentException(errMsg);
    }
    ContractDTO contract = contractWorkerClient.getContractByContractNumber(job.getContractNumber());
    if (contract == null) {
        throw new IllegalArgumentException("A job must always have a contract.");
    }
    Optional<OffsetDateTime> sinceValue = Optional.ofNullable(job.getSince());
    if (sinceValue.isPresent()) {
        // If the user provided a 'since' value
        job.setSinceSource(SinceSource.USER);
        jobRepository.save(job);
    } else if (job.getFhirVersion().supportDefaultSince() && !contract.hasDateIssue()) {
        // If the user did not, but this version supports a default 'since', populate it
        job = updateSinceTime(job, contract);
        jobRepository.save(job);
    }
    try {
        if (!coverageDriver.isCoverageAvailable(job, contract)) {
            log.info("coverage metadata is not up to date so job will not be started");
            return job;
        }
        eventLogger.logAndAlert(EventUtils.getJobChangeEvent(job, IN_PROGRESS, EOB_JOB_STARTED + " for " + contract.getContractNumber() + " in progress"), PUBLIC_LIST);
        job.setStatus(IN_PROGRESS);
        job.setStatusMessage(null);
        job = jobRepository.save(job);
    } catch (CoverageDriverException coverageDriverException) {
        eventLogger.logAndAlert(EventUtils.getJobChangeEvent(job, FAILED, EOB_JOB_COVERAGE_ISSUE + " Job for " + contract.getContractNumber() + " in progress"), PUBLIC_LIST);
        job.setStatus(FAILED);
        job.setStatusMessage("could not pull coverage information for contract");
        job = jobRepository.save(job);
    } catch (InterruptedException ie) {
        throw new RuntimeException("could not determine whether coverage metadata was up to date", ie);
    }
    return job;
}
Also used : ContractDTO(gov.cms.ab2d.common.dto.ContractDTO) OffsetDateTime(java.time.OffsetDateTime) CoverageDriverException(gov.cms.ab2d.worker.processor.coverage.CoverageDriverException) Job(gov.cms.ab2d.common.model.Job) Transactional(org.springframework.transaction.annotation.Transactional)

Aggregations

ContractDTO (gov.cms.ab2d.common.dto.ContractDTO)43 Test (org.junit.jupiter.api.Test)31 DisplayName (org.junit.jupiter.api.DisplayName)27 CoveragePeriod (gov.cms.ab2d.coverage.model.CoveragePeriod)18 ArrayList (java.util.ArrayList)13 CoverageMapping (gov.cms.ab2d.coverage.model.CoverageMapping)12 CoverageSearch (gov.cms.ab2d.coverage.model.CoverageSearch)12 CoverageSearchEvent (gov.cms.ab2d.coverage.model.CoverageSearchEvent)12 ContractToContractCoverageMapping (gov.cms.ab2d.worker.config.ContractToContractCoverageMapping)12 Job (gov.cms.ab2d.common.model.Job)11 CoverageCount (gov.cms.ab2d.coverage.model.CoverageCount)10 List (java.util.List)10 HashMap (java.util.HashMap)8 ContractForCoverageDTO (gov.cms.ab2d.coverage.model.ContractForCoverageDTO)7 CoveragePagingRequest (gov.cms.ab2d.coverage.model.CoveragePagingRequest)5 CoveragePagingResult (gov.cms.ab2d.coverage.model.CoveragePagingResult)5 Contract (gov.cms.ab2d.common.model.Contract)4 OffsetDateTime (java.time.OffsetDateTime)4 ZonedDateTime (java.time.ZonedDateTime)4 PdpClientDTO (gov.cms.ab2d.common.dto.PdpClientDTO)3