diff --git a/lib/les.js b/lib/les.js index 7a41d479..ab3b6133 100644 --- a/lib/les.js +++ b/lib/les.js @@ -30,8 +30,8 @@ * Collects when returned value is 100 */ - //NOTE: set to false is running from file in YOUR code DEFUALT: false - var USELOCALGUN = false; + //NOTE: set to false to use require for getting gun DEFUALT: false + var USELOCALGUN = true; //NOTE: adds some debug messages DEFUALT: false @@ -41,11 +41,31 @@ console.log("NOTE: You currently have LES.js set to use the 'local' file version of gun, This might crash if set wrong!"); var Gun = (typeof window !== "undefined") ? window.Gun : (USELOCALGUN ? require('../gun') : require("gun")); - var ev = {}; - var empty = {}; - - Gun.on('opt', function(root) { + + //Removes a node from the garbage collection until next write + Gun.chain.gcDequeue = function() { + //console.log(this._.root.dequeueNode); + if(this._.root.dequeueNode) { // check that we actually have the dequeue command on this node + let ctx = this; + this.get(function (soul) { + ctx._.root.dequeueNode(soul); + }, true); + } + } + + //Puts node at the front for garbage collection, NOTE: only collects when it is hit it's time + Gun.chain.gcCollect = function() { + if(this._.root.collectNode) { // check that we actually have the dequeue command on this node + let ctx = this; + + this.get(function (soul) { + ctx._.root.collectNode(soul); + }, true); + } + } + + Gun.on('opt', function(root) { //Setup various options const gc_enable = root.opt.gc_enable ? root.opt.gc_enable : true; @@ -55,7 +75,7 @@ const gc_info = root.opt.gc_info ? root.opt.gc_info : 5000; const gc_info_mini = root.opt.gc_info_mini ? root.opt.gc_info_mini : false; - //This is kindof long but it works + //This is long, but it works well const calcRemoveImportance = root.opt.gc_importance_func ? root.opt.gc_importance_func : function (timestamp, ctime, memoryUsageRatio) { var time = (ctime - timestamp) * 0.001; return time * 10 * (memoryUsageRatio * memoryUsageRatio); @@ -64,6 +84,7 @@ if(DEBUG) console.log(root.opt); this.to.next(root); + if (root.once) return; if (typeof process == 'undefined') @@ -76,6 +97,9 @@ if (!mem) //exit because we are in the browser return; + var ev = {}; //stores the environment + var empty = {}; //An empty list used to prevent crashes + //Figure out the most amount of memory we can use. TODO: make configurable? ev.max = parseFloat(root.opt.memory || process.env.WEB_MEMORY || 512) * 0.8; @@ -83,36 +107,33 @@ var nodesArray = []; //used to easily sort everything and store info about the nodes var memoryUpdate = 0; // last time we printed the current memory stats + root.dequeueNode = (soul) => { //forward the call to our gc + dequeueNode(soul); + } + + root.collectNode = (soul) => { //forward the call to our gc + collectNode(soul); + } + var check = function() { ev.used = mem().rss / 1024 / 1024; //Contains the amt. of used ram in MB setTimeout(function() { // So we can handle requests etc. before we start collecting GC(ev.used / ev.max); // Calculate the memory ratio, and execute the garbage collector + //GC(0.99); }, 1); } - setInterval(check, gc_delay); // set the garbage collector to run every second, TODO: make configurable + setInterval(check, gc_delay); // set the garbage collector to run every second - //Executed every time a node gets modifyed + //Executed every time a node gets modified root.on("put", function(e) { var ctime = Date.now(); - var souls = Object.keys(e.put || empty); - for (var i = 0; i < souls.length; i++) { + var souls = Object.keys(e.put || empty); // get all of the nodes in the update + for (var i = 0; i < souls.length; i++) { // iterate over them and add them enqueueNode(souls[i], ctime); } }); - //Removes a node from the queue - function dequeueNode(soul) { - if (nodes[soul] == true) { //The node already exists in the queue - var index = nodesArray.findIndex(function(e) { - return e[0] === soul; - }); - if (index != -1) { - nodesArray.splice(index, 1); // remove the existing ref. - } - } - } - //Adds a soul the garbage collectors "freeing" queue function enqueueNode(soul, ctime) { if (nodes[soul] == true) { //The node already exists in the queue @@ -120,7 +141,7 @@ return e[0] === soul; }); if (index == -1) { - console.err("Something happened and the node '" + soul + "' won't get garbage collection unless the value is updated agian"); + console.error("Something happened and the node '" + soul + "' won't get garbage collection unless the value is updated again"); return; } else { nodesArray.splice(index, 1); // remove the existing ref. faster than dequeue @@ -132,21 +153,50 @@ } } + //Removes a node from the queue + function dequeueNode(soul) { + if (nodes[soul] == true) { //The node already exists in the queue + var index = nodesArray.findIndex(function(e) { + return e[0] === soul; + }); + if (index != -1) { + //nodesArray.splice(index, 1); // remove the existing ref. + nodesArray.shift(); + nodes[soul] = false; // store that we no longer have that node in the queue + } + } + } + + //Moves a node to the start of the queue + function collectNode(soul) { + if (nodes[soul] == true) { //The node already exists in the queue + var index = nodesArray.findIndex(function(e) { + return e[0] === soul; + }); + if (index != -1) { + //nodesArray.splice(index, 1); // remove the existing ref. + nodesArray.shift(); // WAY faster than splice + } + nodesArray.unshift([soul, nodesArray[0][1]]); // create a new node with the next nodes time stamp + nodes[soul] = true; // store that we no longer have that node in the queue + } + } + //The main garbage collecting routine function GC(memRatio) { var curTime = Date.now(); // get the current time - if (gc_info_enable && curTime - memoryUpdate >= gc_info) { + if (gc_info_enable && curTime - memoryUpdate >= gc_info) { // check if we need to print info if(!gc_info_mini) console.log("|GC| %s | Current Memory Ratio: %d | Current Ram Usage %sMB | Nodes in Memory %s", new Date().toLocaleString(), round(memRatio, 2), round(ev.used, 2), Object.keys(root.graph || empty).length); else - console.log("|GC| %s, Mem Ratio %d, Ram %sMB, Nodes in mem %s", new Date().toLocaleString(), round(memRatio, 2), round(ev.used, 2), Object.keys(root.graph || empty).length); - memoryUpdate = curTime; + console.log("|GC| %s, Mem Ratio %d, Ram %sMB, Nodes in mem %s, Tracked Nodes %s", new Date().toLocaleString(), round(memRatio, 2), round(ev.used, 2), Object.keys(root.graph || empty).length, nodesArray.length); + memoryUpdate = curTime; // reset the last update time } - var freed = 0; + var freed = 0; // Just a nice performance counter - while (nodesArray.length > 0) { + while (nodesArray.length > 0) { // iterate over all of our nodes var soul = nodesArray[0][0]; var nts = nodesArray[0][1]; if (DEBUG) @@ -155,21 +205,16 @@ if (calcRemoveImportance(nodesArray[0][1], curTime, memRatio) >= 100) { root.gun.get(nodesArray[0][0]).off(); //Remove the node delete nodes[nodesArray[0][0]]; // remove the lookup value - nodesArray.splice(0, 1); - freed++; + //nodesArray.splice(0, 1); + nodesArray.shift(); + freed++; // add one to our perf counter } else - break; + break; // Break out of the loop because we don't have any more nodes to free } if (freed > 0) console.log("|GC| Removed %s nodes in %s seconds-----------------------------------------------------------------", freed, (Date.now() - curTime) * 0.001); } - - //Generates a number that, after it hits a threshold, the node gets removed - //function calcRemoveImportance(timestamp, ctime, memoryUsageRatio) { - // var time = (ctime - timestamp) * 0.001; - // return time * 10 * (memoryUsageRatio * memoryUsageRatio) - //} - + function round(value, decimals) { //a basic rounding function return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals); }