SO_REUSEADDR was controversial also for socket.create_server(). In the end
I concluded the best solution was to not expose a reuse_address parameter.
See:
https://github.com/python/cpython/blob/94e165096fd65e8237e60de570fb609604ab94c9/Lib/socket.py#L891-L899

It must be noted that right now also asyncio's create_server() method
allows passing reuse_address=True, which on Windows should probably be
turned into a no-op.

As for asyncio's create_datagram_endpoint() I partly agree with Antoine's
solution.
https://bugs.python.org/issue37228#msg357068
My course of action, though, would be the following:

* in 3.8: turn reuse_address parameter into a no-op, update doc
* in 3.9: raise error if reuse_address=True, update doc

Note: differently from TCP / create_server(), with UDP you can set
SO_REUSEADDR manually after calling create_datagram_endpoint() if you
really want to.

On Tue, Nov 19, 2019 at 4:48 AM <vai...@vaizki.fi> wrote:

> When creating UDP servers with asyncio's create_datagram_endpoint(), the
> default value for reuse_address = True, resulting in a dangerous (and
> currently incorrectly documented) situation. I have proposed changing the
> default value but understandably such a change for a core library function
> parameter is not to be taken lightly. Thus I put this up for discussion on
> the list.
>
> As background, when creating TCP servers on UNIX-like systems, it is
> almost boilerplate to set SO_REUSEADDR for all server sockets to make sure
> that a restarting server can immediately bind the socket again. Without the
> SO_REUSEADDR sockopt, the kernel will hold the addr:port in a TIME_WAIT
> state for a while, preventing reuse. Thus, when creating TCP servers with
> loop.create_server(), the parameter reuse_address has a very reasonable
> default value of True.
>
> However things are very different in UDP-land. The kernel does not hold
> UDP server ports in a waiting state so the SO_REUSEADDR sockopt was
> repurposed in Linux (and *BSD afaik) to allow multiple processes to bind
> the SAME addr:port for a UDP server. The kernel will then feed incoming UDP
> packets to all such processes in a semi-fair-roundrobin manner. This is
> very useful in some scenarios, for example I've used it myself in C++
> projects to allow UDP servers to be scaled easily and rolling upgrades to
> be implemented without separate load-balancing. But for this to be the
> default behaviour is quite dangerous.
>
> I discovered this default behaviour accidentally by having 2 separate
> Python programs (both doing SIP over UDP) accidentally configured to use
> the same UDP port. The result was that my 2 processes were indeed "sharing
> the load" - neither of them threw an exception at startup about the port
> being already in use and both started getting ~half of the incoming
> packets. So off to the docs I went and discovered that the documentation
> for create_datagram_endpoint() does not mention this behaviour at all,
> instead it mistakenly refers to the TCP protocol use of SO_REUSEADDR:
> "reuse_address tells the kernel to reuse a local socket in TIME_WAIT state,
> without waiting for its natural timeout to expire. If not specified will
> automatically be set to True on Unix."
>
> https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint
>
> What makes this default especially dangerous is,
> - Most people are not aware of this special mode that Linux allows for UDP
> sockets
> - Even if it was documented to be the default, many people would miss it
> unless a big warning was slapped on the docs
> - The problems are unlikely to appear in test scenarios and much more
> likely to pop up in production months or years after rolling out the code
> - If you have never used it on purpose, it is very confusing to debug,
> causing you to doubt your own and the kernel's sanity
> - The behaviour changes again if you happen to use a multicast address...
>
> Thus, my proposal is to change the default value for UDP to False or
> deprecate the function and introduce a new one as suggested by Yuri in my
> original bug report at:  https://bugs.python.org/issue37228
> _______________________________________________
> Python-Dev mailing list -- python-dev@python.org
> To unsubscribe send an email to python-dev-le...@python.org
> https://mail.python.org/mailman3/lists/python-dev.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-dev@python.org/message/TK2NTPWID7RBUUNUU5JYAZHR6FKEABRU/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
Giampaolo - http://grodola.blogspot.com
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/GSWPWB63DF4AT6I4R6O4MCJ5A52UZO3T/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to