|  | @@ -0,0 +1,121 @@
 | 
											
												
													
														|  | 
 |  | +;
 | 
											
												
													
														|  | 
 |  | +(function() {
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +	//  _     _____ ____    _     
 | 
											
												
													
														|  | 
 |  | +	// | |   | ____/ ___|  (_)___ 
 | 
											
												
													
														|  | 
 |  | +	// | |   |  _| \___ \  | / __|
 | 
											
												
													
														|  | 
 |  | +	// | |___| |___ ___) | | \__ \
 | 
											
												
													
														|  | 
 |  | +	// |_____|_____|____(_)/ |___/
 | 
											
												
													
														|  | 
 |  | +	// ----------------------------
 | 
											
												
													
														|  | 
 |  | +	// LES.js (Last rEcently uSed)
 | 
											
												
													
														|  | 
 |  | +	// ----------------------------
 | 
											
												
													
														|  | 
 |  | +	// A Small, lightweight, queue-based
 | 
											
												
													
														|  | 
 |  | +	// Garbage Collector for Gun
 | 
											
												
													
														|  | 
 |  | +	// Originally By: Collin Conrad (@masterex1000)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +	//NOTE: set to false is running from file in YOUR code
 | 
											
												
													
														|  | 
 |  | +	var USELOCALGUN = true; 
 | 
											
												
													
														|  | 
 |  | +	
 | 
											
												
													
														|  | 
 |  | +	//NOTE: adds some debug messages
 | 
											
												
													
														|  | 
 |  | +	var DEBUG = false;
 | 
											
												
													
														|  | 
 |  | +	
 | 
											
												
													
														|  | 
 |  | +	
 | 
											
												
													
														|  | 
 |  | +	var Gun = (typeof window !== "undefined") ? window.Gun : (USELOCALGUN ? require('../gun') : require("gun"));
 | 
											
												
													
														|  | 
 |  | +	var ev = {};
 | 
											
												
													
														|  | 
 |  | +	var empty = {};
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +	Gun.on('opt', function(root) {
 | 
											
												
													
														|  | 
 |  | +		this.to.next(root);
 | 
											
												
													
														|  | 
 |  | +		if (root.once)
 | 
											
												
													
														|  | 
 |  | +			return;
 | 
											
												
													
														|  | 
 |  | +		if (typeof process == 'undefined')
 | 
											
												
													
														|  | 
 |  | +			return
 | 
											
												
													
														|  | 
 |  | +		var mem = process.memoryUsage;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +		if (!mem) //exit because we are in the browser
 | 
											
												
													
														|  | 
 |  | +			return;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +		//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;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +		var nodes = {}; //checks if the node already exists
 | 
											
												
													
														|  | 
 |  | +		var nodesArray = []; //used to easily sort everything and store info about the nodes
 | 
											
												
													
														|  | 
 |  | +		var memoryUpdate = 0; // last time we printed the current memory stats
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +		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
 | 
											
												
													
														|  | 
 |  | +			}, 1);
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +		
 | 
											
												
													
														|  | 
 |  | +		setInterval(check, 1000); // set the garbage collector to run every second, TODO: make configurable
 | 
											
												
													
														|  | 
 |  | +		
 | 
											
												
													
														|  | 
 |  | +		//Executed every time a node gets modifyed
 | 
											
												
													
														|  | 
 |  | +		root.on("put", function(e) {
 | 
											
												
													
														|  | 
 |  | +			var ctime = Date.now();
 | 
											
												
													
														|  | 
 |  | +			var souls = Object.keys(e.put || empty);
 | 
											
												
													
														|  | 
 |  | +			for (var i = 0; i < souls.length; i++) {
 | 
											
												
													
														|  | 
 |  | +				enqueueNode(souls[i], ctime);
 | 
											
												
													
														|  | 
 |  | +			}
 | 
											
												
													
														|  | 
 |  | +		});
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +		//Adds a soul the garbage collectors "freeing" queue
 | 
											
												
													
														|  | 
 |  | +		function enqueueNode(soul, ctime) {
 | 
											
												
													
														|  | 
 |  | +			if (nodes[soul] == true) { //The node already exists in the queue
 | 
											
												
													
														|  | 
 |  | +				var index = nodesArray.findIndex(function(e) {
 | 
											
												
													
														|  | 
 |  | +					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");
 | 
											
												
													
														|  | 
 |  | +					return;
 | 
											
												
													
														|  | 
 |  | +				} else {
 | 
											
												
													
														|  | 
 |  | +					nodesArray.splice(index, 1); // remove the existing ref.
 | 
											
												
													
														|  | 
 |  | +					nodesArray.push([soul, ctime]); // push the new instance
 | 
											
												
													
														|  | 
 |  | +				}
 | 
											
												
													
														|  | 
 |  | +			} else {
 | 
											
												
													
														|  | 
 |  | +				nodesArray.push([soul, ctime]);
 | 
											
												
													
														|  | 
 |  | +				nodes[soul] = true;
 | 
											
												
													
														|  | 
 |  | +			}
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +		//The main garbage collecting routine
 | 
											
												
													
														|  | 
 |  | +		function GC(memRatio) {
 | 
											
												
													
														|  | 
 |  | +			var curTime = Date.now(); // get the current time
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +			if (curTime - memoryUpdate >= 5000) {
 | 
											
												
													
														|  | 
 |  | +				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);
 | 
											
												
													
														|  | 
 |  | +				memoryUpdate = curTime;
 | 
											
												
													
														|  | 
 |  | +			}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +			var freed = 0;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +			while (nodesArray.length > 0) {
 | 
											
												
													
														|  | 
 |  | +				var soul = nodesArray[0][0];
 | 
											
												
													
														|  | 
 |  | +				var nts = nodesArray[0][1];
 | 
											
												
													
														|  | 
 |  | +				if (DEBUG)
 | 
											
												
													
														|  | 
 |  | +					console.log("Soul: " + soul + " | Remove Importance: " + calcRemoveImportance(nts, curTime, memRatio) +
 | 
											
												
													
														|  | 
 |  | +						" | Memory Ratio: " + memRatio + " | Time Existed: " + (curTime - nts) / 1000);
 | 
											
												
													
														|  | 
 |  | +				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++;
 | 
											
												
													
														|  | 
 |  | +				} else
 | 
											
												
													
														|  | 
 |  | +					break;
 | 
											
												
													
														|  | 
 |  | +			}
 | 
											
												
													
														|  | 
 |  | +			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);
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +	});
 | 
											
												
													
														|  | 
 |  | +}());
 |