db: use 'nameplate_sides' instead of cols in 'nameplates'
This commit is contained in:
parent
355ece3e47
commit
6c725e4a86
|
@ -13,23 +13,27 @@ CREATE TABLE `version`
|
||||||
-- nameplates, but the protocol and server allow can use arbitrary strings.
|
-- nameplates, but the protocol and server allow can use arbitrary strings.
|
||||||
CREATE TABLE `nameplates`
|
CREATE TABLE `nameplates`
|
||||||
(
|
(
|
||||||
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
`app_id` VARCHAR,
|
`app_id` VARCHAR,
|
||||||
`name` VARCHAR,
|
`name` VARCHAR,
|
||||||
`mailbox_id` VARCHAR, -- really a foreign key
|
`mailbox_id` VARCHAR, -- really a foreign key
|
||||||
`side1` VARCHAR, -- side name, or NULL
|
|
||||||
`side2` VARCHAR, -- side name, or NULL
|
|
||||||
`request_id` VARCHAR, -- from 'allocate' message, for future deduplication
|
`request_id` VARCHAR, -- from 'allocate' message, for future deduplication
|
||||||
`crowded` BOOLEAN, -- at some point, three or more sides were involved
|
`updated` INTEGER -- time of last activity, used for pruning
|
||||||
`updated` INTEGER, -- time of last activity, used for pruning
|
|
||||||
-- timing data
|
|
||||||
`started` INTEGER, -- time when nameplace was opened
|
|
||||||
`second` INTEGER -- time when second side opened
|
|
||||||
);
|
);
|
||||||
CREATE INDEX `nameplates_idx` ON `nameplates` (`app_id`, `name`);
|
CREATE INDEX `nameplates_idx` ON `nameplates` (`app_id`, `name`);
|
||||||
CREATE INDEX `nameplates_updated_idx` ON `nameplates` (`app_id`, `updated`);
|
CREATE INDEX `nameplates_updated_idx` ON `nameplates` (`app_id`, `updated`);
|
||||||
CREATE INDEX `nameplates_mailbox_idx` ON `nameplates` (`app_id`, `mailbox_id`);
|
CREATE INDEX `nameplates_mailbox_idx` ON `nameplates` (`app_id`, `mailbox_id`);
|
||||||
CREATE INDEX `nameplates_request_idx` ON `nameplates` (`app_id`, `request_id`);
|
CREATE INDEX `nameplates_request_idx` ON `nameplates` (`app_id`, `request_id`);
|
||||||
|
|
||||||
|
CREATE TABLE `nameplate_sides`
|
||||||
|
(
|
||||||
|
`nameplates_id` REFERENCES `nameplates`(`id`),
|
||||||
|
`claimed` BOOLEAN, -- True after claim(), False after release()
|
||||||
|
`side` VARCHAR,
|
||||||
|
`added` INTEGER -- time when this side first claimed the nameplate
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
-- Clients exchange messages through a "mailbox", which has a long (randomly
|
-- Clients exchange messages through a "mailbox", which has a long (randomly
|
||||||
-- unique) identifier and a queue of messages.
|
-- unique) identifier and a queue of messages.
|
||||||
CREATE TABLE `mailboxes`
|
CREATE TABLE `mailboxes`
|
||||||
|
|
|
@ -219,84 +219,96 @@ class AppNamespace:
|
||||||
del mailbox_id # ignored, they'll learn it from claim()
|
del mailbox_id # ignored, they'll learn it from claim()
|
||||||
return nameplate_id
|
return nameplate_id
|
||||||
|
|
||||||
def claim_nameplate(self, nameplate_id, side, when, _test_mailbox_id=None):
|
def claim_nameplate(self, name, side, when, _test_mailbox_id=None):
|
||||||
# when we're done:
|
# when we're done:
|
||||||
# * there will be one row for the nameplate
|
# * there will be one row for the nameplate
|
||||||
# * side1 or side2 will be populated
|
# * there will be one 'side' attached to it, with claimed=True
|
||||||
# * started or second will be populated
|
# * a mailbox id will be created, but not a mailbox row
|
||||||
# * a mailbox id will be created, but not a mailbox row
|
# (ids are randomly unique, so we can defer creation until 'open')
|
||||||
# (ids are randomly unique, so we can defer creation until 'open')
|
assert isinstance(name, type("")), type(name)
|
||||||
assert isinstance(nameplate_id, type("")), type(nameplate_id)
|
|
||||||
assert isinstance(side, type("")), type(side)
|
assert isinstance(side, type("")), type(side)
|
||||||
db = self._db
|
db = self._db
|
||||||
row = db.execute("SELECT * FROM `nameplates`"
|
row = db.execute("SELECT * FROM `nameplates`"
|
||||||
" WHERE `app_id`=? AND `name`=?",
|
" WHERE `app_id`=? AND `name`=?",
|
||||||
(self._app_id, nameplate_id)).fetchone()
|
(self._app_id, name)).fetchone()
|
||||||
if row:
|
if not row:
|
||||||
mailbox_id = row["mailbox_id"]
|
|
||||||
try:
|
|
||||||
sr = add_side(row, side)
|
|
||||||
except CrowdedError:
|
|
||||||
db.execute("UPDATE `nameplates` SET `crowded`=?"
|
|
||||||
" WHERE `app_id`=? AND `name`=?",
|
|
||||||
(True, self._app_id, nameplate_id))
|
|
||||||
db.commit()
|
|
||||||
raise
|
|
||||||
if sr.changed:
|
|
||||||
db.execute("UPDATE `nameplates` SET"
|
|
||||||
" `side1`=?, `side2`=?, `updated`=?, `second`=?"
|
|
||||||
" WHERE `app_id`=? AND `name`=?",
|
|
||||||
(sr.side1, sr.side2, when, when,
|
|
||||||
self._app_id, nameplate_id))
|
|
||||||
else:
|
|
||||||
if self._log_requests:
|
if self._log_requests:
|
||||||
log.msg("creating nameplate#%s for app_id %s" %
|
log.msg("creating nameplate#%s for app_id %s" %
|
||||||
(nameplate_id, self._app_id))
|
(name, self._app_id))
|
||||||
if _test_mailbox_id is not None: # for unit tests
|
if _test_mailbox_id is not None: # for unit tests
|
||||||
mailbox_id = _test_mailbox_id
|
mailbox_id = _test_mailbox_id
|
||||||
else:
|
else:
|
||||||
mailbox_id = generate_mailbox_id()
|
mailbox_id = generate_mailbox_id()
|
||||||
db.execute("INSERT INTO `nameplates`"
|
sql = ("INSERT INTO `nameplates`"
|
||||||
" (`app_id`, `name`, `mailbox_id`, `side1`, `crowded`,"
|
" (`app_id`, `name`, `mailbox_id`, `updated`)"
|
||||||
" `updated`, `started`)"
|
" VALUES(?,?,?,?)")
|
||||||
" VALUES(?,?,?,?,?, ?,?)",
|
npid = db.execute(sql,
|
||||||
(self._app_id, nameplate_id, mailbox_id, side, False,
|
(self._app_id, name, mailbox_id, when)
|
||||||
when, when))
|
).lastrowid
|
||||||
|
else:
|
||||||
|
npid = row["id"]
|
||||||
|
mailbox_id = row["mailbox_id"]
|
||||||
|
|
||||||
|
row = db.execute("SELECT * FROM `nameplate_sides`"
|
||||||
|
" WHERE `nameplates_id`=? AND `side`=?",
|
||||||
|
(npid, side)).fetchone()
|
||||||
|
if not row:
|
||||||
|
db.execute("INSERT INTO `nameplate_sides`"
|
||||||
|
" (`nameplates_id`, `claimed`, `side`, `added`)"
|
||||||
|
" VALUES(?,?,?,?)",
|
||||||
|
(npid, True, side, when))
|
||||||
|
db.execute("UPDATE `nameplates` SET `updated`=? WHERE `id`=?",
|
||||||
|
(when, npid))
|
||||||
db.commit()
|
db.commit()
|
||||||
|
rows = db.execute("SELECT * FROM `nameplate_sides`"
|
||||||
|
" WHERE `nameplates_id`=?", (npid,)).fetchall()
|
||||||
|
if len(rows) > 2:
|
||||||
|
raise CrowdedError("too many sides have claimed this nameplate")
|
||||||
return mailbox_id
|
return mailbox_id
|
||||||
|
|
||||||
def release_nameplate(self, nameplate_id, side, when):
|
def release_nameplate(self, name, side, when):
|
||||||
# when we're done:
|
# when we're done:
|
||||||
# * in the nameplate row, side1 or side2 will be removed
|
# * the 'claimed' flag will be cleared on the nameplate_sides row
|
||||||
# * if the nameplate is now unused:
|
# * if the nameplate is now unused (no claimed sides):
|
||||||
# * mailbox.nameplate_closed will be populated
|
# * mailbox.nameplate_closed will be populated
|
||||||
# * the nameplate row will be removed
|
# * the nameplate row will be removed
|
||||||
assert isinstance(nameplate_id, type("")), type(nameplate_id)
|
# * the nameplate sides will be removed
|
||||||
|
assert isinstance(name, type("")), type(name)
|
||||||
assert isinstance(side, type("")), type(side)
|
assert isinstance(side, type("")), type(side)
|
||||||
db = self._db
|
db = self._db
|
||||||
row = db.execute("SELECT * FROM `nameplates`"
|
np_row = db.execute("SELECT * FROM `nameplates`"
|
||||||
" WHERE `app_id`=? AND `name`=?",
|
" WHERE `app_id`=? AND `name`=?",
|
||||||
(self._app_id, nameplate_id)).fetchone()
|
(self._app_id, name)).fetchone()
|
||||||
|
if not np_row:
|
||||||
|
return
|
||||||
|
npid = np_row["id"]
|
||||||
|
row = db.execute("SELECT * FROM `nameplate_sides`"
|
||||||
|
" WHERE `nameplates_id`=? AND `side`=?",
|
||||||
|
(npid, side)).fetchone()
|
||||||
if not row:
|
if not row:
|
||||||
return
|
return
|
||||||
sr = remove_side(row, side)
|
db.execute("UPDATE `nameplate_sides` SET `claimed`=?"
|
||||||
if sr.empty:
|
" WHERE `nameplates_id`=? AND `side`=?",
|
||||||
db.execute("DELETE FROM `nameplates`"
|
(False, npid, side))
|
||||||
" WHERE `app_id`=? AND `name`=?",
|
db.commit()
|
||||||
(self._app_id, nameplate_id))
|
|
||||||
self._summarize_nameplate_and_store(row, when, pruned=False)
|
|
||||||
db.commit()
|
|
||||||
elif sr.changed:
|
|
||||||
db.execute("UPDATE `nameplates`"
|
|
||||||
" SET `side1`=?, `side2`=?, `updated`=?"
|
|
||||||
" WHERE `app_id`=? AND `name`=?",
|
|
||||||
(sr.side1, sr.side2, when,
|
|
||||||
self._app_id, nameplate_id))
|
|
||||||
db.commit()
|
|
||||||
|
|
||||||
def _summarize_nameplate_and_store(self, row, delete_time, pruned):
|
# now, are there any remaining claims?
|
||||||
|
side_rows = db.execute("SELECT * FROM `nameplate_sides`"
|
||||||
|
" WHERE `nameplates_id`=?",
|
||||||
|
(npid,)).fetchall()
|
||||||
|
claims = [1 for sr in side_rows if sr["claimed"]]
|
||||||
|
if claims:
|
||||||
|
return
|
||||||
|
# delete and summarize
|
||||||
|
db.execute("DELETE FROM `nameplate_sides` WHERE `nameplates_id`=?",
|
||||||
|
(npid,))
|
||||||
|
db.execute("DELETE FROM `nameplates` WHERE `id`=?", (npid,))
|
||||||
|
self._summarize_nameplate_and_store(side_rows, when, pruned=False)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
def _summarize_nameplate_and_store(self, side_rows, delete_time, pruned):
|
||||||
# requires caller to db.commit()
|
# requires caller to db.commit()
|
||||||
u = self._summarize_nameplate_usage(row, delete_time, pruned)
|
u = self._summarize_nameplate_usage(side_rows, delete_time, pruned)
|
||||||
self._db.execute("INSERT INTO `nameplate_usage`"
|
self._db.execute("INSERT INTO `nameplate_usage`"
|
||||||
" (`app_id`,"
|
" (`app_id`,"
|
||||||
" `started`, `total_time`, `waiting_time`, `result`)"
|
" `started`, `total_time`, `waiting_time`, `result`)"
|
||||||
|
@ -304,20 +316,21 @@ class AppNamespace:
|
||||||
(self._app_id,
|
(self._app_id,
|
||||||
u.started, u.total_time, u.waiting_time, u.result))
|
u.started, u.total_time, u.waiting_time, u.result))
|
||||||
|
|
||||||
def _summarize_nameplate_usage(self, row, delete_time, pruned):
|
def _summarize_nameplate_usage(self, side_rows, delete_time, pruned):
|
||||||
started = row["started"]
|
times = sorted([row["added"] for row in side_rows])
|
||||||
|
started = times[0]
|
||||||
if self._blur_usage:
|
if self._blur_usage:
|
||||||
started = self._blur_usage * (started // self._blur_usage)
|
started = self._blur_usage * (started // self._blur_usage)
|
||||||
waiting_time = None
|
waiting_time = None
|
||||||
if row["second"]:
|
if len(times) > 1:
|
||||||
waiting_time = row["second"] - row["started"]
|
waiting_time = times[1] - times[0]
|
||||||
total_time = delete_time - row["started"]
|
total_time = delete_time - times[0]
|
||||||
result = "lonely"
|
result = "lonely"
|
||||||
if row["second"]:
|
if len(times) == 2:
|
||||||
result = "happy"
|
result = "happy"
|
||||||
if pruned:
|
if pruned:
|
||||||
result = "pruney"
|
result = "pruney"
|
||||||
if row["crowded"]:
|
if len(times) > 2:
|
||||||
result = "crowded"
|
result = "crowded"
|
||||||
return Usage(started=started, waiting_time=waiting_time,
|
return Usage(started=started, waiting_time=waiting_time,
|
||||||
total_time=total_time, result=result)
|
total_time=total_time, result=result)
|
||||||
|
@ -470,13 +483,11 @@ class AppNamespace:
|
||||||
# if it is old:
|
# if it is old:
|
||||||
# if the nameplate is new:
|
# if the nameplate is new:
|
||||||
# classify mailbox as new
|
# classify mailbox as new
|
||||||
all_nameplates = {}
|
old_nameplate_ids = []
|
||||||
all_nameplate_rows = {}
|
|
||||||
for row in db.execute("SELECT * FROM `nameplates`"
|
for row in db.execute("SELECT * FROM `nameplates`"
|
||||||
" WHERE `app_id`=?",
|
" WHERE `app_id`=?",
|
||||||
(self._app_id,)).fetchall():
|
(self._app_id,)).fetchall():
|
||||||
nameplate_id = row["name"]
|
npid = row["id"]
|
||||||
all_nameplate_rows[nameplate_id] = row
|
|
||||||
if row["updated"] > old:
|
if row["updated"] > old:
|
||||||
which = NEW
|
which = NEW
|
||||||
else:
|
else:
|
||||||
|
@ -488,21 +499,23 @@ class AppNamespace:
|
||||||
else:
|
else:
|
||||||
if which == NEW:
|
if which == NEW:
|
||||||
all_mailboxes[mailbox_id] = NEW
|
all_mailboxes[mailbox_id] = NEW
|
||||||
all_nameplates[nameplate_id] = which
|
if which == OLD:
|
||||||
#log.msg(" 6: all_nameplates", all_nameplates, all_nameplate_rows)
|
old_nameplate_ids.append(npid)
|
||||||
|
#log.msg(" 6: old_nameplate_ids", old_nameplate_ids)
|
||||||
|
|
||||||
# delete all old nameplates
|
# delete all old nameplates
|
||||||
# invariant check: if there is a linked mailbox, it is old
|
# invariant check: if there is a linked mailbox, it is old
|
||||||
|
|
||||||
for nameplate_id, which in all_nameplates.items():
|
for npid in old_nameplate_ids:
|
||||||
if which == OLD:
|
log.msg(" deleting nameplate", npid)
|
||||||
log.msg(" deleting nameplate", nameplate_id)
|
side_rows = db.execute("SELECT * FROM `nameplate_sides`"
|
||||||
row = all_nameplate_rows[nameplate_id]
|
" WHERE `nameplates_id`=?",
|
||||||
self._summarize_nameplate_and_store(row, now, pruned=True)
|
(npid,)).fetchall()
|
||||||
db.execute("DELETE FROM `nameplates`"
|
db.execute("DELETE FROM `nameplate_sides` WHERE `nameplates_id`=?",
|
||||||
" WHERE `app_id`=? AND `name`=?",
|
(npid,))
|
||||||
(self._app_id, nameplate_id))
|
db.execute("DELETE FROM `nameplates` WHERE `id`=?", (npid,))
|
||||||
modified = True
|
self._summarize_nameplate_and_store(side_rows, now, pruned=True)
|
||||||
|
modified = True
|
||||||
|
|
||||||
# delete all messages for old mailboxes
|
# delete all messages for old mailboxes
|
||||||
# delete all old mailboxes
|
# delete all old mailboxes
|
||||||
|
|
|
@ -48,90 +48,106 @@ class Server(ServerBase, unittest.TestCase):
|
||||||
biggest = max(nids)
|
biggest = max(nids)
|
||||||
self.assert_(1000 <= biggest < 1000000, biggest)
|
self.assert_(1000 <= biggest < 1000000, biggest)
|
||||||
|
|
||||||
def _nameplate(self, app, nameplate_id):
|
def _nameplate(self, app, name):
|
||||||
return app._db.execute("SELECT * FROM `nameplates`"
|
np_row = app._db.execute("SELECT * FROM `nameplates`"
|
||||||
" WHERE `app_id`='appid' AND `name`=?",
|
" WHERE `app_id`='appid' AND `name`=?",
|
||||||
(nameplate_id,)).fetchone()
|
(name,)).fetchone()
|
||||||
|
if not np_row:
|
||||||
|
return None, None
|
||||||
|
npid = np_row["id"]
|
||||||
|
side_rows = app._db.execute("SELECT * FROM `nameplate_sides`"
|
||||||
|
" WHERE `nameplates_id`=?",
|
||||||
|
(npid,)).fetchall()
|
||||||
|
return np_row, side_rows
|
||||||
|
|
||||||
def test_nameplate(self):
|
def test_nameplate(self):
|
||||||
app = self._rendezvous.get_app("appid")
|
app = self._rendezvous.get_app("appid")
|
||||||
nameplate_id = app.allocate_nameplate("side1", 0)
|
name = app.allocate_nameplate("side1", 0)
|
||||||
self.assertEqual(type(nameplate_id), type(""))
|
self.assertEqual(type(name), type(""))
|
||||||
nid = int(nameplate_id)
|
nid = int(name)
|
||||||
self.assert_(0 < nid < 10, nid)
|
self.assert_(0 < nid < 10, nid)
|
||||||
self.assertEqual(app.get_nameplate_ids(), set([nameplate_id]))
|
self.assertEqual(app.get_nameplate_ids(), set([name]))
|
||||||
# allocate also does a claim
|
# allocate also does a claim
|
||||||
row = self._nameplate(app, nameplate_id)
|
np_row, side_rows = self._nameplate(app, name)
|
||||||
self.assertEqual(row["side1"], "side1")
|
self.assertEqual(len(side_rows), 1)
|
||||||
self.assertEqual(row["side2"], None)
|
self.assertEqual(side_rows[0]["side"], "side1")
|
||||||
self.assertEqual(row["crowded"], False)
|
self.assertEqual(side_rows[0]["added"], 0)
|
||||||
self.assertEqual(row["started"], 0)
|
|
||||||
self.assertEqual(row["second"], None)
|
|
||||||
|
|
||||||
mailbox_id = app.claim_nameplate(nameplate_id, "side1", 1)
|
|
||||||
self.assertEqual(type(mailbox_id), type(""))
|
|
||||||
# duplicate claims by the same side are combined
|
# duplicate claims by the same side are combined
|
||||||
row = self._nameplate(app, nameplate_id)
|
mailbox_id = app.claim_nameplate(name, "side1", 1)
|
||||||
self.assertEqual(row["side1"], "side1")
|
self.assertEqual(type(mailbox_id), type(""))
|
||||||
self.assertEqual(row["side2"], None)
|
self.assertEqual(mailbox_id, np_row["mailbox_id"])
|
||||||
|
np_row, side_rows = self._nameplate(app, name)
|
||||||
|
self.assertEqual(len(side_rows), 1)
|
||||||
|
self.assertEqual(side_rows[0]["added"], 0)
|
||||||
|
self.assertEqual(mailbox_id, np_row["mailbox_id"])
|
||||||
|
|
||||||
mailbox_id2 = app.claim_nameplate(nameplate_id, "side1", 2)
|
# and they don't updated the 'added' time
|
||||||
|
mailbox_id2 = app.claim_nameplate(name, "side1", 2)
|
||||||
self.assertEqual(mailbox_id, mailbox_id2)
|
self.assertEqual(mailbox_id, mailbox_id2)
|
||||||
row = self._nameplate(app, nameplate_id)
|
np_row, side_rows = self._nameplate(app, name)
|
||||||
self.assertEqual(row["side1"], "side1")
|
self.assertEqual(len(side_rows), 1)
|
||||||
self.assertEqual(row["side2"], None)
|
self.assertEqual(side_rows[0]["added"], 0)
|
||||||
self.assertEqual(row["started"], 0)
|
|
||||||
self.assertEqual(row["second"], None)
|
|
||||||
|
|
||||||
# claim by the second side is new
|
# claim by the second side is new
|
||||||
mailbox_id3 = app.claim_nameplate(nameplate_id, "side2", 3)
|
mailbox_id3 = app.claim_nameplate(name, "side2", 3)
|
||||||
self.assertEqual(mailbox_id, mailbox_id3)
|
self.assertEqual(mailbox_id, mailbox_id3)
|
||||||
row = self._nameplate(app, nameplate_id)
|
np_row, side_rows = self._nameplate(app, name)
|
||||||
self.assertEqual(row["side1"], "side1")
|
self.assertEqual(len(side_rows), 2)
|
||||||
self.assertEqual(row["side2"], "side2")
|
self.assertEqual(sorted([row["side"] for row in side_rows]),
|
||||||
self.assertEqual(row["crowded"], False)
|
sorted(["side1", "side2"]))
|
||||||
self.assertEqual(row["started"], 0)
|
self.assertIn(("side2", 3),
|
||||||
self.assertEqual(row["second"], 3)
|
[(row["side"], row["added"]) for row in side_rows])
|
||||||
|
|
||||||
# a third claim marks the nameplate as "crowded", but leaves the two
|
# a third claim marks the nameplate as "crowded", and adds a third
|
||||||
# existing claims alone
|
# claim (which must be released later), but leaves the two existing
|
||||||
|
# claims alone
|
||||||
self.assertRaises(rendezvous.CrowdedError,
|
self.assertRaises(rendezvous.CrowdedError,
|
||||||
app.claim_nameplate, nameplate_id, "side3", 0)
|
app.claim_nameplate, name, "side3", 4)
|
||||||
row = self._nameplate(app, nameplate_id)
|
np_row, side_rows = self._nameplate(app, name)
|
||||||
self.assertEqual(row["side1"], "side1")
|
self.assertEqual(len(side_rows), 3)
|
||||||
self.assertEqual(row["side2"], "side2")
|
|
||||||
self.assertEqual(row["crowded"], True)
|
|
||||||
|
|
||||||
# releasing a non-existent nameplate is ignored
|
# releasing a non-existent nameplate is ignored
|
||||||
app.release_nameplate(nameplate_id+"not", "side4", 0)
|
app.release_nameplate(name+"not", "side4", 0)
|
||||||
|
|
||||||
# releasing a side that never claimed the nameplate is ignored
|
# releasing a side that never claimed the nameplate is ignored
|
||||||
app.release_nameplate(nameplate_id, "side4", 0)
|
app.release_nameplate(name, "side4", 0)
|
||||||
row = self._nameplate(app, nameplate_id)
|
np_row, side_rows = self._nameplate(app, name)
|
||||||
self.assertEqual(row["side1"], "side1")
|
self.assertEqual(len(side_rows), 3)
|
||||||
self.assertEqual(row["side2"], "side2")
|
|
||||||
|
|
||||||
# releasing one side leaves the second claim
|
# releasing one side leaves the second claim
|
||||||
app.release_nameplate(nameplate_id, "side1", 5)
|
app.release_nameplate(name, "side1", 5)
|
||||||
row = self._nameplate(app, nameplate_id)
|
np_row, side_rows = self._nameplate(app, name)
|
||||||
self.assertEqual(row["side1"], "side2")
|
claims = [(row["side"], row["claimed"]) for row in side_rows]
|
||||||
self.assertEqual(row["side2"], None)
|
self.assertIn(("side1", False), claims)
|
||||||
|
self.assertIn(("side2", True), claims)
|
||||||
|
self.assertIn(("side3", True), claims)
|
||||||
|
|
||||||
# releasing one side multiple times is ignored
|
# releasing one side multiple times is ignored
|
||||||
app.release_nameplate(nameplate_id, "side1", 5)
|
app.release_nameplate(name, "side1", 5)
|
||||||
row = self._nameplate(app, nameplate_id)
|
np_row, side_rows = self._nameplate(app, name)
|
||||||
self.assertEqual(row["side1"], "side2")
|
claims = [(row["side"], row["claimed"]) for row in side_rows]
|
||||||
self.assertEqual(row["side2"], None)
|
self.assertIn(("side1", False), claims)
|
||||||
|
self.assertIn(("side2", True), claims)
|
||||||
|
self.assertIn(("side3", True), claims)
|
||||||
|
|
||||||
# releasing the second side frees the nameplate, and adds usage
|
# release the second side
|
||||||
app.release_nameplate(nameplate_id, "side2", 6)
|
app.release_nameplate(name, "side2", 6)
|
||||||
row = self._nameplate(app, nameplate_id)
|
np_row, side_rows = self._nameplate(app, name)
|
||||||
self.assertEqual(row, None)
|
claims = [(row["side"], row["claimed"]) for row in side_rows]
|
||||||
|
self.assertIn(("side1", False), claims)
|
||||||
|
self.assertIn(("side2", False), claims)
|
||||||
|
self.assertIn(("side3", True), claims)
|
||||||
|
|
||||||
|
# releasing the third side frees the nameplate, and adds usage
|
||||||
|
app.release_nameplate(name, "side3", 7)
|
||||||
|
np_row, side_rows = self._nameplate(app, name)
|
||||||
|
self.assertEqual(np_row, None)
|
||||||
usage = app._db.execute("SELECT * FROM `nameplate_usage`").fetchone()
|
usage = app._db.execute("SELECT * FROM `nameplate_usage`").fetchone()
|
||||||
self.assertEqual(usage["app_id"], "appid")
|
self.assertEqual(usage["app_id"], "appid")
|
||||||
self.assertEqual(usage["started"], 0)
|
self.assertEqual(usage["started"], 0)
|
||||||
self.assertEqual(usage["waiting_time"], 3)
|
self.assertEqual(usage["waiting_time"], 3)
|
||||||
self.assertEqual(usage["total_time"], 6)
|
self.assertEqual(usage["total_time"], 7)
|
||||||
self.assertEqual(usage["result"], "crowded")
|
self.assertEqual(usage["result"], "crowded")
|
||||||
|
|
||||||
|
|
||||||
|
@ -695,6 +711,18 @@ class WebSocketAPI(ServerBase, unittest.TestCase):
|
||||||
nids.add(n["id"])
|
nids.add(n["id"])
|
||||||
self.assertEqual(nids, set([nameplate_id1, "np2"]))
|
self.assertEqual(nids, set([nameplate_id1, "np2"]))
|
||||||
|
|
||||||
|
def _nameplate(self, app, name):
|
||||||
|
np_row = app._db.execute("SELECT * FROM `nameplates`"
|
||||||
|
" WHERE `app_id`='appid' AND `name`=?",
|
||||||
|
(name,)).fetchone()
|
||||||
|
if not np_row:
|
||||||
|
return None, None
|
||||||
|
npid = np_row["id"]
|
||||||
|
side_rows = app._db.execute("SELECT * FROM `nameplate_sides`"
|
||||||
|
" WHERE `nameplates_id`=?",
|
||||||
|
(npid,)).fetchall()
|
||||||
|
return np_row, side_rows
|
||||||
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def test_allocate(self):
|
def test_allocate(self):
|
||||||
c1 = yield self.make_client()
|
c1 = yield self.make_client()
|
||||||
|
@ -710,11 +738,11 @@ class WebSocketAPI(ServerBase, unittest.TestCase):
|
||||||
c1.send("allocate")
|
c1.send("allocate")
|
||||||
m = yield c1.next_non_ack()
|
m = yield c1.next_non_ack()
|
||||||
self.assertEqual(m["type"], "allocated")
|
self.assertEqual(m["type"], "allocated")
|
||||||
nameplate_id = m["nameplate"]
|
name = m["nameplate"]
|
||||||
|
|
||||||
nids = app.get_nameplate_ids()
|
nids = app.get_nameplate_ids()
|
||||||
self.assertEqual(len(nids), 1)
|
self.assertEqual(len(nids), 1)
|
||||||
self.assertEqual(nameplate_id, list(nids)[0])
|
self.assertEqual(name, list(nids)[0])
|
||||||
|
|
||||||
c1.send("allocate")
|
c1.send("allocate")
|
||||||
err = yield c1.next_non_ack()
|
err = yield c1.next_non_ack()
|
||||||
|
@ -722,13 +750,11 @@ class WebSocketAPI(ServerBase, unittest.TestCase):
|
||||||
self.assertEqual(err["error"],
|
self.assertEqual(err["error"],
|
||||||
"you already allocated one, don't be greedy")
|
"you already allocated one, don't be greedy")
|
||||||
|
|
||||||
c1.send("claim", nameplate=nameplate_id) # allocate+claim is ok
|
c1.send("claim", nameplate=name) # allocate+claim is ok
|
||||||
yield c1.sync()
|
yield c1.sync()
|
||||||
row = app._db.execute("SELECT * FROM `nameplates`"
|
np_row, side_rows = self._nameplate(app, name)
|
||||||
" WHERE `app_id`='appid' AND `name`=?",
|
self.assertEqual(len(side_rows), 1)
|
||||||
(nameplate_id,)).fetchone()
|
self.assertEqual(side_rows[0]["side"], "side")
|
||||||
self.assertEqual(row["side1"], "side")
|
|
||||||
self.assertEqual(row["side2"], None)
|
|
||||||
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def test_claim(self):
|
def test_claim(self):
|
||||||
|
@ -751,6 +777,9 @@ class WebSocketAPI(ServerBase, unittest.TestCase):
|
||||||
nids = app.get_nameplate_ids()
|
nids = app.get_nameplate_ids()
|
||||||
self.assertEqual(len(nids), 1)
|
self.assertEqual(len(nids), 1)
|
||||||
self.assertEqual("np1", list(nids)[0])
|
self.assertEqual("np1", list(nids)[0])
|
||||||
|
np_row, side_rows = self._nameplate(app, "np1")
|
||||||
|
self.assertEqual(len(side_rows), 1)
|
||||||
|
self.assertEqual(side_rows[0]["side"], "side")
|
||||||
|
|
||||||
# claiming a nameplate will assign a random mailbox id, but won't
|
# claiming a nameplate will assign a random mailbox id, but won't
|
||||||
# create the mailbox itself
|
# create the mailbox itself
|
||||||
|
@ -796,10 +825,10 @@ class WebSocketAPI(ServerBase, unittest.TestCase):
|
||||||
m = yield c1.next_non_ack()
|
m = yield c1.next_non_ack()
|
||||||
self.assertEqual(m["type"], "released")
|
self.assertEqual(m["type"], "released")
|
||||||
|
|
||||||
row = app._db.execute("SELECT * FROM `nameplates`"
|
np_row, side_rows = self._nameplate(app, "np1")
|
||||||
" WHERE `app_id`='appid' AND `name`='np1'").fetchone()
|
claims = [(row["side"], row["claimed"]) for row in side_rows]
|
||||||
self.assertEqual(row["side1"], "side2")
|
self.assertIn(("side", False), claims)
|
||||||
self.assertEqual(row["side2"], None)
|
self.assertIn(("side2", True), claims)
|
||||||
|
|
||||||
c1.send("release") # no longer claimed
|
c1.send("release") # no longer claimed
|
||||||
err = yield c1.next_non_ack()
|
err = yield c1.next_non_ack()
|
||||||
|
@ -941,19 +970,20 @@ class Summary(unittest.TestCase):
|
||||||
def test_nameplate(self):
|
def test_nameplate(self):
|
||||||
a = rendezvous.AppNamespace(None, None, False, None)
|
a = rendezvous.AppNamespace(None, None, False, None)
|
||||||
# starts at time 1, maybe gets second open at time 3, closes at 5
|
# starts at time 1, maybe gets second open at time 3, closes at 5
|
||||||
base_row = {"started": 1, "second": None, "crowded": False}
|
def s(rows, pruned=False):
|
||||||
def summ(num_sides, pruned=False, **kwargs):
|
return a._summarize_nameplate_usage(rows, 5, pruned)
|
||||||
row = base_row.copy()
|
|
||||||
row.update(kwargs)
|
|
||||||
return a._summarize_nameplate_usage(row, 5, pruned)
|
|
||||||
|
|
||||||
self.assertEqual(summ(1), Usage(1, None, 4, "lonely"))
|
rows = [dict(added=1)]
|
||||||
self.assertEqual(summ(1, crowded=True), Usage(1, None, 4, "crowded"))
|
self.assertEqual(s(rows), Usage(1, None, 4, "lonely"))
|
||||||
|
rows = [dict(added=1), dict(added=3)]
|
||||||
|
self.assertEqual(s(rows), Usage(1, 2, 4, "happy"))
|
||||||
|
|
||||||
self.assertEqual(summ(2, second=3), Usage(1, 2, 4, "happy"))
|
rows = [dict(added=1), dict(added=3)]
|
||||||
|
self.assertEqual(s(rows, pruned=True), Usage(1, 2, 4, "pruney"))
|
||||||
|
|
||||||
|
rows = [dict(added=1), dict(added=3), dict(added=4)]
|
||||||
|
self.assertEqual(s(rows), Usage(1, 2, 4, "crowded"))
|
||||||
|
|
||||||
self.assertEqual(summ(2, second=3, pruned=True),
|
|
||||||
Usage(1, 2, 4, "pruney"))
|
|
||||||
|
|
||||||
def test_blur(self):
|
def test_blur(self):
|
||||||
db = get_db(":memory:")
|
db = get_db(":memory:")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user