Permalink
Browse files

Backup data control is working

  • Loading branch information...
1 parent 6fbac8d commit eb2c6dac353fd7622a2cf5582f4c35440abc572a @3rd-Eden 3rd-Eden committed Sep 16, 2011
Showing with 123 additions and 17 deletions.
  1. +1 −0 .gitignore
  2. +19 −0 LICENSE
  3. +6 −0 index.js
  4. +73 −16 lib/kju.js
  5. +8 −1 tests/kju.test.js
  6. +16 −0 tests/simulate.death.js
View
@@ -1 +1,2 @@
node_modules
+*.kju
View
@@ -0,0 +1,19 @@
+Copyright (c) 2011 Observer (http://observer.no.de) <info@3rd-Eden.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
View
@@ -1,3 +1,9 @@
+/**!
+ * kju
+ * @copyright (c) 2011 Observer (observer.no.de) <info@3rd-Eden.com>
+ * MIT Licensed
+ */
+
/**
* Require the core module
*/
View
@@ -1,3 +1,9 @@
+/**!
+ * kju
+ * @copyright (c) 2011 Observer (observer.no.de) <info@3rd-Eden.com>
+ * MIT Licensed
+ */
+
/**
* Module dependencies
*/
@@ -44,8 +50,8 @@ function kju (configuration) {
this.enabled = true;
this.recover = true;
this.dump = true;
- this.path = __dirname + '/';
- this.name = 'kju.{sequence}.kju'
+ this.path = process.cwd() + '/';
+ this.name = 'node_kju_backup.{sequence}.kju';
// apply the configuration
for (var option in configuration)
@@ -70,7 +76,13 @@ function kju (configuration) {
// make sure our backup path exists
path.exists(this.path, function exists (exists) {
- if (!exists) self.emit('error', new Error(self.path + ' does not exist.'));
+ if (!exists) {
+ return self.emit('error', new Error(self.path + ' does not exist.'));
+ }
+
+ if (self.path[self.path.length - 1] !== '/') {
+ return self.emit('error', new Error(self.path + ' should end with a slash'));
+ }
});
};
@@ -188,6 +200,8 @@ kju.prototype.update = function update () {
// don't reinitialize the timeout, we are probably disabled
if (!this.timeout) return this;
+ var self = this;
+
if (this.timeout) {
clearTimeout(this.timeout);
delete this.timeout;
@@ -202,7 +216,9 @@ kju.prototype.update = function update () {
// did we reach our maximum interval?
if (this.warnings && this.interval >= this.maximum) {
- this.emit('maximum interval.warning');
+ process.nextTick(function maximum () {
+ self.emit('maximum interval.warning');
+ });
}
} else {
// check if we are reaching our minimum level here
@@ -213,7 +229,9 @@ kju.prototype.update = function update () {
// did we reach our minimum interval?
if (this.warnings && this.interval <= this.minimum) {
- this.emit('minimum interval.warning');
+ process.nextTick(function minimum () {
+ self.emit('minimum interval.warning');
+ })
}
}
}
@@ -248,25 +266,48 @@ kju.prototype.metrics = function metrics () {
/**
* Backup the current internal queue, please note that we need to use sync
- * methods here because event loop has already been stopped.
+ * methods here because event loop has already been stopped. We could wrap all
+ * methods in try catch blocks because we use a sync version, but as this
+ * method is only called on exit, it doesn't really matter as the process is
+ * going to die anyways.
*
* @api private
*/
kju.prototype.backup = function backup () {
if (!this.recover || !this.length || !path.existsSync(this.path)) return;
- var data = JSON.stringify(this.buffer)
- , files = fs.readdirSync(this.path).filter(function (file) {
- return /\.kju$/.test(file);
- });
+ var filename = /(\d+)\.kju$/
+ , data = JSON.stringify(this.buffer)
+ , sequence = 0;
+
+ // read the files in the set path, see if we can find old kju back up files
+ // and parse our the {sequence} number so we clash
+ var files = [];
+
+ fs.readdirSync(this.path)
+ .filter(function map (file) { return filename.test(file) })
+ .map(function map (file) { return +filename.exec(file)[1] })
+ .sort(function sort (a, b) { return a - b })
+ .forEach(function forEach (file) {
+ if (files.indexOf(file) === -1) files.push(file)
+ })
+
+ // determin sequence number
+ if (files.length) {
+ sequence = ++files[files.length - 1];
+ }
+
+ // write to file
+ fs.writeFileSync(this.path + this.name.replace('{sequence}', sequence), data);
// do we need to output the dump of data to the stderr?
if (this.dump) {
console.error('-- begin kju backup output');
console.error(data);
console.error('-- end kju backup output');
}
+
};
/**
@@ -290,10 +331,10 @@ kju.prototype.recovery = function recover () {
*/
function filter (err, files) {
- if (err) return self.emit('error.backup', err);
+ if (err) return self.emit('error', err);
var kjud = files.filter(function (file) {
- return /\.kju$/.test(file);
+ return /(\d)\.kju$/.test(file);
});
// if we don't have any files we don't really need to emit 'recovered' so
@@ -314,18 +355,20 @@ kju.prototype.recovery = function recover () {
function read (file, index, files) {
fs.readFile(self.path + file, function readFile (err, contents) {
- if (err) return done(), self.emit('error.recover', err);
+ if (err) return done(), self.emit('error', err);
// try to parse the file as JSON, if that doesn't work we have big issue
// because the file is damaged.
try {
var todo = JSON.parse(contents.toString('utf8'));
- // pfew, it worked re-add it to kju and remove the file
+ // pfew, it worked re-add it to kju and remove the file, I don't really
+ // care about the clean up process. So I'm not gonna handle the errors
+ // that could occure there.
self.push.apply(self, todo);
+ fs.unlink(self.path + file, function () {});
} catch (e) {
- self.emit(
- 'error.recover'
+ self.emit('error'
, new Error('corrupted file, unable to parse contents of ' + file)
);
}
@@ -334,6 +377,16 @@ kju.prototype.recovery = function recover () {
});
}
+ /**
+ * Simple way to keep track of async events
+ *
+ * @api private
+ */
+
+ function done () {
+ if (--processed) self.emit('recovered');
+ }
+
// check if the path exists and if there are files to filter
path.exists(this.path, function existsPath (exists) {
if (!exists) return;
@@ -351,4 +404,8 @@ kju.prototype.recovery = function recover () {
kju.version = '0.0.1';
+/**
+ * Expose the API
+ */
+
module.exports = kju;
View
@@ -1,5 +1,12 @@
+/**!
+ * kju
+ * @copyright (c) 2011 Observer (observer.no.de) <info@3rd-Eden.com>
+ * MIT Licensed
+ */
+
var kju = require('../index')
- , should = require('should');
+ , should = require('should')
+ , spawn = require('child_process').spwan;
// make sure we have proper stack traces for when things fail
require('long-stack-traces');
@@ -0,0 +1,16 @@
+/**!
+ * kju
+ * @copyright (c) 2011 Observer (observer.no.de) <info@3rd-Eden.com>
+ * MIT Licensed
+ */
+
+var kju = new (require('../index'));
+
+// add some items to the kju so we need to
+// store them once process is existing.
+kju.push(1, 2, 'three', 4, 10);
+
+// kill the processes
+setTimeout(function () {
+ process.exit();
+}, 100);

0 comments on commit eb2c6da

Please sign in to comment.