Skip to content

Commit 03a333c

Browse files
committed
Fix race condition: purge anonymous ops AFTER queue is loaded
IdentityVerificationService.onModelReplaced (config HYDRATE) could fire before OperationRepo.loadSavedOperations() finished, causing removeOperationsWithoutExternalId() to run against an empty queue. Wrap the HYDRATE handler in suspendifyOnIO + awaitInitialized() so the purge waits for the queue to be fully populated, following the same pattern as RecoverFromDroppedLoginBug.
1 parent 3f3dd09 commit 03a333c

2 files changed

Lines changed: 17 additions & 14 deletions

File tree

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/IdentityVerificationService.kt

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.onesignal.core.internal.config.impl
33
import com.onesignal.common.modeling.ISingletonModelStoreChangeHandler
44
import com.onesignal.common.modeling.ModelChangeTags
55
import com.onesignal.common.modeling.ModelChangedArgs
6+
import com.onesignal.common.threading.suspendifyOnIO
67
import com.onesignal.core.internal.config.ConfigModel
78
import com.onesignal.core.internal.config.ConfigModelStore
89
import com.onesignal.core.internal.operations.IOperationRepo
@@ -42,21 +43,25 @@ internal class IdentityVerificationService(
4243

4344
val useIV = model.useIdentityVerification
4445

45-
var jwtInvalidatedExternalId: String? = null
46-
if (useIV == true) {
47-
Logging.debug("IdentityVerificationService: IV enabled, purging anonymous operations")
48-
_operationRepo.removeOperationsWithoutExternalId()
46+
suspendifyOnIO {
47+
_operationRepo.awaitInitialized()
4948

50-
val externalId = _identityModelStore.model.externalId
51-
if (externalId != null && _jwtTokenStore.getJwt(externalId) == null) {
52-
Logging.debug("IdentityVerificationService: IV enabled but no JWT for $externalId, will fire invalidated event after queue wake")
53-
jwtInvalidatedExternalId = externalId
49+
var jwtInvalidatedExternalId: String? = null
50+
if (useIV == true) {
51+
Logging.debug("IdentityVerificationService: IV enabled, purging anonymous operations")
52+
_operationRepo.removeOperationsWithoutExternalId()
53+
54+
val externalId = _identityModelStore.model.externalId
55+
if (externalId != null && _jwtTokenStore.getJwt(externalId) == null) {
56+
Logging.debug("IdentityVerificationService: IV enabled but no JWT for $externalId, will fire invalidated event after queue wake")
57+
jwtInvalidatedExternalId = externalId
58+
}
5459
}
55-
}
5660

57-
_operationRepo.forceExecuteOperations()
61+
_operationRepo.forceExecuteOperations()
5862

59-
jwtInvalidatedExternalId?.let { _userManager.fireJwtInvalidated(it) }
63+
jwtInvalidatedExternalId?.let { _userManager.fireJwtInvalidated(it) }
64+
}
6065
}
6166

6267
override fun onModelUpdated(

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -555,9 +555,7 @@ internal class OperationRepo(
555555
_operationModelStore.remove(it.operation.id)
556556
it.waiter?.wake(false)
557557
}
558-
if (toRemove.isNotEmpty()) {
559-
Logging.debug("OperationRepo: removed ${toRemove.size} anonymous operations (no externalId)")
560-
}
558+
Logging.debug("OperationRepo: removeOperationsWithoutExternalId removed ${toRemove.size} of ${toRemove.size + queue.size} operations")
561559

562560
// IV=ON never transfers anonymous state; clear existingOnesignalId so
563561
// the executor takes the createUser (upsert) path.

0 commit comments

Comments
 (0)