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

FHIR Core Enhancements #3587

Merged
merged 47 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
0b96d0f
Upgrade dependencies
ndegwamartin Sep 26, 2024
3236ad9
Implement CPU async foreach extension method
ndegwamartin Sep 26, 2024
eb8a6b5
Refactor P2P data transer to execute in parallel
ndegwamartin Sep 26, 2024
7e220b0
Phase 1
ndegwamartin Oct 9, 2024
294da56
Clean up log
ndegwamartin Nov 1, 2024
4cc0d85
Clean up benchmarking logs
ndegwamartin Nov 1, 2024
a01e476
Add coroutine threadsafety on Knowledge Manager resources install
ndegwamartin Nov 1, 2024
16b4dba
Migrate to latest FHIR SDK Libraries
ndegwamartin Nov 4, 2024
ac31613
Upgrade SDC Artifact Minor fix ⬆️
ndegwamartin Nov 5, 2024
28b836a
⬆️ Upgrade SDK engine lib
dubdabasoduba Nov 7, 2024
00a71a9
Add content to test workflow APIs
f-odhiambo Nov 14, 2024
82445e8
Merge remote-tracking branch 'origin/main' into enhancements
ndegwamartin Nov 15, 2024
a9f1b4e
Merge branch 'main' into enhancements
ndegwamartin Nov 16, 2024
e05c62a
Revert Quest CI log level to stacktrace
ndegwamartin Nov 16, 2024
83b84a9
Fix Config Registration Unit Test
ndegwamartin Nov 18, 2024
69609d6
Update sync jobs configuration
ndegwamartin Nov 18, 2024
0810ccf
Merge remote-tracking branch 'origin/main' into enhancements
ndegwamartin Nov 18, 2024
c0ade04
Revert P2P refactors
ndegwamartin Nov 18, 2024
dc03710
Revert configs updates
ndegwamartin Nov 18, 2024
281cc2b
Upgrade Engine SDK Lib : Performance opt. update
ndegwamartin Nov 18, 2024
d555208
Merge branch 'main' into enhancements
pld Nov 19, 2024
d57bb07
Merge branch 'main' into enhancements
pld Nov 19, 2024
7a1d242
Merge branch 'main' into enhancements
pld Nov 21, 2024
342cc31
Merge remote-tracking branch 'origin/main' into enhancements
ndegwamartin Nov 22, 2024
ab82471
Performance UX Improvements ⚡️
ndegwamartin Nov 22, 2024
b56be6f
Fix unit tests ✅
ndegwamartin Nov 25, 2024
12e4858
Merge remote-tracking branch 'origin/main' into enhancements
ndegwamartin Nov 25, 2024
151a51e
Merge remote-tracking branch 'origin/main' into enhancements
ndegwamartin Dec 2, 2024
7c0de4f
Refactor Sync notifications management to Foreground service
ndegwamartin Dec 2, 2024
986720e
Refactor Sync process for Optimization
ndegwamartin Dec 4, 2024
9c0747c
Merge remote-tracking branch 'origin/main' into enhancements
ndegwamartin Dec 4, 2024
c216652
Spotless clean
ndegwamartin Dec 4, 2024
763de01
Migrate to thread safe Knowledge SDK Artifact
ndegwamartin Dec 5, 2024
30ce967
Fix build 💚
ndegwamartin Dec 5, 2024
14ba241
Merge remote-tracking branch 'origin/main' into enhancements
ndegwamartin Dec 5, 2024
b8ac87d
Fix failing unit test
ndegwamartin Dec 5, 2024
fe7297e
Fix ConfigurationRegistryTest unit tests ✅
ndegwamartin Dec 6, 2024
582abec
Fix build 💚
ndegwamartin Dec 6, 2024
d013d2a
Fix concurrency modification ResourceExtension helper methods
ndegwamartin Dec 6, 2024
6a3abd8
Add Foreground Service data sync permission
ndegwamartin Dec 6, 2024
b0672b7
Fix bug foreground service exception Android 14
ndegwamartin Dec 6, 2024
bb11930
Merge remote-tracking branch 'origin/main' into enhancements
ndegwamartin Dec 7, 2024
430eb14
Fix Foreround issue with Data Sync
ndegwamartin Dec 9, 2024
dfcb3df
Merge remote-tracking branch 'origin/main' into enhancements
ndegwamartin Dec 10, 2024
d456165
Revert concurrency optimizations
ndegwamartin Dec 10, 2024
d7875ef
Merge remote-tracking branch 'origin/main' into enhancements
ndegwamartin Dec 11, 2024
73cb387
Merge remote-tracking branch 'origin/main' into enhancements
ndegwamartin Dec 11, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ jobs:
force-avd-creation: true
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: ./gradlew clean -PlocalPropertiesFile=local.properties :quest:fhircoreJacocoReport --info -Pandroid.testInstrumentationRunnerArguments.notPackage=org.smartregister.fhircore.quest.performance
script: ./gradlew clean -PlocalPropertiesFile=local.properties :quest:fhircoreJacocoReport --stacktrace -Pandroid.testInstrumentationRunnerArguments.notPackage=org.smartregister.fhircore.quest.performance

- name: Run Quest module unit and instrumentation tests and generate aggregated coverage report (Disabled)
if: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.ResourceBundle
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
Expand Down Expand Up @@ -76,6 +77,7 @@
import org.smartregister.fhircore.engine.util.extension.retrieveRelatedEntitySyncLocationState
import org.smartregister.fhircore.engine.util.extension.searchCompositionByIdentifier
import org.smartregister.fhircore.engine.util.extension.updateLastUpdated
import org.smartregister.fhircore.engine.util.forEachAsync
import org.smartregister.fhircore.engine.util.helper.LocalizationHelper
import retrofit2.HttpException
import timber.log.Timber
Expand Down Expand Up @@ -431,7 +433,7 @@
} else {
val chunkedResourceIdList = entry.value.chunked(MANIFEST_PROCESSOR_BATCH_SIZE)

chunkedResourceIdList.forEach { sectionComponents ->
chunkedResourceIdList.forEachAsync(Dispatchers.IO) { sectionComponents ->

Check warning on line 436 in android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/configuration/ConfigurationRegistry.kt#L436

Added line #L436 was not covered by tests
Timber.d(
"Fetching config resource ${entry.key}: with ids ${
sectionComponents.joinToString(
Expand Down Expand Up @@ -558,7 +560,7 @@
private suspend fun processResultBundleEntries(
resultBundleEntries: List<Bundle.BundleEntryComponent>,
) {
resultBundleEntries.forEach { bundleEntryComponent ->
resultBundleEntries.forEachAsync(Dispatchers.IO) { bundleEntryComponent ->
when (bundleEntryComponent.resource) {
is Bundle -> {
val bundle = bundleEntryComponent.resource as Bundle
Expand Down Expand Up @@ -711,7 +713,7 @@
}
}
} else {
sectionComponentEntry.value.forEach {
sectionComponentEntry.value.forEachAsync(Dispatchers.IO) {
fetchResources(
gatewayModeHeaderValue = FHIR_GATEWAY_MODE_HEADER_VALUE,
url =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,20 @@
package org.smartregister.fhircore.engine.sync

import android.content.Context
import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import com.google.android.fhir.FhirEngine
import com.google.android.fhir.sync.BackoffCriteria
import com.google.android.fhir.sync.CurrentSyncJobStatus
import com.google.android.fhir.sync.LastSyncJobStatus
import com.google.android.fhir.sync.PeriodicSyncConfiguration
import com.google.android.fhir.sync.PeriodicSyncJobStatus
import com.google.android.fhir.sync.RepeatInterval
import com.google.android.fhir.sync.RetryConfiguration
import com.google.android.fhir.sync.Sync
import com.google.android.fhir.sync.SyncJobStatus
import com.google.android.fhir.sync.download.ResourceParamsBasedDownloadWorkManager
Expand Down Expand Up @@ -77,6 +81,12 @@
.setConstraints(
Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build(),
)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
10,
TimeUnit.SECONDS,

Check warning on line 88 in android/engine/src/main/java/org/smartregister/fhircore/engine/sync/SyncBroadcaster.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/sync/SyncBroadcaster.kt#L84-L88

Added lines #L84 - L88 were not covered by tests
)
.build(),
)
}
Expand All @@ -95,6 +105,16 @@
syncConstraints =
Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build(),
repeat = RepeatInterval(interval = interval, timeUnit = TimeUnit.MINUTES),
retryConfiguration =
RetryConfiguration(

Check warning on line 109 in android/engine/src/main/java/org/smartregister/fhircore/engine/sync/SyncBroadcaster.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/sync/SyncBroadcaster.kt#L109

Added line #L109 was not covered by tests
backoffCriteria =
BackoffCriteria(
backoffDelay = 10,
timeUnit = TimeUnit.SECONDS,
backoffPolicy = BackoffPolicy.EXPONENTIAL,

Check warning on line 114 in android/engine/src/main/java/org/smartregister/fhircore/engine/sync/SyncBroadcaster.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/sync/SyncBroadcaster.kt#L111-L114

Added lines #L111 - L114 were not covered by tests
),
maxRetries = 3,

Check warning on line 116 in android/engine/src/main/java/org/smartregister/fhircore/engine/sync/SyncBroadcaster.kt

View check run for this annotation

Codecov / codecov/patch

android/engine/src/main/java/org/smartregister/fhircore/engine/sync/SyncBroadcaster.kt#L116

Added line #L116 was not covered by tests
),
),
)
.handlePeriodicSyncJobStatus(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

package org.smartregister.fhircore.engine.util

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch

/**
* Launch a new coroutine for each map iteration using async. From
Expand All @@ -34,10 +37,16 @@ suspend fun <A, B> Iterable<A>.pmap(f: suspend (A) -> B): Iterable<B> = coroutin
}

/**
* Launch a new coroutine for each loop iteration using async.
* Launch a new coroutine for each loop iteration using launch and the Default Dispatcher for
* computationaly intensive tasks.
*
* @param T the type of elements in the iterable
*/
suspend fun <T> Iterable<T>.forEachAsync(action: suspend (T) -> Unit): Unit = coroutineScope {
forEach { async { action(it) } }
forEach { launch(Dispatchers.Default) { action(it) } }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Launching coroutines in a for each would negatively impact the app's performance.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is because of the overhead incurred when launching a coroutine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I suppose it depends on the intended usage for the async for each. e.g. if we are going to make a network request for each item in the foreach then the overhead will be negligible compared?

}

suspend fun <T> Iterable<T>.forEachAsync(
dispatcher: CoroutineDispatcher,
action: suspend (T) -> Unit,
): Unit = coroutineScope { forEach { launch(dispatcher) { action(it) } } }
1 change: 1 addition & 0 deletions android/engine/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
<item name="indicatorColor">@color/colorPrimary</item>
</style>


<style name="AppTheme.QuestionnaireSubmitButtonStyle" parent="Widget.MaterialComponents.Button">
<item name="android:text">@string/str_save</item>
<item name="android:textAllCaps">true</item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent
import org.hl7.fhir.r4.model.Composition
import org.hl7.fhir.r4.model.Composition.SectionComponent
import org.hl7.fhir.r4.model.Enumerations
import org.hl7.fhir.r4.model.Group
import org.hl7.fhir.r4.model.Identifier
import org.hl7.fhir.r4.model.ListResource
import org.hl7.fhir.r4.model.Reference
Expand Down Expand Up @@ -1006,17 +1007,18 @@ class ConfigurationRegistryTest : RobolectricTest() {

assertEquals(4, requestPathArgumentSlot.size)

assertEquals("Bundle/the-commodities-bundle-id", requestPathArgumentSlot.first().id)
assertEquals(ResourceType.Bundle, requestPathArgumentSlot.first().resourceType)
val bundles = requestPathArgumentSlot.filterIsInstance<Bundle>().first()
assertEquals("Bundle/the-commodities-bundle-id", bundles.id)
assertEquals(ResourceType.Bundle, bundles.resourceType)

assertEquals("Group/1000001", requestPathArgumentSlot.second().id)
assertEquals(ResourceType.Group, requestPathArgumentSlot.second().resourceType)
val groups = requestPathArgumentSlot.filterIsInstance<Group>()
assertEquals(2, groups.size)
assertTrue(groups.any { it.id == "Group/1000001" })
assertTrue(groups.any { it.id == "Group/2000001" })

assertEquals("Group/2000001", requestPathArgumentSlot[2].id)
assertEquals(ResourceType.Group, requestPathArgumentSlot[2].resourceType)

assertEquals("composition-id-1", requestPathArgumentSlot.last().id)
assertEquals(ResourceType.Composition, requestPathArgumentSlot.last().resourceType)
val compositions = requestPathArgumentSlot.filterIsInstance<Composition>().first()
assertEquals("composition-id-1", compositions.id)
assertEquals(ResourceType.Composition, compositions.resourceType)
}

@Test
Expand Down
2 changes: 1 addition & 1 deletion android/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ espresso-core = "3.6.1"
fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-rc1-SNAPSHOT"
fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-rc1-SNAPSHOT"
fhir-sdk-data-capture = "1.2.0-preview4-SNAPSHOT"
fhir-sdk-engine = "1.0.0-preview16-SNAPSHOT"
fhir-sdk-engine = "1.0.0-preview16.1-SNAPSHOT"
fhir-sdk-knowledge = "0.1.0-alpha03-preview5-rc1-SNAPSHOT"
fhir-sdk-workflow = "0.1.0-alpha04-preview10-rc1-SNAPSHOT"
fragment-ktx = "1.8.3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import java.net.UnknownHostException
import javax.inject.Inject
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.RequestBody.Companion.toRequestBody
Expand All @@ -50,6 +51,7 @@ import org.smartregister.fhircore.engine.util.extension.getActivity
import org.smartregister.fhircore.engine.util.extension.launchActivityWithNoBackStackHistory
import org.smartregister.fhircore.engine.util.extension.retrieveCompositionSections
import org.smartregister.fhircore.engine.util.extension.retrieveImplementationGuideDefinitionResources
import org.smartregister.fhircore.engine.util.forEachAsync
import org.smartregister.fhircore.quest.ui.login.LoginActivity
import retrofit2.HttpException
import timber.log.Timber
Expand Down Expand Up @@ -163,7 +165,7 @@ constructor(
.forEach { entry: Map.Entry<String, List<Composition.SectionComponent>> ->
val chunkedResourceIdList =
entry.value.chunked(ConfigurationRegistry.MANIFEST_PROCESSOR_BATCH_SIZE)
chunkedResourceIdList.forEach { parentIt ->
chunkedResourceIdList.forEachAsync(Dispatchers.IO) { parentIt ->
Timber.d(
"Fetching config resource ${entry.key}: with ids ${StringUtils.join(parentIt,",")}",
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,18 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import dagger.hilt.android.lifecycle.HiltViewModel
import io.sentry.Sentry
import io.sentry.protocol.User
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlinx.coroutines.launch
import org.hl7.fhir.r4.model.Bundle as FhirR4ModelBundle
Expand Down Expand Up @@ -460,6 +463,12 @@ constructor(
.setConstraints(
Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build(),
)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
LZRS marked this conversation as resolved.
Show resolved Hide resolved
.setBackoffCriteria(
BackoffPolicy.LINEAR,
10,
TimeUnit.SECONDS,
)
.build(),
)
}
Expand Down
Loading