-
Notifications
You must be signed in to change notification settings - Fork 531
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
Not calling Subscription methods in onComplete #481
Comments
Do you doubt the TCK can detect this case or do you want to detect this case and then, well, not really do much about it? |
I doubt this rule adds value to If a |
Yes, upon a call to Knowing non Rx-style implementations, I'm almost certain this rule has been added to prevent the overhead and delayed side-effects caused by calling request/cancel to affect state/performance. What's the real worry about rule §2.3 is that, in principle, it prevents retrying the same |
It's a Subscriber check. So it is attempting to check that a Subscriber isn't interacting with the Subscription in a place where it shouldn't. Given how many implementations which run this TCK, I'm not sure what the problem seems to be? |
In a concurrent world it is not possible to know whether Efficiency argument is a good argument for not calling |
That would be a bug. |
A You can try heuristics to inspect the call stack in The spec doesn't specify sanctions and many constructs automatically defend against such "rogue" calls so I don't see any issues for creating dependable implementations. |
2.3 is about the Subscriber.
While processing the onComplete or onError signal, the subscription or the Publisher should not be touched by the Subscriber. |
@viktorklang a bug in what?
|
Yes, that's the rule I am talking about. It makes no sense in the presence of the other requirements. |
@olotenko The spec aims to create well-behaved integrations, and the Subscriber adhering to this rule means that it is less likely to cause when connecting to other implementations. I still fail to see what issue this is causing for you. Can you elaborate? |
Basically, the 2.3 rule of the spec requires mutual exclusion between |
@olotenko I still don't see what your issue is. Upon processing an onError/onComplete signal the Subscriber should not touch the subscription or Publisher, which is fine, since it has already signalled an unrecoverable failure or end-of-stream. How is this creating issues for you? |
Let me rephrase. "Mutual exclusion" above is a literal requirement to have a non-reentrant lock around If you see that concurrent cases are necessarily allowed (and all implementations that I've seen do not do this locking business, if you want a "proof by consensus"), how can the synchronous invocation of For these reasons rule
No, the question should be posed the other way. What problems does it solve? |
@olotenko To call |
@viktorklang I am trying to get across a very simple argument: the As simple as that. Rule All I am saying that you can throw away any statement following the first If this is too hard to see, imagine the Now, I can assume that maybe there is something useful in restricting program order specifically? A conforming
No, I do not agree that the invocation of Ok, the simplest thing is a
|
@olotenko It would seem like you are conflating the Publisher issuing the onComplete signal and the Subscriber receiving the onComplete signal.
This does not make any sense to me, when the Subscriber receives either onError or onComplete, it has no reason to interact with the subscription—the communication channel is already "closed".
Do you have a concrete example?
It would seem like you are conflating the upstream Subscription and the downstream Subscription in that case?
No. Processing of a signal in the Subscriber does not need any locking, especially for the case where the Subscriber is synchronous (runs on the calling thread). Can you illustrate your interpretation of this problem with some code? |
@olotenko Initially when we started formalizing the spec we were able to run exchanges between Publishers and Subscribers on a typical dev laptop at ~200mops, are you having requirements exceeding that number? |
I am not conflating anything. I have explained the problem in terms of temporal logic. You do understand happens-before, right? This is as close as it is going to get to disclosing internal code. |
@olotenko The following looks to me to indicate a misunderstanding/conflation between signal issuing and signal processing:
|
This is a statement without temporal logic. How are you going to reason about "during", "before" and "after", if there is no temporal logic? I've translated the "during" into the temporal logic statements that make sense to me in the context of how the TCK test is implemented. |
It’s in the spec. Sending a signal happens-before the processing of said
signal.
--
Cheers,
√
|
:) and all you can measure in the test is that You can neither specify this, nor test reliably. |
There’s always an open window between request and onX. Regardless of that,
calling request after observing onError/onComplete is definitely indicative
of a bug, since it will not do what you want.
On Wed, 26 Feb 2020 at 18:22, Oleksandr Otenko ***@***.***> wrote:
:) and all you can measure in the test is that request exit
happened-before onComplete exit. You cannot prove anything about the
timing of when it entered request.
You can neither specify this, nor test reliably.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#481?email_source=notifications&email_token=AAACU57YQIN765IJ4LT6BJTRE2QMFA5CNFSM4KYOUWK2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOENBDXBI#issuecomment-591543173>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAACU543HF73YP6QR3E2JM3RE2QMFANCNFSM4KYOUWKQ>
.
--
Cheers,
√
|
Calling I disagree with the spec requiring an impossible condition that is not the same as observing the Also, I posit that there is no statement about the required order of |
I've just run into a false positive with rule 2.3. I'm implementing the Eclipse Microprofile Reactive Streams Operator Also there is another rule for when the outlet fails with Since the MPRS uses the Reactive Streams TCK internally, I can't just disable these rule tests. |
@akarnokd bingo! This is the sort of case I had in mind. Anything that is |
@akarnokd Sounds more like an issue with the test than the spec tho? I'm all for making the test case better—if you have any ideas on how to improve it, you know the drill :) |
@viktorklang Sure, I'll look into the test implementation. |
:) yes, just make it harder to reproduce |
Reading the whole thread I hardly seeing any problems with the rule discussed. The problem discussed are unrelated to the rule. I guess @viktorklang clearly stated that It means if a It also means if Subscriber receives onComplete call - it does not have to use subscription as well (same as above). For many cases, if racing occurse: For example, On the other hand, it does not violate what is written in spec, when both Summing up what I have seen in this thread. There is a mess between understanding the rule which simply says "do not call Hope I got it correctly and feel free to correct me @viktorklang @akarnokd @DougLea Regards, |
Thanks @DougLea and @OlegDokuka, now I finally understood what this thread is about! As @OlegDokuka says, the rule itself does not demand any of the behavior discussed between @olotenko and @viktorklang, linear logic is not required for its understanding: it is simply the statement that the onError/onComplete handler itself shall not call the request/cancel methods. This is explicit in the wording: I could see the confusion above arising if it said “The Subscriber MUST NOT call … ”. With this understanding, I think the intent clarification for this rule is misleading, as race conditions are not the target of this rule — races between request/onError for example are genuine and permitted. |
So if we replace spec rule with my suggested: Upon onComplete and/or onError, a subscriber must eventually stop calling any Publisher or Subscription methods. We should replace intent with: Sessions must eventually terminate. Among the ways to help ensure this, subscribers should not call Publisher or Subscription methods from within onComplete or onError methods. Yes? |
I think it should be a separate rule for I don't think that rule 2.3 has to be rewritten for the I would like to say even more. Sometimes the internal ecosystem of every implementation may violate specification and does it in many cases. The point is that once there is integration with an external implementation then it should follow specification and provides all the guarantees (see |
@DougLea @olotenko @rkuhn @OlegDokuka It's important to remember 3.1. Exposing Subscriptions in potentially concurrent situation makes it really difficult to reason about what the source of truth is: If one "owner" of a Subscription calls As for 2.3, a wording like "A Subscriber MUST eventually discard its Subscription once One complicating factor of proposing this verbiage is that we cannot guarantee that it will not break any implementations of Publisher / Subscriber which is relying on the fact that the Subscription should not be touched onError/onComplete or thereafter. https://github.com/reactive-streams/reactive-streams-jvm/tree/v1.0.3#2.3 Sure, we could make an educated guess, but to be honest, changing the spec for "easier implementation" while taking the cost of potentially breaking others' TCK compliant code seems like a bad tradeoff in my book. |
@viktorklang I don't see how replacing "MUST NOT call" with "eventually should stop calling" can be a breaking change.
I have no problem reasoning about this. My mental model permits concurrent All of these combined mean that the A slightly related view: in TCP/IP This means if the But I'd like to underscore that these problems do not depend on synchronization between @OlegDokuka et al.
|
@DougLea I think any wording that replaces "must" with a weaker verb is fine. |
This is a red herring: the spec only says that Subscription.cancel MUST NOT be on the stack while the Subscriber.onError pertaining to that same Subscription is also on the stack. This clearly is enforceable. I still have not seen a motivation or example for why this rule should be changed. What I have seen is a lot of enlarging the scope beyond what is written and then showing that that leads to undesirable consequences.
This, as a general statement, is not true: we currently have the rule that people MUST NOT kill other people. Weakening this into SHOULD NOT is definitely a breaking change. Now whether a weakening would break existing Publishers in this particular case is a much more difficult question (it would not break Akka Streams, for example, but that is due to its use of an overly safe implementation primitive). |
@rkuhn I appreciate your input on this, I think you're hitting the core of the matter—a Publisher cannot know whether a Subscriber is sync or async, so perhaps the rule should be clarified to specifically refer to the sending part of onComplete/onError rather than the all-encompassing observation of aforementioned signals? |
Well, I am looking at the spec and it is not saying that. It is not saying anything about the stack, and the motivation plugs in a few red herrings about cycles and race conditions, and ambiguities about what "during" means. Plus I really really really fail to see what problem such a requirement solves. Why wouldn't a
No, not difficult at all. It may be hard to find this in this long thread, but I think it's been discussed there are provisions in the spec already that require the |
@viktorklang I guess it is an extra rule to Publisher side that it should stop receiving any signals eventually. Do you have anything else in mind? |
@OlegDokuka The problem is that such a rule is generally unverifiable. |
@DougLea I think that wording is very good. It made me think of the flip side of the same problem. I could not find the "guaranteed progress" easily. For example, as follows: Guaranteed Progress
(Imagine a I am fully conscious of the fact that this is non-enforceable, and is subject to the Halting Problem. However, the intent is to make the designers of the This is a flip side of: Guaranteed Quiescence
(your wording) |
We all agree that the intent clarification for this rule needs to change because it is misleading, so let’s stop discussing this point. The remaining discussion is about the rule itself:
This references two function symbols and then says that these “must not call X” — this can only be interpreted to apply to the implementation of said symbols in the respective Subscriber. To “call” methods is a well-defined notion in Java that does not require further definition. A reasonable extension is that methods called from these two symbols must respect the same constraint, otherwise the rule would be trivially uneffective. Thus, I have used normal logic (nothing linear) to prove my point. Statements to the contrary must include a proof that my derivation is flawed. Now, what you propose is to shift the responsibility of avoiding cycles from Subscriber to Publisher: currently rule 2.3 mandates that the Subscriber must break a possible cycle, while you explicitly propose the opposite:
It is obvious and irrefutable that this is a breaking change to the spec, so no matter how desirable it is, we cannot do it without changing the major version number. The only remaining question is whether this change is worth such a cost. |
@rkuhn you have not demonstrated how a conformant I have given an example of how a non-conformant I get a feeling no one really knows what that rule saves from. I have seen claims of "races", "cycles", "logical errors", and some unknown problems with existing Here's my reasoning of why the change is not breaking conforming Premises:
So the conformant I think it is up to the supporters of the idea that synchronous invocations of |
Now we’re talking: while the responsibility for cycle avoidance would change, you are proving that cycles cannot actually occur, so we were arguing about the empty set. It may be that the rules are overprotective because in some earlier iteration (before 1.0) your current proof didn’t work; or it may be that there is another issue that this rule guards against that I cannot recall right now. @viktorklang does this ring a bell? The only other point I can find is that we were worried that the Subscription’s resources have already been released when entering If this holds, then we could indeed remove rule §2.3 without any effect on the spec. Archeology:
|
@rkuhn I can think of a design that almost fits the current spec, where a thread executing |
@olotenko @rkuhn If some other thread rather than the thread which receives the onComplete, calls |
Yes! (That's the starting point of my reasoning about separation of concerns :)) |
@olotenko But that does not address the Publisher issue w.r.t. reentrancy. |
Ah, you’re saying that with the current spec the Publisher may rely on rule 2.3 to not handle calls to cancel/request while onError/onComplete are ongoing? That would be a gray zone already, given that at the point of signal emission these Subscription methods need to equate to NOP. (Please let me know if I understood you correctly.) |
I don't have anything new to say about this, so will just restate:: Specs (especially of protocols) normally include few if any "MUST NOT" clauses, which is one reason most lack progress guarantees. But we'd like to at least guarantee termination after onError/onComplete. The current rule 2.3 singles out one case that may otherwise prevent termination. Rephrasing a but more generally would be more accurately reflect intent, at the cost of no longer being able to test for this one particular case that may (or may not) cause non-termination. I think this is an OK tradeoff. |
Example of the implementation that violates this part of the spec as it stands: helidon-io/helidon@1673ddf#diff-0f519ea307142812e3a65e47f248e24cR178 |
@olotenko FYI. It is not if you put return instead of I do not fully understand why |
@OlegDokuka Both assumptions are not correct. We don't need to discuss what's wrong with these suggestions, nor any other issues with that code. I only point out yet another reasonable implementation that passes the TCK only in letter. |
I guessed if you point to the implementation which violates something, then there should be reasons why it works like that. From what I saw there are bags in the implementation and only because of that the implementation violates the spec. Also, I do not fully understand 'passes the TCK only in letter'. It either passes or not. Yup some rules are not testable, but this is another challenge, so better to contribute there rather than pushing spec changes because of the bags in the implementation. Please correct me if I missed something important which should change my mind. Cheers |
No. I am questioning the rationality of the existence of the rule. So you can't use the argument that the code can be fixed to comply. First show me what
Refers to the inability of the TCK to test the condition. Yes, some rules are not testable. This is one of them, as was my premise all along. |
I can imagine a pooled resource that can be shared between several subscribers. If the subscriber is not going to follow the instruction, and continue calling subscription, then we can get an incorrect state or data from an unrelated source. That is more as a unique case, and I tend to agree that most probably nothing is going to be broken, but as of me, this is a natural recommendation that MUST be followed. If folks stop going to follow that, we can have more issues rather than profit. And yes, what profit is going to be brought by removing that rule? |
I agree with a general advice to stop calling subscription methods eventually.
Obviously, stop introducing unnecessary complications into perfectly good implementations. |
https://github.com/reactive-streams/reactive-streams-jvm/blob/master/tck/src/main/java/org/reactivestreams/tck/SubscriberBlackboxVerification.java#L127 - the test and the requirement are not enforceable.
The requirement is unnecessary for
Publisher
s whoseSubscription
is meant to be as good as cancelled at the timeonComplete
is called (reads: beforePublisher
callsonComplete
) - it is required to behave as no-op. The detection of whetheronComplete
is in progress at the time aSubscription
method is called, is not possible.The text was updated successfully, but these errors were encountered: