Improve set and clear data model functions

This commit is contained in:
Zaid Abdulla committed 2017-08-04 19:29:25 -07:00
1 parent 4eb1c61c4b
commit 7306f7a8a5
2 files changed
+106 -98

No files matched your search

+88 -94
View File
@@ -22,9 +22,9 @@ const common = require('./common');
const CHANGE_FLAGS = { const CHANGE_FLAGS = {
object: 4, object: 2,
writable: 16, writable: 4,
value: 64 value: 8
}; };
@@ -63,6 +63,7 @@ function sanitizeParameterValue(parameterValue) {
throw new Error(`Parameter value type "${parameterValue[1]}" not recognized.`); throw new Error(`Parameter value type "${parameterValue[1]}" not recognized.`);
parameterValue[0] = JSON.parse(JSON.stringify(parameterValue[0])); parameterValue[0] = JSON.parse(JSON.stringify(parameterValue[0]));
break;
} }
} }
return parameterValue; return parameterValue;
@@ -171,36 +172,59 @@ function unpack(deviceData, path, revision) {
} }
function clear(deviceData, path, timestamp, attributes) { function clear(deviceData, path, timestamp, attributes, changeFlags = 0) {
for (let p of deviceData.paths.find(path, true, true, timestamp ? 99 : path.length)) { let changeTrackers = {};
for (let k in deviceData.trackers.get(p))
deviceData.changes.add(k);
let t = deviceData.timestamps.get(p); timestamp = timestamp || 0;
if (!t)
let descendantsTimestamp = timestamp;
if (attributes && attributes.object) {
if (attributes.object > descendantsTimestamp)
descendantsTimestamp = attributes.object;
if (!(attributes.object <= attributes.value))
attributes.value = attributes.object;
}
for (let p of deviceData.paths.find(path, true, true, descendantsTimestamp ? 99 : path.length)) {
let tracker = deviceData.trackers.get(p);
for (let k in tracker)
changeTrackers[k] |= tracker[k];
let currentTimestamp = deviceData.timestamps.get(p);
if (currentTimestamp === undefined)
continue; continue;
if (!(timestamp <= t)) { if (timestamp > currentTimestamp ||
(descendantsTimestamp > currentTimestamp && p.length > path.length)) {
deviceData.timestamps.delete(p); deviceData.timestamps.delete(p);
deviceData.attributes.delete(p); deviceData.attributes.delete(p);
} else if (t && attributes) { changeFlags |= 1;
}
else if (attributes && p.length == path.length) {
let currentAttributes = deviceData.attributes.get(p); let currentAttributes = deviceData.attributes.get(p);
if (currentAttributes) { if (currentAttributes) {
let newAttrs; let newAttrs;
for (let attrName in attributes) { for (let attrName in attributes) {
if (attrName in currentAttributes && if (attrName in currentAttributes &&
attributes[attrName][0] > currentAttributes[attrName][0]) { attributes[attrName] > currentAttributes[attrName][0]) {
if (!newAttrs) changeFlags |= CHANGE_FLAGS[attrName];
newAttrs = JSON.parse(JSON.stringify(currentAttributes)); if (!newAttrs) {
newAttrs = Object.assign({}, currentAttributes);
deviceData.attributes.set(p, newAttrs);
}
delete newAttrs[attrName]; delete newAttrs[attrName];
} }
} }
if (newAttrs)
deviceData.attributes.set(p, newAttrs);
} }
} }
} }
// Note: For performance, we're merging all changes together rather than
// mark changes based the exact parameters affected.
for (let k in changeTrackers) {
if (changeTrackers[k] & changeFlags)
deviceData.changes.add(k);
}
} }
@@ -227,9 +251,11 @@ function set(deviceData, path, timestamp, attributes, toClear) {
if (path.wildcard) if (path.wildcard)
attributes = undefined; attributes = undefined;
else else if (currentTimestamp)
currentAttributes = deviceData.attributes.get(path); currentAttributes = deviceData.attributes.get(path);
let changeFlags = 0;
if (attributes) { if (attributes) {
if (attributes.value && attributes.value[1] if (attributes.value && attributes.value[1]
&& attributes.value[0] >= (attributes.object ? attributes.object[0] : 0)) && attributes.value[0] >= (attributes.object ? attributes.object[0] : 0))
@@ -239,67 +265,29 @@ function set(deviceData, path, timestamp, attributes, toClear) {
&& attributes.object[0] >= (attributes.value ? attributes.value[0] : 0)) && attributes.object[0] >= (attributes.value ? attributes.value[0] : 0))
attributes.value = [attributes.object[0]]; attributes.value = [attributes.object[0]];
if (currentAttributes) { let newAttributes = Object.assign({}, currentAttributes, attributes);
let changeFlags = 0;
if (currentAttributes) {
for (let attrName in attributes) { for (let attrName in attributes) {
timestamp = Math.max(timestamp, attributes[attrName][0]); timestamp = Math.max(timestamp, attributes[attrName][0]);
if (attrName in currentAttributes) { if (!(attrName in currentAttributes))
if (attributes[attrName][0] > currentAttributes[attrName][0]) { changeFlags |= CHANGE_FLAGS[attrName];
changeFlags |= CHANGE_FLAGS[attrName]; else if (attributes[attrName][0] <= currentAttributes[attrName][0])
if (!compareEquality(attributes[attrName][1], currentAttributes[attrName][1])) newAttributes[attrName] = currentAttributes[attrName];
changeFlags |= CHANGE_FLAGS[attrName] << 1; else if (!compareEquality(attributes[attrName][1], currentAttributes[attrName][1]))
}
else
delete attributes[attrName];
}
else {
changeFlags |= CHANGE_FLAGS[attrName]; changeFlags |= CHANGE_FLAGS[attrName];
changeFlags |= CHANGE_FLAGS[attrName] << 1;
}
} }
if (Object.keys(attributes).length) {
if (attributes.object && currentAttributes.object &&
attributes.object[1] !== currentAttributes.object[1]) {
toClear = toClear || [];
toClear.push([path.concat('*'), attributes.object[0]]);
}
for (let attrName in currentAttributes)
if (!(attrName in attributes))
attributes[attrName] = currentAttributes[attrName];
deviceData.attributes.set(path, attributes);
}
if (timestamp > currentTimestamp) {
changeFlags |= 1;
deviceData.timestamps.set(path, timestamp);
if (path.length > 1)
toClear = set(deviceData, path.slice(0, path.length - 1), timestamp,
{object: [timestamp, 1]}, toClear);
}
if (changeFlags)
for (let sup of deviceData.paths.find(path, true, false)) {
let c = deviceData.trackers.get(sup);
for (let k in c)
if (c[k] & changeFlags) {
deviceData.changes.add(k);
}
}
} }
else if (!(timestamp <= currentTimestamp)) { else
deviceData.timestamps.set(path, timestamp); changeFlags |= 1;
deviceData.attributes.set(path, attributes);
deviceData.attributes.set(path, newAttributes);
if (!(timestamp <= currentTimestamp)) {
deviceData.timestamps.set(path, timestamp);
if (path.length > 1) if (path.length > 1)
toClear = set(deviceData, path.slice(0, path.length - 1), timestamp, toClear = set(deviceData, path.slice(0, path.length - 1), timestamp,
{object: [timestamp, 1]}, toClear); {object: [timestamp, 1]}, toClear);
toClear = toClear || [];
toClear.push([path, timestamp]);
} }
} }
else if (!(timestamp <= currentTimestamp)) { else if (!(timestamp <= currentTimestamp)) {
@@ -307,43 +295,49 @@ function set(deviceData, path, timestamp, attributes, toClear) {
if (currentAttributes) { if (currentAttributes) {
deviceData.attributes.delete(path); deviceData.attributes.delete(path);
toClear = toClear || []; changeFlags |= 1;
toClear.push([path, timestamp]);
} }
else if (path.wildcard) {
if (path.wildcard) for (let p of deviceData.paths.find(path, false, true, path.length))
for (let sub of deviceData.paths.find(path, false, true)) if (timestamp > deviceData.timestamps.get(p)) {
if (timestamp > deviceData.timestamps.get(sub)) { toClear = toClear || [];
deviceData.timestamps.delete(sub); toClear.push([p, timestamp]);
if (deviceData.attributes.delete(sub)) {
toClear = toClear || [];
toClear.push([sub, timestamp]);
}
} }
}
}
if (changeFlags) {
if (changeFlags & 1) {
toClear = toClear || [];
toClear.push([path, timestamp, null, changeFlags]);
}
else if (changeFlags & CHANGE_FLAGS.object) {
toClear = toClear || [];
toClear.push([path, 0, {object: attributes.object[0]}, changeFlags]);
}
else {
for (let p of deviceData.paths.find(path, true, false, path.length)) {
let tracker = deviceData.trackers.get(p);
for (let k in tracker) {
if (tracker[k] & changeFlags)
deviceData.changes.add(k);
}
}
}
} }
return toClear; return toClear;
} }
function track(deviceData, path, marker, timestamp, attributeTimestamps, exist, attributeValues) { function track(deviceData, path, marker, attributes) {
path = deviceData.paths.add(path); path = deviceData.paths.add(path);
let f = 0; let f = 1;
if (timestamp) if (attributes)
f |= 1; for (let attrName of attributes)
if (exist)
f |= 2;
if (attributeTimestamps)
for (let attrName of attributeTimestamps)
f |= CHANGE_FLAGS[attrName]; f |= CHANGE_FLAGS[attrName];
if (attributeValues)
for (let attrName of attributeValues)
f |= CHANGE_FLAGS[attrName] << 1;
let cur = deviceData.trackers.get(path); let cur = deviceData.trackers.get(path);
if (!cur) { if (!cur) {
cur = {}; cur = {};
+18 -4
View File
@@ -506,7 +506,7 @@ runDeclarations = (sessionContext, declarations) ->
attrTrackers.push(k) attrTrackers.push(k)
cur[k] = Math.max(v, cur[k] or 0) cur[k] = Math.max(v, cur[k] or 0)
device.track(sessionContext.deviceData, p, 'prerequisite', false, null, true, attrTrackers) device.track(sessionContext.deviceData, p, 'prerequisite', attrTrackers)
unpacked = device.unpack(sessionContext.deviceData, path) unpacked = device.unpack(sessionContext.deviceData, path)
for u in unpacked for u in unpacked
@@ -538,7 +538,7 @@ runDeclarations = (sessionContext, declarations) ->
for k, v of declaration[4] for k, v of declaration[4]
cur[k] = v cur[k] = v
device.track(sessionContext.deviceData, path, 'prerequisite', false, null, true) device.track(sessionContext.deviceData, path, 'prerequisite')
if declaration[3]? if declaration[3]?
if Array.isArray(declaration[3]) if Array.isArray(declaration[3])
@@ -603,6 +603,14 @@ rpcRequest = (sessionContext, _declarations, callback) ->
fault.timestamp = sessionContext.timestamp fault.timestamp = sessionContext.timestamp
return callback(null, fault) return callback(null, fault)
# Enforce max clear timestamp
for c in toClear
if c[1] > sessionContext.timestamp
c[1] = sessionContext.timestamp
for k, v of c[2]
if v > sessionContext.timestamp
c[2] = sessionContext.timestamp
sessionContext.declarations.push(decs) sessionContext.declarations.push(decs)
sessionContext.provisionsRet[inception] = ret sessionContext.provisionsRet[inception] = ret
@@ -1307,14 +1315,20 @@ clear = (sessionContext, toClear, callback) ->
MAX_DEPTH = config.get('MAX_DEPTH', sessionContext.deviceId) MAX_DEPTH = config.get('MAX_DEPTH', sessionContext.deviceId)
toClear.forEach((c) -> toClear.forEach((c) ->
loadPath(sessionContext, c[0], ((1 << c[0].length) - 1) ^ ((1 << MAX_DEPTH) - 1)) if c[1]
p = c[0].slice(0, -1) # in order to include superset
loadPath(sessionContext, p, ((1 << p.length) - 1) ^ ((1 << MAX_DEPTH) - 1))
else if (c[2] and c[2].object)
loadPath(sessionContext, c[0], (((1 << c[0].length) - 1) >> 1) ^ ((1 << MAX_DEPTH) - 1))
else
loadPath(sessionContext, c[0], (1 << c[0].length) >> 1)
) )
loadParameters(sessionContext, (err) -> loadParameters(sessionContext, (err) ->
return callback(err) if err return callback(err) if err
toClear.forEach((c) -> toClear.forEach((c) ->
device.clear(sessionContext.deviceData, c[0], c[1], c[2]) device.clear(sessionContext.deviceData, c[0], c[1], c[2], c[3])
) )
return callback() return callback()
) )