From 2a09433e2a393adf2184987f809e590bf139b8a2 Mon Sep 17 00:00:00 2001 From: Luke Sikina Date: Wed, 6 Oct 2021 15:31:54 -0400 Subject: [PATCH] Cache slower study view requests at Controller level - some requests take a while even when cached at the mapper level because they have a significant amount of service level logic - to combat this, we cache these requests at the controller level when the study view filter is in its default state --- log.out | 2 + .../cbioportal/web/StudyViewController.java | 58 ++++++++++++++++--- .../cbioportal/web/TreatmentController.java | 49 ++++++++++++++-- 3 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 log.out diff --git a/log.out b/log.out new file mode 100644 index 00000000000..cbcab756f1c --- /dev/null +++ b/log.out @@ -0,0 +1,2 @@ +*** Couldn't find a suitable DSN, Sentry operations will do nothing! See documentation: https://docs.sentry.io/clients/java/ *** +No 'stacktrace.app.packages' was configured, this option is highly recommended as it affects stacktrace grouping and display on Sentry. See documentation: https://docs.sentry.io/clients/java/config/#in-application-stack-frames diff --git a/web/src/main/java/org/cbioportal/web/StudyViewController.java b/web/src/main/java/org/cbioportal/web/StudyViewController.java index 85ef8510d88..ea18e12aa08 100644 --- a/web/src/main/java/org/cbioportal/web/StudyViewController.java +++ b/web/src/main/java/org/cbioportal/web/StudyViewController.java @@ -160,7 +160,18 @@ public ResponseEntity> fetchMutatedGenes( @ApiIgnore // prevent reference to this attribute in the swagger-ui interface. this attribute is needed for the @PreAuthorize tag above. @Valid @RequestAttribute(required = false, value = "interceptedStudyViewFilter") StudyViewFilter interceptedStudyViewFilter ) throws StudyNotFoundException { + boolean singleStudyUnfiltered = studyViewFilterUtil.isSingleStudyUnfiltered(interceptedStudyViewFilter); + List alterationCountByGenes = instance.cachedFetchMutatedGenes(interceptedStudyViewFilter, singleStudyUnfiltered); + return new ResponseEntity<>(alterationCountByGenes, HttpStatus.OK); + } + @Cacheable( + cacheResolver = "staticRepositoryCacheOneResolver", + condition = "@cacheEnabledConfig.getEnabled() && #singleStudyUnfiltered" + ) + public List cachedFetchMutatedGenes( + StudyViewFilter interceptedStudyViewFilter, boolean singleStudyUnfiltered + ) throws StudyNotFoundException { AlterationFilter annotationFilters = interceptedStudyViewFilter.getAlterationFilter(); List sampleIdentifiers = studyViewFilterApplier.apply(interceptedStudyViewFilter); @@ -171,7 +182,7 @@ public ResponseEntity> fetchMutatedGenes( studyViewFilterUtil.extractStudyAndSampleIds(sampleIdentifiers, studyIds, sampleIds); alterationCountByGenes = studyViewService.getMutationAlterationCountByGenes(studyIds, sampleIds, annotationFilters); } - return new ResponseEntity<>(alterationCountByGenes, HttpStatus.OK); + return alterationCountByGenes; } @PreAuthorize("hasPermission(#involvedCancerStudies, 'Collection', T(org.cbioportal.utils.security.AccessLevel).READ)") @@ -187,8 +198,21 @@ public ResponseEntity> fetchStructuralVariantGenes( @Valid @RequestAttribute(required = false, value = "interceptedStudyViewFilter") StudyViewFilter interceptedStudyViewFilter ) throws StudyNotFoundException { + boolean singleStudyUnfiltered = studyViewFilterUtil.isSingleStudyUnfiltered(interceptedStudyViewFilter); + List alterationCountByGenes = + instance.cacheableFetchStructuralVariantGenes(interceptedStudyViewFilter, singleStudyUnfiltered); + return new ResponseEntity<>(alterationCountByGenes, HttpStatus.OK); + } + + @Cacheable( + cacheResolver = "staticRepositoryCacheOneResolver", + condition = "@cacheEnabledConfig.getEnabled() && #singleStudyUnfiltered" + ) + public List cacheableFetchStructuralVariantGenes( + StudyViewFilter interceptedStudyViewFilter, boolean singleStudyUnfiltered + ) throws StudyNotFoundException { AlterationFilter annotationFilters = interceptedStudyViewFilter.getAlterationFilter(); - + List sampleIdentifiers = studyViewFilterApplier.apply(interceptedStudyViewFilter); List alterationCountByGenes = new ArrayList<>(); if(CollectionUtils.isNotEmpty(sampleIdentifiers)) { @@ -197,10 +221,10 @@ public ResponseEntity> fetchStructuralVariantGenes( studyViewFilterUtil.extractStudyAndSampleIds(sampleIdentifiers, studyIds, sampleIds); alterationCountByGenes = studyViewService.getStructuralVariantAlterationCountByGenes(studyIds, sampleIds, annotationFilters); } - return new ResponseEntity<>(alterationCountByGenes, HttpStatus.OK); + return alterationCountByGenes; } - + @PreAuthorize("hasPermission(#involvedCancerStudies, 'Collection', T(org.cbioportal.utils.security.AccessLevel).READ)") @RequestMapping(value = "/cna-genes/fetch", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @@ -213,9 +237,18 @@ public ResponseEntity> fetchCNAGenes( @ApiIgnore // prevent reference to this attribute in the swagger-ui interface. this attribute is needed for the @PreAuthorize tag above. @Valid @RequestAttribute(required = false, value = "interceptedStudyViewFilter") StudyViewFilter interceptedStudyViewFilter ) throws StudyNotFoundException { + boolean singleStudyUnfiltered = studyViewFilterUtil.isSingleStudyUnfiltered(interceptedStudyViewFilter); + List copyNumberCountByGenes = instance.cacheableFetchCNAGenes(interceptedStudyViewFilter, singleStudyUnfiltered); + return new ResponseEntity<>(copyNumberCountByGenes, HttpStatus.OK); + } + @Cacheable( + cacheResolver = "staticRepositoryCacheOneResolver", + condition = "@cacheEnabledConfig.getEnabled() && #singleStudyUnfiltered" + ) + public List cacheableFetchCNAGenes(StudyViewFilter interceptedStudyViewFilter, boolean singleStudyUnfiltered) throws StudyNotFoundException { AlterationFilter alterationFilter = interceptedStudyViewFilter.getAlterationFilter(); - + List sampleIdentifiers = studyViewFilterApplier.apply(interceptedStudyViewFilter); List copyNumberCountByGenes = new ArrayList<>(); if(CollectionUtils.isNotEmpty(sampleIdentifiers)) { @@ -224,7 +257,7 @@ public ResponseEntity> fetchCNAGenes( studyViewFilterUtil.extractStudyAndSampleIds(sampleIdentifiers, studyIds, sampleIds); copyNumberCountByGenes = studyViewService.getCNAAlterationCountByGenes(studyIds, sampleIds, alterationFilter); } - return new ResponseEntity<>(copyNumberCountByGenes, HttpStatus.OK); + return copyNumberCountByGenes; } @PreAuthorize("hasPermission(#involvedCancerStudies, 'Collection', T(org.cbioportal.utils.security.AccessLevel).READ)") @@ -265,6 +298,17 @@ public List fetchMolecularProfileSampleCounts( @RequestAttribute(required = false, value = "involvedCancerStudies") Collection involvedCancerStudies, @ApiIgnore // prevent reference to this attribute in the swagger-ui interface. this attribute is needed for the @PreAuthorize tag above. @Valid @RequestAttribute(required = false, value = "interceptedStudyViewFilter") StudyViewFilter interceptedStudyViewFilter) { + boolean singleStudyUnfiltered = studyViewFilterUtil.isSingleStudyUnfiltered(interceptedStudyViewFilter); + return cacheableFetchMolecularProfileSampleCounts(interceptedStudyViewFilter, singleStudyUnfiltered); + } + + @Cacheable( + cacheResolver = "staticRepositoryCacheOneResolver", + condition = "@cacheEnabledConfig.getEnabled() && #singleStudyUnfiltered" + ) + public List cacheableFetchMolecularProfileSampleCounts( + StudyViewFilter interceptedStudyViewFilter, boolean singleStudyUnfiltered + ) { List sampleIdentifiers = studyViewFilterApplier.apply(interceptedStudyViewFilter); List genomicDataCounts = new ArrayList<>(); if(CollectionUtils.isNotEmpty(sampleIdentifiers)) { @@ -275,7 +319,7 @@ public List fetchMolecularProfileSampleCounts( } return genomicDataCounts; } - + private static boolean isLogScalePossibleForAttribute(String clinicalAttributeId) { return clinicalAttributeId.equals("MUTATION_COUNT"); } diff --git a/web/src/main/java/org/cbioportal/web/TreatmentController.java b/web/src/main/java/org/cbioportal/web/TreatmentController.java index c027bd2e446..f2a933c4616 100644 --- a/web/src/main/java/org/cbioportal/web/TreatmentController.java +++ b/web/src/main/java/org/cbioportal/web/TreatmentController.java @@ -12,6 +12,8 @@ import org.cbioportal.web.util.StudyViewFilterApplier; import org.cbioportal.web.util.StudyViewFilterUtil; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.context.ApplicationContext; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -20,6 +22,7 @@ import org.springframework.web.bind.annotation.*; import springfox.documentation.annotations.ApiIgnore; +import javax.annotation.PostConstruct; import javax.validation.Valid; import javax.validation.constraints.Size; import java.util.ArrayList; @@ -31,6 +34,16 @@ @Validated @Api(tags = "Treatments", description = " ") public class TreatmentController { + @Autowired + private ApplicationContext applicationContext; + TreatmentController instance; + @PostConstruct + private void init() { + instance = applicationContext.getBean(TreatmentController.class); + } + + @Autowired + private StudyViewFilterUtil studyViewFilterUtil; @Autowired private StudyViewFilterUtil filterUtil; @@ -61,14 +74,26 @@ public ResponseEntity> getAllPatientTreatments( @Valid @RequestAttribute(required = false, value = "interceptedStudyViewFilter") StudyViewFilter interceptedStudyViewFilter + ) { + boolean singleStudyUnfiltered = studyViewFilterUtil.isSingleStudyUnfiltered(interceptedStudyViewFilter); + List treatments = + instance.cachableGetAllPatientTreatments(tier, interceptedStudyViewFilter, singleStudyUnfiltered); + return new ResponseEntity<>(treatments, HttpStatus.OK); + } + + @Cacheable( + cacheResolver = "staticRepositoryCacheOneResolver", + condition = "@cacheEnabledConfig.getEnabled() && #singleStudyUnfiltered" + ) + public List cachableGetAllPatientTreatments( + ClinicalEventKeyCode tier, StudyViewFilter interceptedStudyViewFilter, boolean singleStudyUnfiltered ) { List sampleIdentifiers = studyViewFilterApplier.apply(interceptedStudyViewFilter); List sampleIds = new ArrayList<>(); List studyIds = new ArrayList<>(); filterUtil.extractStudyAndSampleIds(sampleIdentifiers, studyIds, sampleIds); - - List treatments = treatmentService.getAllPatientTreatmentRows(sampleIds, studyIds, tier); - return new ResponseEntity<>(treatments, HttpStatus.OK); + + return treatmentService.getAllPatientTreatmentRows(sampleIds, studyIds, tier); } @@ -93,14 +118,26 @@ public ResponseEntity> getAllSampleTreatments( @Valid @RequestAttribute(required = false, value = "interceptedStudyViewFilter") StudyViewFilter interceptedStudyViewFilter + ) { + boolean singleStudyUnfiltered = studyViewFilterUtil.isSingleStudyUnfiltered(interceptedStudyViewFilter); + List treatments = + instance.cacheableGetAllSampleTreatments(tier, interceptedStudyViewFilter, singleStudyUnfiltered); + return new ResponseEntity<>(treatments, HttpStatus.OK); + } + + @Cacheable( + cacheResolver = "staticRepositoryCacheOneResolver", + condition = "@cacheEnabledConfig.getEnabled() && #singleStudyUnfiltered" + ) + public List cacheableGetAllSampleTreatments( + ClinicalEventKeyCode tier, StudyViewFilter interceptedStudyViewFilter, boolean singleStudyUnfiltered ) { List sampleIdentifiers = studyViewFilterApplier.apply(interceptedStudyViewFilter); List sampleIds = new ArrayList<>(); List studyIds = new ArrayList<>(); filterUtil.extractStudyAndSampleIds(sampleIdentifiers, studyIds, sampleIds); - - List treatments = treatmentService.getAllSampleTreatmentRows(sampleIds, studyIds, tier); - return new ResponseEntity<>(treatments, HttpStatus.OK); + + return treatmentService.getAllSampleTreatmentRows(sampleIds, studyIds, tier); } @PreAuthorize("hasPermission(#studyIds, 'Collection', T(org.cbioportal.utils.security.AccessLevel).READ)")