-
Notifications
You must be signed in to change notification settings - Fork 306
Description
When using a Query subscription, I think there is an implicit assumption that the last call to onData
will contain the most recent data in the database. Or in other words, if I put 'A', 'B', 'C' into the database, I expect my onData
callbacks to receive 'A', 'B', 'C' in that order. Or, at the very least, I expect 'C' to be the last emission I get. Unfortunately, that is not currently always the case.
It seems like this ordering was trying to be enforced at the BoxStore level. In the ObjectClassPublisher a concentrated effort is made to ensure that entity id changes are processed in a synchronized fashion by locking it to one thread at a time. In other words, it ensures that changes are emitted to data observers in the order that they were published.
The problem appears within the QueryPublisher because it takes those changes and publishes them to an unbounded thread pool. Once there, it executes the query to get the data, and emits them to all child observers. This is problematic, because there is nothing preventing multiple threads in the pool from querying and emitting data to the child observer. An example best illustrates the problem.
Lets say we have a Box with a list of car brands. For simplicity, imagine only one thread runs at a time.
- Some time in the past, data observer is subscribed to query to get all car brands
- InsertThread - Put Toyota into car brand box
- ThreadPool-1 - Receives change, queries box, and gets { Toyota } ... Context switch occurs
- InsertThread - Put Lexus into car brand box
- ThreadPool-2 - Receives changes, queries box, and gets { Toyota, Lexus }. Emits to data observer
- ThreadPool-1 - Emits list { Toyota } to data observer
Now, the query data observer incorrectly believes that the box contains only Toyota instead of Toyota and Lexus.
The way we are currently working around this problem is only using the entity class observers. Then in the observer we have a synchronized
block that will run find()
on our query and emit the data. You might think the synchronized block wouldn't be required, but the initial publishSingle
for entity class observers has the possibility of running concurrently with the entity id based change processing.