-
Notifications
You must be signed in to change notification settings - Fork 535
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
cover the allowed call sequences on Subscriber in a spec rule #202
Comments
onComplete is currently legal before onSubscribe. I don't know how much you would gain by ordering them, given that onError can come before onSubscribe anyway. In my experience the only real help to implementors would be if onSubscribe is always the first message, even if it is immediately followed by onComplete or onError. Less state variations to maintain. I am not sure we want that though. |
According to the docs the legal status of “onComplete first” is at least dubious, and #199 introduces a test that fails in this case, so we should definitely fix this either way. |
The spec (the rules) does not mandate if onComplete can come first or not currently, as mentioned in #199 and for more details see this comment It is a bit accidental that current behaviour is "onComplete can not come first", as such is the diagram in section https://github.com/reactive-streams/reactive-streams#api-components which isn't really a spec rule. |
In the Akka impl we definitely treated onComplete as valid response instead of onSubscribe, in fact we have testkit methods like: expectCompletedOrSubscriptionFollowedByComplete() |
So the current language accepted by Akka Subscribers is:
(Kleene star here allows infinite words) |
@drewhk |
Well, you can always send a dummy subscription, but in general I agree. I don't see though then why cannot we send an onComplete instead of an onSubscribe. Since we need to handle the onError case the onComplete case is not that much to add (from an implementors perspective). |
@rkuhn I agree with "so I am leaning towards adding a new rule that orders onComplete and onSubscribe.". The question is if the symmetry of being able to send either |
@viktorklang so you propose the following language?
|
@drewhk Since it is verboten by the spec to call any methods on the Current semantics as present in the README.md: |
@reactive-streams/contributors Thoughts? |
My point (experienced in Akka Streams) is that if you have to handle onError without onSubscribe then it is not that hard to add onComplete handling there, too. I personally prefer what we use now:
Not because I don't want to rewrite it, but because it makes sense. The simplest implementation wise of course would be:
but that would mean that the Publisher must send dummy Subscriptions even when it knows that it is already empty or in error state. Edited |
@drewhk Given 2.3 (Subscriber.onComplete() and Subscriber.onError(Throwable t) MUST NOT call any methods on the Subscription or the Publisher) I think changing to |
What rule does exclude now sending an onComplete instead of a Subscription? Because if there is such rule I am pretty sure we have some impls that violate it. i.e. I am not sure if that rule is tested then by the TCK, or we had classes that has been not verified. |
@drewhk This Issue is about making that clearer in the spec, and then of course make sure that the TCK properly verifies it, then we need to make sure that all Akka impls are properly TCKd :) |
Maybe it helps, I created a regex like (non-complete) spec of the language that can be seen by a man-in-the-middle observer that orders concurrent events arbitrarily:
(Maybe it would be more digestable using a drawing. Beware of bugs.) |
Status here? |
Ping @rkuhn, |
I think I'm okay with In other words, a |
@benjchristensen I think that is right. Given 2.3:
So it doesn't matter if The question though is how we make it clear in the spec that it is:
|
My initial reading of this (and how I implemented) is that a subscription cannot be "complete" if it never began, so I force onSubscribe before onComplete when publisher is already closed. I still think this is a good policy, but I now don't see any wording forcing this. |
@DougLea Your intuition is right. The existing intent is to only allow onComplete after an onSubscribe. So there are 2 questions here:
I'm all ears on solutions to this :) |
I prefer onComplete and onError to be symmetric, they can either come without onSubscribe, or they must come after onSubscribe. (onError can be made to always come after onSubscribe, since it is always possible to send a dummy subscription) |
I like this, and prefer not having to send dummy subscriptions. |
@benjchristensen Great, so you're in the |
Count me in that camp as well—with the small pedantic fix of adding a final |
@rkuhn Never-ending Publishers would still send an onSubscribe, though, because otherwise there is no association happening? |
@DougLea I can understand this point (always requiring |
If we go down the route of the simplest possible grammar (as Doug proposes) then we should also include a DummySubscription in the reactive streams artifact because that will then be needed in many cases: final public class DummySubscription implements Subscription {
public static final Subscription instance = new DummySubscription;
@Override public void request(long n) {
if (n <= 0) throw new IllegalArgumentException("...");
}
@Override public void cancel() {}
} |
if (n <= 0) throw new IllegalArgumentException("..."); is not legal though. |
@viktorklang I'd be surprised if there is even a performance advantage -- in the simpler version, most clients need fewer special-case checks that would only rarely trigger. |
I don't think I can add anything more to the discussion, so I summarize my opinion (a.k.a vote) and leave it to the others to decide:
|
d’oh, of course; signaling that exception correctly would mean allocating a DummySubscription for each such subscription :-( |
You don't need to signal anything, the relation is already "terminated" it is just being in a race with the faulty request, but you can always arbitrarily define the order and say that the onComplete or onError that was already scheduled "won" (by definition, not by reality). |
good point, thanks; so the corrected code is final public class AlreadyCompletedSubscription implements Subscription {
public static final Subscription instance = new AlreadyCompletedSubscription;
@Override public void request(long n) {}
@Override public void cancel() {}
} |
I don't like adding things like this. I strongly prefer keeping it as just interface definitions.
I also prefer this.
The simplicity of this for me is that |
Is there a link that describes the protocol syntax/grammar so we can link to it like we do to https://www.ietf.org/rfc/rfc2119.txt in the README? |
I, too, would like the simplicity along the lines of: class P[T] extends Publisher[T] { override def subscribe(s: Subscriber[_ >: T]) = () } (I'd guess we'd have to have a clause in the rules that'd prevent it from being legal) So, I guess my stance right now is: I don't like the status quo, I think it should be symmetric w.r.t onError and onComplete. I'm OK with requiring to pass in a Subscription that is already cancelled into onSubscribe but it seems like a code smell to me so I tend to lean a bit towards making onComplete be signallable before onSubscribe. I think I need to experiment with the example implementations to see what makes implementations more or less ugly. |
I don't think that should be prevented. In fact, RxJava legitimately has a never() factory method that creates an Most of the time a stream that never does anything is undesirable, but it's not illegal. And practically what's the difference between a stream that never emits and one that will emit in 4 hours if the consumer wanted it in 100ms? Async consumers needs to choose to protect themselves by stating their assumptions with timeouts and/or consumption limits (like |
@benjchristensen Now that is a compelling argument! |
Hm, I have to disagree here. I agree that a stream that never does anything is sometimes useful, but that has nothing to do with onSubscribe being required or not per se. As an analogy, a TCP connection is also completely fine doing nothing, but the three-way handshake is still required at the beginning. I don't say that sending onError/onComplete any time has no merit (this is what we implemented anyway, so it is even less work), all I want to say that you can have streams that do nothing while still establishing a proper subscribe-onSubscribe handshake. There is something satisfying about that a "never" like operation is not implicit but explicit by having a clear handshake that proves the linkage between the "never" element and its downstream. I don't have a stong opinion though. |
Alright, so I think we all agree that the current asymmetric definition should be changed. I'll try to find time to experiment with the impact of either of the suggestions (onSubscribe always && onComplete without preceding onSubscribe) on the example Publisher, I think that would probably convince myself what direction I'll vote. |
So the choice is between:
and
|
I'm okay with that. We just shouldn't declare it illegal to never emit |
Agreed on allowing “silent” Publishers; the remaining question then is whether onSubscribe should be mandatory or whether “one of onSubscribe/onError/onComplete” should be mandatory. While implementing the spec as well as while writing our own tests we encountered extra effort due to the uncertainty of what the first invocation will be, requiring an initial onSubscribe would make the logic more regular. Just as Endre I have a preference for the second choice presented by Viktor an hour ago (read: Endre’s argument convinced me). |
I like “one of onSubscribe/onError/onComplete should be mandatory” but both can work. |
@tmontgomery are there other considerations that we have not yet included? |
Agree with Roland, implementations are slightly confused by this. Mandatory OnSubscribe is slightly more verbose but we can deal more efficiently with this by providing wrappers in Publisher factories. Sent from my iPhone
|
I don't understand the extra effort for handling an |
My take on it: Having onSusbcribe always come first is an invariant which is simpler to For the permanently failed and completed publishers the cost of the Spec-wise I found it simpler and more straightforward to amend in the I think it will be an improvement over what's currently in master, and any
|
Currently it is only implicit that legal sequences start with
onError
oronSubscribe
and end withonComplete
oronError
(or not at all), and it is not regulated within the spec rules thatonComplete
is illegal beforeonSubscribe
.I see two ways to approach this:
onComplete
The second approach would lead to duplication, since legal sequence grammars are almost perfectly constrained already, so I am leaning towards adding a new rule that orders
onComplete
andonSubscribe
.The text was updated successfully, but these errors were encountered: