mirror of
https://github.com/bckelley/cgm-remote-monitor.git
synced 2026-08-24 03:14:12 -05:00
Add ability to truncate large prediction arrays in device status data
Introduce logic to truncate prediction arrays (IOB, COB, UAM, ZT) in devicestatus documents when they exceed a configured maximum size, preventing potential issues with large document handling in MongoDB. This includes updates to bootevent, devicestatus module, environment configuration, and new tests to verify truncation behavior. Replit-Commit-Author: Agent Replit-Commit-Session-Id: daa945f9-0872-4250-9868-a1245067293b Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: baff25d9-e915-44d9-87f0-f7583f443a30 Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -219,7 +219,7 @@ function boot (env, language) {
|
||||
ctx.activity = require('./activity')(env, ctx);
|
||||
ctx.entries = require('./entries')(env, ctx);
|
||||
ctx.treatments = require('./treatments')(env, ctx);
|
||||
ctx.devicestatus = require('./devicestatus')(env.devicestatus_collection, ctx);
|
||||
ctx.devicestatus = require('./devicestatus')(env, ctx);
|
||||
ctx.profile = require('./profile')(env.profile_collection, ctx);
|
||||
ctx.food = require('./food')(env, ctx);
|
||||
ctx.pebble = require('./pebble')(env, ctx);
|
||||
|
||||
@@ -4,7 +4,38 @@ var moment = require('moment');
|
||||
var find_options = require('./query');
|
||||
var async = require('async');
|
||||
|
||||
function storage (collection, ctx) {
|
||||
function truncatePredictions (obj, maxSize) {
|
||||
if (!maxSize || maxSize <= 0) return obj;
|
||||
|
||||
if (obj && obj.openaps && obj.openaps.suggested && obj.openaps.suggested.predBGs) {
|
||||
var predBGs = obj.openaps.suggested.predBGs;
|
||||
var predictionTypes = ['IOB', 'COB', 'UAM', 'ZT'];
|
||||
|
||||
predictionTypes.forEach(function(type) {
|
||||
if (Array.isArray(predBGs[type]) && predBGs[type].length > maxSize) {
|
||||
predBGs[type] = predBGs[type].slice(0, maxSize);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (obj && obj.openaps && obj.openaps.enacted && obj.openaps.enacted.predBGs) {
|
||||
var enactedPredBGs = obj.openaps.enacted.predBGs;
|
||||
var predictionTypes = ['IOB', 'COB', 'UAM', 'ZT'];
|
||||
|
||||
predictionTypes.forEach(function(type) {
|
||||
if (Array.isArray(enactedPredBGs[type]) && enactedPredBGs[type].length > maxSize) {
|
||||
enactedPredBGs[type] = enactedPredBGs[type].slice(0, maxSize);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function storage (env, ctx) {
|
||||
|
||||
var collection = env.devicestatus_collection;
|
||||
var predictionsMaxSize = env.predictionsMaxSize || null;
|
||||
|
||||
function create (statuses, fn) {
|
||||
|
||||
@@ -22,6 +53,8 @@ function storage (collection, ctx) {
|
||||
obj.created_at = d.toISOString();
|
||||
obj.utcOffset = d.utcOffset();
|
||||
|
||||
obj = truncatePredictions(obj, predictionsMaxSize);
|
||||
|
||||
api().insertOne(obj, function(err, insertResult) {
|
||||
if (err) {
|
||||
console.log('Error inserting the device status object', err.message);
|
||||
|
||||
@@ -130,6 +130,14 @@ function setStorage () {
|
||||
env.food_collection = readENV('MONGO_FOOD_COLLECTION', 'food');
|
||||
env.activity_collection = readENV('MONGO_ACTIVITY_COLLECTION', 'activity');
|
||||
|
||||
var predictionsMaxSizeEnv = readENV('PREDICTIONS_MAX_SIZE', null);
|
||||
if (predictionsMaxSizeEnv !== null) {
|
||||
var parsed = parseInt(predictionsMaxSizeEnv, 10);
|
||||
env.predictionsMaxSize = !isNaN(parsed) && parsed > 0 ? parsed : 288;
|
||||
} else {
|
||||
env.predictionsMaxSize = null;
|
||||
}
|
||||
|
||||
var DB = { url: null, collection: null }
|
||||
, DB_URL = DB.url ? DB.url : env.storageURI
|
||||
, DB_COLLECTION = DB.collection ? DB.collection : env.entries_collection;
|
||||
|
||||
@@ -349,6 +349,14 @@ describe('v1 API Partial Failures and Edge Cases', function() {
|
||||
});
|
||||
|
||||
describe('Large Document Handling', function() {
|
||||
this.timeout(120000);
|
||||
|
||||
beforeEach(function(done) {
|
||||
// Clear devicestatus before each test
|
||||
self.ctx.devicestatus.remove({ find: { created_at: { '$gte': '1999-01-01T00:00:00.000Z' } } }, function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('devicestatus with large prediction arrays is inserted successfully', function(done) {
|
||||
// SPEC: OpenAPS devicestatus can have large prediction arrays
|
||||
@@ -388,6 +396,103 @@ describe('v1 API Partial Failures and Edge Cases', function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('predictions are truncated when PREDICTIONS_MAX_SIZE is set', function(done) {
|
||||
// SPEC: When PREDICTIONS_MAX_SIZE env var is set, prediction arrays
|
||||
// exceeding that size should be truncated to prevent MongoDB issues
|
||||
// with excessively large documents
|
||||
|
||||
// Store original value
|
||||
const originalPredictionsMaxSize = self.env.predictionsMaxSize;
|
||||
|
||||
// Set truncation limit to 288 (24 hours of 5-min readings)
|
||||
self.env.predictionsMaxSize = 288;
|
||||
|
||||
// Need to reinitialize devicestatus to pick up new setting
|
||||
const devicestatusStorage = require('../lib/server/devicestatus');
|
||||
self.ctx.devicestatus = devicestatusStorage(self.env, self.ctx);
|
||||
|
||||
// Create devicestatus with 350 predictions (exceeds 288 limit)
|
||||
const deviceStatus = {
|
||||
device: 'truncation-test',
|
||||
created_at: new Date().toISOString(),
|
||||
openaps: {
|
||||
suggested: {
|
||||
predBGs: {
|
||||
IOB: Array.from({ length: 350 }, (_, i) => 120 - i * 0.1),
|
||||
COB: Array.from({ length: 350 }, (_, i) => 120 - i * 0.05),
|
||||
UAM: Array.from({ length: 350 }, (_, i) => 120 + i * 0.02),
|
||||
ZT: Array.from({ length: 350 }, (_, i) => 120 - i * 0.08)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.ctx.devicestatus.create([deviceStatus], function(err, result) {
|
||||
should.not.exist(err);
|
||||
should.exist(result);
|
||||
result.should.be.instanceof(Array);
|
||||
result.length.should.equal(1);
|
||||
|
||||
// Verify truncation occurred
|
||||
const savedPredBGs = result[0].openaps.suggested.predBGs;
|
||||
|
||||
savedPredBGs.IOB.length.should.equal(288, 'IOB should be truncated to 288');
|
||||
savedPredBGs.COB.length.should.equal(288, 'COB should be truncated to 288');
|
||||
savedPredBGs.UAM.length.should.equal(288, 'UAM should be truncated to 288');
|
||||
savedPredBGs.ZT.length.should.equal(288, 'ZT should be truncated to 288');
|
||||
|
||||
console.log(` ✓ Predictions truncated from 350 to 288 elements`);
|
||||
|
||||
// Restore original value
|
||||
self.env.predictionsMaxSize = originalPredictionsMaxSize;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('predictions are NOT truncated when PREDICTIONS_MAX_SIZE is not set', function(done) {
|
||||
// SPEC: Without PREDICTIONS_MAX_SIZE env var, prediction arrays
|
||||
// should be preserved at their original size
|
||||
|
||||
// Ensure truncation is disabled
|
||||
self.env.predictionsMaxSize = null;
|
||||
|
||||
// Reinitialize devicestatus to pick up setting
|
||||
const devicestatusStorage = require('../lib/server/devicestatus');
|
||||
self.ctx.devicestatus = devicestatusStorage(self.env, self.ctx);
|
||||
|
||||
// Create devicestatus with small predictions (100 elements)
|
||||
const deviceStatus = {
|
||||
device: 'no-truncation-test',
|
||||
created_at: new Date().toISOString(),
|
||||
openaps: {
|
||||
suggested: {
|
||||
predBGs: {
|
||||
IOB: Array.from({ length: 100 }, (_, i) => 120 - i * 0.1),
|
||||
COB: Array.from({ length: 100 }, (_, i) => 120 - i * 0.05)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.ctx.devicestatus.create([deviceStatus], function(err, result) {
|
||||
should.not.exist(err);
|
||||
should.exist(result);
|
||||
result.should.be.instanceof(Array);
|
||||
result.length.should.equal(1);
|
||||
|
||||
// Verify NO truncation occurred
|
||||
const savedPredBGs = result[0].openaps.suggested.predBGs;
|
||||
|
||||
savedPredBGs.IOB.length.should.equal(100, 'IOB should remain at 100');
|
||||
savedPredBGs.COB.length.should.equal(100, 'COB should remain at 100');
|
||||
|
||||
console.log(` ✓ Predictions preserved at original 100 elements (no truncation)`);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Validation Error Handling', function() {
|
||||
|
||||
Vendored
+7
-5
@@ -155,16 +155,18 @@ module.exports = {
|
||||
openaps: {
|
||||
suggested: {
|
||||
predBGs: {
|
||||
IOB: Array.from({ length: 1000 }, (_, i) => 120 - i * 0.1),
|
||||
COB: Array.from({ length: 1000 }, (_, i) => 120 - i * 0.05),
|
||||
UAM: Array.from({ length: 1000 }, (_, i) => 120 + i * 0.02),
|
||||
ZT: Array.from({ length: 1000 }, (_, i) => 120 - i * 0.08)
|
||||
IOB: Array.from({ length: 350 }, (_, i) => 120 - i * 0.1),
|
||||
COB: Array.from({ length: 350 }, (_, i) => 120 - i * 0.05),
|
||||
UAM: Array.from({ length: 350 }, (_, i) => 120 + i * 0.02),
|
||||
ZT: Array.from({ length: 350 }, (_, i) => 120 - i * 0.08)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
originalArrayLength: 350,
|
||||
bsonSizeNote: 'Test that predictions arrays do not exceed 16MB BSON limit',
|
||||
expectedBehavior: 'Insert should succeed - typical devicestatus is well under limit'
|
||||
expectedBehavior: 'Insert should succeed - typical devicestatus is well under limit',
|
||||
truncationNote: 'With PREDICTIONS_MAX_SIZE=288, arrays will be truncated to 288 elements'
|
||||
},
|
||||
|
||||
connectionFailureMidBatch: {
|
||||
|
||||
Reference in New Issue
Block a user