Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,15 @@ protected void setUnCachedData(List<Entity<TreeReference>> entities) {
// Old cache and index pathway where we only cache sort fields
@Deprecated
protected void setUnCachedDataOld(List<Entity<TreeReference>> entities) {
for (int i = 0; i < entities.size(); i++) {
if (isCancelled) return;
AsyncEntity e = (AsyncEntity)entities.get(i);
for (int col = 0; col < e.getNumFields(); ++col) {
if (mEntityCache != null) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we would want put this behind a feature flag for now.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like commcare-core doesn't have access to feature flags, is that right? We could pass through a boolean if we needed to - might be nice to easily toggle on and off for performance testing, but coordinating across three repos is tedious, and Shubham seemed optimistic about this change, so maybe not worth the effort. Dunno, I could go either way.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I am quite confident for this to be rolloed out without a feature flag.

for (int i = 0; i < entities.size(); i++) {
if (isCancelled) return;
AsyncEntity e = (AsyncEntity) entities.get(i);
for (int col = 0; col < e.getNumFields(); ++col) {
e.getSortField(col);
}
updateProgress(PHASE_UNCACHED_CALCULATION, i, entities.size());
}
updateProgress(PHASE_UNCACHED_CALCULATION, i, entities.size());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.commcare.cases.entity;

import org.commcare.suite.model.Detail;
import org.commcare.suite.model.DetailField;
import org.commcare.suite.model.Text;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.instance.TreeReference;
import org.javarosa.core.util.OrderedHashtable;
import org.javarosa.xpath.expr.XPathExpression;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

import static org.junit.Assert.assertEquals;

/**
* Regression test for USH-6551: sort field expressions must not be evaluated during the cache

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious if it's a common practice on USS to add Jira ticket # in code comments, we have refrained from doing so on mobile team in past as the code is open source and putting these refrences make it cryptic for anyone external looking into the code.

* priming loop when no EntityStorageCache is present (formplayer always passes null).
*
* Before the fix, setUnCachedDataOld iterated every entity × every column and called
* getSortField unconditionally, evaluating expensive XPath even though there was nowhere to
* store the result.
*/
public class AsyncNodeEntityFactoryTest {

@Test
public void prepareEntities_doesNotCallGetSortField_whenCacheIsNull() {
Detail detail = buildDetailWithSortField();

AsyncNodeEntityFactory factory = new AsyncNodeEntityFactory(
detail, new EvaluationContext(null), null, false);

SpyAsyncEntity spy1 = new SpyAsyncEntity(detail);
SpyAsyncEntity spy2 = new SpyAsyncEntity(detail);
List<Entity<TreeReference>> entities = new ArrayList<>();
entities.add(spy1);
entities.add(spy2);

factory.prepareEntities(entities);

assertEquals("getSortField must not be called when cache is null", 0, spy1.getSortFieldCallCount);
assertEquals("getSortField must not be called when cache is null", 0, spy2.getSortFieldCallCount);
}

private static Detail buildDetailWithSortField() {
DetailField.Builder fieldBuilder = new DetailField.Builder();
fieldBuilder.setSortOrder(1);
fieldBuilder.setSort(Text.PlainText("sort_value"));

Vector<DetailField> fields = new Vector<>();
fields.add(fieldBuilder.build());

return new Detail("test_detail", null, null, null,
new Vector<>(), fields, new OrderedHashtable<>(),
null, null, null, null, null, null, null, null, null, null,
false, false, null);
}

private static class SpyAsyncEntity extends AsyncEntity {
int getSortFieldCallCount = 0;

SpyAsyncEntity(Detail detail) {
super(detail, new EvaluationContext(null), new TreeReference(),
new Hashtable<String, XPathExpression>(), null, null, null);
}

@Override
public String getSortField(int i) {
getSortFieldCallCount++;
return super.getSortField(i);
}
}

}
Loading