Skip to content
This repository has been archived by the owner on May 10, 2019. It is now read-only.

Commit

Permalink
database changes to handle connection failures, slow queries, and dri…
Browse files Browse the repository at this point in the history
…ver stalls without taking down the server, and with minimal user impact. closes #990 closes #1211
  • Loading branch information
lloyd committed Feb 28, 2012
2 parents fb72cc1 + b949816 commit 0c74c69
Show file tree
Hide file tree
Showing 32 changed files with 859 additions and 260 deletions.
6 changes: 4 additions & 2 deletions lib/browserid/fake_verification.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ const
configuration = require('../configuration.js'),
url = require('url'),
db = require('../db.js');
logger = require('../logging.js').logger;
logger = require('../logging.js').logger,
wsapi = require('../wsapi');

logger.warn("HEAR YE: Fake verfication enabled, aceess via /wsapi/fake_verification?email=foo@bar.com");
logger.warn("THIS IS NEVER OK IN A PRODUCTION ENVIRONMENT");

exports.addVerificationWSAPI = function(app) {
app.get('/wsapi/fake_verification', function(req, res) {
var email = url.parse(req.url, true).query['email'];
db.verificationSecretForEmail(email, function(secret) {
db.verificationSecretForEmail(email, function(err, secret) {
if (err) return wsapi.databaseDown(resp, err);
if (secret) res.write(secret);
else res.writeHead(400, {"Content-Type": "text/plain"});
res.end();
Expand Down
11 changes: 10 additions & 1 deletion lib/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,16 @@ var conf = module.exports = convict({
env: 'DATABASE_NAME'
},
password: 'string?',
host: 'string?'
host: 'string?',
max_query_time_ms: {
format: 'integer = 5000',
doc: "The maximum amount of time we'll allow a query to run before considering the database to be sick",
env: 'MAX_QUERY_TIME_MS'
},
max_reconnect_attempts: {
format: 'integer = 1',
doc: "The maximum number of times we'll attempt to reconnect to the database before failing all outstanding queries"
}
},
smtp: {
host: 'string?',
Expand Down
2 changes: 1 addition & 1 deletion lib/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ exports.open = function(cfg, cb) {
ready = true;
waiting.forEach(function(f) { f() });
waiting = [];
if (cb) cb();
if (cb) cb(null);
}
});
};
Expand Down
59 changes: 29 additions & 30 deletions lib/db/json.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,41 +81,40 @@ exports.open = function(cfg, cb) {
logger.debug("opening JSON database: " + dbPath);

sync();

setTimeout(cb, 0);
process.nextTick(function() { cb(null); });
};

exports.closeAndRemove = function(cb) {
// if the file cannot be removed, it's not an error, just means it was never
// written or deleted by a different process
try { fs.unlinkSync(dbPath); } catch(e) { }
setTimeout(function() { cb(undefined); }, 0);
process.nextTick(function() { cb(null); });
};

exports.close = function(cb) {
// don't flush database here to disk, the database is flushed synchronously when
// written - If we were to flush here we could overwrite changes made by
// another process - see issue #557
setTimeout(function() { cb(undefined) }, 0);
process.nextTick(function() { cb(null) });
};

exports.emailKnown = function(email, cb) {
sync();
var m = jsel.match(".emails ." + ESC(email), db.users);
setTimeout(function() { cb(m.length > 0) }, 0);
process.nextTick(function() { cb(null, m.length > 0) });
};

exports.emailType = function(email, cb) {
sync();
var m = jsel.match(".emails ." + ESC(email), db.users);
process.nextTick(function() { cb(m.length ? m[0].type : undefined); });
process.nextTick(function() { cb(null, m.length ? m[0].type : undefined); });
};

exports.isStaged = function(email, cb) {
if (cb) {
setTimeout(function() {
sync();
cb(db.stagedEmails.hasOwnProperty(email));
cb(null, db.stagedEmails.hasOwnProperty(email));
}, 0);
}
};
Expand All @@ -127,15 +126,15 @@ exports.lastStaged = function(email, cb) {
if (db.stagedEmails.hasOwnProperty(email)) {
d = new Date(db.staged[db.stagedEmails[email]].when);
}
setTimeout(function() { cb(d); }, 0);
setTimeout(function() { cb(null, d); }, 0);
}
};

exports.emailsBelongToSameAccount = function(lhs, rhs, cb) {
sync();
var m = jsel.match(".emails:has(."+ESC(lhs)+"):has(."+ESC(rhs)+")", db.users);
process.nextTick(function() {
cb(m && m.length == 1);
cb(null, m && m.length == 1);
});
};

Expand All @@ -145,15 +144,15 @@ exports.emailToUID = function(email, cb) {
if (m.length === 0) m = undefined;
else m = m[0];
process.nextTick(function() {
cb(m);
cb(null, m);
});
};

exports.userOwnsEmail = function(uid, email, cb) {
sync();
var m = jsel.match(":root > object:has(:root > .id:expr(x=" + ESC(uid) + ")):has(.emails > ." + ESC(email) + ")", db.users);
process.nextTick(function() {
cb(m && m.length == 1);
cb(null, m && m.length == 1);
});
};

Expand All @@ -172,7 +171,7 @@ function addEmailToAccount(userID, email, type, cb) {
emails[0][email] = { type: type };
flush();
}
cb();
cb(null);
});
}

Expand All @@ -187,7 +186,7 @@ exports.stageUser = function(email, cb) {
};
db.stagedEmails[email] = secret;
flush();
setTimeout(function() { cb(secret); }, 0);
process.nextTick(function() { cb(null, secret); });
});
};

Expand All @@ -204,7 +203,7 @@ exports.stageEmail = function(existing_user, new_email, cb) {
db.stagedEmails[new_email] = secret;
flush();

setTimeout(function() { cb(secret); }, 0);
process.nextTick(function() { cb(null, secret); });
});
};

Expand All @@ -219,14 +218,14 @@ exports.createUserWithPrimaryEmail = function(email, cb) {
});
flush();
process.nextTick(function() {
cb(undefined, uid);
cb(null, uid);
});
};

exports.haveVerificationSecret = function(secret, cb) {
process.nextTick(function() {
sync();
cb(!!(db.staged[secret]));
cb(null, !!(db.staged[secret]));
});
};

Expand All @@ -235,8 +234,8 @@ exports.emailForVerificationSecret = function(secret, cb) {
process.nextTick(function() {
sync();
if (!db.staged[secret]) return cb("no such secret");
exports.checkAuth(db.staged[secret].existing_user, function (hash) {
cb(undefined, {
exports.checkAuth(db.staged[secret].existing_user, function (err, hash) {
cb(err, {
email: db.staged[secret].email,
needs_password: !hash
});
Expand All @@ -247,7 +246,7 @@ exports.emailForVerificationSecret = function(secret, cb) {
exports.verificationSecretForEmail = function(email, cb) {
setTimeout(function() {
sync();
cb(db.stagedEmails[email]);
cb(null, db.stagedEmails[email]);
}, 0);
};

Expand All @@ -261,7 +260,7 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
delete db.stagedEmails[o.email];
flush();
if (o.type === 'add_account') {
exports.emailKnown(o.email, function(known) {
exports.emailKnown(o.email, function(err, known) {
function createAccount() {
var emailVal = {};
emailVal[o.email] = { type: 'secondary' };
Expand All @@ -272,7 +271,7 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
emails: emailVal
});
flush();
cb(undefined, o.email, uid);
cb(null, o.email, uid);
}

// if this email address is known and a user has completed a re-verification of this email
Expand All @@ -291,7 +290,7 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
}
});
} else if (o.type === 'add_email') {
exports.emailKnown(o.email, function(known) {
exports.emailKnown(o.email, function(err, known) {
function addIt() {
addEmailToAccount(o.existing_user, o.email, 'secondary', function(e) {
cb(e, o.email, o.existing_user);
Expand All @@ -313,7 +312,7 @@ exports.gotVerificationSecret = function(secret, hash, cb) {

exports.addPrimaryEmailToAccount = function(userID, emailToAdd, cb) {
sync();
exports.emailKnown(emailToAdd, function(known) {
exports.emailKnown(emailToAdd, function(err, known) {
function addIt() {
addEmailToAccount(userID, emailToAdd, 'primary', cb);
}
Expand All @@ -336,15 +335,15 @@ exports.checkAuth = function(userID, cb) {
if (m.length === 0) m = undefined;
else m = m[0];
}
process.nextTick(function() { cb(m) });
process.nextTick(function() { cb(null, m) });
};

exports.userKnown = function(userID, cb) {
sync();
var m = jsel.match(":root > object:has(:root > .id:expr(x=" + ESC(userID) + "))", db.users);
if (m.length === 0) m = undefined;
else m = m[0];
process.nextTick(function() { cb(m) });
process.nextTick(function() { cb(null, m) });
};

exports.updatePassword = function(userID, hash, cb) {
Expand Down Expand Up @@ -378,7 +377,7 @@ exports.removeEmail = function(authenticated_user, email, cb) {
delete emails[email];
flush();
}
setTimeout(function() { cb(); }, 0);
setTimeout(function() { cb(null); }, 0);
};

function removeEmailNoCheck(email, cb) {
Expand All @@ -389,7 +388,7 @@ function removeEmailNoCheck(email, cb) {
delete emails[email];
flush();
}
process.nextTick(function() { cb(); });
process.nextTick(function() { cb(null); });
};

exports.cancelAccount = function(authenticated_uid, cb) {
Expand All @@ -405,7 +404,7 @@ exports.cancelAccount = function(authenticated_uid, cb) {
flush();
}

process.nextTick(function() { cb(); });
process.nextTick(function() { cb(null); });
};

exports.addTestUser = function(email, hash, cb) {
Expand All @@ -419,10 +418,10 @@ exports.addTestUser = function(email, hash, cb) {
emails: emailVal
});
flush();
cb();
cb(null);
});
};

exports.ping = function(cb) {
setTimeout(function() { cb(); }, 0);
process.nextTick(function() { cb(null); });
};
Loading

0 comments on commit 0c74c69

Please sign in to comment.