From 74a1902fcd0601802b2b810ff308666f745d38f0 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Thu, 23 Jun 2016 15:09:45 -0700 Subject: [PATCH] schema: add v3, rename nameplates.id to .name --- src/wormhole/server/database.py | 2 +- src/wormhole/server/db-schemas/v3.sql | 105 ++++++++++++++++++++++++++ src/wormhole/server/rendezvous.py | 22 +++--- src/wormhole/test/test_server.py | 10 +-- 4 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 src/wormhole/server/db-schemas/v3.sql diff --git a/src/wormhole/server/database.py b/src/wormhole/server/database.py index d2589ad..00bd508 100644 --- a/src/wormhole/server/database.py +++ b/src/wormhole/server/database.py @@ -16,7 +16,7 @@ def get_upgrader(new_version): "db-schemas/upgrade-to-v%d.sql" % new_version) return schema_bytes.decode("utf-8") -TARGET_VERSION = 2 +TARGET_VERSION = 3 def dict_factory(cursor, row): d = {} diff --git a/src/wormhole/server/db-schemas/v3.sql b/src/wormhole/server/db-schemas/v3.sql new file mode 100644 index 0000000..c28f00a --- /dev/null +++ b/src/wormhole/server/db-schemas/v3.sql @@ -0,0 +1,105 @@ + +-- note: anything which isn't an boolean, integer, or human-readable unicode +-- string, (i.e. binary strings) will be stored as hex + +CREATE TABLE `version` +( + `version` INTEGER -- contains one row, set to 3 +); + + +-- Wormhole codes use a "nameplate": a short name which is only used to +-- reference a specific (long-named) mailbox. The codes only use numeric +-- nameplates, but the protocol and server allow can use arbitrary strings. +CREATE TABLE `nameplates` +( + `app_id` VARCHAR, + `name` VARCHAR, + `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 + `crowded` BOOLEAN, -- at some point, three or more sides were involved + `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_updated_idx` ON `nameplates` (`app_id`, `updated`); +CREATE INDEX `nameplates_mailbox_idx` ON `nameplates` (`app_id`, `mailbox_id`); +CREATE INDEX `nameplates_request_idx` ON `nameplates` (`app_id`, `request_id`); + +-- Clients exchange messages through a "mailbox", which has a long (randomly +-- unique) identifier and a queue of messages. +CREATE TABLE `mailboxes` +( + `app_id` VARCHAR, + `id` VARCHAR, + `side1` VARCHAR, -- side name, or NULL + `side2` VARCHAR, -- side name, or NULL + `crowded` BOOLEAN, -- at some point, three or more sides were involved + `first_mood` VARCHAR, + -- timing data for the mailbox itself + `started` INTEGER, -- time when opened + `second` INTEGER -- time when second side opened +); +CREATE INDEX `mailboxes_idx` ON `mailboxes` (`app_id`, `id`); + +CREATE TABLE `messages` +( + `app_id` VARCHAR, + `mailbox_id` VARCHAR, + `side` VARCHAR, + `phase` VARCHAR, -- numeric or string + `body` VARCHAR, + `server_rx` INTEGER, + `msg_id` VARCHAR +); +CREATE INDEX `messages_idx` ON `messages` (`app_id`, `mailbox_id`); + +CREATE TABLE `nameplate_usage` +( + `app_id` VARCHAR, + `started` INTEGER, -- seconds since epoch, rounded to "blur time" + `waiting_time` INTEGER, -- seconds from start to 2nd side appearing, or None + `total_time` INTEGER, -- seconds from open to last close/prune + `result` VARCHAR -- happy, lonely, pruney, crowded + -- nameplate moods: + -- "happy": two sides open and close + -- "lonely": one side opens and closes (no response from 2nd side) + -- "pruney": channels which get pruned for inactivity + -- "crowded": three or more sides were involved +); +CREATE INDEX `nameplate_usage_idx` ON `nameplate_usage` (`app_id`, `started`); + +CREATE TABLE `mailbox_usage` +( + `app_id` VARCHAR, + `started` INTEGER, -- seconds since epoch, rounded to "blur time" + `total_time` INTEGER, -- seconds from open to last close + `waiting_time` INTEGER, -- seconds from start to 2nd side appearing, or None + `result` VARCHAR -- happy, scary, lonely, errory, pruney + -- rendezvous moods: + -- "happy": both sides close with mood=happy + -- "scary": any side closes with mood=scary (bad MAC, probably wrong pw) + -- "lonely": any side closes with mood=lonely (no response from 2nd side) + -- "errory": any side closes with mood=errory (other errors) + -- "pruney": channels which get pruned for inactivity + -- "crowded": three or more sides were involved +); +CREATE INDEX `mailbox_usage_idx` ON `mailbox_usage` (`app_id`, `started`); + +CREATE TABLE `transit_usage` +( + `started` INTEGER, -- seconds since epoch, rounded to "blur time" + `total_time` INTEGER, -- seconds from open to last close + `waiting_time` INTEGER, -- seconds from start to 2nd side appearing, or None + `total_bytes` INTEGER, -- total bytes relayed (both directions) + `result` VARCHAR -- happy, scary, lonely, errory, pruney + -- transit moods: + -- "errory": one side gave the wrong handshake + -- "lonely": good handshake, but the other side never showed up + -- "happy": both sides gave correct handshake +); +CREATE INDEX `transit_usage_idx` ON `transit_usage` (`started`); diff --git a/src/wormhole/server/rendezvous.py b/src/wormhole/server/rendezvous.py index 0f5ac82..3b964ff 100644 --- a/src/wormhole/server/rendezvous.py +++ b/src/wormhole/server/rendezvous.py @@ -191,9 +191,9 @@ class AppNamespace: def get_nameplate_ids(self): db = self._db # TODO: filter this to numeric ids? - c = db.execute("SELECT DISTINCT `id` FROM `nameplates`" + c = db.execute("SELECT DISTINCT `name` FROM `nameplates`" " WHERE `app_id`=?", (self._app_id,)) - return set([row["id"] for row in c.fetchall()]) + return set([row["name"] for row in c.fetchall()]) def _find_available_nameplate_id(self): claimed = self.get_nameplate_ids() @@ -230,7 +230,7 @@ class AppNamespace: assert isinstance(side, type("")), type(side) db = self._db row = db.execute("SELECT * FROM `nameplates`" - " WHERE `app_id`=? AND `id`=?", + " WHERE `app_id`=? AND `name`=?", (self._app_id, nameplate_id)).fetchone() if row: mailbox_id = row["mailbox_id"] @@ -238,14 +238,14 @@ class AppNamespace: sr = add_side(row, side) except CrowdedError: db.execute("UPDATE `nameplates` SET `crowded`=?" - " WHERE `app_id`=? AND `id`=?", + " 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 `id`=?", + " WHERE `app_id`=? AND `name`=?", (sr.side1, sr.side2, when, when, self._app_id, nameplate_id)) else: @@ -257,7 +257,7 @@ class AppNamespace: else: mailbox_id = generate_mailbox_id() db.execute("INSERT INTO `nameplates`" - " (`app_id`, `id`, `mailbox_id`, `side1`, `crowded`," + " (`app_id`, `name`, `mailbox_id`, `side1`, `crowded`," " `updated`, `started`)" " VALUES(?,?,?,?,?, ?,?)", (self._app_id, nameplate_id, mailbox_id, side, False, @@ -275,21 +275,21 @@ class AppNamespace: assert isinstance(side, type("")), type(side) db = self._db row = db.execute("SELECT * FROM `nameplates`" - " WHERE `app_id`=? AND `id`=?", + " WHERE `app_id`=? AND `name`=?", (self._app_id, nameplate_id)).fetchone() if not row: return sr = remove_side(row, side) if sr.empty: db.execute("DELETE FROM `nameplates`" - " WHERE `app_id`=? AND `id`=?", + " WHERE `app_id`=? AND `name`=?", (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 `id`=?", + " WHERE `app_id`=? AND `name`=?", (sr.side1, sr.side2, when, self._app_id, nameplate_id)) db.commit() @@ -475,7 +475,7 @@ class AppNamespace: for row in db.execute("SELECT * FROM `nameplates`" " WHERE `app_id`=?", (self._app_id,)).fetchall(): - nameplate_id = row["id"] + nameplate_id = row["name"] all_nameplate_rows[nameplate_id] = row if row["updated"] > old: which = NEW @@ -500,7 +500,7 @@ class AppNamespace: row = all_nameplate_rows[nameplate_id] self._summarize_nameplate_and_store(row, now, pruned=True) db.execute("DELETE FROM `nameplates`" - " WHERE `app_id`=? AND `id`=?", + " WHERE `app_id`=? AND `name`=?", (self._app_id, nameplate_id)) modified = True diff --git a/src/wormhole/test/test_server.py b/src/wormhole/test/test_server.py index 6a9066b..c62cf27 100644 --- a/src/wormhole/test/test_server.py +++ b/src/wormhole/test/test_server.py @@ -50,7 +50,7 @@ class Server(ServerBase, unittest.TestCase): def _nameplate(self, app, nameplate_id): return app._db.execute("SELECT * FROM `nameplates`" - " WHERE `app_id`='appid' AND `id`=?", + " WHERE `app_id`='appid' AND `name`=?", (nameplate_id,)).fetchone() def test_nameplate(self): @@ -355,7 +355,7 @@ class Prune(unittest.TestCase): rv.prune(now=123, old=50) - nameplates = set([row["id"] for row in + nameplates = set([row["name"] for row in db.execute("SELECT * FROM `nameplates`").fetchall()]) self.assertEqual(new_nameplates, nameplates) mailboxes = set([row["id"] for row in @@ -439,7 +439,7 @@ class Prune(unittest.TestCase): rv.prune(now=123, old=50) - nameplates = set([row["id"] for row in + nameplates = set([row["name"] for row in db.execute("SELECT * FROM `nameplates`").fetchall()]) self.assertEqual(nameplate_survives, bool(nameplates), ("nameplate", nameplate_survives, nameplates, desc)) @@ -725,7 +725,7 @@ class WebSocketAPI(ServerBase, unittest.TestCase): c1.send("claim", nameplate=nameplate_id) # allocate+claim is ok yield c1.sync() row = app._db.execute("SELECT * FROM `nameplates`" - " WHERE `app_id`='appid' AND `id`=?", + " WHERE `app_id`='appid' AND `name`=?", (nameplate_id,)).fetchone() self.assertEqual(row["side1"], "side") self.assertEqual(row["side2"], None) @@ -797,7 +797,7 @@ class WebSocketAPI(ServerBase, unittest.TestCase): self.assertEqual(m["type"], "released") row = app._db.execute("SELECT * FROM `nameplates`" - " WHERE `app_id`='appid' AND `id`='np1'").fetchone() + " WHERE `app_id`='appid' AND `name`='np1'").fetchone() self.assertEqual(row["side1"], "side2") self.assertEqual(row["side2"], None)