-
Notifications
You must be signed in to change notification settings - Fork 73.3k
Expand file tree
/
Copy pathcache.js
More file actions
127 lines (104 loc) · 3.86 KB
/
Copy pathcache.js
File metadata and controls
127 lines (104 loc) · 3.86 KB
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
'use strict';
/* This is a simple cache intended to reduce the amount of load
* Nightscout puts on MongoDB. The cache is based on identifying
* elements based on the MongoDB _id field and implements simple
* semantics for adding data to the cache in the runtime, intended
* to be accessed by the persistence layer as data is inserted, updated
* or deleted, as well as the periodic dataloader, which polls Mongo
* for new inserts.
*
* Longer term, the cache is planned to allow skipping the Mongo polls
* altogether.
*/
const constants = require('../constants');
function cache (env, ctx) {
const data = {
treatments: []
, devicestatus: []
, entries: []
};
const retentionPeriods = {
treatments: constants.ONE_HOUR * 60
, devicestatus: env.extendedSettings.devicestatus && env.extendedSettings.devicestatus.days && env.extendedSettings.devicestatus.days == 2 ? constants.TWO_DAYS : constants.ONE_DAY
, entries: constants.TWO_DAYS
};
/* Each removal bumps the generation counter for the affected datatype.
* The dataloader reads the counter before querying Mongo, so it can tell
* when a delete landed while a query was in flight and the results may
* still contain the deleted documents. Merging such results into the
* cache would resurrect them until the retention period expires, since
* incremental loads never revisit that time window.
*/
const removalGenerations = {
treatments: 0
, devicestatus: 0
, entries: 0
};
function getObjectAge(object) {
let age = object.mills || object.date;
if (isNaN(age) && object.created_at) age = Date.parse(object.created_at).valueOf();
return age;
}
function mergeCacheArrays (oldData, newData, retentionPeriod) {
const ageLimit = Date.now() - retentionPeriod;
var filteredOld = filterForAge(oldData, ageLimit);
var filteredNew = filterForAge(newData, ageLimit);
const merged = ctx.ddata.idMergePreferNew(filteredOld, filteredNew);
return merged.sort((a, b) => getObjectAge(b) - getObjectAge(a));
function filterForAge(data, ageLimit) {
return data.filter(function hasId(object) {
const hasId = object._id != null && object._id !== '';
const age = getObjectAge(object);
const isFresh = age >= ageLimit;
return isFresh && hasId;
});
}
}
data.isEmpty = (datatype) => {
return data[datatype].length < 20;
}
data.getData = (datatype) => {
// Deep clone data to prevent external modifications affecting cache
return JSON.parse(JSON.stringify(data[datatype]));
}
data.insertData = (datatype, newData) => {
data[datatype] = mergeCacheArrays(data[datatype], newData, retentionPeriods[datatype]);
return data.getData(datatype);
}
data.getRemovalGeneration = (datatype) => {
return removalGenerations[datatype];
}
function dataChanged (operation) {
if (!data[operation.type]) return;
if (operation.op == 'remove') {
// if multiple items were deleted, flush entire cache
if (!operation.changes) {
data.treatments = [];
data.devicestatus = [];
data.entries = [];
removalGenerations.treatments += 1;
removalGenerations.devicestatus += 1;
removalGenerations.entries += 1;
} else {
removeFromArray(data[operation.type], operation.changes);
removalGenerations[operation.type] += 1;
}
}
if (operation.op == 'update') {
data[operation.type] = mergeCacheArrays(data[operation.type], operation.changes, retentionPeriods[operation.type]);
}
}
ctx.bus.on('data-update', dataChanged);
function removeFromArray (array, id) {
for (let i = 0; i < array.length; i++) {
const o = array[i];
if (o._id == id) {
//console.log('Deleting object from cache', id);
array.splice(i, 1);
break;
}
}
}
return data;
}
module.exports = cache;