Skip to content

Commit

Permalink
Fixes progress tracking plus tracking the live worker in async task
Browse files Browse the repository at this point in the history
  • Loading branch information
shubham1g5 committed Dec 25, 2024
1 parent ede6caf commit f145dd6
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 58 deletions.
2 changes: 1 addition & 1 deletion app/src/org/commcare/activities/EntitySelectActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ public boolean loadEntities() {

if (loader == null && !EntityLoaderTask.attachToActivity(this)) {
setProgressText(StringUtils.getStringRobust(this, R.string.entity_list_initializing));
EntityLoaderTask entityLoader = new EntityLoaderTask(shortSelect, evalContext(), this);
EntityLoaderTask entityLoader = new EntityLoaderTask(shortSelect, evalContext());
entityLoader.attachListener(this);
entityLoader.executeParallel(selectDatum.getNodeset());
return true;
Expand Down
89 changes: 48 additions & 41 deletions app/src/org/commcare/entity/AndroidAsyncNodeEntityFactory.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.commcare.entity

import androidx.lifecycle.LifecycleOwner
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import org.commcare.CommCareApplication
import org.commcare.cases.entity.AsyncNodeEntityFactory
import org.commcare.cases.entity.Entity
Expand All @@ -19,17 +21,13 @@ import org.javarosa.core.services.Logger
class AndroidAsyncNodeEntityFactory(
d: Detail,
ec: EvaluationContext?,
entityStorageCache: EntityStorageCache?,
private val lifecycleOwner: LifecycleOwner? = null
) :
AsyncNodeEntityFactory(d, ec, entityStorageCache) {
entityStorageCache: EntityStorageCache?
) : AsyncNodeEntityFactory(d, ec, entityStorageCache) {

companion object {
const val TWO_MINUTES = 2 * 60 * 1000
const val TEN_MINUTES = 10 * 60 * 1000L
}

private var completedCachePrime = false

override fun prepareEntitiesInternal(
entities: MutableList<Entity<TreeReference>>
) {
Expand All @@ -40,56 +38,65 @@ class AndroidAsyncNodeEntityFactory(
if (primeEntityCacheHelper.isInProgress()) {
// if we are priming something else at the moment, expedite the current detail
if (!primeEntityCacheHelper.isDetailInProgress(detail.id)) {
primeEntityCacheHelper.expediteDetailWithId(getCurrentCommandId(), detail, entities, progressListener)
primeEntityCacheHelper.expediteDetailWithId(
getCurrentCommandId(),
detail,
entities,
progressListener
)
} else {
primeEntityCacheHelper.setListener(progressListener)
primeEntityCacheHelper.cachedEntitiesLiveData.observe(lifecycleOwner!!) { cachedEntities ->
if (cachedEntities != null) {
entities.clear()
entities.addAll(cachedEntities)
primeEntityCacheHelper.cachedEntitiesLiveData.removeObservers(lifecycleOwner)
}
}

// otherwise wait for existing prime process to complete
waitForCachePrimeWork(entities, primeEntityCacheHelper)
observePrimeCacheWork(primeEntityCacheHelper, entities)
}
} else {
// we either have not started priming or already completed. In both cases
// we want to re-prime to make sure we calculate any uncalculated data first
primeEntityCacheHelper.primeEntityCacheForDetail(getCurrentCommandId(), detail, entities, progressListener)
primeEntityCacheHelper.primeEntityCacheForDetail(
getCurrentCommandId(),
detail,
entities,
progressListener
)
}
}
} else {
super.prepareEntitiesInternal(entities)
}
}

private fun getCurrentCommandId(): String {
return CommCareApplication.instance().currentSession.command
}

private fun waitForCachePrimeWork(
entities: MutableList<Entity<TreeReference>>,
private fun observePrimeCacheWork(
primeEntityCacheHelper: PrimeEntityCacheHelper,
entities: MutableList<Entity<TreeReference>>
) {
val startTime = System.currentTimeMillis()
while (!completedCachePrime && (System.currentTimeMillis() - startTime) < TWO_MINUTES) {
// wait for it to be completed
runBlocking {
try {
Thread.sleep(100)
} catch (_: InterruptedException) {
}
}
if (!completedCachePrime) {
Logger.log(LogTypes.TYPE_MAINTENANCE, "Still Waiting for cache priming work to complete")
// confirm if we are still priming in the worker. If yes, wait more
// otherwise recall prepareEntitiesInternal to re-evaluate the best thing to do
if (primeEntityCacheHelper.isInProgress() && primeEntityCacheHelper.isDetailInProgress(detail.id)) {
waitForCachePrimeWork(entities, primeEntityCacheHelper)
} else {
prepareEntitiesInternal(entities)
withTimeout(TEN_MINUTES) {
primeEntityCacheHelper.cachedEntitiesState.collect { cachedEntities ->
if (cachedEntities != null) {
entities.clear()
entities.addAll(cachedEntities)
return@collect
}
}
}
} catch (e: TimeoutCancellationException) {
Logger.log(
LogTypes.TYPE_MAINTENANCE,
"Timeout while waiting for the prime cache worker to finish"
)
if (primeEntityCacheHelper.isInProgress() &&
primeEntityCacheHelper.isDetailInProgress(detail.id)
) {
// keep observing
observePrimeCacheWork(primeEntityCacheHelper, entities)
} else {
prepareEntitiesInternal(entities)
}
}
}
}

private fun getCurrentCommandId(): String {
return CommCareApplication.instance().currentSession.command
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
if (this.adapter == null && this.loader == null && !EntityLoaderTask.attachToActivity(this)) {
// Set up task to fetch entity data
EntityLoaderTask theLoader =
new EntityLoaderTask(detailToDisplay, getFactoryContextForRef(referenceToDisplay), getActivity());
new EntityLoaderTask(detailToDisplay, getFactoryContextForRef(referenceToDisplay));
theLoader.attachListener(this);
theLoader.executeParallel(detailToDisplay.getNodeset().contextualize(referenceToDisplay));

Expand Down
8 changes: 2 additions & 6 deletions app/src/org/commcare/tasks/EntityLoaderHelper.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package org.commcare.tasks

import android.content.Context
import android.util.Pair
import androidx.lifecycle.LifecycleOwner
import io.reactivex.functions.Cancellable
import org.commcare.activities.EntitySelectActivity
import org.commcare.cases.entity.AsyncNodeEntityFactory
import org.commcare.cases.entity.Entity
import org.commcare.cases.entity.EntityLoadingProgressListener
import org.commcare.cases.entity.EntityStorageCache
Expand All @@ -20,7 +17,6 @@ import org.javarosa.core.model.instance.TreeReference
class EntityLoaderHelper(
detail: Detail,
evalCtx: EvaluationContext,
lifecycleOwner: LifecycleOwner? = null,
) : Cancellable {

var focusTargetIndex: Int = -1
Expand All @@ -31,7 +27,7 @@ class EntityLoaderHelper(
evalCtx.addFunctionHandler(EntitySelectActivity.getHereFunctionHandler())
if (detail.useAsyncStrategy() || detail.shouldCache()) {
val entityStorageCache: EntityStorageCache = CommCareEntityStorageCache("case")
factory = AndroidAsyncNodeEntityFactory(detail, evalCtx, entityStorageCache, lifecycleOwner)
factory = AndroidAsyncNodeEntityFactory(detail, evalCtx, entityStorageCache)
} else {
factory = NodeEntityFactory(detail, evalCtx)
if (DeveloperPreferences.collectAndDisplayEntityTraces()) {
Expand Down Expand Up @@ -105,6 +101,6 @@ class EntityLoaderHelper(

override fun cancel() {
stopLoading = true
factory.cancel()
factory.cancelLoading()
}
}
6 changes: 2 additions & 4 deletions app/src/org/commcare/tasks/EntityLoaderTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import android.util.Pair;

import androidx.lifecycle.LifecycleOwner;

import org.commcare.android.logging.ForceCloseLogger;
import org.commcare.cases.entity.Entity;
import org.commcare.cases.entity.EntityLoadingProgressListener;
Expand Down Expand Up @@ -31,8 +29,8 @@ public class EntityLoaderTask
private final EntityLoaderHelper entityLoaderHelper;
private Exception mException = null;

public EntityLoaderTask(Detail detail, EvaluationContext evalCtx, LifecycleOwner lifecycleOwner) {
entityLoaderHelper = new EntityLoaderHelper(detail, evalCtx, lifecycleOwner);
public EntityLoaderTask(Detail detail, EvaluationContext evalCtx) {
entityLoaderHelper = new EntityLoaderHelper(detail, evalCtx);
}

@Override
Expand Down
10 changes: 5 additions & 5 deletions app/src/org/commcare/tasks/PrimeEntityCacheHelper.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package org.commcare.tasks

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import io.reactivex.functions.Cancellable
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.commcare.CommCareApplication
import org.commcare.cases.entity.Entity
import org.commcare.cases.entity.EntityLoadingProgressListener
Expand All @@ -31,8 +31,8 @@ class PrimeEntityCacheHelper private constructor() : Cancellable {
private var currentDetailInProgress: String? = null
private var listener: EntityLoadingProgressListener? = null

private val _cachedEntitiesLiveData = MutableLiveData<List<Entity<TreeReference>>>()
val cachedEntitiesLiveData: LiveData<List<Entity<TreeReference>>> get() = _cachedEntitiesLiveData
private val _cachedEntitiesState = MutableStateFlow<List<Entity<TreeReference>>?>(null)
val cachedEntitiesState: StateFlow<List<Entity<TreeReference>>?> get() = _cachedEntitiesState


companion object {
Expand Down Expand Up @@ -161,7 +161,7 @@ class PrimeEntityCacheHelper private constructor() : Cancellable {
}
else -> return
}
_cachedEntitiesLiveData.postValue(cachedEntities)
_cachedEntitiesState.value = cachedEntities
currentDetailInProgress = null
}

Expand Down

0 comments on commit f145dd6

Please sign in to comment.