relay: improve the way we allocate channels

Now the server allocates a channel randomly from set of available ids
with the shortest possible length. So concurrency=1 will always yield a
channel-id between 1 and 9 (inclusive). If we have 9 simultaneous
sessions, we'll start allocating channels from 10 to 99. 100
simultaneous connections kicks us into the 100-999 bucket, etc.
This commit is contained in:
Brian Warner 2015-04-09 22:44:04 -05:00
parent ecc04ff675
commit c8d2fc8750

View File

@ -1,5 +1,5 @@
from __future__ import print_function from __future__ import print_function
import re, json, time import re, json, time, random
from twisted.python import log from twisted.python import log
from twisted.internet import protocol from twisted.internet import protocol
from twisted.application import strports, service, internet from twisted.application import strports, service, internet
@ -56,13 +56,12 @@ WELCOME = {
} }
# relay URLs are: # relay URLs are:
# # GET /list -> {channel-ids: [INT..]}
# POST /allocate -> {channel-id: INT} # POST /allocate -> {channel-id: INT}
# these return all messages for CHANNEL-ID= and MSGNUM= but SIDE!= : # these return all messages for CHANNEL-ID= and MSGNUM= but SIDE!= :
# POST /CHANNEL-ID/SIDE/post/MSGNUM {message: STR} -> {messages: [STR..]} # POST /CHANNEL-ID/SIDE/post/MSGNUM {message: STR} -> {messages: [STR..]}
# POST /CHANNEL-ID/SIDE/poll/MSGNUM -> {messages: [STR..]} # POST /CHANNEL-ID/SIDE/poll/MSGNUM -> {messages: [STR..]}
# GET /CHANNEL-ID/SIDE/poll/MSGNUM (eventsource) -> STR, STR, .. # GET /CHANNEL-ID/SIDE/poll/MSGNUM (eventsource) -> STR, STR, ..
#
# POST /CHANNEL-ID/SIDE/deallocate -> waiting | deleted # POST /CHANNEL-ID/SIDE/deallocate -> waiting | deleted
class Channel(resource.Resource): class Channel(resource.Resource):
@ -166,7 +165,6 @@ class Relay(resource.Resource):
def __init__(self): def __init__(self):
resource.Resource.__init__(self) resource.Resource.__init__(self)
self.channels = {} self.channels = {}
self.next_channel = 1
def prune_old_channels(self): def prune_old_channels(self):
now = time.time() now = time.time()
@ -176,14 +174,26 @@ class Relay(resource.Resource):
log.msg("expiring %d" % channel_id) log.msg("expiring %d" % channel_id)
self.free_child(channel_id) self.free_child(channel_id)
def allocate_channel_id(self):
for size in range(1,4): # stick to 1-999 for now
available = set()
for cid in range(10**(size-1), 10**size):
if cid not in self.channels:
available.add(cid)
if available:
return random.choice(list(available))
# ouch, 999 currently allocated. Try random ones for a while.
for tries in range(1000):
cid = random.randrange(1000, 1000*1000)
if cid not in self.channels:
return cid
raise ValueError("unable to find a free channel-id")
def getChild(self, path, request): def getChild(self, path, request):
if path == "allocate": if path == "allocate":
# be more clever later. Rotate through 1-99 unless they're all channel_id = self.allocate_channel_id()
# full, then rotate through 1-999, etc.
channel_id = self.next_channel
self.next_channel += 1
self.channels[channel_id] = Channel(channel_id, self) self.channels[channel_id] = Channel(channel_id, self)
log.msg("allocated %d, now have %d channels" % log.msg("allocated #%d, now have %d channels" %
(channel_id, len(self.channels))) (channel_id, len(self.channels)))
return Allocated(channel_id) return Allocated(channel_id)
if path == "list": if path == "list":
@ -202,10 +212,8 @@ class Relay(resource.Resource):
def free_child(self, channel_id): def free_child(self, channel_id):
self.channels.pop(channel_id) self.channels.pop(channel_id)
log.msg("freed %d, now have %d channels" % log.msg("freed #%d, now have %d channels" %
(channel_id, len(self.channels))) (channel_id, len(self.channels)))
if not self.channels:
self.next_channel = 1
class TransitConnection(protocol.Protocol): class TransitConnection(protocol.Protocol):
def __init__(self): def __init__(self):