Skip to content

Commit b89494a

Browse files
committed
[NEMO-201] only retrieve data set once
1 parent c81802b commit b89494a

File tree

3 files changed

+60
-13
lines changed

3 files changed

+60
-13
lines changed

data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ import org.tidepool.sdk.model.data.NewDataSource
4545
import org.tidepool.sdk.runCatchingNetworkExceptions
4646
import java.time.Instant
4747
import kotlin.collections.toTypedArray
48+
import kotlinx.coroutines.CompletableDeferred
49+
import kotlinx.coroutines.sync.Mutex
50+
import kotlinx.coroutines.sync.withLock
4851

4952
class DataRepositoryImpl(
5053
private val dataApi: DataApi,
@@ -63,15 +66,58 @@ class DataRepositoryImpl(
6366

6467
private val KEY_CACHED_DATA_SET_ID: String = "KEY_CACHED_DATA_SET_ID"
6568

66-
override var cachedDataSetId: String? = null
67-
get() {
68-
return field ?: keyValueStorage.getString(KEY_CACHED_DATA_SET_ID)
69-
}
69+
private var cachedDataSetId: String? = null
70+
get() = field ?: keyValueStorage.getString(KEY_CACHED_DATA_SET_ID)
71+
.also { field = it }
7072
set(value) {
7173
field = value
7274
keyValueStorage.putString(KEY_CACHED_DATA_SET_ID, value)
7375
}
7476

77+
private val dataSetIdMutex: Mutex = Mutex()
78+
private var dataSetIdDeferred: CompletableDeferred<String>? = null
79+
80+
override suspend fun awaitOrCreateCachedDataSetId(
81+
create: suspend () -> Result<String>,
82+
): Result<String> {
83+
// Fast-path when already available
84+
cachedDataSetId?.let { return Result.success(it) }
85+
86+
var isCreator = false
87+
val localDeferred: CompletableDeferred<String> = dataSetIdMutex.withLock {
88+
// Re-check after acquiring the lock
89+
cachedDataSetId?.let { return Result.success(it) }
90+
if (dataSetIdDeferred == null) {
91+
dataSetIdDeferred = CompletableDeferred()
92+
isCreator = true
93+
}
94+
dataSetIdDeferred!!
95+
}
96+
97+
if (isCreator) {
98+
val result = create()
99+
dataSetIdMutex.withLock {
100+
if (result.isSuccess) {
101+
val id = result.getOrThrow()
102+
cachedDataSetId = id
103+
dataSetIdDeferred?.complete(id)
104+
} else {
105+
val throwable = result.exceptionOrNull()
106+
?: IllegalStateException("Unknown error creating dataset id")
107+
dataSetIdDeferred?.completeExceptionally(throwable)
108+
}
109+
dataSetIdDeferred = null
110+
}
111+
return result
112+
}
113+
114+
return try {
115+
Result.success(localDeferred.await())
116+
} catch (t: Throwable) {
117+
Result.failure(t)
118+
}
119+
}
120+
75121
override suspend fun getDataForUser(
76122
userId: String,
77123
uploadId: String?,

domain/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepository.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,14 @@ import java.time.Instant
1010

1111
interface DataRepository {
1212

13-
var cachedDataSetId: String?
13+
/**
14+
* Returns the cached data set id if available. If not available, ensures that at most one
15+
* coroutine performs the discovery/creation via [create], while all other callers wait for
16+
* the result. This provides a three-state behavior: Uninitialized, Loading, and Ready.
17+
*/
18+
suspend fun awaitOrCreateCachedDataSetId(
19+
create: suspend () -> Result<String>,
20+
): Result<String>
1421

1522
suspend fun getDataForUser(
1623
userId: String,

domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -343,11 +343,8 @@ class DataService internal constructor(
343343

344344
suspend fun uploadData(data: BaseData): Result<List<BaseData>> = uploadData(listOf(data))
345345

346-
private suspend fun getDataSetId(): Result<String> {
347-
println("DataService: Getting data set ID")
348-
return dataRepository.cachedDataSetId?.let {
349-
Result.success(it)
350-
} ?: getUserDataSets()
346+
private suspend fun getDataSetId() = dataRepository.awaitOrCreateCachedDataSetId {
347+
getUserDataSets()
351348
.flatMap { dataSets ->
352349
dataSets
353350
.filter { it.uploadId != null }
@@ -387,8 +384,5 @@ class DataService internal constructor(
387384
it.id?.let { Result.success(it) }
388385
?: Result.failure(IllegalStateException("No id"))
389386
}
390-
.onSuccess {
391-
dataRepository.cachedDataSetId = it
392-
}
393387
}
394388
}

0 commit comments

Comments
 (0)