Skip to content

Commit

Permalink
Version 0.1.57
Browse files Browse the repository at this point in the history
* Implemented cache memory limits, issue #60
* Fixed cluster strategy "specialization", issue #61
* Fixed error on live changing main impress config files: TypeError:
Cannot read property 'mixinClient' of undefined;
Object.application.preprocessConfig (impress.js:241:37);
  • Loading branch information
tshemsedinov committed Jul 17, 2014
1 parent f4d369a commit 7d83627
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 64 deletions.
7 changes: 7 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
0.1.57 / 2014-07-17
==================

* Implemented cache memory limits, issue #60
* Fixed cluster strategy "specialization", issue #61
* Fixed error on live changing main impress config files: TypeError: Cannot read property 'mixinClient' of undefined; Object.application.preprocessConfig (impress.js:241:37);

0.1.56 / 2014-07-15
==================

Expand Down
5 changes: 3 additions & 2 deletions examples/copyContentToProjectFolder/config/cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = {
// "sticky" - multiple processes, one master and workers with sticky by IP (master should listen ports)
//
workers: os.cpus().length-1, // worker count, e.g. os.cpus().length-1 or just number
nagle: false, // Nagle algorithm
gcInterval: 0 // garbage collector interval "1h" - 1 hour, "10m" - 10 minutes
nagle: false, // Nagle algorithm
gcInterval: 0, // garbage collector interval "1h" - 1 hour, "10m" - 10 minutes
// cacheLimit: "10mb" // cache memory limit
}
35 changes: 32 additions & 3 deletions lib/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,10 +304,39 @@ var initContext = function(context) {
// Size in bytes to Kb, Mb, Gb and Tb
//
context.bytesToSize = function(bytes) {
var sizes = ['', ' Kb', ' Mb', ' Gb', ' Tb'];
var sizes = ['', ' Kb', ' Mb', ' Gb', ' Tb', ' Pb', ' Eb', ' Zb', ' Yb'];
if (bytes === 0) return '0';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.round(bytes / Math.pow(1024, i), 2)+sizes[i];
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1000)));
return Math.round(bytes / Math.pow(1000, i), 2)+sizes[i];
};

context.sizeToBytes = function(size) {
if (typeof(size) === 'number') return size;
size = size.toUpperCase();
var units = {
yb: { rx:/(\d+)\s*YB/, pow:24 },
zb: { rx:/(\d+)\s*ZB/, pow:21 },
eb: { rx:/(\d+)\s*EB/, pow:18 },
pb: { rx:/(\d+)\s*PB/, pow:15 },
tb: { rx:/(\d+)\s*TB/, pow:12 },
gb: { rx:/(\d+)\s*GB/, pow:9 },
mb: { rx:/(\d+)\s*MB/, pow:6 },
kb: { rx:/(\d+)\s*KB/, pow:3 }
};
var result = 0, unit, match;
if (typeof(size) === 'string') {
var found = false;
for (var key in units) {
unit = units[key];
match = size.match(unit.rx);
if (match) {
result += parseInt(match[1])*Math.pow(10,unit.pow);
found = true;
}
}
if (!found) result = parseInt(size);
}
return result;
};

};
Expand Down
57 changes: 42 additions & 15 deletions lib/global.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,47 @@ impress.test({
} ],
],
"bytesToSize": [
[ 0, "0" ],
[ 1, "1" ],
[ 100, "100" ],
[ 1000, "1000" ],
[ 1023, "1023" ],
[ 1024, "1 Kb" ],
[ 1025, "1 Kb" ],
[ 1111, "1 Kb" ],
[ 2222, "2 Kb" ],
[ 10000, "10 Kb" ],
[ 1000000, "977 Kb" ],
[ 100000000, "95 Mb" ],
[ 10000000000, "9 Gb" ],
[ 1000000000000, "931 Gb" ],
[ 100000000000000, "91 Tb" ],
[ 0, "0" ],
[ 1, "1" ],
[ 100, "100" ],
[ 999, "999" ],
[ 1000, "1 Kb" ],
[ 1023, "1 Kb" ],
[ 1024, "1 Kb" ],
[ 1025, "1 Kb" ],
[ 1111, "1 Kb" ],
[ 2222, "2 Kb" ],
[ 10000, "10 Kb" ],
[ 1000000, "1 Mb" ],
[ 100000000, "100 Mb" ],
[ 10000000000, "10 Gb" ],
[ 1000000000000, "1 Tb" ],
[ 100000000000000, "100 Tb" ],
[ 10000000000000000, "10 Pb" ],
[ 1000000000000000000, "1 Eb" ],
[ 100000000000000000000, "100 Eb" ],
[ 10000000000000000000000, "10 Zb" ],
[ 1000000000000000000000000, "1 Yb" ],
],
"sizeToBytes": [
[ 0, 0 ],
[ "0", 0 ],
[ "1", 1 ],
[ 512, 512 ],
[ "100", 100 ],
[ "999", 999 ],
[ "1 Kb", 1000 ],
[ "2 Kb", 2000 ],
[ "10 Kb", 10000 ],
[ "1 Mb", 1000000 ],
[ "100 Mb", 100000000 ],
[ "10 Gb", 10000000000 ],
[ "1 Tb", 1000000000000 ],
[ "100 Tb", 100000000000000 ],
[ "10 Pb", 10000000000000000 ],
[ "1 Eb", 1000000000000000000 ],
[ "100 Eb", 100000000000000000000 ],
[ "10 Zb", 10000000000000000000000 ],
[ "1 Yb", 1000000000000000000000000 ],
],
});
6 changes: 4 additions & 2 deletions lib/impress.client.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Client.prototype.accessLog = function() {
this.schema+'://'+this.req.headers.host+this.url+'\t'+
this.req.headers['user-agent']
);
}
};

// Slow log client request
//
Expand All @@ -54,7 +54,7 @@ Client.prototype.slowLog = function() {
this.req.headers['user-agent']
);
}
}
};

// Fork long worker
//
Expand Down Expand Up @@ -117,6 +117,8 @@ Client.prototype.destroySession = function() {
Client.prototype.setCookie = function(name, value, host, httpOnly) {
var expires = new Date(2100,1,1).toUTCString();
host = host || this.req.headers.host;
var pos = host.indexOf(':');
if (pos>-1) host = host.substring(0, pos);
if (typeof(httpOnly) === 'undefined') httpOnly = true;
this.cookies.push(name+"="+value+"; expires="+expires+"; Path=/; Domain="+host+ (httpOnly ? "; HttpOnly" : ""));
};
Expand Down
97 changes: 56 additions & 41 deletions lib/impress.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,32 +227,39 @@ function mixinApplication(application, callback) {
else if (application.log) application.log.error('Configuration error: empty or wrong hosts.js');
if (config.files && config.files.static) config.files.staticRx = arrayRegExp(config.files.static);
if (config.application) config.application.slowTime = duration(config.application.slowTime || impress.defaultSlowTime);

// Prepare mixins
if (config.plugins) {
var pluginName, pluginPath, plugin, mixin;
for (var i = 0; i < config.plugins.length; ++i) {
pluginName = config.plugins[i];
plugin = impress.dataByPath(global, pluginName);
if (plugin && plugin !== impress) {
pluginPath = pluginName.split(".");
pluginName = pluginPath[pluginPath.length-1];
if (plugin.mixinApplication) plugin.mixinApplication(application);
mixin = application[pluginName].mixinClient;
if (mixin) application.clientMixins[pluginName] = mixin;
if (config.cluster) {
config.cluster.gc = duration(config.cluster.gc);
if (config.cluster.cacheLimit) config.cluster.cacheLimit = sizeToBytes(config.cluster.cacheLimit);
else config.cluster.cacheLimit = Infinity;
}
if (application !== impress) {

// Prepare mixins
if (config.plugins) {
var pluginName, pluginPath, plugin, mixin;
for (var i = 0; i < config.plugins.length; ++i) {
pluginName = config.plugins[i];
plugin = impress.dataByPath(global, pluginName);
if (plugin && plugin !== impress) {
pluginPath = pluginName.split(".");
pluginName = pluginPath[pluginPath.length-1];
if (plugin.mixinApplication) plugin.mixinApplication(application);
mixin = application[pluginName].mixinClient;
if (mixin) application.clientMixins[pluginName] = mixin;
}
}
}
}

// Prepare application routes
if (config.routes) {
var route, rx, routes = config.routes;
for (var i = 0; i < routes.length; ++i) {
route = routes[i];
if (route.escaping === false) rx = route.url;
else rx = '^'+route.url.replace(/(\/|\?|\.)/g, "\\$1").replace(/\(\\\.\*\)/, "(.*)")+'$';
route.urlRx = new RegExp(rx);
route.slowTime = duration(route.slowTime || impress.defaultSlowTime);
// Prepare application routes
if (config.routes) {
var route, rx, routes = config.routes;
for (var i = 0; i < routes.length; ++i) {
route = routes[i];
if (route.escaping === false) rx = route.url;
else rx = '^'+route.url.replace(/(\/|\?|\.)/g, "\\$1").replace(/\(\\\.\*\)/, "(.*)")+'$';
route.urlRx = new RegExp(rx);
route.slowTime = duration(route.slowTime || impress.defaultSlowTime);
}
}
}
};
Expand All @@ -263,7 +270,7 @@ function mixinApplication(application, callback) {
stack = err.stack.replace(rxPath, '').replace(/\n\s{4,}at/g, ';');
if (application.log && application.log.error) application.log.error(stack);
else console.log(stack);
}
};

// Create or clear application cache
//
Expand All @@ -283,7 +290,8 @@ function mixinApplication(application, callback) {
scripts: [], // compiled vm scripts
watchers: [], // directory watchers indexed by directory name
static: [], // static files cache
pages: [] // rendered pages cache
pages: [], // rendered pages cache
size: 0 // cache size
};
};
application.clearCache();
Expand Down Expand Up @@ -357,6 +365,7 @@ impress.server.start = function() {
impress.serverName = process.env['WORKER_SERVER_NAME'];
if (impress.cluster.isMaster) console.log('Impress Application Server'.bold.green+' starting, reading configuration'.green);
impress.loadConfig(function() {
impress.preprocessConfig();
loadPlugins();
if (impress.log) {
impress.log.init(impress);
Expand Down Expand Up @@ -405,11 +414,10 @@ impress.server.start = function() {
if (global.cms) cms.init();
}
// Set garbage collection interval
var gcInterval = duration(impress.config.cluster.gc);
if (typeof(global.gc) === 'function' && gcInterval > 0) {
if (typeof(global.gc) === 'function' && impress.config.cluster.gc > 0) {
setInterval(function() {
global.gc();
}, gcInterval*1000);
}, impress.config.cluster.gc);
}
isFirstStart = false;
}
Expand Down Expand Up @@ -737,24 +745,31 @@ impress.compress = function(filePath, stats, application, client, httpCode) {
if (!inArray(impress.compressedExt, ext) && stats.size>impress.compressAbove) {
impress.zlib.gzip(data, function(err, data) {
stats.size = data.length;
if (client) {
client.res.writeHead(httpCode, impress.baseHeader(ext, stats, true));
client.end(data);
}
application.cache.static[filePath] = { data:data, stats:stats, compressed:true };
impress.compressSend(filePath, stats, application, client, httpCode, ext, true, data);
});
} else {
if (client) {
client.res.writeHead(httpCode, impress.baseHeader(ext, stats));
client.end(data);
}
application.cache.static[filePath] = { data:data, stats:stats, compressed:false };
}
} else impress.compressSend(filePath, stats, application, client, httpCode, ext, false, data);
impress.watchCache(application, filePath);
}
});
};

impress.compressSend = function(filePath, stats, application, client, httpCode, ext, compressed, data) {
if (client) {
client.res.writeHead(httpCode, impress.baseHeader(ext, stats, compressed));
client.end(data);
}
application.cache.static[filePath] = { data:data, stats:stats, compressed:compressed };
application.cache.size += data.length;
if (application.cache.size > impress.config.cluster.cacheLimit) {
for (var name in application.cache.static) {
console.log('remove: '+name);
application.cache.size -= application.cache.static[name].data.length;
delete application.cache.static[name];
if (application.cache.size < impress.config.cluster.cacheLimit) return;
}
}
};

// Send HTTP headers
//
impress.baseHeader = function(ext, stats, compressed) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "impress",
"version": "0.1.56",
"version": "0.1.57",
"author": "Timur Shemsedinov <timur.shemsedinov@gmail.com>",
"description": "Impressive Totalitarian-style Multipurpose Application Server for node.js. All decisions are made. Ready for applied development",
"keywords": [
Expand Down

0 comments on commit 7d83627

Please sign in to comment.