-
Notifications
You must be signed in to change notification settings - Fork 523
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
Add Promise style semantics to reactive streams #268
Comments
@daviddawson Given that we're talking about an end-user API, would it make sense to use CompletionStage in stead of the RS interfaces for these kinds of methods? (It would be a rather trivial exercise to write a bridge between |
@viktorklang Good idea, but needs to support Java 7 as a minimum IMO. |
I think this is somewhat related as well: in the @reactive-ipc/reactive-ipc-jvm project we're [ab]using the RS API to indicate completion or error state by using a This type of situation has come up for us quite a few times and I really think that providing for it at the RS level is better because it allows us to stick to that API rather than extending it to our own and requiring some other kind of component that isn't sharable like the RS API is. |
Yes, I'd like Java 7 support. Also, tbh CompletionStage is a bit opaque on how it should be handled by a user :-) Reactor in particular exposes the reactive streams interfaces all over the place, so having a potential Promise there too would be fairly natural. If a Promise in reactive streams were just defined as interface Promise extends Publisher {} Then it would be totally non invasive and integrate with existing reactive streams implementations while extending the semantics of reactive streams into an area ripe for it. Thinking about it, we'll do this anyway (define a Promise interface), but it would be good to have it standardised. |
I would like to take it one step further and specifically call out the special case of |
First, let me state that I am not trying to be a contrarian, or otherwise be difficult—I'm merely trying to see if there is a problem worth solving here, and if it is worth solving or if there is alternate solutions that would work in the place of the proposed solution.
@jbrisbin Normally I would agree, but given that J7 is EOL and that it is possible to run j.u.c.CompletionStage via -Xbootclasspath if one must target an obsolete platform, does it really make sense to add yet another thing that maps directly to a JDK type?
But Promise wouldn't be better than Publisher? It's still no value expected, right?
Technically "Void" could be correct here, but the problem I see is that its Javadoc is terrible: https://docs.oracle.com/javase/6/docs/api/java/lang/Void.html In a language with a more…sophisticated…type system one would use something more like So what you could do instead is to define: /**
* A type that indicates that no value of this type can exist.
*/
public enum Nothing {;} and then use: Publisher<Nothing>
Agreed. But do you agree that the existence of |
@viktorklang I would expect you and @benjchristensen to push back on most things as it ends up being better for having people who don't just agree right away but pick at the solution and ask tough questions. I don't see anything wrong with being a contrarian! ;)
Have to disagree pretty completely here. I realize that Java 7 is technically EOL but in the real world that makes no difference (and the bootclasspath option is a non-starter...I can't imagine the discussion with ops that would ensue to describe the need to add a custom JVM option to be distributed with the application...that is an unreasonable burden IMO). The fact of life is that it's unrealistic for someone writing applications designed to be adopted by folks still using legacy applications to upgrade to Java 8 just to get a simple feature that can be provided by an earlier JDK. I don't agree that the JDK 8 type adds anything meaningful (par for the course...there's me being the contrarian!).
True, but I think we need to address the use specifically because what we're saying is not "null values", which is really what
I don't think the effort of including it weighs on its usefulness. Those may be logistical hurdles to overcome but they shouldn't be factors in deciding whether or not the I believe it is important to include in the core RS API (one which gets reused and is guided and documented by a concomitant specification) a component for expressing the intent that a |
That's definitely more explicit (and likely preferable) but I don't know that it does anything meaningful more than The one subtle thing that might argue in its favor is that
|
Maybe @daviddawson and @smaldini could comment (and feel free to disagree if you want!) but I think we need two classes and two specification provisions: public interface Promise<T> extends Publisher<T> {}
public enum Nothing {;} The The The reason for including these here is that this is the lowest common denominator when writing libraries that expose their Reactive Streams support. It seems less useful to have LibraryA create their own version of The alternative is to defer dealing with this to implementations and get what we have today which is very vague and not interoperable (I can't communicate the idea of a Promise from one library to the next, only a |
I strongly agree. I would very much like to have a standardised Promise whose primary purpose is interop between FRP style libraries and application stacks. As an experiment, I just implemented a Promise across a test codebase where previously it was Publisher and and it becomes much clearer what does what. Looking through the Reactor codebase a little, I can see that a standardised interface could be retrofitted in there too and help clear things up (although since there's an internal Promise already, possibly not so much) The other interface (Nothing) I'm not sure I've seen the need yet, but I understand your description and could see it being useful for us in the future. I agree with @jbrisbin on standardisation. There are half a dozen Promise implementations now extant in the codebases we use regularly, and they are starting to grow in functionality to mean something more like 'stream processing' than 'I may give you something later' (looking at the Ratpack one in particular). If an attempt isn't made to give some interop under the purview of the reactive streams specifications, then I don't see any interop being possible around this concept, which I think would be a shame. Also, this is too easy to pass up, my apologies. public interface Promise<T> extends Publisher<T> {}
public enum YouAreDeadToMe {;}
Promise<YouAreDeadToMe>(); |
😆 |
One of the benefits of using a promise (a.k.a. a one, or zero-or-one depending on perspective, element stream) is that you don't have to worry about demand, hot vs. cold, async cancellation (i.e. in that it's simpler). You can easily adapt a promise to/from |
Thanks for being extremely understanding of the reason! :)
Why don't you think the JDK8 type adds anything meaningful? The entire purpose of adding it was to accommodate for exactly this type of thing, at least that's what I recall from talking to @DougLea.
But if Promise does not solve the problem, why add it?
If logistical hurdles are not to be taken into account, then requiring people to go through the logistical hurdle of upgrading to JDK8 shouldn't be an issue either, right? :-)
So it guarantees that onError will never be signalled?
Understood. But where does it stop? Do we need a Publisher type for something that only ever signals onComplete? Reading the above, it is also clear that the definition of
The attempt of unifying these is CompletionStage for the JDK.
But that's not the definition of a promise. A promise needs to publish the -same- element to all consumers (deterministic).
Having thought about it for a while, I think there's enough prior art to use Void: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html (In Scala one'd use Nothing (the bottom type) since it has other implications from a type system perspective).
Publisher generically is not effectually a Promise (from the definition I use) since Publisher does not distinguish hot/cold, and this for a reason, because it creates a terrible matrix of permutations, tracking hotness/coldness through combinators is a real pain, and that doesn't even take into the account things like never-completes, never-fails situations.
The RS API is for interop (not end user, since it doesn't have any combinators or otherwise) and given my argumentation around the permutations I don't see the adding of a Promise interface as something that carries its weight from a utility PoV (especially considering j.u.c.CompletionStage)
There is a standardised Promise! :)
Turning a Promise into a Publisher is very simple, as @alkemist pointed out, if you want to go in that direction and it's even simpler if you control your own Promise interface (since Java still doesn't have things like extension methods/typeclasses) as you can add a "toPublisher" method on it, don't you agree?
It improves over Void in the sense that it can be properly documented as to its purpose where j.l.Void is poorly documented in that regard.
While unification of Future/Promise implementations is a worthy goal (which I contend is solved in jdk8 with CompletionStage), Promises isn't what Reactive Streams sets out to solve (even if I am very fond of Promises:
👍 Also, Futures/Promises are typically not demand driven.
I'll have to side with @alkemist on this one! |
I'm aligned with @viktorklang on that I would not encourage a generic extension for this. There are however things to clarify in the specification about the contract around this I agree tho. |
@viktorklang Thanks for taking the time to respond thoroughly. I disagree with you and I suspect we'll have to just leave it at that. But your input is appreciated! I don't want to address individual points because I don't want to drag this out since I don't see any solution hovering over the horizon, just more ocean. But I do want to clarify a few things for the sake of people reading later:
Reactor will be happy to continue doing what we're doing by providing a |
Maybe there is potential for a sister specification for this instead then? Taking the points that @alkemist made and moving out of reactive-streams per se to it's own, highly related yet separate thing? If there could be agreement between the major implementations of Promise for interop then that might avoid the need to overload reactive-streams and instead build upon it for reactive-promises (or whatever). |
There are so many for the only using of the |
@daviddawson That's an interesting proposal. We're already "extending" Reactive Streams in a standardized way with @reactive-ipc. Maybe a collaborative It would depend on and extend Reactive Streams proper but spec out the promissory behavior somewhat like Promises/A+ did. It doesn't have to deal with state and all the implementation details of things but it should provide a solid and predictable foundation that library authors can use to expose APIs to other libraries with consistent and predictable results. |
The use cases that are discussed here are
As far as I can see solving the latter can be used for solving the former (as @jbrisbin proposes), but that would unnecessarily complicate the solution—passing a single event to asynchronous listeners can be done without all the ceremony that is needed for setting up and operating a stream subscription. There are a plethora of existing solutions out there, the immense duplication in this field should to my mind be attributed to Java’s unwillingness of hosting a sensible abstraction in this space prior to the introduction of lambdas. Now, since the central, blessed space has been vacant for too long—and I sympathize with the argument that in practice it has not been filled for a sizable fraction of the user base yet—it would be great if there were another standard that we could use as our solution for expressing single event transmission. That standard would then receive Reactive Streams bridges in the form of an official Reactive Streams add-on library, since this problem does not be solved more than once. Is this what we are talking about here? Another thought: if we consider this only from the viewpoint of Reactive Streams, would it not suffice to provide bridges for all relevant existing Promise implementations? Why do we need to add yet another one? This is the same argument that was used against SIP-14 (Scala’s Futures), and the answer was that this kind of process only makes sense if enough of the pre-existing implementations vouch to converge on the to-be-agreed standard—is there a chance that this will happen here? Achieving this feat will be tricky, much more difficult than Reactive Streams, since Futures/Promises are already deeply ingrained in languages and libraries and performing such a change without breaking compatibility guarantees that were given to the respective communities (source as well as binary) will be a challenge. I’m not saying that it is impossible—and I believe that we are in a unique position to use our collaborative experience to this end—but we should be clear about what we are setting out to do here. |
This is a topic we have discussed for a while in RxJava. You can see the discussion at ReactiveX/RxJava#1594 and code here ReactiveX/RxJava#2641. In particular this comment where I summarize why we're considering it: ReactiveX/RxJava#1594 (comment) I don't want to spam this discussion with that entire comment as a copy/paste, so I'll just let you read it over there. In short, I think the However, I am hesitant to immediately make it part of Reactive Streams. The biggest challenge for me on it currently is that I could be swayed, but I lean towards not pursuing this as part of Reactive Streams. |
Thanks, @benjchristensen, for the added reasoning, and I lean in the same direction (in case that was not entirely clear from my comment). One thing I should have mentioned is that we use Scala’s Future to encode this in Akka Streams, with natural bridges mediating between these and our Source/Sink types, and I think that is a nice and clean solution that is not in need of being fixed—unless of course I’m missing something (assuming that all RS implementations will supply suitable bridges to/from streams for their respective Promises of choice). |
The problem as I see it is that:
The power of Reactive Streams is that it has been accepted as a standard by the strength of the spec and the TCK. What's being suggested--whether it exists in the reactive-streams-jvm repo or not, is a type of Promise abstraction that builds on Reactive Streams proper but has expectations clearly defined by a spec and tested by a TCK and that the author of Library A can expose to their users a Reactive @benjchristensen @rkuhn I actually see this as taking the pollution of the space which exists today and filtering it to produce a completely non-polluted promissory API that allows full interop, just like |
@benjchristensen What about the suggestion that a FTR I can totally understand the reticence to include this in RS proper. I don't completely disagree. But we know this area is ripe for doing exactly what we did for streams. |
I know that netty add promise/future for the version 4,and the idea is from scala's.If we don't want to revent the wheel here and there or doing some hack and prave for that this kind of wheel works should/may works like that kind.Scala's Future and Promise is grate but we can't use it in java,and in fact we all need something like this.Reactive Stream is for composeable,if such a little thing won't compose how could we expect that when the user using it ,it will compose well? all of this,I think is a waste of life,both for the author and the user,Reactive Stream is a good start and I think the collabration of the Promise will show its worth too,just like the scala's. make it a pain long or short ,it's a problem.But after I learned scala's Promise and Future,I seems find my god. |
@jbrisbin I see what you mean now, and there are several points that need to be clarified. The first is that we are not actually talking about Promises/Futures, hence we should not use that term, it is not adequate. A Future is a read-only handle to a value that may be provided asynchronously and a Promise is a writable handle that backs a Future. The focus here is on the value that is to be filled in or taken out, which is why the callbacks that are installed on a Future in order to compose transformations are anonymous and not usually considered to be active participants at all. What we are talking about here is an asynchronous provider of a single value plus an asynchronous consumer for exactly one value. This view is prescribed by the nature of Reactive Streams as we have defined them. The provider will need to have a registration method for consumers and the consumer will need to have methods like The second clarification follows from this, and this is the main reason that keeps me from agreeing with you: your assertion that this new abstraction must in any case be a Publisher is wrong. As discussed in the previous paragraph requiring the full Publisher–Subscriber protocol would be overkill. It would both be very inefficient (requiring too many method calls to be exchanged for a single transfer) and also too expressive, it does not adequately limit the scope of the abstraction. I can only follow you in this quest if we first remove the assertion that passing a single element across an asynchronous boundary is in any way related to stream processing. This does not preclude reaching a similar solution in the end, but having this initial requirement would severely obstruct the effort. On a side note: yes, I can see where you are going, but if we allow ourselves to jump to conclusions then the end result will be not nearly as good as it could be—we will need the same amount of patience that we had last time. |
@rkuhn it was my original suggestion that a possible That came out of my desire to have something standardised and the observation that, since If we just assume for the moment that a |
Everyone, if I have got things correctly from this discussion (and please do let me know if that's not true), what we are really talking about is whether it makes sense to lead a new standardization effort for Futures/Promises for Java (or the JVM in general). This standardization effort is distinct enough to be standalone from Reactive Streams (which arguably solves a different, but important, problem). I was a part of standardizing Futures/Promises in the Scala ecosystem a couple of years ago, and my experience with that is what @rkuhn mentioned earlier in this thread: In order to create a successful standard for such a thing when there already exists many current implementation with more than likely different semantics and different execution strategies, one needs to rally the lion's share of the current implementation to actively take part in shaping the end result. Keep in mind something one wants to avoid w.r.t creating standards. Such a Future/Promise standard could most definitely have a Reactive Streams bridge to-from for interop, this could exist as an optional add-on. Just so that I set expectations accordingly, expect it to take a lot of time and a lot of effort into getting everyone involved pulling in the same direction, the following questions will arise for sure:
I'm sympathetic to the idea of standardizing this for the JVM, but it would be a long road to get there, and in the light of CompletionStage and CompletableFuture for Java8, I'm not sure it would be worth the effort, no matter how much one likes the CompletionStage API. If you got this far, thank you for listening, I appreciate it. |
@viktorklang I fully agree with your points about Promises, but given the original motivation for this discussion I don’t think that it is a foregone conclusion that we are actually talking about standardizing Promises. We could instead go for solving the problem of passing a single value across an asynchronous boundary, and this would not be fraught with the difficulties you mention. But it requires the discipline to explicitly acknowledge that this is not a Promise. |
Perhaps even more clear: using Provider and Consumer as the terms for the parties that exchange the single value, the correspondences would be that
In particular, the Promise is not the Provider. |
@rkuhn The challenges I see with baking any single-value-thing into RS was outlined in this comment #268 (comment) In essence my stance is currently—passing (optionally) a single value, while extremely useful, doesn't really match the (more general) goals of Reactive Streams: "Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure." So while you can most definitely optionally transmit a single value over Reactive Streams, anything more specific would not really fall under the goals of this project, and as such I'd say that it is a different project, albeit a good one! |
It is not the base case because Publishers can decide to accept only one Subscriber per the spec and call onComplete immediately for new subscribers. ie one publisher per one subscriber. Instead of base case I meant the most failsafe. Creating a new |
Per spec its MAY... See section Furthermore I have implemented many publishers that expect only one subscriber particularly because Publishers do not have a |
What you described is called "hot Publisher" and usually discouraged, because you can't https://github.com/reactive-streams/reactive-streams-jvm#1.11 that (I guess) you're referring to covers the opposite case -
See section 1.10:
"MAY" here means that it should be ready to be subscribed multiple times, because that's a normal use case.
Why?
|
But the standard relationship is by no means 1 |
@bsideup and @simonbasle Yes I realize it could be implemented that way but I wouldn't assume it so.
I do not define that as Hot. The Publisher may or may not be doing anything once its created. If it is doing something and I may loose event it is Hot. Cold doesn't guarantee a repeat me if other subscriptions are active. I'm talking 1 publisher to 1 subscriber. And since @bsideup since you are familiar with RabbitMQ its an example where you might want only one Publisher to Subscriber particularly if you are handling ACKs downstream.
So what if the the Publisher is expensive to keep around. Again let's say its a RabbitMQ exclusive queue. How do I close it when I'm done? The Subscriber can just signal its done and the Publisher can now cleanup resources. Given how poorly documented that rd2bc method is I can't really make any assumptions and thus I probably would call |
Yes I know that but I'm talking about the more than likely case to work coming from a library consumer. See https://javadoc.io/doc/io.r2dbc/r2dbc-spi/latest/io/r2dbc/spi/ConnectionFactory.html
The only thing according to that doc is that I will get a Publisher that will deliver a Connection to a Subscriber (not all subscribers). The doc says Hell the implementation might implement caching. I mean why not for @bsideup retry point. Why reclaim a connection if you already have it and it's fine. Anyway my point is I just think using Reactive Streams for everything instead of the specific reactive interfaces is ripe for confusion. |
"doing anything once its created" remembering that it was subscribing before receiving a next subscriber sounds like "doing something" to me ;)
Well, it definitely does, because it is "stateless", and does not depend on a number of subscriptions started.
You're mixing application concerns with the SPI. No, I would never want 1 Publisher = 1 Subscriber relationship.
Is it? https://r2dbc.io/spec/0.8.1.RELEASE/spec/html/#connections.factory
Since it is
Yes, that's how Reactive Streams work. You can check the spec that clearly states that this is the expected behaviour.
Again, it is the expectation from the spec. Even if the docs says "creates new Connection", it implicitly means "by subscription", because it is cold. You don't need to clarify such things in Reactive APIs.
That would be implementation's choice, I don't see a point here?
I only know one, |
The publisher in this case would not be doing anything till it received a The semantics of Hot and Cold are confusing and are debatable. Some cases people claim it has to replay all of the exact same events. Some say stateless but publishers are inherently very stateful. I really can't find a formal definition. For most I think it just means it's lazy and will not start doing work in the background till a Anyway you can surely have special databases that only allow one connection right? Or a resource that only allows one subscription at a time. You act like this is a scenario that never happens but I can assure you it does. Besides you can implement the cold retry logic with a simply decorator to the Publisher which is exactly what we did for some scenarios. It is just easier to initially write a Publisher that only allows one Subscription particularly if you want to guarantee unicast single step delivery. |
Oh I don't think anybody here believes it never happens, just that for |
I've never seen this expectation in the wild, TBH. People do seem to have problems understanding Hot vs Cold at first, but because of other reasons and definitely not replaying semantics.
Usually,
Start here:
I've seen many times resources that do not allow concurrent access. That said...
When you say "subscription" here do you mean "subscription" or I guess all of it is described very well here:
You never know whether you need to decorate |
I meant lower case subscription.
That is my point. I agree with you the cold case is ideal and preferred but the spec doesn't force one way or the other. If you want to enforce specific logic for the case of r2dbc I think it's better to return something like R2DBCMono than the raw MonoPublisher interface. Then have all your sub interfaces say they are cold and or single etc etc. The reason I'm sort of being contentious on this if you look the original title of this issue its Promise style semantics. Not add Single Publisher semantics. I see the inherit benefit of Single Publishers aka Mono aka Singles but its not really a Promise or Reactive-like Futures (e.g. Guava's ListenableFuture or CompletableFuture). Those types (e.g. Completable) despite having far more complicated interfaces are more constrained and thus I think easier to understand. After implementing a lot of reactive stuff in the real world for our company it is becoming difficult to explain to other developers particularly if it is just RPC like call composition that is needed. So yeah I'm very much for the new MonoPublisher but I have doubts how much easier it will make reactive programming. Anyway thanks for the discussion and patience! I'm sold! |
@bsideup I just thought I would comment... Above in response to
you referenced the spec and said
I would just like to point out that it is perfectly valid for an implementation of the spec to decide This is spelled out explicitly and clearly in the spec in: The spec is well written and it is commonly held that the use of the capitalisd "MAY" has a very specific meaning in this context. See RFC 2119: "The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL 5. MAY This word, or the adjective "OPTIONAL", mean that an item is Also it is possible to pass whole TCK with only one Subscriber per Publisher. Apologies for being contrary but I have learned that it is worth attending |
@hutchig But you must follow the spec, and you MUST NOT throw from So yes, it is "MAY be subscribed multiple times" but "MUST be ready to receive multiple |
My original point I was hoping to make is the easiest Publisher is a Publisher that only allows one subscription (besides the null case of an error or empty publisher). It's not just the easiest it's also the least expensive. Implementing multiple subscriptions requires a collection and generally not just any collection but a concurrent one. Even @bsideup brought up ceremony and the semantics given #428 of having to call Instead of agreeing that the only guaranteed publisher a library consumer can expect is 1-1 of publisher vs subscription we went down a rat hole of semantics and what @bsideup thinks is a default reactive library or model should be. Thats what makes me dubious and contentious about this change. It appears to make a few libraries needs/agendas which are trying to model a concurrency data structure with a stream one because the JDK option isn't an interface. I get that its low hanging fruit but I much rather have a tighter constrained type than what MonoPublisher is. By going down this seamless path I'm worried there will be an abuse of libraries putting that type all over the place when a more constrained type would be better. That being said despite my doubts I still see the value and overall think its worth it. |
My main concern is that adding a new type doesn't make anyone implement it (all existing implementations would need new releases using the new type), and ultimately it doesn't provide any tangible benefit (i.e. the interface is semantically equivalent to connecting a Processor to any Publisher which only produces the first element, a.k.a. |
I represent Project Reactor here. We will definitely implement the type as soon as it is released :D
There are many projects in the ecosystem that would benefit from this new type immediately, and there is even a migration path where a project is using Reactive Framework X and X does not support public MonoPublisher<String> getString() {
MonoLikeType<String> p = ...;
return new MonoPublisher<String>() {
public void subscribe(Subscriber<String> subscriber) {
p.subscribe(subscriber);
}
};
} (or a custom type that extends framework's)
This is the behaviour, but let's not ignore the type safety part of the proposal. Also, such |
@bsideup I'm curious is the optimization buffering and/or smaller/simpler publishers in reactor? I have no doubt optimizations can be made knowing its a mono but I'm curious what Reactor does. Ideally for general performance improvements ie |
Rather the opposite - we can avoid allocating queues for prefetching, have less code to handle such publishers, etc etc
In Reactor, we have |
As RSocket project representative, I can say that we will use it ASAP as well |
@OlegDokuka that is because RSocket is using Reactor right? So yeah it would be trivial for it to be added to your library. For my own implementation that doesn't rely on reactor or rxjava but does provide some It's already an incredible burden for implementors of libraries that do want to provide streams aka They either need to:
So to go back to @bsideup saying how r2dbc connection Publisher is obviously cold and will obviously give new connections and manage subscriptions etc etc... I say that logic is non trivial to implement for a database driver implementor. The easiest logic is for a database driver implementor to offer a Publisher that only does 1-1 to subscription and fail or replay for future subscriptions.... even easier would be to provide @bsideup Just to just how Stream libraries have their own biases... RxJava's Single.cache is cold where as your Reactor Mono.cache is hot (and hence I guess your confusion when I was saying you can have a replay without it being hot): http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Single.html#cache-- (RxJava's is presumably a decorator and I assume Monos is a Processor). Anyway to @bsideup point on off topic I think an entirely different spec should be made for Reactive Promise like types. One that decides whether its hot, cold and cached/replay etc. I recommend filing a new issue of "Add Mono/Single (0..1) to reactive streams" or something like that. |
Fair enough. I may have misunderstood the initial purpose of this issue (although, after reading the conversation, I got a feeling that it is precisely about One way or another, since there is an open PR, I suggest we continue the conversation (about the |
oh, and btw:
I disagree here and think that it is actually easier to implement a
As I previously mentioned,
I guess the docs need to be adjusted. If you look at the implementation, |
Not really. We are in the midway of adding That abstraction exposes lazy value providing:
It is clear that Having
so now, it is going to be compatible with all vendors single value sources |
Yes I guess for this case because |
I think you clearly misunderstand the abstraction here :) |
I didn't. What I don't know is if the subscription provided by Does it? I assume it does? And if so does it interrupt (although I suppose that doesn't matter in a full reactive platform)? Do I need to do a We agree that there is a 1-1 subscription to connection right? Resource management is confusing with reactive frameworks. Actually almost all of it is. After all this discussion Project loom is starting to look better and better for doing Request/Reply. I will still use Flow aka Publisher for true streams but I think a 0..1 stream is the wrong abstraction for modeling the apparently 90% use case of microservice REST frameworks which are not 0..1 stream but 1 or error. It is really sort of telling that most libraries are just single response and not stream. Anyway I'm not going to implement |
As @viktorklang said it once. Reactive-Streams is not a resource management framework |
No, why should it? O_O
No, it is not. Because there is no "Resource management" at all. "Reactive Streams is not a resource management framework" (c) #482 (comment)
Okay, this is an "atomic bomb" dropped into this discussion. I simply will not answer that.
Out of curiosity, do you have it publicly available somewhere? Some OSS libs maybe? |
It's not OSS. A lot of our problems are stream based. I'll see if I can figure out some medium where we can discuss more. As for Loom.. yeah I am sorry for bringing that up but I have spent countless time trying to teach reactive programming to developers and for non-streaming RESTful microservices I have come to the conclusion it is not worth the slight performance benefits. So what I see these days is people writing RESTful microservices using reactive frameworks adding significant cognitive load either through callbacks or monoids when the reality is request/reply is just not very reactive.
And while agree on that but the reality is if you are bridging to other APIs like for example The reason it seems so obvious is because this is a |
I guess in less words I mean resource management is complicated with reactive streams precisely because it doesn't have semantics around it. Blocking APIs don't have this problem because they can fail fast and there is try catch, etc. Anyway it's been a couple of years since I have actually worked with reactive frameworks (I do more boring biz stuff these days) so I'm not the best to chime in but I wanted to give input from a non-library writer. As in we try (and tried) to use things like this in the real world and it's a lot more complicated than I think many of the reactive library writers might think. |
We are currently building a set of network APIs that use reactive streams. We're migrating more and more over the reactive streams to gain the benefits of swappable implementations over the standardised interfaces, back pressure support and good FRP.
When dealing with streaming data, the semantics are well understood by the users of the API. When dealing with something that is naturally request/ response (think similar to an HTTP request), then the semantics begin to break down.
We used a Future for this reason. Developers approaching the API expect to get a single response back, and we feel that the API should indicate to them what they should expect to happen. It's about reasoning and learning rather than mechanics.
Naturally, we would want to return a Promise from this type of API, indicating to the developer what they can reasonably expect to occur. Currently, our only option from reactive streams is a full publisher.
Mechanically, this works fine, but that isn't the issue. From the point of view of building an API for consumption by randoms on the internet, I don't want to have to indicate in the method name nor the documentation that a single item will be returned.
The case of up to 1 item being returned is special, and has spawned Future and Promise in the JDK and everywhere else respectively. For this reason, and for reactive streams to become even more useful for the developing of highly interoperable and usable APIs, I would like to suggest that a Promise interface be added to the core set.
Mechnically, it would be an extension of Publisher, and nothing more. Semantically, it would be constrained in the specification to return up to a single item.
The text was updated successfully, but these errors were encountered: