Open stephentoub opened 7 months ago
If it returns false, that indicates to the consumer that they might try again, in which case they could end up writing the same value multiple times to some of the participants.
This would adhere "at least once" semantics, and I think that's the way to go here. "At most once" is bad, as data may be missing, and "exactly once" is almost impossible to have in such scenarios.
When that behavior is documented, then the consumers need to be aware of this, and ideally the operations should be idempotent so "at least once" isn't a real problem.
Another option would be to throw a NotSuppotedException
for the problematic methods, but IMO that would be bad and violates the principle of least astonishment.
The option w/o deriving from ChannelWriter<T>
would be better, but my vote is for the "at least once" semantics (see above).
so "at least once" isn't a real problem.
I'm missing why duplicated data would be any less of a problem than missing data. If this was just about logging, then sure. But for many other situations, either way you get a wrong answer: if the data was numbers and a consumer was summing the data, missing or duplication both show up as a wrong answer.
You quoted only a piece of the information 😉 A better quote would be
When that behavior is documented, then the consumers need to be aware of this, and ideally the operations should be idempotent so "at least once" isn't a real problem.
So just in that context (documented, push consumers towards idempotent implementation) "at least once" isn't a problem. Otherwise you're right.
But that's a problem that many (all?) messaging systems have in common.
So just in that context (documented, push consumers towards idempotent implementation) "at least once" isn't a problem.
Sure, but I could just as easily say we document that data might be missing, at which point when someone codes to that, "at most once" isn't a problem. :smile:
I've had a few discussions lately with folks that have wanted something akin to a broadcasting ChannelWriter, where you could supply multiple channel writers and multiplex across them, with a single writer writing the same value to all of them. There's also been a desire for participants to come and go, such that targets could be added/removed dynamically. Any targets there when the write is issued would receive it, just forwarding along the write.
Effectively, it would look like this:
Unfortunately, I don't know how to define the semantics for this in a way that makes sense for an arbitrary consumer of the type via the abstraction. WriteAsync is straightforward, e.g. the equivalent of:
But... what, for example, should the semantics of TryWrite be? If TryWrite returns true for one participant but then false for another, what should the method return? If it returns true, that indicates to the consumer that the data was written, but it wasn't to all targets. If it returns false, that indicates to the consumer that they might try again, in which case they could end up writing the same value multiple times to some of the participants.
I'm opening this issue in case folks who are interested have suggestions for how to rationalize this, since at present I'm not comfortable adding something like this. The best I've come up with is to have the type not actually be a ChannelWriter, e.g.
such that you can't use it polymorphically and use the problematic operations. But that then also loses meaningful functionality.