Skip to content

2.x: Surprising scheduler behaviour #6696

Closed
@dariuszseweryn

Description

@dariuszseweryn

Brief description

Given a shared upstream and two flows using .observeOn() before .filter() may change the original emission order.

RxJava Version

2.2.13

Code sample

private val subject = PublishSubject.create<Int>().toSerialized()
private val singleScheduler = Schedulers.single()

fun filterSubjectForIdOnScheduler(id: Int): Observable<Int> {
    return subject
        .observeOn(singleScheduler)
        .filter {
            Log.e("filter", "id = $id value = $it")
            it == id
        }
}

fun main() {
    Observable.merge(
        filterSubjectForIdOnScheduler(1),
        filterSubjectForIdOnScheduler(2)
    )
        .subscribe { Log.e("subscribe", "value = $it") }

    subject.onNext(2)
    subject.onNext(1)
}

Actual (surprising) result

14491-14855 E/filter: id = 1 value = 2
14491-14855 E/filter: id = 1 value = 1
14491-14855 E/subscribe: value = 1
14491-14855 E/filter: id = 2 value = 2
14491-14855 E/subscribe: value = 2
14491-14855 E/filter: id = 2 value = 1

Expected result

14491-14855 E/filter: id = 1 value = 2
14491-14855 E/filter: id = 2 value = 2
14491-14855 E/subscribe: value = 2
14491-14855 E/filter: id = 1 value = 1
14491-14855 E/subscribe: value = 1
14491-14855 E/filter: id = 2 value = 1

Notes

  1. The code originally used a Schedulers.from(Executors.newSingleThreadExecutor()) — I thought that somehow the executors may be optimised in a way that batches individual runnables and cycles them before cycling the queue of observers. I found Schedulers.single() which states (emphasis mine):
 * Returns a default, shared, single-thread-backed {@link Scheduler} instance for work
 * requiring >>>strongly-sequential<<< execution on the same background thread.
  1. Instead of using .observeOn() calling via scheduleDirect yields expected results:
singleScheduler.scheduleDirect { subject.onNext(2) }
singleScheduler.scheduleDirect { subject.onNext(1) }
// or
singleScheduler.scheduleDirect {
    subject.onNext(2)
    subject.onNext(1)
}
  1. It seems that the scheduler has two queues. One for emissions and one for observers. The emissions queue seems to be cycled before the queue of observers where sequential processing seemingly would need different priority of cycling (notify all observers about the first emission before proceeding to a next one).

Question

Is the actual result an expected one?
If so — could you explain why? Is it possible to alter the flow without reordering operators and achieve expected results?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions