-
Notifications
You must be signed in to change notification settings - Fork 299
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
Updated PerResourcePatchGenerator to return ordered PatchMapping to avoid referential integrity issues. #2442
Conversation
…avoid referential integrity issues.
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 feel we can improve complexity of this solution by using LocalChangeReferenceEntity table to fetch all the outgoing references.
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PerResourcePatchGenerator.kt
Outdated
Show resolved
Hide resolved
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PerResourcePatchGenerator.kt
Outdated
Show resolved
Hide resolved
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PerResourcePatchGenerator.kt
Outdated
Show resolved
Hide resolved
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PerResourcePatchGenerator.kt
Outdated
Show resolved
Hide resolved
…ResourceReferenceEntity
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.
Nice job stitching the LocalChangeResourceReferenceEntity
here.
Although I dont like PatchGenerator having access to database which is from another package. I think we should abstract this out in a separate interface which is parent of the two packages: db and sync.
Maybe FhirSyncDbInteractor works well in this case as well. Will raise a PR soon so that this can be imagined better.
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PatchGenerator.kt
Outdated
Show resolved
Hide resolved
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PerResourcePatchGenerator.kt
Show resolved
Hide resolved
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PerResourcePatchGenerator.kt
Outdated
Show resolved
Hide resolved
I feel this approach cannot solve for SingResource uploading strategy. Issue #2454. But I also believe we should not discard this strategy as some fhir servers may not support bundle transaction and relying on SingleChange strategies can become expensive. Currently for SingleResource upload strategy we fetch relevant LocalChanges from db, generate a patch and upload it. |
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.
thanks aditya! great job!
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PatchOrdering.kt
Outdated
Show resolved
Hide resolved
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PatchOrdering.kt
Outdated
Show resolved
Hide resolved
engine/src/test/java/com/google/android/fhir/sync/upload/patch/PatchOrderingTest.kt
Outdated
Show resolved
Hide resolved
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PatchOrdering.kt
Outdated
Show resolved
Hide resolved
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PatchOrdering.kt
Outdated
Show resolved
Hide resolved
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PatchOrdering.kt
Outdated
Show resolved
Hide resolved
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PatchOrdering.kt
Show resolved
Hide resolved
engine/src/test/java/com/google/android/fhir/sync/upload/patch/PatchOrderingTest.kt
Outdated
Show resolved
Hide resolved
engine/src/test/java/com/google/android/fhir/sync/upload/patch/PatchOrderingTest.kt
Show resolved
Hide resolved
engine/src/test/java/com/google/android/fhir/sync/upload/patch/PatchOrderingTest.kt
Show resolved
Hide resolved
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PatchOrdering.kt
Outdated
Show resolved
Hide resolved
engine/src/main/java/com/google/android/fhir/sync/upload/patch/PerResourcePatchGenerator.kt
Outdated
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 think we can have a better design:-
Crux: Instead of fetching LocalChanges we will fetch LocalChange.id as we don't need full LocalChange objects to create a toposort of Patches.
Steps:-
- fetchLocalChangeIds(): List<LocalChangeId>
- orderByResourceTypeAndId(): Map<ResourceTypeWithId, List<LocalChangeId>>
[ referencing ResourceTypeWithId as RTWI, and List<LocalChangeId> as IDPatch ]
So orderByResourceTypeAndId(): Map<RTWI, IDPatch> - buildTopoSort(): List<Pair<RTWI, IDPatch>> (use database.getLocalChangeResourceReferenceIds)
- Process this list based on UploadStrategy. For "single-resource" strategy process one by one, for "all-local-changes" process all of them at once. process = fetch whole of local changes: Flow<List<LocalChange>>
- patchGenerator(List<LocalChange>>): List<Patch>
- uploadRequestGenerator(List<Patch>): List<UploadRequest>
Pros:-
This will significantly reduce the memory consumed by delaying the fetching of full LocalChange objects.
Supports both upload strategies.
Con:-
One caveat could be double database fetching. But fetching just the LocalChangeIds should not take much time as its LocalChangeEntity's PrimaryKey.
I think given we want to support offline, memory is a more concerning issue than time complexity to sync here. But without profiling the two designs agains a db it's difficult to say.
Also, maybe we can take this up as a new issue and a new PR.
thanks for this suggestion jajoo! i'd like to understand a bit better the exact savings of this approach. but yes we should do this in a separate issue. |
looks good - if jajoo is ok with this you can merge this aditya! thanks! |
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.
LGTM
@Query( | ||
""" | ||
SELECT * | ||
FROM LocalChangeResourceReferenceEntity | ||
WHERE localChangeId = (:localChangeId) | ||
""", | ||
) | ||
abstract suspend fun getReferencesForLocalChanges( | ||
localChangeId: List<Long>, | ||
): List<LocalChangeResourceReferenceEntity> | ||
|
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.
Upload on sync fails here with
(1) row value misused in "SELECT *
FROM LocalChangeResourceReferenceEntity
WHERE localChangeId = (?,?,?,?,?,?,?,?,?,?,?)"
java.util.concurrent.ExecutionException: android.database.sqlite.SQLiteException: row value misused (code 1 SQLITE_ERROR): , while compiling: SELECT *
FROM LocalChangeResourceReferenceEntity
WHERE localChangeId = (?,?,?,?,?,?,?,?,?,?,?)
at androidx.work.impl.utils.futures.AbstractFuture.getDoneValue(AbstractFuture.java:515)
at androidx.work.impl.utils.futures.AbstractFuture.get(AbstractFuture.java:474)
at androidx.work.impl.WorkerWrapper$2.run(WorkerWrapper.java:316)
at androidx.work.impl.utils.SerialExecutorImpl$Task.run(SerialExecutorImpl.java:96)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
Caused by: android.database.sqlite.SQLiteException: row value misused (code 1 SQLITE_ERROR): , while compiling: SELECT *
FROM LocalChangeResourceReferenceEntity
WHERE localChangeId = (?,?,?,?,?,?,?,?,?,?,?)
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1069)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:673)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:590)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:62)
at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:46)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1714)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1689)
at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.query(FrameworkSQLiteDatabase.kt:156)
at androidx.room.RoomDatabase.query(RoomDatabase.kt:490)
at androidx.room.util.DBUtil.query(DBUtil.kt:75)
at com.google.android.fhir.db.impl.dao.LocalChangeDao_Impl$20.call(LocalChangeDao_Impl.java:927)
at com.google.android.fhir.db.impl.dao.LocalChangeDao_Impl$20.call(LocalChangeDao_Impl.java:924)
at androidx.room.CoroutinesRoom$Companion$execute$4$job$1.invokeSuspend(CoroutinesRoom.kt:88)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
... 3 more
Guessing the query should probably use an IN
instead of =
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.
Same here.
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.
the temporary change below worked for me
SELECT *
FROM LocalChangeResourceReferenceEntity
WHERE localChangeId IN (:localChangeId)
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 it should be IN
. Strange the test case didn't track this.
Probably the function is being called with one LocalChangeId in the list.
@aditya-07 might wanna add / modify the test case to cover this scenario ?!
…ing to avoid referential integrity issues. (google#2442)" This reverts commit 9697a8a.
IMPORTANT: All PRs must be linked to an issue (except for extremely trivial and straightforward changes).
Fixes #[issue number]
Description
LocalChangeResourceReferenceEntity
which stores each outgoing references associated with a particularLocalChange
as new entry.Alternative(s) considered
Type
Choose one: Bug fix
Screenshots (if applicable)
Checklist
./gradlew spotlessApply
and./gradlew spotlessCheck
to check my code follows the style guide of this project../gradlew check
and./gradlew connectedCheck
to test my changes locally.