lundi 2 février 2015

Delayed state checking in Meteor

Delayed state checking


tl;dr - What are some good design patterns for reacting when an action has not taken place for a specified duration?




Problem Setup


I'm making a Farmville-like application. In this application, users have gardens that they take care of. There's a few variables that are tracked about each garden -- humidity, temperature, ph, nitrate, etc. The application guides you through taking care of the garden, creating tasks for you to do. It informs you if any of those variables are too low or too high based on readings you've submitted. In addition, it reminds you to take a reading for a variable if you haven't taken it in some specified amount of time.


The garden data looks like



// garden object
{
name: "Home garden",
variables: {
nitrate: {
currentValue: 1.7,
lastMeasuredAt: // some Date
},
nitrite: {
currentValue: 0.5,
lastMeasuredAt: // some Date
}
}
}


So let's say there's a task called "Add fertilizer". The trigger for this task is the condition where nitrate is below 2.5ppm. Let's say that task data looks like



// task object
{
name: "Add fertilizer",
instructions: "Open bag, dump on garden",
condition: {
variable: "nitrate",
operator: "$lt",
threshold: 2.5
}
}


I build a search query with the condition data, and query the Gardens to see if any match that condition. I'm using MongoDB, so can form that query using plain old Javascript objects, and Meteor, so I have a live-updating cursor.



query = { variables.nitrate.currentValue: { $lt : 2.5 }};
Gardens.find(query).observe({
added: function( garden ) {
// add the task to the garden's task list
},
removed: function( garden ) {
// remove the task from the garden's task list
}
});


The problem


The good news, this pattern works for the task I described. But what about when the task is based on a duration of time having passed?



// time-based task object
{
name: "Take a nitrate reading",
instructions: "Insert the nitrate probe",
condition: {
variable: "nitrate",
operator: "$lt",
interval: 2880 // 2 days in minutes
}
}


I see that it's a time task since it has an interval and not a threshold and make the query...



// using moment.js
expiration = moment().subtract(interval, 'minutes').toDate();
// find when lastMeasuredAt + interval < Now
query = { variables.nitrate.lastMeasuredAt: { $gt: expiration }};
Gardens.find(query).observe({
added: function( garden ) {
// add the task to the garden's task list
},
removed: function( garden ) {
// remove the task from the garden's task list
}
});


But this needs to be checked at some point in the future, instead of right now. This brings me to the crux of my question. What's a good way of checking when it expires?


The questions


Is there any way of doing this reactively?


Meteor's Tracker is only supported to work on the client, but the peerlibrary:server-autorun does enable it on the server. I could make the Date a reactive data source, updated every few minutes or something, and wrap this in a Tracker.autorun.


Is this usually handled with some sort of job queue?


Aucun commentaire:

Enregistrer un commentaire