-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcache.js
546 lines (513 loc) · 17.8 KB
/
cache.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
/**
Class is intended to hold the cache table and all its statistics, the functions
exposed are for usage in storing and extracting values from the cache table
@class Cache
@constructor
**/
function Cache () {
'use strict';
//=============================================================================
// Variable Declarations and Initializations
//=============================================================================
var CACHE = {},
table = {},
queue = [],
callback_queue = {},
size = 0,
log = false,
debug = false,
maxCacheSize = 0,
minFetched = 0,
cacheMisses = 0,
cacheHits = 0,
cache_obj = Object.create({
"size":0
, "data":{}
, "fetched":1
, "last_fetched":null
});
//=============================================================================
// Method Declarations and Definitions
//=============================================================================
/**
This method is used to clear the cache table and empty it in one go.
@method clearCache
**/
var clearCache = function () {
if(log){console.log("Cache Table Cleared");}
table = {};
queue = [];
callback_queue = {};
size = 0;
minFetched = 0;
cacheMisses = 0;
cacheHits = 0;
/*cache_obj = Object.create({
"size":0
, "data":{}
, "fetched":1
, "last_fetched":null
});*/
},
/**
Method is used for checking the cache and firing a ajax call if the data for
that hash id is not found in the cache. It also has the task of updating the
cache statistics and cacheObject statistics when the object is found in the
cache. It also does the special function of queueing requests for a particular
cache object if the id has already been sent via an AJAX request and once the
data returns the callbacks are executed in the order they arrived.
@method checkCache
@param {String} id is the cache object hash id (URL and params)
@param {Function} callback is the callback to be exected when the object data
is available.
**/
checkCache = function(id,callback) {
if(table[id]) {
if(log){console.log("Data found in Cache Table");}
cacheHits ++;
updateCacheObjectFlags(id);
callback(table[id].data);
}
else {
if(log){console.log("Data not found in Cache Table");}
if(!callback_queue[id]) {
callback_queue[id] = [];
cacheMisses ++;
getObject(id,callback);
}
else {
if(log){console.log("Pushing request into Callback Queue");}
callback_queue[id].push(callback);
}
}
},
/**
This method is used for updating cache object flags or statistics so that when
the objects are sent for an evaluation by a replacement algorithm their flag
values decide whether they will be removed or retained in the cache.
@method updateCacheObjectFlags
@param {String} id is the hash id of the cacheObject (URL and params)
**/
updateCacheObjectFlags = function(id) {
if(log){console.log("Updating Cache Object flags");}
table[id].fetched ++;
table[id].last_fetched = Date.parse(new Date());
},
/**
Method is used for fetching cache object and calls another function internally
called checkCache which checks and executes the callback on the cacheObject
@method getCacheObject
@param {String} id is the hash id of the cache object (URL and params)
@return {Function} checkCache which executes the callback with cacheObject
**/
getCacheObject = function (id,callback) {
if(log){console.log("Checking for requested Cache Object");}
return checkCache(id,callback);
},
/**
The method is used primarily to set a cacheObject manually if one does not want
to make a AJAX call via getObject. Must be used in emergency cases only.
@method setCacheObject
@param {String} id is the hash id of the cache object (URL and params)
@param {Object} obj is the cache object template which contains flags and data
value set to a particular value.
@return {Object} CACHE is the cache instance which is being set with data.
**/
setCacheObject = function (id,obj) {
if(log){console.log("Setting Cache Object through manual feed");}
table[id].data = obj;
return CACHE;
},
/**
This method is used for calculating the approximate size of the cache object as
a whole along with nseted object structure it returns the size in Bytes. It
recursively calculates the size of a nested object.
@method getSize
@param {Object} obj is the object for which the size needs to be calculated
@return {Number} s is the size of the object in Bytes
**/
getSize = function(obj) {
var sum = 0, x,
checkSize = function(o){
switch (typeof o) {
case 'string': sum += o.length * 2;
break;
case 'number': sum += 8;
break;
case 'boolean': sum += 4;
break;
case 'object': for(var c in o){
checkSize(o[c]);
}
break;
default : sum += 1;
break;
}
};
for(x in obj) {
checkSize(obj[x]);
}
return sum;
},
/**
Function calculates the size of a given object by passing it to an other
function called getSize. This methos is used in case of any pre processing to
be doone on the object or parsing purposes
@method calculateSize
@param {Object} obj
@return {Function} getSize which returns the size of the object passed to it
**/
calculateSize = function (obj) {
if(log){console.log("Calculating Size of the Object");}
return getSize(obj);
},
/**
Method returns the size of an individual cache object when passed to it by id.
@method getCacheObjectSize
@param {String} id is the hash id of the cach object to be used for size
calculation
@return {Function} calculateSize function is used for calculating size of any
object after pre processing.
**/
getCacheObjectSize = function (id) {
return calculateSize(table[id].data);
},
/**
This method is used for returning the cacheSize of the overall cache when
called. This maintains a size variable which gets updated when any new object
is added to the cache and when any object is removed from the cache
@method getCacheSize
@return {Number} size in Bytes
**/
getCacheSize = function () {
return size;
},
/**
This method sets logging as true or false
@method setLogging
@param {Boolean} l is the log flag either true or false
**/
setLogging = function(l){
log = l;
},
/**
This method is used for fetching the cache statistics of number of items,
minimum number of fetches done for an object, cache hits, cache misses and
cache size.
@method getStats
@return {Object} containing all the above described elements
**/
getStats = function () {
return {
"items":Object.keys(table).length
,"minimum_fetched":minFetched
,"cache_size":size
,"cache_miss":cacheMisses
,"cache_hits":cacheHits
};
},
/**
This method is used for setting the maximum cache size in bytes that can be
used and when the cache size is about to exceed, one of the many algorithms
comes into play to replace the cahce object based on some criteria.
@method setMaximumCacheSize
@param {Number} sz is the size based on which the criteria is chosen
@return {Object} CACHE is the instance of this object
**/
setMaximumCacheSize = function (sz) {
if(log){console.log("Setting Maximum Cache Size");}
maxCacheSize = sz;
return CACHE;
},
executeCallbackQueue = function(id) {
},
/**
Method is used for fetching the required data from the backend or a web
service using AJAX call and is then used for executing the callback passed to
the cacheObject, it also executes the callbacks which have been queued after
the ajax call was made and before it returned with the data.
@method getObject
@param {String} id is the hash Cache Object id (URL with params)
@param {Function} callback is a callback function which gets executed once
data is available
**/
getObject = function (id,callback) {
if(log){console.log("Sending AJAX request to fetch requested data");}
var xhr = new XMLHttpRequest();
xhr.open("GET",id,true);
xhr.onreadystatechange = function () {
if(xhr.readyState === 4) {
var data = JSON.parse(xhr.responseText);
var obj = addCacheObject(id,data);
callback(obj);
if(callback_queue[id].length) {
if(log){console.log("Starting execution of callback queue");}
callback_queue[id].map(function(f){
cacheHits ++;
updateCacheObjectFlags(id);
f.call(this,obj);
});
}
}
};
xhr.send();
},
/**
This method is used for removing a cacheObject based on the id passed to it.
@method removeCacheObject
@param {String} id is the hash id of the cacheObject (URL with params)
**/
removeCacheObject = function(id) {
if(log){console.log("Removing Cache Object id: ",id);}
size -= table[id].size;
delete table[id];
},
/**
Method is used for creating a template cache object and then adding data
returned to it from the ajax call and then setting up its size and checks for
Cahce memory overflow. If overflow then warning is given and then a particular
algorithm is executed to balance the memory. After this balancing operation
cacheObject is added to the cache and is returned as well.
@method addCacheObject
@param {String} id this is the hash id of the object (URL with params)
@param {Object} data this is the data that has been returned from the ajax
call.
@return {Object} cacheObject is the fully formed object that contains flag
data and the ajax data.
**/
addCacheObject = function (id,data) {
if(log){console.log("Adding new cache object to the Cache Table");}
var cacheObject = {
"size":0
,"data":{}
,"fetched":1
,"last_fetched":Date.parse(new Date())
};
cacheObject.data = data;
cacheObject.size = getSize(cacheObject);
if((size + cacheObject.size) > maxCacheSize) {
var overflow = maxCacheSize - (size + cacheObject.size);
if(log){console.warn("Cache Size will be exceeded by: " +
Math.abs(overflow) + " Bytes");}
//removeLeastFetched(cacheObject.size);
}
size += cacheObject.size;
minFetched = minFetched > cacheObject.fetched ? minFetched : cacheObject.fetched;
table[id] = cacheObject;
return cacheObject;
},
/**
This is a Object replacement algorithm that removes any object that has been
fetched the least so that new objects can take its place and cache is made
useful and efficient.
@method removeLeastFetched
@param {Number} o_size is the size of the object that needs to be added to
cache so that replacement of one or more cache objects can be carried out till
the required size is freed in the cache
**/
removeLeastFetched = function (o_size) {
if(log){console.log("Calling remove least fetched algoritm");}
var obj_id,
min_fetch = minFetched;
for(var o in table) {
if(table[o].fetched <= min_fetch) {
min_fetch = table[o].fetched;
obj_id = o;
}
}
removeCacheObject(obj_id);
},
/**
This Cache Object replacement algorithm replaces objects by criterion as to
which was the object that was fetched very long ago and has not been fetched
again. All cache objects are updated with the latest fetch time and the oldest
one is replaced.
@method removeLeastUsed
@param {Number} o_size is the size of the object that needs to be added to the
cache
**/
removeLeastUsed = function (o_size) {
if(log){console.log("calling remove least used algorithm");}
var obj_id,
rare_fetch = Date.parse(new Date());
for(var o in table) {
if(table[o].last_fetched <= rare_fetch) {
rare_fetch = table[o].last_fetched;
obj_id = o;
}
}
removeCacheObject(obj_id);
},
/**
This replacement algorithm reoves the biggest cache object to make way for many
smaller objects, it needs to be used in conjunction with the least used
algorithm fir best results
@method removeBiggest
@param {Number} o_size is the size of the object that needs to be added into
the cache.
**/
removeBiggest = function (o_size) {
if(log){console.log("calling remove biggest algorithm");}
var obj_id,
biggest_size = 0;
for(var o in table) {
if(table[o].size > biggest_size) {
biggest_size = table[o].size;
obj_id = o;
}
}
removeCacheObject(obj_id);
},
removeSmallest = function (o_size) {
if(log){console.log("calling remove smallest algorithm");}
var obj_id,
smallest_size = Infinity;
if(table[o].size < smallest_size) {
smallest_size = table[o].size;
obj_id = o;
}
removeCacheObject(obj_id);
},
//=============================================================================
//This feature can be used when parts of data needs to be fetched like pagination
//=============================================================================
/**
The method is used for fetching the chunked cache object by checking for its
existence and executing the callback when the data is returned to the function.
@method getChunkedCacheObject
@param {String} id is the id used for storing the cache object, this may be
different from the actual url that will be called.
@param {Function} callback is the function that is executed when the
cacheobject returns.
@param {Object} options configuration object
@param {String} options.urltemplate is the url template that has a value tag
that gets replaced with the keys
@param {Array} options.keyarray is the array of keys that need to be replaced
in the template when making the url call
@param {Number} options.interval is the interval after which the next call
needs to be made
@param {String} options.path is the path which needs to be used for mxing the
incoming data with existing data
**/
getChunkedCacheObject = function(id,callback,options) {
if(table[id]) {
return table[id];
}
else {
if(!callback_queue[id]) {
chunkData(options.urltemplate
,options.keyarray
,id
,options.interval
,options.path
,callback);
callback_queue[id] = [];
}
else {
callback_queue[id].push(callback);
}
}
},
/**
The function is intended for fetching and adding the chunked cache object into
the cache table and also executes the callback given upon addition of object
in the table
@method chunkData
@param {String} urltemplate is the string template having value surrounded by
braces which is replaced by the keys when the url call is being made
@param {Array} is the array of keys that get attached to the urltemplate when
the xhr is being made
@param {String} objectname is the string id for the cache table object that is
going to be stored
@param {Number} interval is the time interval after which the next xhr call
needs to be made
@param {String} path is the string path for the object where the new objects
needs to be added to the existing cache object
@param {Function} callback is the callback to be executed when the cacheobject
gets added to the cache table
**/
chunkData = function (urltemplate,keyarray,objectname,interval,path,callback) {
var template = urltemplate,
keys = keyarray,
obj_id = objectname,
fetchChunk = function (key) {
var url = template.replace("{value}",key),
resCallback = function (data) {
var obj = addCacheChunkObject(obj_id,data,path);
callback(obj);
};
fetchCacheChunkObject(url,resCallback);
};
keys.map(fetchChunk);
},
/**
This method makes the xhr call and upon receiving of data executes a callback
that involves adding the cache object to the cache table after calculating the
size of the object and optionaly executing a replacement algorithm
@method fetchCacheChunkObject
@param {String} url is the URL to be called for the required object
@param {Function} callback which is supposed to be executed after getting the
object
**/
fetchCacheChunkObject = function (url,callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET",url,true);
xhr.onreadystatechange = function () {
if(xhr.readyState === 4) {
var data = JSON.parse(xhr.responseText);
callback(data);
}
};
xhr.send();
},
/**
The method is used for adding the cache object into the table depending on the
size and other conditions where the object might exceed the max cache size set
by user
@method addCacheChunkObject
@param {String} id is the is of the cache table object
@param {Object} data is the object that is content of the cache object
@param {String} path is the object key which is where the new data gets added
after successive calls are made to the chunked object
**/
addCacheChunkObject = function (id,data,path) {
var cacheObject = {
"size":0
,"data":{}
,"fetched":1
,"last_fetched":Date.parse(new Date())
};
if(!table[id]) {
cacheObject.data = data;
cacheObject.size = getSize(cacheObject);
if((size + cacheObject.size) > maxCacheSize) {
var overflow = maxCacheSize - (size + cacheObject.size);
if(log){console.warn("Cache Size will be exceeded by: " +
Math.abs(overflow) + " Bytes");}
//removeLeastFetched(cacheObject.size);
}
size += cacheObject.size;
minFetched = minFetched > cacheObject.fetched ? minFetched : cacheObject.fetched;
table[id] = cacheObject;
return cacheObject;
}
else {
cacheObject = table[id];
cacheObject.data[path] = cacheObject.data[path].concat(data[path]);
return cacheObject;
}
};
//=============================================================================
// Setting public variables and methods
//=============================================================================
CACHE.maxSize = maxCacheSize;
CACHE.log = setLogging;
CACHE.getStats = getStats;
CACHE.clearCache = clearCache;
CACHE.getCacheObject = getCacheObject;
CACHE.setCacheObject = setCacheObject;
CACHE.getCacheSize = getCacheSize;
CACHE.setMaximumCacheSize = setMaximumCacheSize;
CACHE.getChunkedCacheObject = getChunkedCacheObject;
return CACHE;
}