Skip to content

Commit 5159c9d

Browse files
Merge pull request #447 from Breeding-Insight/feature/BI-2578
[BI-2578][BI-2489] - Optimize BrAPI Germplasm Search
2 parents 32ac1b2 + 7212dc0 commit 5159c9d

File tree

8 files changed

+74
-19
lines changed

8 files changed

+74
-19
lines changed

.env.template

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ BRAPI_READ_TIMEOUT=60m
3131
# Max number of records to POST to the BrAPI service per request
3232
POST_CHUNK_SIZE=1000
3333

34+
# Request cache records paginating through available records per program by CACHE_BRAPI_FETCH_PAGE_SIZE
35+
CACHE_PAGINATE_GERMPLASM=false
36+
CACHE_BRAPI_FETCH_PAGE_SIZE=65000
37+
3438
# BrAPI Server Variables
3539
BRAPI_SERVER_PORT=8083
3640

src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ public class BrAPIGermplasmDAO {
6565
@Property(name = "micronaut.bi.api.run-scheduled-tasks")
6666
private boolean runScheduledTasks;
6767

68+
@Property(name = "brapi.paginate.germplasm")
69+
private boolean paginateGermplasm;
70+
6871
private final ProgramCache<BrAPIGermplasm> programGermplasmCache;
6972

7073
private final BrAPIEndpointProvider brAPIEndpointProvider;
@@ -141,11 +144,22 @@ private Map<String, BrAPIGermplasm> fetchProgramGermplasm(UUID programId) throws
141144
BrAPIGermplasmSearchRequest germplasmSearch = new BrAPIGermplasmSearchRequest();
142145
germplasmSearch.externalReferenceIDs(List.of(programId.toString()));
143146
germplasmSearch.externalReferenceSources(List.of(Utilities.generateReferenceSource(referenceSource, ExternalReferenceSource.PROGRAMS)));
144-
return processGermplasmForDisplay(brAPIDAOUtil.search(
145-
api::searchGermplasmPost,
146-
api::searchGermplasmSearchResultsDbIdGet,
147-
germplasmSearch
148-
), program.getKey());
147+
148+
if (paginateGermplasm) {
149+
log.debug("Fetching germplasm with pagination to BrAPI");
150+
return processGermplasmForDisplay(brAPIDAOUtil.search(
151+
api::searchGermplasmPost,
152+
api::searchGermplasmSearchResultsDbIdGet,
153+
germplasmSearch),
154+
program.getKey());
155+
} else {
156+
log.debug("Fetching germplasm without pagination to BrAPI");
157+
return processGermplasmForDisplay(brAPIDAOUtil.searchNoPaging(
158+
api::searchGermplasmPost,
159+
api::searchGermplasmSearchResultsDbIdGet,
160+
germplasmSearch),
161+
program.getKey());
162+
}
149163
}
150164

151165
/**

src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,45 +60,59 @@ public class BrAPIDAOUtil {
6060
private final Duration searchTimeout;
6161
private final int pageSize;
6262
private final int postGroupSize;
63+
private final int brapiFetchPageSize;
6364
private final ProgramService programService;
6465

6566
@Inject
6667
public BrAPIDAOUtil(@Property(name = "brapi.search.wait-time") int searchWaitTime,
6768
@Property(name = "brapi.read-timeout") Duration searchTimeout,
6869
@Property(name = "brapi.page-size") int pageSize,
6970
@Property(name = "brapi.post-group-size") int postGroupSize,
71+
@Property(name = "brapi.cache.fetch-page-size") int brapiFetchPageSize,
7072
ProgramService programService) {
7173
this.searchWaitTime = searchWaitTime;
7274
this.searchTimeout = searchTimeout;
7375
this.pageSize = pageSize;
7476
this.postGroupSize = postGroupSize;
77+
this.brapiFetchPageSize = brapiFetchPageSize;
7578
this.programService = programService;
7679
}
7780

7881
public <T, U extends BrAPISearchRequestParametersPaging, V> List<V> search(Function<U, ApiResponse<Pair<Optional<T>, Optional<BrAPIAcceptedSearchResponse>>>> searchMethod,
7982
Function3<String, Integer, Integer, ApiResponse<Pair<Optional<T>, Optional<BrAPIAcceptedSearchResponse>>>> searchGetMethod,
8083
U searchBody
8184
) throws ApiException {
82-
return searchInternal(searchMethod, searchGetMethod, null, searchBody);
85+
return searchInternal(searchMethod, searchGetMethod, null, searchBody, true);
8386
}
8487

8588
public <T, U extends BrAPISearchRequestParametersPaging, V> List<V> search(Function<U, ApiResponse<Pair<Optional<T>, Optional<BrAPIAcceptedSearchResponse>>>> searchMethod,
8689
Function4<BrAPIWSMIMEDataTypes, String, Integer, Integer, ApiResponse<Pair<Optional<T>, Optional<BrAPIAcceptedSearchResponse>>>> searchGetMethod,
8790
U searchBody
8891
) throws ApiException {
89-
return searchInternal(searchMethod, null, searchGetMethod, searchBody);
92+
return searchInternal(searchMethod, null, searchGetMethod, searchBody, true);
93+
}
94+
95+
public <T, U extends BrAPISearchRequestParametersPaging, V> List<V> searchNoPaging(Function<U, ApiResponse<Pair<Optional<T>, Optional<BrAPIAcceptedSearchResponse>>>> searchMethod,
96+
Function3<String, Integer, Integer, ApiResponse<Pair<Optional<T>, Optional<BrAPIAcceptedSearchResponse>>>> searchGetMethod,
97+
U searchBody
98+
) throws ApiException {
99+
return searchInternal(searchMethod, searchGetMethod, null, searchBody, false);
90100
}
91101

92102
private <T, U extends BrAPISearchRequestParametersPaging, V> List<V> searchInternal(Function<U, ApiResponse<Pair<Optional<T>, Optional<BrAPIAcceptedSearchResponse>>>> searchMethod,
93103
Function3<String, Integer, Integer, ApiResponse<Pair<Optional<T>, Optional<BrAPIAcceptedSearchResponse>>>> searchGetMethod,
94104
Function4<BrAPIWSMIMEDataTypes, String, Integer, Integer, ApiResponse<Pair<Optional<T>, Optional<BrAPIAcceptedSearchResponse>>>> searchGetMethodWithMimeType,
95-
U searchBody) throws ApiException {
105+
U searchBody, boolean sendPaging) throws ApiException {
96106
try {
97107
List<V> listResult = new ArrayList<>();
98-
//NOTE: Because of the way Breedbase implements BrAPI searches, the page size is initially set to an
99-
//arbitrary, large value to ensure that in the event that a 202 response is returned, the searchDbId
100-
//stored will refer to all records of the BrAPI variable.
101-
searchBody.pageSize(10000000);
108+
109+
if (sendPaging) {
110+
// This should be set to whatever the maximum allowable value is configured in the brapi test server,
111+
// perhaps it should be configurable on bi side as well.
112+
// For reference, that prop name is paging.page-size.max-allowed
113+
searchBody.pageSize(brapiFetchPageSize);
114+
}
115+
102116
ApiResponse<Pair<Optional<T>, Optional<BrAPIAcceptedSearchResponse>>> response = searchMethod.apply(searchBody);
103117
if (response.getBody().getLeft().isPresent()) {
104118
BrAPIResponse listResponse = (BrAPIResponse) response.getBody().getLeft().get();
@@ -108,7 +122,7 @@ private <T, U extends BrAPISearchRequestParametersPaging, V> List<V> searchInter
108122
pagination params are handled for POST search endpoints or the corresponding endpoints in Breedbase are
109123
changed or updated
110124
*/
111-
if(hasMorePages(listResponse)) {
125+
if(sendPaging && hasMorePages(listResponse)) {
112126
int currentPage = listResponse.getMetadata().getPagination().getCurrentPage() + 1;
113127
int totalPages = listResponse.getMetadata().getPagination().getTotalPages();
114128

@@ -137,7 +151,7 @@ private <T, U extends BrAPISearchRequestParametersPaging, V> List<V> searchInter
137151
BrAPIResponse listResponse = (BrAPIResponse) searchGetResponse.getBody().getLeft().get();
138152
listResult = getListResult(searchGetResponse);
139153

140-
if(hasMorePages(listResponse)) {
154+
if(sendPaging && hasMorePages(listResponse)) {
141155
currentPage++;
142156
int totalPages = listResponse.getMetadata()
143157
.getPagination()

src/main/resources/application.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,19 @@ brapi:
173173
search:
174174
wait-time: 1000
175175
post-group-size: ${POST_CHUNK_SIZE:1000}
176+
paginate:
177+
# These props are used as flippers for specific cache entities to be fetched all at once or paginated.
178+
# If the props are set to true, there's a possibility the BrAPI server could run out of memory grabbing all the
179+
# data and assigning it to entities in memory given a program that has a large amount of those types of entities.
180+
# At that point these flippers should be set to true.
181+
germplasm: ${CACHE_PAGINATE_GERMPLASM:false}
182+
cache:
183+
# This prop sets how many program cache entity records can be fetched at a time from BrAPI. Today, if over 65000 is used,
184+
# a SQL error can happen for programs that have more than this page size, because to fetch these certain entities
185+
# the same amount of IDs must be passed for any collections that need to be fetched in the entity, and SQL has a max of ~65355 params.
186+
# The ProgramCache will iterate through all pages of an entity to retrieve all the data from BrAPI.
187+
fetch-page-size: ${CACHE_BRAPI_FETCH_PAGE_SIZE:65000}
188+
176189

177190
email:
178191
relay-server:

src/main/resources/brapi/properties/application.properties

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,13 @@ spring.mvc.dispatch-options-request=true
3636

3737
security.oidc_discovery_url=https://example.com/auth/.well-known/openid-configuration
3838
security.enabled=false
39+
security.issuer_url=http://example.com/issuerurl
40+
41+
# This should either be set in accordance with a maximum number of SQL parameters (on JOIN FETCHES of collections,
42+
# if there is more than one collection the IDs of each entity need to be passed through as parameters, and there is a SQL
43+
# maximum of 65535. See GermplasmService.findGermplasmEntities()),
44+
# whatever returns in a reasonable amount of time,
45+
# or if you want to limit for the sake of server efficiency.
46+
paging.page-size.max-allowed=65000
47+
48+
paging.page-size.default=1000

src/test/java/org/breedinginsight/daos/BrAPIDAOUtilUnitTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public ApiResponse<Pair<Optional<BrAPIGermplasmListResponse>, Optional<BrAPIAcce
7070
@BeforeEach
7171
void setup() {
7272
//Create instance of DAO
73-
brAPIDAOUtil = new BrAPIDAOUtil(1000, Duration.of(10, ChronoUnit.MINUTES), 1, 100, programService());
73+
brAPIDAOUtil = new BrAPIDAOUtil(1000, Duration.of(10, ChronoUnit.MINUTES), 1, 100, 65000, programService());
7474

7575
//Set the page size field
7676
Field pageSize = BrAPIDAOUtil.class.getDeclaredField("pageSize");

src/test/java/org/breedinginsight/services/BrAPIGermplasmServiceUnitTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,9 @@ public void getGermplasmListExport() {
141141
when(programDAO.getAll()).thenReturn(Arrays.asList(Program.builder().id(testProgramId).name("Test Program").active(true).build()));
142142
when(programDAO.fetchOneById(any(UUID.class))).thenReturn(testProgram);
143143
when(programDAO.get(any(UUID.class))).thenReturn(Arrays.asList(testProgram));
144-
when(brAPIDAOUtil.search(any(Function.class),
145-
any(Function3.class),
146-
any(BrAPIGermplasmSearchRequest.class))).thenReturn(germplasm);
144+
when(brAPIDAOUtil.searchNoPaging(any(Function.class),
145+
any(Function3.class),
146+
any(BrAPIGermplasmSearchRequest.class))).thenReturn(germplasm);
147147

148148
//Create germplasm cache of stub data
149149
Method setupMethod = BrAPIGermplasmDAO.class.getDeclaredMethod("setup");

src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ ProgramService programService() {
201201

202202
@MockBean(BrAPIDAOUtil.class)
203203
BrAPIDAOUtil brAPIDAOUtil() {
204-
return spy(new BrAPIDAOUtil(1000, Duration.of(10, ChronoUnit.MINUTES), 1000, 100, programService()));
204+
return spy(new BrAPIDAOUtil(1000, Duration.of(10, ChronoUnit.MINUTES), 1000, 100, 65000, programService()));
205205
}
206206

207207
@MockBean(SimpleStorageService.class)

0 commit comments

Comments
 (0)