计划的MongoDB查询"/>
运行大量计划的MongoDB查询
问题:我有一个项目列表,这些项目的到期时间必须通过调度程序进行检查。如果项目已过期,则必须在数据库中将其标记。
我有超过2k的项目列表变得越来越繁琐(使用我当前的逻辑运行)。我也在使用node-schedule
。我将其设置为45分钟(这并不理想,现在开始拖动)
问题:如何优化此计划作业?因为如果可能的话,我希望这些到期可以实时进行。我正在使用projects[x].activities[y].status.expired
和projects[x].activities[y].status.startTime
模式
const StudentSchema= new Schema({
...
projects: [
{
title: {
type: String,
required: [true, "Title is required for Project"]
},
activities: [
{
name: {
type: String,
required: true
},
gradingMechanism: {
type: String,
enum: ["Peer to Peer", "Mentor", "Instructor"]
},
totalPointsPossible: { type: Number },
status: {
text: {
type: String,
enum: [
"Not Started",
"Started",
"Awaiting Grade",
"Passing",
"Failing"
],
default: "Not Started"
},
graded: { type: Boolean, default: false },
grade: { type: Number, default: 0 },
pass: { type: Boolean, default: false },
expired: { type: Boolean, default: false },
completed: { type: Boolean, default: false },
lastSave: { type: Date },
hasStarted: { type: Boolean, default: false },
startTime: { type: Date },
completionTime: { type: Date }
}
}
],
}
],
...
CODE
// Expire Activities in Projects
schedule.scheduleJob("*/45 * * * *", async function () {
const profile = await StudentProfile.find({});
StudentProfile.find(
{ "projects.activities.status.expired": { $eq: false } },
"projects"
)
.then((profiles) => {
let promiseList = [];
profiles.map((profile) => {
const { projects } = profile;
let saveProfile = false;
projects.map((project, projKey) => {
project.activities.map((activity, actKey) => {
if (
typeof activity.status.startTime !== "undefined" &&
!activity.status.expired
) {
var startTime = new Date(activity.status.startTime);
var activityTimeout = 72;
// If startTime+<X TIME> is less than or equal to current time,
// Mark activity as expired
if (
startTime.setHours(startTime.getHours() + activityTimeout) <=
new Date()
) {
profile.projects[projKey].activities[
actKey
].status.expired = true;
profile.projects[projKey].activities[actKey].status.text =
"Awaiting Grade";
saveProfile = true;
}
}
});
});
if (saveProfile) {
promiseList.push(profile.save());
}
});
Promise.all(promiseList)
.then((profiles) => {
if (profiles.length > 0) {
console.log("Marked " + profiles.length + " profile's projects");
}
})
.catch((err) => {
console.error(err);
});
})
.catch((err) => {
console.error(err);
});
})
回答如下:当前,您的实现使用多个嵌套循环来评估返回的数据,从而达到您的要求。
这可以简化,使用数据库来满足此要求的一部分。使用$ where语句和带有计算出的到期日期的startTime
,这是一个简单的条件,消除了大多数所需的循环。
而不是返回所有未过期记录的列表,而是返回满足过期条件的所有记录的列表。 (理论上,该数量应小于未过期记录的数量)。
为了达到要求的另一部分,您将执行最后的操作(更新),因为您知道这些记录已过期(因为它们符合您的条件)。
此应该可以大大改善计划作业的性能,您不再需要评估代码中的返回值,而仅执行更新操作(这是您无法避免的事情。)>]
这里的主要问题是复杂性,您要遍历数组多次以评估不同的条件,每个其他项目都会增加处理时间和内存使用量。
TL; DR:MongoDB的部分不必很沉重,可以使用某种方法(甚至不是上面的方法)来减轻它的负担,找到一种不太复杂的方式来处理工作负载。
- https://mongoosejs/docs/api/query.html#query_Query-gt
- https://mongoosejs/docs/api/schema.html#schema_Schema-pre
关于.pre
选项,我什至会考虑对过期时间的作业进行预先调度,这也是简化逻辑的一种方式。
我个人使用Bull,Bull包含用于延迟/计划工作的机制,因此,这几乎是您寻求的实时解决方案。您需要在节点计划中找到等效的方法。
更多推荐
运行大量计划的MongoDB查询
发布评论