You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Query returns same result even when different parameter value given #1071
Describe the bug
Despite explicit setting different parameter value subsequent query executions return same result sometimes (if made quickly enough, or maybe two different parameter values have same hash?)
Basic info (please complete the following information):
ObjectBox version: 3.1.2
Reproducibility: occasionally without visible pattern
Device: emulator, Sony Xperia XII
OS: Android 10
To Reproduce
See code section
Expected behavior
For different parameters values results must be different.
Code
@Entity
classKAEntry(
@Unique valkey:String,
valvalue:String,
@Id varid:Long = 0
)
...
val boxStore:BoxStore=MyObjectBox.builder().androidContext(context).build()
val kAEntryBox = boxStore.boxFor(KAEntry::class.java)
val byKeyQuery = kAEntryBox.query().equal(KAEntry_.key, "", CASE_SENSITIVE).parameterAlias("key").build()
kAEntryBox.put(KAEntry("currentUser.lastUnlockTimestamp","123123"))
kAEntryBox.put(KAEntry("currentUser.navigationStack","some other value"))
val result1:KAEntry?= byKeyQuery.setParameter("key", "currentUser.lastUnlockTimestamp").findUnique()
val result2:KAEntry?= byKeyQuery.setParameter("key", "currentUser.navigationStack").findUnique()
if (result1?.key == result2?.key) {
// this shouldn't be possible since I'm querying by different keys.
error("Query returned same result twice")
} else {
// fine this time
}
Additional context
I assumed that this is issue with reuse of byKeyQuery object and instead of reusing it I create new query every time. I'm not 100% sure yet if this helped since this issue is very sporadic, but I'll know if it reappears.
I think you have to put this code in a loop (query part) to reproduce issue. In my project this happens very rarely but usually when two requests are executed in rapid succession. Here are log lines produced by my application when this happens:
04-20 13:19:57.916 D/PropsRepository(19599): setPropValue existing.key = currentUser.navigationStack [23] for currentUser.lastUnlockTimestamp
04-20 13:19:57.916 D/PropsRepository(19599): setPropValue existing.key = currentUser.navigationStack [23] for currentUser.navigationStack
And here how code that logs this looks like:
{ it:Map.Entry<String, Any> ->val existing:KAEntry?= byKeyQuery.setParameter("key", it.key).findUnique()
logd { "setPropValue existing.key = ${existing?.key} [${existing?.id}] for ${it.key}" }
// do some other stuff
}
You can see that it.key had value currentUser.lastUnlockTimestamp in first query and currentUser.navigationStack in second one and in both cases existing.key was currentUser.navigationStack. This happened very quickly (milliseconds haven't changed even) and in same thread (19599), so this unlikely caused by concurrent access.
Thanks for the additional details. However, I failed to reproduce this. I tested using a local unit test on Windows and an instrumentation test on an Android 12 emulator.
I think that this is a race condition during call to setParameter on byKeyQuery object (i.e. one thread set parameter and another changed it underneath before findUnique happen or used its value). I can consistently reproduce this scenario in my test project and this also means that this is not ObjectBox problem but rather me using byKeyQuery in non multi-thread safe way. Though I still think this api needs some love so e.g. setParameter returns new object every time and therefore can be re-used from multiple threads.
While this is the way it is (i.e. non-safe to call setParameter from different threads) - how big of impact it will be to re-create query every time (workaround I've mentioned in very first message)?
Also with the new 3.5.0 release, it is possible to create a copy of a Query using copy() to use it in a different thread. It also includes QueryThreadLocal which mirrors the behavior of ThreadLocal, but gets a Query copy for each thread.
Also with the new 3.5.0 release, it is possible to create a copy of a Query using copy() to use it in a different thread. It also includes QueryThreadLocal which mirrors the behavior of ThreadLocal, but gets a Query copy for each thread.
Activity
greenrobot commentedon Apr 20, 2022
Really weird. Will it become reproducible if you put the relevant code in a loop?
cc @ivahnenkoAnna @greenrobot-team
AngryGami commentedon Apr 20, 2022
I think you have to put this code in a loop (query part) to reproduce issue. In my project this happens very rarely but usually when two requests are executed in rapid succession. Here are log lines produced by my application when this happens:
And here how code that logs this looks like:
{ it:Map.Entry<String, Any> -> val existing: KAEntry? = byKeyQuery.setParameter("key", it.key).findUnique() logd { "setPropValue existing.key = ${existing?.key} [${existing?.id}] for ${it.key}" } // do some other stuff }You can see that
it.keyhad valuecurrentUser.lastUnlockTimestampin first query andcurrentUser.navigationStackin second one and in both casesexisting.keywascurrentUser.navigationStack. This happened very quickly (milliseconds haven't changed even) and in same thread (19599), so this unlikely caused by concurrent access.greenrobot-team commentedon Apr 26, 2022
Thanks for the additional details. However, I failed to reproduce this. I tested using a local unit test on Windows and an instrumentation test on an Android 12 emulator.
Used test code like:
If you are happy to spend the time, could you provide a small example project that reproduces this issue?
AngryGami commentedon Apr 26, 2022
What
getUniqueNotNulldo? I can guess from name :) but would prefer not to.AngryGami commentedon Apr 26, 2022
I think that this is a race condition during call to
setParameteronbyKeyQueryobject (i.e. one thread set parameter and another changed it underneath before findUnique happen or used its value). I can consistently reproduce this scenario in my test project and this also means that this is not ObjectBox problem but rather me usingbyKeyQueryin non multi-thread safe way. Though I still think this api needs some love so e.g. setParameter returns new object every time and therefore can be re-used from multiple threads.greenrobot commentedon Apr 27, 2022
@AngryGami Agreed.
AngryGami commentedon Apr 27, 2022
While this is the way it is (i.e. non-safe to call
setParameterfrom different threads) - how big of impact it will be to re-create query every time (workaround I've mentioned in very first message)?greenrobot commentedon Apr 27, 2022
If it's not a hotspot, it is usually fine.
greenrobot-team commentedon Dec 5, 2022
https://docs.objectbox.io/queries#reusing-queries-and-parameters now has a note about re-using queries across multiple threads.
Also with the new
3.5.0release, it is possible to create a copy of aQueryusingcopy()to use it in a different thread. It also includesQueryThreadLocalwhich mirrors the behavior ofThreadLocal, but gets aQuerycopy for each thread.Let us know if there are issues!
chzhongsending commentedon Aug 1, 2023
Great. But we need it in GO SDK.