Externalized persistence of the cached db and of new state

pull/2/head
Louis Chatriot 12 years ago
parent 395e3e5965
commit 4ae6b55816
  1. 67
      lib/datastore.js
  2. 72
      lib/persistence.js

@ -7,6 +7,7 @@ var fs = require('fs')
, Index = require('./indexes') , Index = require('./indexes')
, util = require('util') , util = require('util')
, _ = require('underscore') , _ = require('underscore')
, Persistence = require('./persistence')
; ;
@ -46,6 +47,9 @@ function Datastore (options) {
this.filename = customUtils.getNWAppFilename(options.nodeWebkitAppName, this.filename); this.filename = customUtils.getNWAppFilename(options.nodeWebkitAppName, this.filename);
} }
// Persistence handling
this.persistence = new Persistence({ db: this });
// This new executor is ready if we don't use persistence // This new executor is ready if we don't use persistence
// If we do, it will only be ready once loadDatabase is called // If we do, it will only be ready once loadDatabase is called
this.executor = new Executor(); this.executor = new Executor();
@ -278,7 +282,7 @@ Datastore.prototype._loadDatabase = function (cb) {
} }
self.datafileSize = treatedData.length; self.datafileSize = treatedData.length;
self.persistCachedDatabase(cb); self.persistence.persistCachedDatabase(cb);
}); });
}); });
}); });
@ -330,61 +334,7 @@ Datastore.treatRawData = function (rawData) {
}; };
/**
* Persist cached database
* This serves as a compaction function since the cache always contains only the number of documents in the collection
* while the data file is append-only so it may grow larger
* @param {Function} cb Optional callback, signature: err
*/
Datastore.prototype.persistCachedDatabase = function (cb) {
var callback = cb || function () {}
, toPersist = ''
;
this.getAllData().forEach(function (doc) {
toPersist += model.serialize(doc) + '\n';
});
if (toPersist.length === 0) { return callback(null); }
fs.writeFile(this.filename, toPersist, function (err) { return callback(err); });
};
/**
* Persist new state for the given newDocs (can be insertion, update or removal)
* Use an append-only format
* @param {Array} newDocs Can be empty if no doc was updated/removed
* @param {Function} cb Optional, signature: err
*/
Datastore.prototype._persistNewState = function (newDocs, cb) {
var self = this
, toPersist = ''
, callback = cb || function () {}
;
// In-memory only datastore
if (self.inMemoryOnly) { return callback(null); }
self.datafileSize += newDocs.length;
newDocs.forEach(function (doc) {
toPersist += model.serialize(doc) + '\n';
});
if (toPersist.length === 0) { return callback(null); }
fs.appendFile(self.filename, toPersist, 'utf8', function (err) {
return callback(err);
});
};
Datastore.prototype.persistNewState = function (newDocs, cb) {
if (this.inMemoryOnly) {
cb();
} else {
this._persistNewState(newDocs, cb);
}
};
/** /**
@ -411,7 +361,7 @@ Datastore.prototype._insert = function (newDoc, cb) {
// Insert in all indexes (also serves to ensure uniqueness) // Insert in all indexes (also serves to ensure uniqueness)
try { self.addToIndexes(insertedDoc); } catch (e) { return callback(e); } try { self.addToIndexes(insertedDoc); } catch (e) { return callback(e); }
this.persistNewState([newDoc], function (err) { this.persistence.persistNewState([newDoc], function (err) {
if (err) { return callback(err); } if (err) { return callback(err); }
return callback(null, newDoc); return callback(null, newDoc);
}); });
@ -546,7 +496,7 @@ Datastore.prototype._update = function (query, updateQuery, options, cb) {
return callback(err); return callback(err);
} }
self.persistNewState(updatedDocs, function (err) { self.persistence.persistNewState(updatedDocs, function (err) {
if (err) { return callback(err); } if (err) { return callback(err); }
return callback(null, numReplaced); return callback(null, numReplaced);
}); });
@ -591,7 +541,7 @@ Datastore.prototype._remove = function (query, options, cb) {
}); });
} catch (err) { return callback(err); } } catch (err) { return callback(err); }
self.persistNewState(removedDocs, function (err) { self.persistence.persistNewState(removedDocs, function (err) {
if (err) { return callback(err); } if (err) { return callback(err); }
return callback(null, numRemoved); return callback(null, numRemoved);
}); });
@ -602,5 +552,4 @@ Datastore.prototype.remove = function () {
module.exports = Datastore; module.exports = Datastore;

@ -0,0 +1,72 @@
/**
* Handle every persistence-related task
*/
var fs = require('fs')
, path = require('path')
, model = require('./model')
, customUtils = require('./customUtils')
;
/**
* Create a new Persistence object for database options.db
* @param {Datastore} options.db
*/
function Persistence (options) {
this.db = options.db;
}
/**
* Persist cached database
* This serves as a compaction function since the cache always contains only the number of documents in the collection
* while the data file is append-only so it may grow larger
* @param {Function} cb Optional callback, signature: err
*/
Persistence.prototype.persistCachedDatabase = function (cb) {
var callback = cb || function () {}
, toPersist = ''
;
this.db.getAllData().forEach(function (doc) {
toPersist += model.serialize(doc) + '\n';
});
if (toPersist.length === 0) { return callback(null); }
fs.writeFile(this.db.filename, toPersist, function (err) { return callback(err); });
};
/**
* Persist new state for the given newDocs (can be insertion, update or removal)
* Use an append-only format
* @param {Array} newDocs Can be empty if no doc was updated/removed
* @param {Function} cb Optional, signature: err
*/
Persistence.prototype.persistNewState = function (newDocs, cb) {
var self = this
, toPersist = ''
, callback = cb || function () {}
;
// In-memory only datastore
if (self.db.inMemoryOnly) { return callback(null); }
self.db.datafileSize += newDocs.length;
newDocs.forEach(function (doc) {
toPersist += model.serialize(doc) + '\n';
});
if (toPersist.length === 0) { return callback(null); }
fs.appendFile(self.db.filename, toPersist, 'utf8', function (err) {
return callback(err);
});
};
// Interface
module.exports = Persistence;
Loading…
Cancel
Save