-
Notifications
You must be signed in to change notification settings - Fork 7
1185: Support incremental update of materialized samples view #7726
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
faa6962
a5864ac
9bd9df0
06039f7
decc2d0
cbaa018
219f73f
68433f7
aaf4b5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| /* | ||
| * Copyright (c) 2026 LabKey Corporation | ||
| * | ||
| * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 | ||
| */ | ||
| -- For samples, incremental materialized-view updates filter exp.material by (CpasType, Modified) to find rows changed | ||
| -- since modification began. This index allows for the query to avoid a full table scan. | ||
| CREATE INDEX IX_Material_CpasType_Modified ON exp.material (CpasType, Modified); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| /* | ||
| * Copyright (c) 2026 LabKey Corporation | ||
| * | ||
| * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 | ||
| */ | ||
| -- For samples, incremental materialized-view updates filter exp.material by (CpasType, Modified) to find rows changed | ||
| -- since modification began. This index allows for the query to avoid a full table scan. | ||
| CREATE INDEX IX_Material_CpasType_Modified ON exp.Material (CpasType, Modified); |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,6 +46,7 @@ | |
| import org.labkey.api.data.RemapCache; | ||
| import org.labkey.api.data.RuntimeSQLException; | ||
| import org.labkey.api.data.SimpleFilter; | ||
| import org.labkey.api.data.SqlSelector; | ||
| import org.labkey.api.data.TableInfo; | ||
| import org.labkey.api.data.TableSelector; | ||
| import org.labkey.api.data.UpdateableTableInfo; | ||
|
|
@@ -116,6 +117,7 @@ | |
|
|
||
| import java.io.IOException; | ||
| import java.sql.SQLException; | ||
| import java.sql.Timestamp; | ||
| import java.util.ArrayList; | ||
| import java.util.Collection; | ||
| import java.util.Collections; | ||
|
|
@@ -149,6 +151,7 @@ | |
| import static org.labkey.api.util.IntegerUtils.asLong; | ||
| import static org.labkey.experiment.ExpDataIterators.incrementCounts; | ||
| import static org.labkey.experiment.api.SampleTypeServiceImpl.SampleChangeType.insert; | ||
| import static org.labkey.experiment.api.SampleTypeServiceImpl.SampleChangeType.merge; | ||
| import static org.labkey.experiment.api.SampleTypeServiceImpl.SampleChangeType.rollup; | ||
| import static org.labkey.experiment.api.SampleTypeServiceImpl.SampleChangeType.update; | ||
|
|
||
|
|
@@ -466,12 +469,16 @@ public int loadRows(User user, Container container, DataIteratorBuilder rows, Da | |
|
|
||
| context.putConfigParameter(ExperimentService.QueryOptions.GetSampleRecomputeCol, true); | ||
| ArrayList<Map<String, Object>> outputRows = new ArrayList<>(); | ||
| InsertOption insertOption = context.getInsertOption(); | ||
| Timestamp changedSince = insertOption.allowUpdate ? captureChangedSince() : null; | ||
|
|
||
| int ret = super.loadRows(user, container, rows, outputRows, context, extraScriptContext); | ||
| if (ret > 0 && !context.getErrors().hasErrors() && _sampleType != null) | ||
| { | ||
| boolean isMediaUpdate = _sampleType.isMedia() && context.getInsertOption().updateOnly; | ||
| onSamplesChanged(!isMediaUpdate ? outputRows : null, context.getConfigParameters(), container, context.getInsertOption().allowUpdate ? update : insert); | ||
| audit(context.getInsertOption().auditAction); | ||
| boolean isMediaUpdate = _sampleType.isMedia() && insertOption.updateOnly; | ||
| SampleTypeServiceImpl.SampleChangeType reason = insertOption.updateOnly ? update : insertOption.allowUpdate ? merge : insert; | ||
| onSamplesChanged(!isMediaUpdate ? outputRows : null, context.getConfigParameters(), container, reason, changedSince); | ||
| audit(insertOption.auditAction); | ||
| } | ||
| return ret; | ||
| } | ||
|
|
@@ -480,10 +487,11 @@ public int loadRows(User user, Container container, DataIteratorBuilder rows, Da | |
| public int mergeRows(User user, Container container, DataIteratorBuilder rows, BatchValidationException errors, @Nullable Map<Enum, Object> configParameters, Map<String, Object> extraScriptContext) | ||
| { | ||
| assert _sampleType != null : "SampleType required for insert/update, but not required for read/delete"; | ||
| Timestamp changedSince = captureChangedSince(); | ||
| int ret = _importRowsUsingDIB(user, container, rows, null, getDataIteratorContext(errors, InsertOption.MERGE, configParameters), extraScriptContext); | ||
| if (ret > 0 && !errors.hasErrors()) | ||
| { | ||
| onSamplesChanged(null, configParameters, container, update); // mergeRows not really used, skip wiring recalc | ||
| onSamplesChanged(null, configParameters, container, merge, changedSince); // mergeRows not really used, skip wiring recalc | ||
| audit(QueryService.AuditAction.MERGE); | ||
| } | ||
| return ret; | ||
|
|
@@ -510,7 +518,7 @@ public List<Map<String, Object>> insertRows(User user, Container container, List | |
|
|
||
| if (results != null && !results.isEmpty() && !errors.hasErrors()) | ||
| { | ||
| onSamplesChanged(results, configParameters, container, SampleTypeServiceImpl.SampleChangeType.insert); | ||
| onSamplesChanged(results, configParameters, container, insert); | ||
| audit(QueryService.AuditAction.INSERT); | ||
| } | ||
| return results; | ||
|
|
@@ -553,6 +561,7 @@ public List<Map<String, Object>> updateRows( | |
| List<Map<String, Object>> results; | ||
| Map<Enum, Object> finalConfigParameters = configParameters == null ? new HashMap<>() : configParameters; | ||
| recordDataIteratorUsed(finalConfigParameters); | ||
| Timestamp changedSince = captureChangedSince(); | ||
|
|
||
| try | ||
| { | ||
|
|
@@ -567,7 +576,7 @@ public List<Map<String, Object>> updateRows( | |
|
|
||
| if (results != null && !results.isEmpty() && !errors.hasErrors()) | ||
| { | ||
| onSamplesChanged(!_sampleType.isMedia() ? results : null, configParameters, container, update); | ||
| onSamplesChanged(!_sampleType.isMedia() ? results : null, configParameters, container, update, changedSince); | ||
| audit(QueryService.AuditAction.UPDATE); | ||
| } | ||
|
|
||
|
|
@@ -1139,6 +1148,11 @@ protected Map<String, Object> getRow(User user, Container container, Map<String, | |
| } | ||
|
|
||
| private void onSamplesChanged(List<Map<String, Object>> results, Map<Enum, Object> params, Container container, SampleTypeServiceImpl.SampleChangeType reason) | ||
| { | ||
| onSamplesChanged(results, params, container, reason, null); | ||
| } | ||
|
|
||
| private void onSamplesChanged(List<Map<String, Object>> results, Map<Enum, Object> params, Container container, SampleTypeServiceImpl.SampleChangeType reason, @Nullable Timestamp changedSince) | ||
| { | ||
| var tx = getSchema().getDbSchema().getScope().getCurrentTransaction(); | ||
| Pair<Set<Long>, Set<String>> parentKeys = getSampleParentsForRecalc(results); | ||
|
|
@@ -1163,7 +1177,7 @@ private void onSamplesChanged(List<Map<String, Object>> results, Map<Enum, Objec | |
| boolean finalUseBackgroundRecalc = useBackgroundRecalc; | ||
| boolean finalSkipRecalc = skipRecalc; | ||
| tx.addCommitTask(() -> { | ||
| fireSamplesChanged(reason); | ||
| fireSamplesChanged(reason, changedSince); | ||
| if (finalUseBackgroundRecalc && !finalSkipRecalc) | ||
| handleRecalc(parentKeys.first, parentKeys.second, true, container); | ||
| }, DbScope.CommitTaskOption.POSTCOMMIT); | ||
|
|
@@ -1173,7 +1187,7 @@ private void onSamplesChanged(List<Map<String, Object>> results, Map<Enum, Objec | |
| } | ||
| else | ||
| { | ||
| fireSamplesChanged(reason); | ||
| fireSamplesChanged(reason, changedSince); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1205,10 +1219,15 @@ private void handleRecalc(Set<Long> rootRowIds, Set<String> parentNames, boolean | |
| } | ||
| } | ||
|
|
||
| private void fireSamplesChanged(SampleTypeServiceImpl.SampleChangeType reason) | ||
| private void fireSamplesChanged(SampleTypeServiceImpl.SampleChangeType reason, @Nullable Timestamp changedSince) | ||
| { | ||
| if (_sampleType != null) | ||
| _sampleType.onSamplesChanged(getUser(), null, reason); | ||
| _sampleType.onSamplesChanged(getUser(), null, reason, changedSince); | ||
| } | ||
|
|
||
| static @Nullable Timestamp captureChangedSince() | ||
| { | ||
| return new SqlSelector(DbScope.getLabKeyScope(), "SELECT CURRENT_TIMESTAMP").getObject(Timestamp.class); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SQLFragment.appendNowTimestamp uses NowTimestamp (System.currentTimeMillis()). Will SELECT CURRENT_TIMESTAMP match System.currentTimeMillis() ? If not, this might result in updated data materialization skipped.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
new SQLFragment("SELECT ").appendNowTimestamp()
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I remember we had to deal with SQL Server rounding issues in the past. Are both of the usages rounded in the same way?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For SQL Server, I added a 500ms buffer for the modified check. See |
||
| } | ||
|
|
||
| void audit(QueryService.AuditAction auditAction) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.