Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.x: Breaking API changes #5622

Closed
akarnokd opened this issue Sep 26, 2017 · 35 comments
Closed

3.x: Breaking API changes #5622

akarnokd opened this issue Sep 26, 2017 · 35 comments
Milestone

Comments

@akarnokd
Copy link
Member

akarnokd commented Sep 26, 2017

This issue collects the API changes proposed for 3.x: unused operators and overloads, mistakes in signatures, etc.

@akarnokd akarnokd added this to the 3.0 milestone Sep 26, 2017
@ZacSweers
Copy link
Contributor

I'd like to propose making takeUntil and similar variants take a Maybe. The contract is that it takes until the first emission, which is the same contract as a Maybe.

@JakeWharton
Copy link
Contributor

JakeWharton commented Oct 3, 2017 via email

@ZacSweers
Copy link
Contributor

I guess it depends. Sometimes you takeUntil() something that may or may not actually emit, like many lifecycle handling patterns do

@JakeWharton
Copy link
Contributor

JakeWharton commented Oct 3, 2017 via email

@ZacSweers
Copy link
Contributor

I suppose this is true, if the source stream completes it just disposes the single?

@artem-zinnatullin
Copy link
Contributor

If we decide to keep target version #5620 lower than Java 8 then:

  • Would be great to remove throws Exception from io.reactivex.functions.Function#apply() method.

else

  • Would be great to remove own io.reactivex.functions.Function and replace it with java.util.function.Function

Sounds good?

@akarnokd
Copy link
Member Author

@artem-zinnatullin Why? Adding the throws Exception was well worth the effort. In fact, the idea was to make it throws Throwable in v3. Btw, the topic warrants its own issue/discussion.

@artem-zinnatullin
Copy link
Contributor

Ah damn checked exceptions. @akarnokd, you're right of course.

For rx chains it's good that Function declares exception and probably should declare Throwable, the use case I was dealing with today is when in our Java code we needed to call function.apply() and had to wrap it in a try-catch. Kotlin 😿

@akarnokd
Copy link
Member Author

akarnokd commented Apr 3, 2018

  • Change: Single.toCompletable to Single.ignoreElement to be consistent with the other types.

@JakeWharton
Copy link
Contributor

Isn't that just a new overload and deprecation that can be done as part of 2.x?

@akarnokd
Copy link
Member Author

akarnokd commented Apr 3, 2018

Well, the alias could be added to 2.x but then toCompletable should not be in 3.x. Should we start aliasing and deprecating these types of methods in v2 so that users get better prepared for changes in 3.x?

@JakeWharton
Copy link
Contributor

I think the general assumption is that everything which is deprecated will be removed in the next major version, yes. Plus it delays the need to even require that a 3.x exist.

@davidmoten
Copy link
Collaborator

In 3.x I'd like to make upstream work more precise (across networks for instance) as discussed in #5077, and do it without adding extra operators. For example, the first operator requests Long.MAX_VALUE of upstream as a perf optimisation that I reckon we can do without.

@akarnokd
Copy link
Member Author

  • Remove: AsyncSubject.getValues()
  • Remove: AsyncProcessor.getValues()

@cerisier
Copy link

cerisier commented Jul 4, 2018

Will .getValue() stay while .getValues() is removed ?
Or do you plan to remove the ability to get any value(s) out of a Subject / Processor.

@akarnokd
Copy link
Member Author

@cerisier getValue() methods are supposed to be already present only on the relevant types.

@CoderSpinoza
Copy link

Will 3.x have different package name from 2.x?

When I follow the package naming history of RxJava 2 in #3173, it seems that the final version of package name has changed to io.reactivex from 1.x's rx, but not to include version number eventually.

As described by Jake Wharton in one of the comments in the above issue, versioned package were somewhat of a rarity back in Aug 2015 when the discussion was active, but I believe some of the popular libraries have adopted the policy, mainly to support co-existence of different incompatible versions of libraries. Retrofit and OkHttp did for 2.0 and 3.0, respectively, as described in https://jakewharton.com/java-interoperability-policy-for-major-version-updates/.

Probably it's too early to ask this question, but I noticed that 3.0 milestone is due by end of 2019 and so many RxJava consumers who are library authors will want to know the direction. Please notify me if this belongs to a separate issue, and I will post a new issue. Thanks in advance :)

@akarnokd
Copy link
Member Author

akarnokd commented Feb 13, 2019

2.x was had the complete architecture remade so it warranted a separate package. 3.x will be very likely mostly API breaking changes but the package will remain io.reactivex and users switching to it may only have to adjust to the API changes with certain operators.

@JakeWharton
Copy link
Contributor

That sounds like a really bad idea!

@akarnokd
Copy link
Member Author

I don't see any reason to have 2.x and 3.x live side-by-side. The top reason for 3.x to exist is to fix the API mistakes.

@JakeWharton
Copy link
Contributor

It makes practical migration impossible for large-scale projects using libraries built on Rx and discourages its use in the future.

@CoderSpinoza
Copy link

Consumers do not voluntarily let 2.x and 3.x live side-by-side. It will be due to incompatible versions of a common library (such as RxJava, networking, or json parsing library) used by both libraries and consumers.

Renaming packages when updating major version makes migration period really smooth and incremental with least amount of headache for both. If this isn't the case, the only choice left to library authors will be to use no library at all and writing everything from scratch, which they really don't want to.

@akarnokd
Copy link
Member Author

akarnokd commented Feb 19, 2019

There is one way to find out: make the breaking changes and see how much code breaks in the popular libraries. Unlike RxJava 2, RxJava 3 would be a drop-in replacement to RxJava 2, minus a few API adjustments.

@JakeWharton
Copy link
Contributor

That is quite a user-hostile approach.

Also "drop-in replacement" and "a few API adjustments" are not compatible. It's either a drop-in replacement that's binary compatible OR it requires API adjustments requiring all libraries update before you can.

@akarnokd
Copy link
Member Author

How many libraries are using the operators in this list? Why are they using blockingGet in non-test code, for example? Do you replace a dependent libraries without recompiling/retesting the host library/application?

I'm almost certain RxAndroid, RxRelay, RxBinding, RxSharedPreferences either won't have to change or would need a simple recompile if the Rx functional interfaces get widened to throws Throwable. There won't be any architectural change with 3.x, create and X.subscribeActual will remain the same, as will fromCallable and the Scheduler/Worker API. JDK support will also remain 6.

So I don't believe the set of breaking changes proposed will do more than just a small inconvenience. I'll personally take the time and post PRs to any open-source project that depended on the old signatures and update them to version 3 usage.

Consumers do not voluntarily let 2.x and 3.x live side-by-side. It will be due to incompatible versions of a common library (such as RxJava, networking, or json parsing library) used by both libraries and consumers.

Do you think 3rd party library authors are willing to support 2 versions, one for 2.x and one for 3.x, at the same time for an overlapping duration? What if the signature changes do not affect them at all?

Let's turn it around. Why do you think I have the time or will to support two versions, again? Why would I have to spend time answering SO questions/issue questions about why Schedulers.io() no longer compiles because the OP forgot to change the import to like io.reactivex.rxjava3?

RxJava's API has to improve, as the minor inconveniences turn into major ones over time. Also I'm pretty positive about the ecosystem's flexibility; they will move forward as well, sooner or later.

All this said, here is the deal:

  1. Fix up the API and release RxJava 3 with the same package structure but updated maven address io.reactivex.rxjava3.
  2. Stop adding new features to RxJava 2, only bugfixes and significant JavaDocs updates.
  3. Keep supporting RxJava 2 this way for 1.5 years tops.
  4. Retire RxJava 2.

@ZacSweers
Copy link
Contributor

ZacSweers commented Feb 19, 2019 via email

@akarnokd
Copy link
Member Author

akarnokd commented Feb 19, 2019

exclude group: 'io.reactivex.rxjava2', module: 'rxjava' or don't upgrade until the dependent libraries upgrade? Let's turn it around: we release under the new package structure -> almost no libraries for months.

Not sure how other libraries match the releases of RxJava 2 at the moment; RxAndroid does not follow for sure. Mine do follow.

@akarnokd
Copy link
Member Author

Also for the throws Throwable. According to the spec: they are checked at compile time only.

In addition, widening seems to be okay too for compile time:

public class ThrowsSubclass {

    interface F {
        void m() throws Throwable; // was throws Exception
    }
    
    public static void main(String[] args) {
        new F() {
            @Override
            public void m() throws Exception {
                
            }
        };
    }
}

@JakeWharton
Copy link
Contributor

Libraries don't need to match the releases of RxJava and there's no reason to do this because of binary compatibility and dependency resolvers. If anything, libraries should be doing the opposite and expressing the minimum version they work with to not force behavior changes on their consumers unless desired.

To answer your specific questions...

How many libraries are using the operators in this list?

It's impossible to know, of course, but it's guaranteed to be more than zero.

Why are they using blockingGet in non-test code, for example?

This is a weird example considering it's the least-used and least-useful of the bunch. So while it might be zero, the others are non-zero.

Do you replace a dependent libraries without recompiling/retesting the host library/application?

Of course. Transitive dependency resolution guarantees this to happen with libraries. It's why we have jars and why Java does linking at runtime instead of statically at compile-time.

I'm almost certain RxAndroid, RxRelay, RxBinding, RxSharedPreferences either won't have to change or would need a simple recompile if the Rx functional interfaces get widened to throws Throwable.

While this represents a popular set of libraries for Android, in the scope of the RxJava ecosystem it's tiny. And this specific change is only source-incompatible, not binary-incompatible, so it's not as worrying as the API changes.

There won't be any architectural change with 3.x, create and X.subscribeActual will remain the same, as will fromCallable and the Scheduler/Worker API. JDK support will also remain 6.

This only reaffirms my belief that there's no need for a version 3 right now. A useful RxJava 3 to me should be built against j.u.c.Flow and architected in a way to take advantage of Project Loom when it arrives.

Do you think 3rd party library authors are willing to support 2 versions, one for 2.x and one for 3.x, at the same time for an overlapping duration? What if the signature changes do not affect them at all?

Some did this for 1.x and 2.x, yes. But the interop library actually means you're not even required to do it. The consumer of libraries could upgrade piecemeal by slowing moving the interop barrier across their application until the need for it was removed.

Why do you think I have the time or will to support two versions, again?

I don't even want an RxJava 3 so I'm not asking you to. But beyond that, there's plenty of people around this project that can and will help.

Later on in the post your plan outlines that you're already going to support both though so based on that you seem to think you have the time.

Why would I have to spend time answering SO questions/issue questions about why Schedulers.io() no longer compiles because the OP forgot to change the import to like io.reactivex.rxjava3?

Are these going to number greater or less than the NoSuchMethodError posts? And the answer easier or harder to explain? But, again, there's a whole community that can help do this and it's the point of StackOverflow's whole system.

RxJava's API has to improve

Sure, but it needs some carrot balanced with the stick.

Right now the value proposition of 3.x is practically non-existent for consumers. We get a few warts fixed, sure, but at a cost that disproportional to the benefit.

as the minor inconveniences turn into major ones over time.

On a long enough timeline they might, but are these really at that point? None of the proposed changes unlock some fundamental potential that's missing.

Also I'm pretty positive about the ecosystem's flexibility; they will move forward as well, sooner or later.

I am too. Except actions like this will actually force a subset to alternate solutions which aren't hostile to their builds which is unfortunate. Or they'll simply stay on 2.x.

we release under the new package structure -> almost no libraries for months.

Does this matter? It doesn't affect this library. At least in this case when consumers want to upgrade they can do so incrementally without a flag day where everything needs to change at once.

I want to see a 3.x that moves the goal posts for what a reactive library can be and makes Reactor look obsolete. Not something that fixes a few dings in the paint of its bumper.

@akarnokd
Copy link
Member Author

@JakeWharton I understand your arguments, but as you put it "Does this matter? It doesn't affect this library". So if 3.x is created, how does it affect existing libraries and their maintainers?

I'm trying to understand the underlying concerns here. If nobody supports it -> nobody upgrades -> 3.x dies out. If some high-profile projects support it -> push others to upgrade -> 3.x thrives eventually. Every supplier upgrades -> clients upgrade -> 3.x thrives. Loom/coroutines win the market -> mostly nobody cares about RxJava ever again. Either way, creating 3.x does not delete 2.x and all those depending on 2.x.

Are you worried about split community? Expect constant nag on projects to upgrade to RxJava 3? Are you worried about RxJava 2 support drying out?

I want to see a 3.x that moves the goal posts for what a reactive library can be and makes Reactor look obsolete.

Reactor is targeting the desktop/server/cloud world and as such, it can always be on the edge of the tech curve. Also let me state that RxJava did not and does not compete with Reactor.

We are held back by Android's Java 6 baseline. I'd be quite happy if that would bump to Java 9 where I'm more willing to repackage RxJava - a very requirement for modules to work properly anyway as we'd have to move code from io.reactivex to io.reactivex.publishers or something due to how modules must be organized. At that point everything already broke so everybody will suffer the same.

@JakeWharton
Copy link
Contributor

JakeWharton commented Feb 19, 2019

Is RxJava held back by that? The Android community had to fight for 2.x to support Java 6 instead of being Java 8+. I'm prepared to argue for 3.x to leave Android far, far behind. We can take care of ourselves by maintaining 2.x indefinitely. Most Java libraries should start to consider abandoning Android as the Java and JVM ecosystem really kicks into high gear and Android shows no sign of keeping pace at any reasonable timeline based on what's happening (or not) in AOSP.

@akarnokd
Copy link
Member Author

The Android community had to fight for 2.x to support Java 6 instead of being Java 8+.

I remember it differently: #3450. I don't see any hard fights there, but correct me if I, or the other maintainers at the time, happen to have resisted somewhere else regarding the support level. I even put in the extra effort and made RxJava 2 Java 6 compatible again, undoing some of my own Java 8 related work in the process.

Android shows no sign of keeping pace at any reasonable timeline

Kotlin has extension methods which makes the need for breaking changes obsolete there, hence 3.x is unnecessary. Just add a new extension method with the correct return type and hide the "bad" methods. Also Kotlin coroutines make one-shot async processing convenient enough most of the time that RxJava is abandoned en-masse already. They don't care what 3.x breaks.

@eygraber
Copy link

As an Android developer, I'd be fine with sticking on RxJava2 until there was an actual need for a major version bump. And neither I, nor anyone I know have abandoned RxJava for coroutines when the use case calls for it, which is often. Some of us are quite bought-in to rx.

I really wouldn't mind seeing RxJava (or a reactive implementation) built on coroutines but that's more for ease of implementing operators (which isn't so bad with Kotlin because of extensions). I guess it would just be cool.

@davidmoten
Copy link
Collaborator

For 3.x I think it would be nice to see PublishSubject.create() return what is the 2.x equivalent of PublishSubject.create().toSerialized(). Unserialized access of PublishSubjects seems to turn up a lot as the cause of people's problems. An additional factory method for the performant but serialize access yourself version could be added - e.g. PublishSubject.createRequiresSerialization().

@akarnokd
Copy link
Member Author

Closing via #6514, #6516 and #6517.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants