Skip to content
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

Optimize redownloading entity forms #6376

Merged
merged 31 commits into from
Sep 6, 2024

Conversation

seadowg
Copy link
Member

@seadowg seadowg commented Aug 26, 2024

Closes #6344
Closes #6389
Blocked by #6372

Why is this the best possible solution? Were any other approaches considered?

There are two keys changes here that lower subsequent download times (with or without changes):

  • Entities that haven't changed aren't saved to the database as an update any longer
  • Entity list CSVs that exactly match the previously processed list are not reprocessed

How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?

The main changes and risks are again all around form update for entity follow-up and update forms.

Before submitting this PR, please make sure you have:

  • added or modified tests for any new or changed behavior
  • run ./gradlew connectedAndroidTest (or ./gradlew testLab) and confirmed all checks still pass
  • added a comment above any new strings describing it for translators
  • added any new strings with date formatting to DateFormatsTest
  • verified that any code or assets from external sources are properly credited in comments and/or in the about file.
  • verified that any new UI elements use theme colors. UI Components Style guidelines

@seadowg
Copy link
Member Author

seadowg commented Aug 30, 2024

@lognaturel I've got a few more changes to make here (the current optimization around skipping extra saves is probably buggy), but it'd be interesting to get your Pixel 3a numbers here. I'd like to update the benchmark targets so that it passes on both the FP3 and that device.

@@ -184,31 +184,6 @@ class LocalEntityUseCasesTest {
assertThat(songs[0].branchId, equalTo(offline.branchId))
}

@Test
fun `updateLocalEntitiesFromServer overrides offline version if the online version is the same`() {
Copy link
Member Author

Choose a reason for hiding this comment

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

@lognaturel I'm not sure if this is behaviour we want or not so I wanted to call it out. It seems much simpler to me for the client to assume that if its local version and the server version of an entity are the same that the data (label and properties) will also be the same. I think this is the case when using Central as edits/fixing conflicts would always increment the version? If that's not the case and Central can return a matching version number with different data, then I can call this out with a more explicit test.

Copy link
Member Author

Choose a reason for hiding this comment

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

@lognaturel and I chatted about this, and we actually want to do the opposite so that the entity "metadata" and data are always in sync. We also want to make sure that trunkVersion and branchId make sense in cases where the server has different data, but the same version. I'll make changes to this.

@seadowg seadowg marked this pull request as ready for review September 2, 2024 11:05
@lognaturel
Copy link
Member

lognaturel commented Sep 4, 2024

Pixel 3A:

Downloading form with http cache: 37s
Downloading form second time with http cache: 2s
Loading form first time: 1s
Loading form second time: 1s
Filtering select: 1s
When manually opening the form after, I got the following exception
java.lang.RuntimeException: StrictMode ThreadPolicy violation
     	at android.os.StrictMode$AndroidBlockGuardPolicy.onThreadPolicyViolation(StrictMode.java:1876)
     	at android.os.StrictMode$AndroidBlockGuardPolicy.handleViolationWithTimingAttempt(StrictMode.java:1733)
     	at android.os.StrictMode$AndroidBlockGuardPolicy.startHandlingViolationException(StrictMode.java:1704)
     	at android.os.StrictMode$AndroidBlockGuardPolicy.onCustomSlowCall(StrictMode.java:1626)
     	at android.os.StrictMode.noteSlowCall(StrictMode.java:2694)
     	at org.odk.collect.db.sqlite.DatabaseConnection.getReadableDatabase(DatabaseConnection.kt:36)
     	at org.odk.collect.android.database.entities.DatabaseEntitiesRepository.listExists(DatabaseEntitiesRepository.kt:317)
     	at org.odk.collect.android.database.entities.DatabaseEntitiesRepository.getAllByProperty(DatabaseEntitiesRepository.kt:224)
     	at org.odk.collect.entities.javarosa.intance.LocalEntitiesInstanceAdapter.queryEq(LocalEntitiesInstanceAdapter.kt:57)
     	at org.odk.collect.entities.javarosa.filter.LocalEntitiesFilterStrategy.filter(LocalEntitiesFilterStrategy.kt:46)
     	at org.javarosa.core.model.condition.EvaluationContext.filterWithPredicate(EvaluationContext.java:382)
     	at org.javarosa.core.model.condition.EvaluationContext.filterWithPredicate(EvaluationContext.java:377)
     	at org.javarosa.core.model.condition.EvaluationContext.expandReferenceAccumulator(EvaluationContext.java:343)
     	at org.javarosa.core.model.condition.EvaluationContext.expandReference(EvaluationContext.java:250)
     	at org.javarosa.core.model.condition.EvaluationContext.expandReference(EvaluationContext.java:219)
     	at org.javarosa.xpath.expr.XPathPathExprEval.eval(XPathPathExprEval.java:34)
     	at org.javarosa.xpath.expr.XPathPathExpr.eval(XPathPathExpr.java:213)
     	at org.javarosa.xpath.XPathConditional.evalNodeset(XPathConditional.java:94)
     	at org.javarosa.core.model.ItemsetBinding.getChoices(ItemsetBinding.java:125)
     	at org.javarosa.form.api.FormEntryPrompt.getSelectChoices(FormEntryPrompt.java:222)
     	at org.javarosa.form.api.FormEntryPrompt.getAnswerValue(FormEntryPrompt.java:98)
     	at org.javarosa.form.api.FormEntryPrompt.getAnswerText(FormEntryPrompt.java:154)
     	at org.odk.collect.android.formhierarchy.QuestionAnswerProcessor.getQuestionAnswer(QuestionAnswerProcessor.kt:28)
     	at org.odk.collect.android.formhierarchy.FormHierarchyActivity.refreshView(FormHierarchyActivity.java:672)
     	at org.odk.collect.android.formhierarchy.FormHierarchyActivity.refreshView(FormHierarchyActivity.java:591)
     	at org.odk.collect.android.formhierarchy.FormHierarchyActivity.onCreate(FormHierarchyActivity.java:278)
     	at android.app.Activity.performCreate(Activity.java:8054)
     	at android.app.Activity.performCreate(Activity.java:8034)
     	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1341)
     	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3688)
     	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3864)
     	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
     	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
     	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
     	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2253)
     	at android.os.Handler.dispatchMessage(Handler.java:106)
     	at android.os.Looper.loopOnce(Looper.java:201)
     	at android.os.Looper.loop(Looper.java:288)
     	at android.app.ActivityThread.main(ActivityThread.java:7870)
     	at java.lang.reflect.Method.invoke(Native Method)
     	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
     	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
     Caused by: android.os.strictmode.CustomViolation: Accessing readable DB
     	at android.os.StrictMode$AndroidBlockGuardPolicy.onCustomSlowCall(StrictMode.java:1626) 
     	at android.os.StrictMode.noteSlowCall(StrictMode.java:2694) 
     	at org.odk.collect.db.sqlite.DatabaseConnection.getReadableDatabase(DatabaseConnection.kt:36) 
     	at org.odk.collect.android.database.entities.DatabaseEntitiesRepository.listExists(DatabaseEntitiesRepository.kt:317) 
     	at org.odk.collect.android.database.entities.DatabaseEntitiesRepository.getAllByProperty(DatabaseEntitiesRepository.kt:224) 
     	at org.odk.collect.entities.javarosa.intance.LocalEntitiesInstanceAdapter.queryEq(LocalEntitiesInstanceAdapter.kt:57) 
     	at org.odk.collect.entities.javarosa.filter.LocalEntitiesFilterStrategy.filter(LocalEntitiesFilterStrategy.kt:46) 
     	at org.javarosa.core.model.condition.EvaluationContext.filterWithPredicate(EvaluationContext.java:382) 
     	at org.javarosa.core.model.condition.EvaluationContext.filterWithPredicate(EvaluationContext.java:377) 
     	at org.javarosa.core.model.condition.EvaluationContext.expandReferenceAccumulator(EvaluationContext.java:343) 
     	at org.javarosa.core.model.condition.EvaluationContext.expandReference(EvaluationContext.java:250) 
     	at org.javarosa.core.model.condition.EvaluationContext.expandReference(EvaluationContext.java:219) 
     	at org.javarosa.xpath.expr.XPathPathExprEval.eval(XPathPathExprEval.java:34) 
     	at org.javarosa.xpath.expr.XPathPathExpr.eval(XPathPathExpr.java:213) 
     	at org.javarosa.xpath.XPathConditional.evalNodeset(XPathConditional.java:94) 
     	at org.javarosa.core.model.ItemsetBinding.getChoices(ItemsetBinding.java:125) 
     	at org.javarosa.form.api.FormEntryPrompt.getSelectChoices(FormEntryPrompt.java:222) 
     	at org.javarosa.form.api.FormEntryPrompt.getAnswerValue(FormEntryPrompt.java:98) 
     	at org.javarosa.form.api.FormEntryPrompt.getAnswerText(FormEntryPrompt.java:154) 
     	at org.odk.collect.android.formhierarchy.QuestionAnswerProcessor.getQuestionAnswer(QuestionAnswerProcessor.kt:28) 
     	at org.odk.collect.android.formhierarchy.FormHierarchyActivity.refreshView(FormHierarchyActivity.java:672) 
     	at org.odk.collect.android.formhierarchy.FormHierarchyActivity.refreshView(FormHierarchyActivity.java:591) 
     	at org.odk.collect.android.formhierarchy.FormHierarchyActivity.onCreate(FormHierarchyActivity.java:278) 
     	at android.app.Activity.performCreate(Activity.java:8054) 
     	at android.app.Activity.performCreate(Activity.java:8034) 
     	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1341) 
     	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3688) 
     	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3864) 
     	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103) 
     	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
     	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
     	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2253) 
     	at android.os.Handler.dispatchMessage(Handler.java:106) 
     	at android.os.Looper.loopOnce(Looper.java:201) 
     	at android.os.Looper.loop(Looper.java:288) 
     	at android.app.ActivityThread.main(ActivityThread.java:7870) 
     	at java.lang.reflect.Method.invoke(Native Method) 
     	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
     	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) 

I believe that was because an update was happening in the background as I tried to open, does that sound right (so related to #6232)?

@seadowg
Copy link
Member Author

seadowg commented Sep 5, 2024

When manually opening the form after, I got the following exception

Interesting! It looks to me like we'll get that for any filtered entity list when opening the view hierarchy (in debug builds) because select choices are loaded on the UI thread there. I'm guessing fixing this is going to be a lot of work, so I might have to look at disabling the strict mode check there for now.

@seadowg
Copy link
Member Author

seadowg commented Sep 5, 2024

I'm guessing fixing this is going to be a lot of work, so I might have to look at disabling the strict mode check there for now.

Digging into it, it doesn't feel like the best use of my time to work around the broken form hierarchy at the moment. I'm going to pull this out as an issue as we can always just disable StrictMode for a bit if we need to.

Copy link
Member

@grzesiek2010 grzesiek2010 left a comment

Choose a reason for hiding this comment

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

There is still one question unanswered #6376 (comment) plus this test name that should be improved.

@lognaturel lognaturel added the high priority Should be looked at before other PRs/issues label Sep 5, 2024
@grzesiek2010 grzesiek2010 merged commit a153d02 into getodk:master Sep 6, 2024
6 checks passed
@seadowg seadowg deleted the optimize-upload branch September 6, 2024 10:22
@dbemke
Copy link

dbemke commented Sep 10, 2024

After changes in the PR the update and follow-up forms don't get updated unless there's a change in entities CSV. When I go to "Download form" I see that the subtext "This is an update to a form you have" even in a case when these forms won't get updated cause there weren't any changes. Should this subtext appear when there aren't any changes in the CSV?
downloadFormNoUpdate

@seadowg
Copy link
Member Author

seadowg commented Sep 10, 2024

Should this subtext appear when there aren't any changes in the CSV?

Good question! That was the case before the PR right? There was no visible update to the forms if the CSV wasn't updated (we just don't do any extra work we don't have to now). I think that's expected right now. As far as I'm concerned, we're not really optimizing the entities experience for manual downloads (although they are very useful for testing different scenarios), but it might be something we want to tweak if we do see a lot of entities use with them.

@dbemke
Copy link

dbemke commented Sep 10, 2024

That was the case before the PR right?

Yes

@dbemke
Copy link

dbemke commented Sep 10, 2024

Tested with Success!

Verified on a device with Android 10

Verified Cases:

@WKobus
Copy link

WKobus commented Sep 10, 2024

Tested with Success

Verified on device with Android 14

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
behavior verified high priority Should be looked at before other PRs/issues
Projects
None yet
5 participants