Search code examples
tornado

Torando write_message parallel yielding


I just realised that WebSocketHandler.write_message() returns a Future. I have not been yielding this function in my functions before:

   @tornado.gen.coroutine
   def ff(self,msg):
      try:
         self.write_message(json.dumps(msg))
      except tornado.websocket.WebSocketClosedError:
         pass

as opposed to:

   @tornado.gen.coroutine
   def ff(self,msg):
      try:
         yield self.write_message(json.dumps(msg))
      except tornado.websocket.WebSocketClosedError:
         pass

Why has this been working without an error? (running on Tornado 4.3)

Also for the design pattern:

for x in messages:
   yield self.write_message(x)

would it be advisable to replace with a parallel?:

yield [self.write_message(x) for x in messages]

** Parallel WebSocketHandler wanting to send the same message to lots of websockets:

# WS is a list of WebSocketHandlers. 
yield [s.write_message(message) for s in WS]

Solution

  • The Future returned by write_message is for flow control: it normally returns immediately, but when outgoing buffers reach a certain size it will wait until earlier messages have been sent. This slows the application down to match the network instead of allowing it to add more and more messages to the outgoing buffer.

    These Futures should not be yielded in parallel: it defeats the purpose, and I don't think it would even work reliably: each call to IOStream.write invalidates the Future returned by the previous call.

    If you're not writing a large volume of messages, you can ignore these Futures, but if you want to better control the amount of memory your application consumes, yield them one at a time.