Skip to content

Commit 0960188

Browse files
perf: stop tracker preheat from fully materializing OptionSet.options
Preheat only the (option set, code) pairs actually referenced by an import payload, via a new OptionValueSupplier that queries optionvalue directly (unnest parallel-array join), instead of relying on OptionSetMapper to hydrate entire OptionSet.options collections (including per-row JSONB attributevalues deserialization) regardless of how many option codes the import actually references. ValidationUtils.validateOptionSet reads from TrackerPreheat's targeted (option set, code) cache instead of OptionSet.getOptions(). Program rule ASSIGN actions are resolved through the same supplier right after calculateRuleEffects, since they can introduce option-set values that were never in the original payload. Verified functionally equivalent to master's #24850, file by file, with 2.41's own type conventions kept where master has since diverged (String ids predating UID, javax.persistence.EntityManager, etc.). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 6781db3 commit 0960188

17 files changed

Lines changed: 204 additions & 75 deletions

File tree

dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/bundle/DefaultTrackerBundleService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004-2022, University of Oslo
2+
* Copyright (c) 2004-2026, University of Oslo
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without

dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/config/TrackerPreheatConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004-2022, University of Oslo
2+
* Copyright (c) 2004-2026, University of Oslo
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without

dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/preheat/TrackerPreheat.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004-2022, University of Oslo
2+
* Copyright (c) 2004-2026, University of Oslo
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without

dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/preheat/mappers/OptionSetMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004-2022, University of Oslo
2+
* Copyright (c) 2004-2026, University of Oslo
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without

dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/preheat/supplier/OptionValueSupplier.java

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004-2022, University of Oslo
2+
* Copyright (c) 2004-2026, University of Oslo
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -28,6 +28,8 @@
2828
package org.hisp.dhis.tracker.imports.preheat.supplier;
2929

3030
import com.google.common.collect.Lists;
31+
import java.sql.PreparedStatement;
32+
import java.sql.ResultSet;
3133
import java.util.ArrayList;
3234
import java.util.HashSet;
3335
import java.util.List;
@@ -43,9 +45,8 @@
4345
import org.hisp.dhis.tracker.imports.domain.TrackerObjects;
4446
import org.hisp.dhis.tracker.imports.preheat.TrackerPreheat;
4547
import org.hisp.dhis.tracker.imports.util.Constant;
48+
import org.springframework.jdbc.core.ConnectionCallback;
4649
import org.springframework.jdbc.core.JdbcTemplate;
47-
import org.springframework.jdbc.core.RowCallbackHandler;
48-
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
4950
import org.springframework.stereotype.Component;
5051

5152
/**
@@ -62,10 +63,16 @@ public class OptionValueSupplier extends JdbcAbstractPreheatSupplier {
6263

6364
private static final String CODE = "code";
6465

66+
// Parallel-array unnest join: each optionsetid/code pair is matched positionally, so the two
67+
// arrays are built directly from the (deduplicated) candidate list, not from independently
68+
// deduplicated per-column sets. This gives an exact pair match sargable via
69+
// optionvalue_unique_optionsetid_and_code, instead of the IN (...) AND IN (...) cross-product
70+
// that the query planner can fall back to filtering in memory once cardinality grows.
6571
private static final String SQL =
66-
"select optionsetid, code from optionvalue"
67-
+ " where optionsetid in (:optionSetIds)"
68-
+ " and code in (:codes)";
72+
"select o.optionsetid, o.code"
73+
+ " from optionvalue o"
74+
+ " join unnest(?::bigint[], ?::text[]) as t(optionsetid, code)"
75+
+ " on o.optionsetid = t.optionsetid and o.code = t.code";
6976

7077
protected OptionValueSupplier(JdbcTemplate jdbcTemplate) {
7178
super(jdbcTemplate);
@@ -144,23 +151,30 @@ private void addCandidates(
144151
}
145152

146153
private void queryChunk(List<Pair<Long, String>> chunk, TrackerPreheat preheat) {
147-
Set<Long> optionSetIds = new HashSet<>();
148-
Set<String> codes = new HashSet<>();
149-
for (Pair<Long, String> pair : chunk) {
150-
optionSetIds.add(pair.getLeft());
151-
codes.add(pair.getRight());
154+
Long[] optionSetIds = new Long[chunk.size()];
155+
String[] codes = new String[chunk.size()];
156+
for (int i = 0; i < chunk.size(); i++) {
157+
optionSetIds[i] = chunk.get(i).getLeft();
158+
codes[i] = chunk.get(i).getRight();
152159
}
153160

154-
MapSqlParameterSource parameters = new MapSqlParameterSource();
155-
parameters.addValue("optionSetIds", optionSetIds);
156-
parameters.addValue("codes", codes);
157-
158161
Set<Pair<Long, String>> confirmed = new HashSet<>();
159-
jdbcTemplate.query(
160-
SQL,
161-
parameters,
162-
(RowCallbackHandler)
163-
rs -> confirmed.add(Pair.of(rs.getLong(OPTION_SET_ID), rs.getString(CODE))));
162+
jdbcTemplate
163+
.getJdbcOperations()
164+
.execute(
165+
(ConnectionCallback<Void>)
166+
connection -> {
167+
try (PreparedStatement ps = connection.prepareStatement(SQL)) {
168+
ps.setArray(1, connection.createArrayOf("bigint", optionSetIds));
169+
ps.setArray(2, connection.createArrayOf("text", codes));
170+
try (ResultSet rs = ps.executeQuery()) {
171+
while (rs.next()) {
172+
confirmed.add(Pair.of(rs.getLong(OPTION_SET_ID), rs.getString(CODE)));
173+
}
174+
}
175+
}
176+
return null;
177+
});
164178

165179
for (Pair<Long, String> pair : chunk) { // NOSONAR confirmed comes from the query in between
166180
if (confirmed.contains(pair)) {

dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/programrule/executor/enrollment/AssignAttributeExecutor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004-2022, University of Oslo
2+
* Copyright (c) 2004-2026, University of Oslo
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without

dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/programrule/executor/event/AssignDataValueExecutor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004-2022, University of Oslo
2+
* Copyright (c) 2004-2026, University of Oslo
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without

dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/validation/validator/ValidationUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004-2022, University of Oslo
2+
* Copyright (c) 2004-2026, University of Oslo
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without

dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/validation/validator/enrollment/AttributeValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004-2022, University of Oslo
2+
* Copyright (c) 2004-2026, University of Oslo
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without

dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/imports/validation/validator/event/DataValuesValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004-2022, University of Oslo
2+
* Copyright (c) 2004-2026, University of Oslo
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without

0 commit comments

Comments
 (0)