This commit is contained in:
Brian Warner 2016-05-19 14:18:49 -07:00
parent 5994eb11d4
commit 0e72422ffa
3 changed files with 82 additions and 34 deletions

View File

@ -17,7 +17,11 @@ CREATE TABLE `nameplates`
`id` VARCHAR,
`mailbox_id` VARCHAR, -- really a foreign key
`side1` VARCHAR, -- side name, or NULL
`side2` VARCHAR -- side name, or NULL
`side2` VARCHAR, -- side name, or NULL
-- timing data
`started` INTEGER, -- time when nameplace was opened
`second` INTEGER, -- time when second side opened
`closed` INTEGER -- time when closed
);
CREATE INDEX `nameplates_idx` ON `nameplates` (`app_id`, `id`);
CREATE INDEX `nameplates_mailbox_idx` ON `nameplates` (`app_id`, `mailbox_id`);
@ -30,10 +34,6 @@ CREATE TABLE `mailboxes`
`id` VARCHAR,
`side1` VARCHAR -- side name, or NULL
`side2` VARCHAR -- side name, or NULL
-- timing data for the (optional) linked nameplate
`nameplate_started` INTEGER, -- time when related nameplace was opened
`nameplate_second` INTEGER, -- time when second side opened
`nameplate_closed` INTEGER, -- time when closed
-- timing data for the mailbox itself
`started` INTEGER, -- time when opened
`second` INTEGER, -- time when second side opened

View File

@ -17,7 +17,7 @@ RELEASE = u"_release"
def get_sides(row):
return set([s for s in [row["side1"], row["side2"]] if s])
def make_sides(side1, side2):
def make_sides(sides):
return list(sides) + [None] * (2 - len(sides))
def generate_mailbox_id():
return base64.b32encode(os.urandom(8)).lower().strip("=")
@ -270,7 +270,7 @@ class AppNamespace:
" WHERE `app_id`=?", (self._app_id,))
return set([row["id"] for row in c.fetchall()])
def find_available_nameplate_id(self):
def _find_available_nameplate_id(self):
claimed = self.get_nameplate_ids()
for size in range(1,4): # stick to 1-999 for now
available = set()
@ -288,6 +288,12 @@ class AppNamespace:
return id
raise ValueError("unable to find a free nameplate-id")
def allocate_nameplate(self, side, when):
nameplate_id = self._find_available_nameplate_id()
mailbox_id = self.claim_nameplate(self, nameplate_id, side, when)
del mailbox_id # ignored, they'll learn it from claim()
return nameplate_id
def _get_mailbox_id(self, nameplate_id):
row = self._db.execute("SELECT `mailbox_id` FROM `nameplates`"
" WHERE `app_id`=? AND `id`=?",
@ -295,36 +301,81 @@ class AppNamespace:
return row["mailbox_id"]
def claim_nameplate(self, nameplate_id, side, when):
# when we're done:
# * there will be one row for the nameplate
# * side1 or side2 will be populated
# * started or second will be populated
# * a mailbox id will be created, but not a mailbox row
# (ids are randomly unique, so we can defer creation until 'open')
assert isinstance(nameplate_id, type(u"")), type(nameplate_id)
assert isinstance(side, type(u"")), type(side)
db = self._db
rows = db.execute("SELECT * FROM `nameplates`"
" WHERE `app_id`=? AND `id`=?",
(self._app_id, nameplate_id))
if rows:
mailbox_id = rows[0]["mailbox_id"]
row = db.execute("SELECT * FROM `nameplates`"
" WHERE `app_id`=? AND `id`=?",
(self._app_id, nameplate_id)).fetchone()
if row:
mailbox_id = row["mailbox_id"]
sides = [row["side1"], row["sides2"]]
if side not in sides:
if sides[0] and sides[1]:
raise XXXERROR("crowded")
sides[1] = side
db.execute("UPDATE `nameplates` SET "
"`side1`=?, `side2`=?, `mailbox_id`=?, `second`=?"
" WHERE `app_id`=? AND `id`=?",
(sides[0], sides[1], mailbox_id, when,
self._app_id, nameplate_id))
else:
if self._log_requests:
log.msg("creating nameplate#%s for app_id %s" %
(nameplate_id, self._app_id))
mailbox_id = UUID()
db.execute("INSERT INTO `mailboxes`"
" (`app_id`, `id`)"
" VALUES(?,?)",
(self._app_id, mailbox_id))
mailbox_id = generate_mailbox_id()
db.execute("INSERT INTO `nameplates`"
" (`app_id`, `id`, `mailbox_id`, `side1`, `side2`)"
" (`app_id`, `id`, `mailbox_id`, `side1`, `started`)"
" VALUES(?,?,?,?,?)",
(self._app_id, nameplate_id, mailbox_id, None, None))
(self._app_id, nameplate_id, mailbox_id, side, when))
db.commit()
return mailbox_id
nameplate = Nameplate(self._app_id, self._db, nameplate_id, mailbox_id)
nameplate.claim(side, when)
return nameplate
def release_nameplate(self, nameplate_id, side, when):
# when we're done:
# * in the nameplate row, side1 or side2 will be removed
# * if the nameplate is now unused:
# * mailbox.nameplate_closed will be populated
# * the nameplate row will be removed
assert isinstance(nameplate_id, type(u"")), type(nameplate_id)
assert isinstance(side, type(u"")), type(side)
db = self._db
row = db.execute("SELECT * FROM `nameplates`"
" WHERE `app_id`=? AND `id`=?",
(self._app_id, nameplate_id)).fetchone()
if not row:
return
sides = get_sides(row)
if side not in sides:
return
sides.discard(side)
if sides:
s12 = make_sides(sides)
db.execute("UPDATE `nameplates` SET `side1`=?, `side2`=?"
" WHERE `app_id`=? AND `id`=?",
(s12[0], s12[1], self._app_id, nameplate_id))
else:
db.execute("DELETE FROM `nameplates`"
" WHERE `app_id`=? AND `id`=?",
(self._app_id, nameplate_id))
self._summarize_nameplate(row)
def claim_channel(self, channelid, side):
def open_mailbox(self, channelid, side):
assert isinstance(channelid, type(u"")), type(channelid)
channel = self.get_channel(channelid)
channel.claim(side)
return channel
# some of this overlaps with open() on a new mailbox
db.execute("INSERT INTO `mailboxes`"
" (`app_id`, `id`, `nameplate_started`, `started`)"
" VALUES(?,?,?,?)",
(self._app_id, mailbox_id, when, when))
def get_channel(self, channelid):
assert isinstance(channelid, type(u""))

View File

@ -155,31 +155,28 @@ class WebSocketRendezvous(websocket.WebSocketServerProtocol):
def handle_allocate(self, server_rx):
if self._did_allocate:
raise Error("You already allocated one channel, don't be greedy")
nameplate_id = self._app.find_available_nameplate_id()
nameplate_id = self._app.allocate_nameplate(self._side, server_rx)
assert isinstance(nameplate_id, type(u""))
self._did_allocate = True
self._nameplate = self._app.claim_nameplate(nameplate_id, self._side,
server_rx)
self.send("nameplate", nameplate=nameplate_id)
def handle_claim(self, msg, server_rx):
if "nameplate" not in msg:
raise Error("claim requires 'nameplate'")
nameplate_id = msg["nameplate"]
self._nameplate_id = nameplate_id
assert isinstance(nameplate_id, type(u"")), type(nameplate)
if self._nameplate and self._nameplate.get_id() != nameplate_id:
raise Error("claimed nameplate doesn't match allocated nameplate")
self._nameplate = self._app.claim_nameplate(nameplate_id, self._side,
server_rx)
mailbox_id = self._nameplate.get_mailbox_id()
mailbox_id = self._app.claim_nameplate(nameplate_id, self._side,
server_rx)
self.send("mailbox", mailbox=mailbox_id)
def handle_release(self, server_rx):
if not self._nameplate:
if not self._nameplate_id:
raise Error("must claim a nameplate before releasing it")
deleted = self._nameplate.release(self._side, server_rx)
self._nameplate = None
deleted = self._app.release_nameplate(self._nameplate_id,
self._side, server_rx)
self._nameplate_id = None
def handle_open(self, msg):