Skip to content

Instantly share code, notes, and snippets.

@Max-Makhrov
Created April 23, 2021 14:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Max-Makhrov/6b7d407414950dd499a41ad75c0b6886 to your computer and use it in GitHub Desktop.
Save Max-Makhrov/6b7d407414950dd499a41ad75c0b6886 to your computer and use it in GitHub Desktop.
Timer is a code for monitoring onMinute triggers
function readTimer() {
// [ 1 ]. Get report
var tags = ['onMinute_admin', 'onMinute_worker', 'onMinute_importer', 'onMinute_sizif1', 'onMinute_sizif2'];
var report = getTimerReps_(tags);
console.log(report);
// => gives 2D Array with a report about executions.
// So U can write it on Spreadsheet.
// [ 2 ]. Gat all data from logs
// var timer = Timer_('onMinute_admin');
// var data = timer.get();
//
//
// returns JSON
// like:
//
/*************
data =
{
log:
{"2021-04-21":
{ firstTime: "2021-04-18T18:24:12.106Z",
lastTime: "2021-04-21T05:42:11.998Z",
totatTime: 9147497,
starts: 252
ends: 250
executions:
{"1618793652120":
{
"start": "2021-04-21T11:17:12.250Z",
"end": "2021-04-21T11:17:12.412Z",
"time": 162,
"startNote": "ok",
"endNote": "Done! => "
}
}
}
}
days: ['2021-04-21', '2021-04-20', '2021-04-21']
}
************/
// notes
// log = the main object with totals:
// firstTime = the time of first execution
// lastTime = the time of last execution
// totatTime = total time of execution, ms
// starts = number of started execution
// ends = number of ended executions
// executions = object with each execution
// days = the array of days in log
/***********
console.log(Object.keys(data)); // [log, days]
************/
}
// _ _ _
// | | | | | |
// | |__| | ___| |_ __ ___ _ __ ___
// | __ |/ _ \ | '_ \ / _ \ '__/ __|
// | | | | __/ | |_) | __/ | \__ \
// |_| |_|\___|_| .__/ \___|_| |___/
// | |
// |_|
// function test_getFormattedDay() {
// // 2021-04-22
// console.log(getFormattedDay_(new Date()));
// }
function getFormattedDay_(date, format) {
date = date || new Date();
format = format || 'yyy-MM-dd';
var d = new Date(date);
var timezone = Session.getScriptTimeZone();
return Utilities.formatDate(d,timezone, 'yyy-MM-dd');
}
//
// function test_millisec2humantime() {
// // 2 min. 3 sec.
// console.log(millisec2humantime_(123456));
// }
function millisec2humantime_(milli) {
if (milli < 1000) { return milli + ' ms.'; }
var mm = parseInt(milli/1000), respo = '';
if (mm < 60) {
respo = mm + ' sec.';
} else {
var min = parseInt(mm / 60);
var sec = mm - min *60;
respo = min + ' min. ' + sec + ' sec.';
}
return respo;
}
// _______ _
// |__ __(_)
// | | _ _ __ ___ ___ _ __
// | | | | '_ ` _ \ / _ \ '__|
// | | | | | | | | | __/ |
// |_| |_|_| |_| |_|\___|_|
//
// function testTimer() {
// var timer = Timer_('BoooDaaaYaaaBooo');
// //
// // 1
// var executionId = timer.start('started');
// // console.log(executionId);
// // Do smth useful
// Utilities.sleep(500);
// var endRes = timer.end(executionId, 'Done!')
// //
// // 2
// var executionId = timer.start('started again');
// // Do smth ueful
// Utilities.sleep(1500);
// var endRes = timer.end(executionId, 'Done again!');
// console.log(JSON.stringify(timer.get(), null,4));
// //
// timer.remove();
// }
function Timer_(logKey) {
// [ 1 ]. constants
// number of days to keep in memory
var daysToKeep = 3;
// seconds of memory life
// is seconds
// ~ 1 hour + 6 minutes
var cacheLife = 4000;
var self = this;
self.logKey = logKey;
//
// [ 2 ]. work with memory
var cache = CacheService.getScriptCache();
var chunky = ChunkyCache_(cache);
self.mem = {
days: [],
log: {} // day { ...info... }
}
self.getMemory_ = function() {
var mem = {};
var s_mem = chunky.get(self.logKey);
if (s_mem) { mem = JSON.parse(s_mem); } else {
mem = self.mem;
}
return mem;
}
self.setMemory_ = function(mem) {
var s_mem = JSON.stringify(mem);
chunky.put(self.logKey, s_mem, cacheLife);
}
self.remove_ = function() {
chunky.remove(self.logKey);
}
//
//
// [ 4 ]. Execute memory tasks
return {
// function to start writing time
start: function(note) {
try {
// get current object
var d = new Date();
var executionId = d.getTime();
// get the day of start
var day = getFormattedDay_(d);
// get memory object
var mem = self.getMemory_();
// get days of memory
var days = [];
days = mem.days || days;
// add current day to memory
if (days.indexOf(day) === -1) {
days.push(day); // add day
}
// delete extra days from cache
if (days.length > daysToKeep) {
// deletes 1 day at a time
var removedDay = days.shift();
delete mem.log[removedDay];
}
// add day to memory
mem.days = days;
var dayLog = mem.log[day];
var execution = {start: d, end: null, time: 0, startNote: note}
if (!dayLog) {
// add new day log
dayLog = {
starts: 1,
ends: 0,
totatTime: 0,
firstTime: d,
lastTime: d,
executions: {}
}
dayLog.executions[executionId] = execution;
} else {
// add new data to log
dayLog.starts++;
dayLog.lastTime = d;
dayLog.executions[executionId] = execution;
}
mem.log[day] = dayLog;
self.setMemory_(mem);
return executionId;
} catch(err) {
console.log('🤷‍♂️ timer mem start = ' + err);
return null;
}
},
// function to end writing time to memory
end: function(executionId, note) {
try {
if (!executionId) { return -500; }
// get current object
var d = new Date();
// get memory object
var mem = self.getMemory_();
// get the day of start from memory
var days = [];
days = mem.days || days;
var l = days.length;
if (l === 0) { return -1; }
var day = days[l - 1];
var dayLog = mem.log[day];
if (!dayLog) { return -2; }
var executions = dayLog.executions;
if (!executions) { return -3; }
var execution = executions[executionId];
if (!execution) { return -4; }
if (execution.end) { return -5; } // ended...
// write the result
execution.end = d;
var time = d - new Date(execution.start)
// execution
execution.time = time;
execution.endNote = note;
executions[executionId] = execution;
// day log
dayLog.executions = executions;
dayLog.totatTime += time;
dayLog.ends++;
mem.log[day] = dayLog;
self.setMemory_(mem);
return mem;
} catch(err) {
console.log('🤷‍♂️ timer mem end ' + err);
}
},
get: function() {
var mem = self.getMemory_();
return mem;
},
remove: function() {
self.remove_();
}
};
}
//
//
//
// Timer Reports -- separate function to get timer reports by tag
function getTimerReps_(tags) {
var out = [];
var header = [
'Tag',
'Day',
'Hour',
'Stat Note',
'End Note',
'Executions Started',
'Executions Ended',
'Time, ms'
];
out.push(header);
//
for (var i = 0; i < tags.length; i++) {
out = out.concat(getTimerRep_(tags[i]));
}
return out;
}
//
function getTimerRep_(tag) {
var timer = Timer_(tag);
var data = timer.get();
var days = data.days;
var out = [];
var row = [], key = '', groups = {}, group = {}, executions = {}, execution = {}, hour, dd;
// key inside Day:
// Hour + Start Note + End Note
for (var i = 0; i < days.length; i++) {
groups = {}; // new groups for new day
executions = data.log[days[i]].executions;
for (var ekey in executions) {
execution = executions[ekey];
// {
// "start": "2021-04-20T16:01:12.111Z",
// "end": "2021-04-20T16:01:19.725Z",
// "time": 7614,
// "startNote": "ok",
// "endNote": "Done! => updateConnections_ClientNewDB"
// }
dd = new Date(execution.start);
hour = dd.getHours() + 1
key = hour + execution.startNote + execution.endNote;
if (!groups[key]) {
group = {
hour: hour,
startNote: execution.startNote,
endNote: execution.endNote,
starts: 1,
ends: 0,
time: 0
};
} else {
group = groups[key];
group.starts++;
}
if (execution.end) {
group.ends++;
group.time += execution.time;
}
groups[key] = group;
}
for (var gkey in groups) {
group = groups[gkey];
row = [
tag,
days[i],
group.hour,
group.startNote,
group.endNote,
group.starts,
group.ends,
group.time
];
out.push(row);
}
}
return out;
}
// _____ _
// / ____| | |
// | | __ _ ___| |__ ___
// | | / _` |/ __| '_ \ / _ \
// | |___| (_| | (__| | | | __/
// \_____\__,_|\___|_| |_|\___|
//
//
// the code is from here: https://gist.github.com/pilbot/9d0567ef1daf556449fb
// function testGetCacheFrom() {
// var cache = CacheService.getScriptCache();
// var chunky = ChunkyCache_(cache);
// var s = '🧞‍♂️'.repeat(1024 * 400);
// chunky.put('Data', s, 120);
// var check = chunky.get('Data');
// // console.log(check);
// //console.log(Utilities.newBlob(s).getBytes().length); // length in KB
// }
function ChunkyCache_(cache){
return {
// ttl = expirationInSeconds
// The minimum is 1 second and the maximum is 21600 seconds (6 hours).
// https://developers.google.com/apps-script/reference/cache/cache#putkey,-value,-expirationinseconds
put: function (key, value, ttl) {
var json = JSON.stringify(value);
// 1024*90 = indicates 90KB of data; should be ok with quota
var chunkSize = 1024*90; // put chunk size here
// this gives chunk size max 45KB
// a half of limit, discussion:
// https://gist.github.com/pilbot/9d0567ef1daf556449fb
var cSize = Math.floor(chunkSize / 2);
var chunks = [];
var index = 0;
while (index < json.length){
cKey = key + "_" + index;
chunks.push(cKey);
// https://developers.google.com/apps-script/reference/cache/cache#putkey,-value,-expirationinseconds
// The cap for cached items is 1,000
// If more than 1,000 items are written, the cache stores the 900 items farthest from expiration.
var part = json.substr(index, cSize);
// console.log(Utilities.newBlob(part).getBytes().length / 1024); // get length in Kb
cache.put(cKey, part, ttl+5);
index += cSize;
}
var superBlk = {
chunkSize: chunkSize,
chunks: chunks,
length: json.length
};
cache.put(key, JSON.stringify(superBlk), ttl);
},
get: function (key) {
var superBlkCache = cache.get(key);
if (superBlkCache != null) {
var superBlk = JSON.parse(superBlkCache);
if (!superBlk.chunks) { return null; }
chunks = superBlk.chunks.map(function (cKey){
return cache.get(cKey);
});
if (chunks.every(function (c) { return c != null; })){
return JSON.parse(chunks.join(''));
}
}
},
remove: function(key) {
var superBlkCache = cache.get(key);
if (superBlkCache != null) {
var superBlk = JSON.parse(superBlkCache);
superBlk.chunks.forEach(function (cKey){
cache.remove(cKey);
});
}
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment