Skip to content
This repository has been archived by the owner on Nov 7, 2020. It is now read-only.

add framework for multiple data driver support. #438

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions INSTALLATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ Create a file and copy the contents of conf.json.example file in it. Name this f
- ``UNHANOGUT_REDIS_DB`` is an index number pointing to the redis database to
use. By default, we use 0 for production. The unit tests use 1, and will
destroy data there.
- ``UNHANOGUT_DB_DRIVER`` Which database driver to use for storing Unhangout
data. Currently supported drivers are 'redis' and 'couchdb'. Using the
couchdb driver requires installing [CouchDB](http://couchdb.apache.org)
and configuring the UNHANGOUT_COUCHDB_* settings. Note that session data
will always be stored in redis regardless of the driver used.
- ``UNHANGOUT_HANGOUT_APP_ID`` The default is the Google app ID for our
hangout gadget -- if you're rolling your own installation, you'll need to
create your own Google Hangout app and provide the ID here -- see the
Expand Down
6 changes: 6 additions & 0 deletions conf.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
"UNHANGOUT_REDIS_PORT":6379,
"UNHANGOUT_REDIS_DB":0,

"UNHANGOUT_COUCHDB_HOST": "localhost",
"UNHANGOUT_COUCHDB_PORT": 5984,
"UNHANGOUT_COUCHDB_DATABASE": "unhangout",

"UNHANGOUT_DB_DRIVER": "redis",

"UNHANGOUT_SMTP": {
"host": "localhost",
"port": "2525",
Expand Down
167 changes: 167 additions & 0 deletions lib/db-driver/couchdb-sync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
var async = require('async'),
Backbone = require('backbone');

// this module defines how backbone models should synchronize themselves.
// backbone provides a basic sync infrastructure, but it is designed
// to operate over http. In our case, we're interested only in syncing
// to a couchdb database that exists on the server. We also don't need
// to support all the different sync verbs (create/read/update/delete)
// just create and update.


// these must be set in init!
var couchdb = null;
var logger = null;
var syncMutex = {};

exports.init = function(l, r) {
couchdb = r;
logger = l;
logger.info("couchdb-sync initialized");
}

// overriding sync per:
// http://documentcloud.github.com/backbone/#Sync
exports.sync = function(method, model, options) {
if(!couchdb || !logger) {
return;
}

var callback = function(err, doc, key) {
// For update and delete functions, this is set to prevent update
// conflicts -- unset here after the server response to unlock the
// document.
if (syncMutex[key]) {
logger.debug("Releasing lock on doc: " + key);
delete syncMutex[key];
}
if (err) {
logger.error("couchdb error: " + err);
options.error && options.error();
}
else {
if (doc) {
model.set('_rev', doc.rev);
}
options.success && options.success();
}
}

/**
* Returns a random integer between min (inclusive) and max (inclusive)
*/
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}

// method – the CRUD method ("create", "read", "update", or "delete")
// model – the model to be saved (or collection to be read)
// options – success and error callbacks, and all other jQuery request options
switch(method) {
case "create":
// if the model doesn't have an id yet, get one by looking at couchdb
// for the next id. Each model type (eg session, event) has its own
// id counter.
if(model.id == null) {
var counter = 'global:' + model.idRoot + '.next.id';
var getNextId = function() {
var saveModel = function(err, count) {
if (err) {
setTimeout(getNextId, getRandomInt(50, 150));
}
else {
model.set("id", count);
couchdb.save(model.url(), model.toJSON(), function (err, doc) {
callback(err, doc, model.url());
});
}
}
couchdb.get(counter, function (err, doc) {
if (err) {
// No counter for this model yet created.
var count = 1;
couchdb.save(counter, {count: count}, function (err) {
saveModel(err, count);
});
}
else {
var count = doc.count + 1;
couchdb.save(counter, doc._rev, {count: count}, function (err) {
saveModel(err, count);
});
}
});
}
getNextId();
} else {
couchdb.save(model.url(), model.toJSON(), function (err, doc) {
callback(err, doc, model.url());
});
}
break;
case "update":
var key = model.url();
var updateFunc = function() {
if (syncMutex[key]) {
logger.warn("Update attempt on locked doc id: " + key);
setTimeout(updateFunc, 100);
}
else {
// Lock the document to prevent update conflicts.
logger.debug("Locking doc for update: " + key);
syncMutex[key] = true;
var rev = model.get('_rev');
if (rev) {
couchdb.save(key, rev, model.toJSON(), function (err, doc) {
callback(err, doc, key);
});
}
else {
couchdb.save(key, model.toJSON(), function (err, doc) {
callback(err, doc, key);
});
}
}
}
updateFunc();
break;
case "read":
logger.error("Fetch is not supported with this sync function.");
break;
case "delete":
var key = model.url();
var deleteFunc = function() {
if (syncMutex[key]) {
logger.warn("Delete attempt on locked doc id: " + key);
setTimeout(deleteFunc, 100);
}
else {
// Lock the document to prevent update conflicts.
logger.debug("Locking doc for deletion: " + key);
syncMutex[key] = true;
couchdb.remove(key, model.get('_rev'), function (err, res) {
callback(err, null, key);
});
}
}
deleteFunc();
break;
}
}

// We use the dummySync method as something we can override the built
// in sync method with to do nothing. This is useful when you want to test
// the server with persistence OFF, which most of our tests want to do for
// simplicity.
exports.dummySync = function(method, model, options) {
return;
}

exports.setPersist = function(persist) {
if(persist) {
Backbone.sync = exports.sync;
} else {
Backbone.sync = exports.dummySync;
}
}

Loading