Skip to content
This repository was archived by the owner on Apr 3, 2019. It is now read-only.

Commit 4b6a92d

Browse files
authored
fix(reminders): adjust mysql procedures (#200) r=rfk
* fix(reminders): optimize mysql procedures for verification reminders Fixes #200
1 parent 0e83db9 commit 4b6a92d

File tree

5 files changed

+178
-5
lines changed

5 files changed

+178
-5
lines changed

fxa-auth-db-server/test/backend/db_tests.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,6 +1779,76 @@ module.exports = function(config, DB) {
17791779
}
17801780
)
17811781

1782+
test(
1783+
'reminders - multi fetch',
1784+
function (t) {
1785+
t.plan(1)
1786+
var fetchQuery = {
1787+
type: 'first',
1788+
reminderTime: 1,
1789+
reminderTimeOutdated: 100,
1790+
limit: 20
1791+
}
1792+
var account = createAccount()
1793+
var account2 = createAccount()
1794+
return db.createAccount(account.uid, account)
1795+
.then(
1796+
function () {
1797+
return db.createAccount(account2.uid, account2)
1798+
}
1799+
)
1800+
.then(
1801+
function () {
1802+
// create 'first' reminder for account one.
1803+
return db.createVerificationReminder({
1804+
uid: account.uid,
1805+
type: 'first'
1806+
})
1807+
}
1808+
)
1809+
.then(
1810+
function () {
1811+
// create 'first' reminder for account two.
1812+
return db.createVerificationReminder({
1813+
uid: account2.uid,
1814+
type: 'first'
1815+
})
1816+
}
1817+
)
1818+
.then(
1819+
function () {
1820+
return P.delay(20).then(function () {
1821+
return P.all([
1822+
// only one query should give results
1823+
db.fetchReminders({}, fetchQuery),
1824+
db.fetchReminders({}, fetchQuery),
1825+
db.fetchReminders({}, fetchQuery),
1826+
db.fetchReminders({}, fetchQuery),
1827+
db.fetchReminders({}, fetchQuery),
1828+
db.fetchReminders({}, fetchQuery),
1829+
db.fetchReminders({}, fetchQuery),
1830+
db.fetchReminders({}, fetchQuery),
1831+
db.fetchReminders({}, fetchQuery),
1832+
db.fetchReminders({}, fetchQuery)
1833+
])
1834+
})
1835+
}
1836+
)
1837+
.then(
1838+
function (results) {
1839+
var found = 0
1840+
results.forEach((result) => {
1841+
if (result.length === 2) {
1842+
found++
1843+
}
1844+
})
1845+
1846+
t.equal(found, 1, 'only one query has the result')
1847+
}
1848+
)
1849+
}
1850+
)
1851+
17821852
test(
17831853
'unblockCodes',
17841854
function (t) {

lib/db/mysql.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -573,37 +573,38 @@ module.exports = function (log, error) {
573573
// VERIFICATION REMINDERS
574574

575575
// INSERT : id, uid, email, emailCode, type, acceptLanguage, createdAt
576-
var CREATE_REMINDER = 'CALL createVerificationReminder_1(?, ?, ?, ?)'
576+
var CREATE_REMINDER = 'CALL createVerificationReminder_2(?, ?, ?)'
577577

578578
MySql.prototype.createVerificationReminder = function (body) {
579579
if (! body || ! body.uid || ! body.type) {
580580
throw error.wrap(new Error('"uid", "type" are required'))
581581
}
582582

583583
var reminderData = {
584-
id: crypto.createHash('sha256').update(body.uid + body.type).digest(),
585584
uid: new Buffer(body.uid),
586585
type: body.type,
587586
createdAt: Date.now()
588587
}
589588

590589
return this.write(CREATE_REMINDER, [
591-
reminderData.id,
592590
reminderData.uid,
593591
reminderData.type,
594592
reminderData.createdAt
595593
])
596594
}
597595

598596
// SELECT:
599-
var FETCH_REMINDERS = 'CALL fetchVerificationReminders_1(?, ?, ?, ?)'
597+
var FETCH_REMINDERS = 'CALL fetchVerificationReminders_2(?, ?, ?, ?, ?)'
600598

601599
MySql.prototype.fetchReminders = function (body, query) {
600+
var now = Date.now()
601+
602602
if (! query || ! query.reminderTime || ! query.reminderTimeOutdated || ! query.type || ! query.limit) {
603603
throw error.wrap(new Error('fetchReminders - reminderTime, reminderTimeOutdated, limit or type missing'))
604604
}
605605

606606
return this.read(FETCH_REMINDERS, [
607+
now,
607608
query.type,
608609
query.reminderTime,
609610
query.reminderTimeOutdated,

lib/db/patch.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
// The expected patch level of the database. Update if you add a new
66
// patch in the ./schema/ directory.
7-
module.exports.level = 39
7+
module.exports.level = 40

lib/db/schema/patch-039-040.sql

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
-- Drop the old reminders table, it was not functioning correctly
2+
DROP TABLE IF EXISTS `verificationReminders`;
3+
4+
5+
CREATE TABLE verificationReminders (
6+
uid BINARY(16) NOT NULL,
7+
type VARCHAR(255) NOT NULL,
8+
createdAt BIGINT SIGNED NOT NULL,
9+
PRIMARY KEY(uid, type),
10+
INDEX reminder_createdAt (createdAt)
11+
) ENGINE=InnoDB;
12+
13+
14+
CREATE PROCEDURE `createVerificationReminder_2` (
15+
IN uid BINARY(16),
16+
IN type VARCHAR(255),
17+
IN createdAt BIGINT SIGNED
18+
)
19+
BEGIN
20+
INSERT INTO verificationReminders(
21+
uid,
22+
type,
23+
createdAt
24+
)
25+
VALUES(
26+
uid,
27+
type,
28+
createdAt
29+
);
30+
END;
31+
32+
33+
CREATE PROCEDURE `fetchVerificationReminders_2` (
34+
IN currentTime BIGINT SIGNED,
35+
IN reminderType VARCHAR(255),
36+
IN reminderTime BIGINT SIGNED,
37+
IN reminderTimeOutdated BIGINT SIGNED,
38+
IN reminderLimit INTEGER
39+
)
40+
BEGIN
41+
DECLARE EXIT HANDLER FOR SQLEXCEPTION
42+
BEGIN
43+
DO RELEASE_LOCK('fxa-auth-server.verification-reminders-lock');
44+
ROLLBACK;
45+
RESIGNAL;
46+
END;
47+
48+
START TRANSACTION;
49+
50+
DO @lockAcquired:=GET_LOCK('fxa-auth-server.verification-reminders-lock', 1);
51+
52+
IF @lockAcquired THEN
53+
DROP TEMPORARY TABLE IF EXISTS reminderResults;
54+
55+
-- Select these straight into the temporary table
56+
-- and avoid using a cursor. Use ORDER BY to
57+
-- ensure the query is deterministic.
58+
CREATE TEMPORARY TABLE reminderResults AS
59+
SELECT uid, type
60+
FROM verificationReminders
61+
-- Since we want to order by `createdAt`, we have to rearrange
62+
-- the `WHERE` clauses so `createdAt` is positive rather than negated.
63+
WHERE createdAt < currentTime - reminderTime
64+
AND createdAt > currentTime - reminderTimeOutdated
65+
AND type = reminderType
66+
ORDER BY createdAt, uid, type
67+
LIMIT reminderLimit;
68+
69+
-- Because the query is deterministic we can delete
70+
-- all the selected items at once, rather than calling
71+
-- deleteVerificationReminder()
72+
DELETE FROM verificationReminders
73+
WHERE createdAt < currentTime - reminderTime
74+
AND createdAt > currentTime - reminderTimeOutdated
75+
AND type = reminderType
76+
ORDER BY createdAt, uid, type
77+
LIMIT reminderLimit;
78+
79+
-- Clean up outdated reminders.
80+
DELETE FROM
81+
verificationReminders
82+
WHERE
83+
createdAt < currentTime - reminderTimeOutdated
84+
AND
85+
type = reminderType;
86+
87+
-- Return the result
88+
SELECT * FROM reminderResults;
89+
90+
DO RELEASE_LOCK('fxa-auth-server.verification-reminders-lock');
91+
92+
END IF;
93+
COMMIT;
94+
END;
95+
96+
97+
UPDATE dbMetadata SET value = '40' WHERE name = 'schema-patch-level';

lib/db/schema/patch-040-039.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- ALTER TABLE `verificationReminders` DROP PRIMARY KEY, ADD PRIMARY KEY (`id`);
2+
-- DROP PROCEDURE `fetchVerificationReminders_2`;
3+
-- DROP PROCEDURE `createVerificationReminder_2`;
4+
5+
-- UPDATE dbMetadata SET value = '39' WHERE name = 'schema-patch-level';

0 commit comments

Comments
 (0)