-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Prevent opening forms that use entities during form updates, and block form updates while filling out a form that uses entities #6440
Conversation
3e4d5f3
to
b008359
Compare
I also like this approach because the user can continue doing something else and try again later rather than waiting for a progress dialog. A few ideas for copy:
|
Do we have a way of knowing at form open time whether a form has an Entity List attachment? I keep thinking about how blocking form open could be really disruptive if form updates take some time. If there were a way to limit it only to forms that really need to be blocked that would reduce the risk a lot. |
No, we could use the new form parser to determine that either during downloading and then saving that in the database or parsing again before opening a form without changing the database schema. |
collect_app/src/main/java/org/odk/collect/android/external/FormUriActivity.kt
Outdated
Show resolved
Hide resolved
Thanks! Both forms that use Entity Lists and those that update/create Entities need to have the blocking behavior. Since there are two things to check from different sources, I think using a db column makes sense. Here's a little brainstorm about how that could work. We would set that column to true if:
Those can be completely independent from each other -- it's ok if we set true multiple times and there's no path to toggling it back to false. We have a similar concept of "entity-related" forms in Central. I think it's fine if forms that get pushed from adb rather than downloaded get missed. Most likely those wouldn't be updated from a server anyway.
For the dialog text, let's go with @alyblenkin's last suggestion but use the default title:
|
d89eccb
to
e16f39a
Compare
collect_app/src/main/java/org/odk/collect/android/external/FormUriActivity.kt
Outdated
Show resolved
Hide resolved
51b53d5
to
c8cfc2c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one-question-entity-follow-up.xml has entities-version in the model. Am I missing something?
We discussed elsewhere that that form doesn't need the version so we can't rely on it.
d367482
to
3c1e382
Compare
16a9ce7
to
c4e1d30
Compare
).build() | ||
) | ||
|
||
changeLock.tryLock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should introduce a lock
method that explodes (probably with an IllegalStateException
) if the already locked. This would be better in tests as we wouldn't have to worry about tryLock
not working.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -47,7 +47,7 @@ class BlankFormListViewModelTest { | |||
private val changeLockProvider: ChangeLockProvider = mock() | |||
private val projectId = "projectId" | |||
|
|||
private val changeLock = BooleanChangeLock() | |||
private val changeLock = ThreadSafeBooleanChangeLock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd prefer to keep BooleanChangeLock
around for tests as we don't need the extra synchronization performance overhead there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
collect_app/src/main/java/org/odk/collect/android/database/forms/FormDatabaseMigrator.java
Show resolved
Hide resolved
collect_app/src/main/java/org/odk/collect/android/database/forms/FormDatabaseMigrator.java
Show resolved
Hide resolved
+ GEOMETRY_XPATH + " text, " | ||
+ DELETED_DATE + " integer, " | ||
+ LAST_DETECTED_ATTACHMENTS_UPDATE_DATE + " integer, " // milliseconds | ||
+ USES_ENTITIES + " text);"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be not null
if we add a default (probably default "false"
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes but there is no advantage in it if we then read those values in the way we do: Boolean.valueOf(cursor.getString(usesEntitiesColumnIndex))
This handles null values correctly.
@@ -202,7 +202,7 @@ class ProjectResetterTest { | |||
saveTestInstanceFiles(currentProjectId) | |||
setupTestInstancesDatabase(currentProjectId) | |||
|
|||
(changeLockProvider.create(currentProjectId).instancesLock as BooleanChangeLock).lock() | |||
(changeLockProvider.create(currentProjectId).instancesLock as ThreadSafeBooleanChangeLock).tryLock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You drop the cast here I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -68,7 +68,7 @@ class InstancesDataServiceTest { | |||
|
|||
@Test | |||
fun `instances should not be deleted if the instances database is locked`() { | |||
(projectDependencyModule.instancesLock as BooleanChangeLock).lock() | |||
(projectDependencyModule.instancesLock as ThreadSafeBooleanChangeLock).tryLock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, we can drop this cast probably.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
collect_app/src/main/java/org/odk/collect/android/formmanagement/metadata/FormMetadataParser.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy for all my outstanding comments to be handled in a follow-up PR. I'll accept this and create a new issue.
I'm not sure about this case: After upgrading Collect with entities form the column "usesEntities" is null. If I open and entity form and wait for the match exactly, it downloads forms/new versions. Is it ok for forms after upgrading? |
Should we also test savepoints? |
Yeah, so form updates will be possible during form entry for that form until it gets its first update with the new Collect right? That's fine as the form won't use offline/local entities until that update is done (and the form is opened again) and by that time
I can't think of any problems that might come up with save points, but if you have some suspicions you should go for it! |
By update you mean the new version of the form or update meaning new entity in the dataset/ new property, or both? |
Sorry that was unclear. Both. I just mean the form update runs (as that will lead to local entities being created from the entity list CSV). |
Closes #6232
Closes #6452
Why is this the best possible solution? Were any other approaches considered?
This pull request addresses two scenarios:
To handle both cases, I used the ChangeLock mechanism as recommended in the issue. The flow is as follows:
If a user attempts to open a form that uses entities, we check whether the forms database is locked and displays an error. This differs slightly from the original approach, which suggested using a progress dialog to wait for the update process to finish. I believe this simpler solution is sufficient for now and aligns with other errors that can occur before opening a form (which we handle in FormUriActivity). The error will look like this:
We might want to convert this to a progress dialog in the future but for now, it should be enough.
If the database isn't locked, we lock it and open the form.
Any form updates triggered during form filling are blocked since the forms database remains locked.
Once the form is closed, we unlock the database, allowing any pending form updates to proceed.
#6452 says only about blocking opening forms that use entities during form updates but I implemented it the other way around as well - if the form being filled doesn’t use entities, then form updates are not blocked. I think it makes sense despite the fact it wasn't explicitly described but let me know if I'm wrong and missed something @seadowg
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?
As mentioned above, I used the mechanism of locking the forms database to control whether a forms update can proceed or if a form can be opened. However, I find this approach somewhat risky. I’m concerned about scenarios where the database may not be properly unlocked after exiting a form (since it must remain locked during form filling to prevent updates). In such cases, if a user tries to open another form, it could fail because the database is still locked. While this shouldn't happen based on my understanding, unexpected issues can always occur. I can go over the details of this process on a call if you'd like.
If you find testing this pull request difficult, as it may not be clear when the forms update process starts or ends, let me know, and I can add some visual indicators specifically for testing.
This pull request updates the forms database, so it’s important to test that aspect as well. A scenario worth testing is:
entitiesVersion
column has been added, with its value set tonull
.Here are the apks for testing:
ODK-Collect.zip
Do we need any specific form for testing your changes? If so, please attach one.
No.
Does this change require updates to documentation? If so, please file an issue here and include the link below.
No.
Before submitting this PR, please make sure you have:
./gradlew connectedAndroidTest
(or./gradlew testLab
) and confirmed all checks still passDateFormatsTest