JuliaInterop / ZMQ.jl

Julia interface to ZMQ
Other
137 stars 58 forks source link

Don't extend Sockets #187

Closed joelfrederico closed 4 months ago

joelfrederico commented 5 years ago

TL;DR: ZMQ makes a pretty big deal about how they aren't your usual sockets. The interface is also fundamentally different from the standard lib's Sockets interface. I don't think we should extend the interface.

I think we need to take this section of the ZMQ guide to heart. Some quotes:

In the ZeroMQ universe, sockets are doorways to fast little background communications engines that manage a whole set of connections automagically for you. You can't see, work with, open, close, or attach state to these connections.

So the general assumption no longer applies. As you read the code examples, your brain will try to map them to what you know. You will read "socket" and think "ah, that represents a connection to another node". That is wrong. You will read "thread" and your brain will again think, "ah, a thread represents a connection to another node", and again your brain will be wrong.

And later, about the API:

To be perfectly honest, ZeroMQ does a kind of switch-and-bait on you, for which we don't apologize. It's for your own good and it hurts us more than it hurts you. ZeroMQ presents a familiar socket-based API, which requires great effort for us to hide a bunch of message-processing engines. However, the result will slowly fix your world view about how to design and write distributed software.

ZMQ sockets are different enough from Sockets that they are their own thing. They don't have the same interface. There is no ZMQ analogue to Sockets.listen, or Sockets analogue to ZMQ's contexts.

Also, this means we can remove the dependency on Sockets, and that's nice too.

vtjnash commented 5 years ago

I think it's a bit hard to take hyperbole that seriously. It does at least start this section by mentioning that multicast is a pre-existing concept, it just calls it "exotic". That's great if your only exposure to networking is tcp or unix domain sockets. But sockets encompassed more than just connection-oriented sockets before ZMQ came along and provided an arguably-better implementation.

The ZMQ pub/sub pattern has always sounded like a variation on UDP broadcast to me (e.g. the methods it's extending here). The ZMQ req/rep pattern is quite different from that, acting a bit like TCP, but more like a lightweight http protocol (http2?), since it's transiently stateful. Super useful?Yes. Completely unrelated concept? Probably not. It's actually even possible to use that connection-oriented api on UDP too, but just pretty rare (since usually folks just use TCP). The HTTP.jl library package implements a pub/sub interface more like this one, but often exposes it as a stream (read/write/eof) instead of a message (recv/send), so 🤷‍♂️.

You will read "socket" and think "ah, that represents a connection to another node". That is wrong.

Amusing read, but that's just a "TCPSocket", and isn't the API being extended here.

You will read "thread" and your brain will again think, "ah, a thread represents a connection to another node", and again your brain will be wrong.

We do something pretty completely different here, so not really relevant. Related past discussion https://github.com/JuliaInterop/ZMQ.jl/issues/52#issuecomment-310874420.

ZMQ sockets are different enough from Sockets that they are their own thing. They don't have the same interface. There is no ZMQ analogue to Sockets.listen, or Sockets analogue to ZMQ's contexts.

Note that in fact the author(s) of ZMQ have later gone on to write a new version of it that is intended to fully implement the posix "bsd sockets" API, including adding listen and removing contexts: https://nanomsg.org/documentation-zeromq.html

joelfrederico commented 5 years ago

Well, I definitely wouldn't call nanomsg a new version: https://sealedabstract.com/rants/nanomsg-postmortem-and-other-stories/

But anyways, I'd hoped the interface and internal functionality would be enough to convince people. Would it be compelling enough to note that you can't connect a ZMQ socket to a Sockets (BSD-style) socket? That really gets at the heart of the issue for me: ZMQ and Sockets are different things, as different as TCP is from UDP. Sure, they both send data, they have similar API's, but they don't actually mix.

vtjnash commented 5 years ago

That rant says that the project may have floundered for political reasons and was eventually re-written in go and/or backported into zmq.

as different as TCP is from UDP

Both of which are part of Sockets, even though they aren't compatible. Typically the question when deciding whether to merge two functions is whether they provide the same API. I'm not sure what "mixing" would mean. For example, Sockets already lets you connect to a unix domain socket too, even though that isn't TCP or UDP. ZMQ in my experience provides a nice blend of some of the best features of both TCP and UDP (and perhaps a bit of HTTP)—but I'm not sure what that gains you here in using different verbs (Sockets.recv vs ZMQ.recv, etc.)?

joelfrederico commented 5 years ago

Well, I just wanted to point out that nanomsg is far from a new version of ZMQ.

ZMQ sockets have a different set of rules, right? You can send or recv whenever you want with non-ZMQ sockets, for instance. The same isn't true of ZMQ, you can't send twice consecutively on a REQ/REP pair for instance. Bind and connect can use "inproc://" in ZMQ while that's not allowed on Sockets. There are frames that you can see or not see depending on whether you're using PUB/SUB or XPUB/XSUB. ZMQ actually has its own spec, ZMTP.

Also, I expect to be able to use ZMQ without importing Sockets. I don't expect to find part of ZMQ's interface somewhere else. I can't do "import ZMQ; ...; ZMQ.send(...)".

vtjnash commented 5 years ago

Those attributes still seem to be confusing TCP/IP streams with "Sockets". A socket doesn't have rules like "you can send send multiple replies" or "data cannot be transferred in-process". It's not much more than an abstraction of moving data between endpoints (https://en.wikipedia.org/wiki/Session_layer), and bundles together a large array of different implementations and protocols with different trade-offs and features. But then then even TCP/IP has frames that you can choose to see or not depending on whether you use the socket as a streams (read/write) or using the methods that are being extended here (send/recv).

Also, I expect to be able to use ZMQ without importing Sockets. I don't expect to find part of ZMQ's interface somewhere else. I can't do "import ZMQ; ...; ZMQ.send(...)".

Yes, that seems like a very good goal. If we can't do ZMQ.send(..) or import ZMQ; send(..), that sounds like an issue that should be fixed.

It also seems like a good general observation that packages should consider importing and re-exporting most of the functions that they extend, if for nothing more than the expectations and convenience of the user!

joelfrederico commented 5 years ago

I'm moving all of my discussion to https://github.com/JuliaInterop/ZMQ.jl/pull/190. I think it's more productive to talk about a PR with concrete proposed changes than in an issue.

IMHO this issue may be closed (but please don't also close https://github.com/JuliaInterop/ZMQ.jl/pull/190).