Skip to content

Instantly share code, notes, and snippets.

@aoberoi
Created January 24, 2019 01:52
Show Gist options
  • Save aoberoi/27d0594fe25f81357258498bd65fc911 to your computer and use it in GitHub Desktop.
Save aoberoi/27d0594fe25f81357258498bd65fc911 to your computer and use it in GitHub Desktop.

An exploration of ideas for the API and implementation of the next generation RTMClient in Python

Multiprocessing agnostic

At a minimum, the RTMClient must provide a means for application developers to bring their own multiprocessing opinions and implementaitons.

The simplest implementation an app developer might use is exactly what we've recommended every developer use in the current version of the package: an infinite loop that polls for new data in the main thread of a single process.

from slackclient import RTMClient

# Initialize a client for a single workspace
client = RTMClient(os.environ["SLACK_TOKEN"])

# An example event handler
def handle_team_join(event_data):
  pass
  
# Blocks until the websocket connection is set up and a `hello` message is recieved
client.start()

while True:

  # Immediately returns event data that was ready to read from the websocket
  # Returns a tuple of the event type and data. When there's no data ready, both are None
  event_type, event_data = client.read()
  
  if event_type == "team_join":
    handle_team_join(event_data)
  
  # ... add other event types here ...

  # Slow down polling to prevent spinning too much
  sleep(1)

Preventing slow apps due to buffering

RTMClient.read() can potentially be a source of latency in the app.

In order to understand why, let's establish some basic facts. First, the reason an app would use sleep(1) in the manner shown above is to prevent a spinlock, which just burns CPU cycles. However, finding the right amount of time to wait (1 second in the previous example) is tricky. Let's call that number the polling interval. If the polling interval is too long, then in a busy workspace the client will end up buffering many incoming events during the polling interval, but only process one. This means that if the polling interval is longer than the average time between events, the app will never be able to process all the events as it will never reach the end of the buffer. If the polling interval is too short, then the app will waste CPU cycles.

The solution to this problem is to make RTMClient.read() return all events in the buffer, instead of just one at a time. This all events in the buffer to be processed before any wait time begins. It also allows for more sophistocated multiprocessing solutions (besides this infinite loop) to more efficiently schedule processing.

I'd recommend that RTMClient.read() should return an iterable of the tuple of (event_type, event_data). If that change is made, the previous example becomes the following:

from slackclient import RTMClient

# Initialize a client for a single workspace
client = RTMClient(os.environ["SLACK_TOKEN"])

# An example event handler
def handle_team_join(event_data):
  pass
  
# Blocks until the websocket connection is set up and a `hello` message is recieved
client.start()

while True:

  # Immediately returns event data that was ready to read from the websocket
  # Returns a tuple of the event type and data. When there's no data ready, both are None
  for event_type, event_data in client.read():
    if event_type == "team_join":
      handle_team_join(event_data)
     
    # ... add other event types here ...

  # Slow down polling to prevent spinning too much
  sleep(1)

Reducing boilerplate

That's a lot of boilerplate code that has to be exactly right, especially for a beginner. We have an opportunity to eliminate most of this to serve the most common use cases and beginners.

from slackclient import RTMClient, RTMDispatcher

client = RTMClient(os.environ["SLACK_TOKEN"])
rtm_event = RTMDispatcher(client)

@rtm_event(type="team_join")
def handle_team_join(event_data)
  pass
  
client.start()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment