Skip to content

Commit 63dc0ca

Browse files
authored
Merge pull request #7453 from Countly/SER-2851-alerts-dont-get-triggered-on-correct-time
[SER-2851] Update alert jobs to use app timezone
2 parents 1004f43 + 59c642f commit 63dc0ca

File tree

15 files changed

+212
-431
lines changed

15 files changed

+212
-431
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## Version 25.03.X
22
Fixes:
33
- [core] Fixed duplicate conditional in form field template
4+
- [alerts] Fixed alert jobs using system's timezone instead of application's
45

56
Enterprise Fixes:
67
- [data-manager] Fix notification message after editing user property

plugins/alerts/api/alertModules/cohorts.js

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,24 @@ const log = require('../../../../api/utils/log.js')('alert:cohorts');
66
const moment = require('moment-timezone');
77
const common = require('../../../../api/utils/common.js');
88
const commonLib = require("../parts/common-lib.js");
9-
const { ObjectId } = require('mongodb');
10-
11-
module.exports.check = async function({ alertConfigs: alert, done, scheduledTo: date }) {
12-
const app = await common.readBatcher.getOne("apps", { _id: new ObjectId(alert.selectedApps[0]) });
13-
if (!app) {
14-
log.e(`App ${alert.selectedApps[0]} couldn't be found`);
15-
return done();
16-
}
179

10+
module.exports.check = async function({ alert, app, done, scheduledTo: date }) {
1811
let { period, alertDataSubType2, compareType, compareValue } = alert;
1912
compareValue = Number(compareValue);
2013

2114
const metricValue = await getCohortMetricByDate(app, alertDataSubType2, date, period) || 0;
15+
log.d(alert._id, "value on", date, "is", metricValue);
2216

2317
if (compareType === commonLib.COMPARE_TYPE_ENUM.MORE_THAN) {
2418
if (metricValue > compareValue) {
19+
log.d(alert._id, "triggered because", metricValue, "is more than", compareValue);
2520
await commonLib.trigger({ alert, app, metricValue, date }, log);
2621
}
2722
}
2823
else {
2924
const before = moment(date).subtract(1, commonLib.PERIOD_TO_DATE_COMPONENT_MAP[period]).toDate();
3025
const metricValueBefore = await getCohortMetricByDate(app, alertDataSubType2, before, period);
26+
log.d(alert._id, "value on", before, "is", metricValueBefore);
3127
if (!metricValueBefore) {
3228
return done();
3329
}
@@ -38,6 +34,7 @@ module.exports.check = async function({ alertConfigs: alert, done, scheduledTo:
3834
: change <= -compareValue;
3935

4036
if (shouldTrigger) {
37+
log.d(alert._id, "triggered because", compareType, String(change) + "%");
4138
await commonLib.trigger({ alert, app, date, metricValue, metricValueBefore }, log);
4239
}
4340
}
@@ -83,14 +80,3 @@ async function getCohortMetricByDate(app, cohortId, date, period) {
8380
}
8481
}
8582

86-
/*
87-
(async function() {
88-
await new Promise(res => setTimeout(res, 2000));
89-
const app = { _id: ObjectId("65c1f875a12e98a328d5eb9e"), timezone: "Europe/Istanbul" };
90-
const date = new Date("2024-02-07T12:00:00.000Z");
91-
const cohort = "3bcc37740d564419586ec26b66ea7c32";
92-
let monthlyData = await getCohortMetricByDate(app, cohort, date, "monthly");
93-
let dailyData = await getCohortMetricByDate(app, cohort, date, "daily");
94-
console.log(monthlyData, dailyData);
95-
})();
96-
*/

plugins/alerts/api/alertModules/crashes.js

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const log = require('../../../../api/utils/log.js')('alert:crashes');
66
const moment = require('moment-timezone');
77
const common = require('../../../../api/utils/common.js');
88
const commonLib = require("../parts/common-lib.js");
9-
const { ObjectId } = require('mongodb');
109

1110
const METRIC_TO_PROPERTY_MAP = {
1211
"non-fatal crashes/errors per session": "crnfses",
@@ -49,26 +48,23 @@ async function triggerByEvent(payload) {
4948
}
5049

5150

52-
module.exports.check = async function({ alertConfigs: alert, done, scheduledTo: date }) {
53-
const app = await common.readBatcher.getOne("apps", { _id: new ObjectId(alert.selectedApps[0]) });
54-
if (!app) {
55-
log.e(`App ${alert.selectedApps[0]} couldn't be found`);
56-
return done();
57-
}
58-
51+
module.exports.check = async function({ alert, app, done, scheduledTo: date }) {
5952
let { alertDataSubType, period, compareType, compareValue, filterValue } = alert;
6053
compareValue = Number(compareValue);
6154

6255
const metricValue = await calculateMetricByDate(app, alertDataSubType, date, period, filterValue) || 0;
56+
log.d(alert._id, "value on", date, "is", metricValue);
6357

6458
if (compareType === commonLib.COMPARE_TYPE_ENUM.MORE_THAN) {
6559
if (metricValue > compareValue) {
60+
log.d(alert._id, "triggered because", metricValue, "is more than", compareValue);
6661
await commonLib.trigger({ alert, app, metricValue, date }, log);
6762
}
6863
}
6964
else {
7065
const before = moment(date).subtract(1, commonLib.PERIOD_TO_DATE_COMPONENT_MAP[period]).toDate();
7166
const metricValueBefore = await calculateMetricByDate(app, alertDataSubType, before, period, filterValue);
67+
log.d(alert._id, "value on", before, "is", metricValueBefore);
7268
if (!metricValueBefore) {
7369
return done();
7470
}
@@ -79,6 +75,7 @@ module.exports.check = async function({ alertConfigs: alert, done, scheduledTo:
7975
: change <= -compareValue;
8076

8177
if (shouldTrigger) {
78+
log.d(alert._id, "triggered because", compareType, String(change) + "%");
8279
await commonLib.trigger({ alert, app, date, metricValue, metricValueBefore }, log);
8380
}
8481
}
@@ -87,7 +84,7 @@ module.exports.check = async function({ alertConfigs: alert, done, scheduledTo:
8784
};
8885

8986
/**
90-
* Abstraction on top of getCrashDataByDate to calculate composite metrics.
87+
* Abstraction on top of getCrashDataByDate to calculate composite metrics.
9188
* Possible metricStrings:
9289
* - non-fatal crashes/errors per session
9390
* - fatal crashes/errors per session
@@ -174,15 +171,3 @@ async function getCrashDataByDate(app, metric, date, period, versions) {
174171
}
175172
return number;
176173
}
177-
178-
/*
179-
(async function() {
180-
await new Promise(res => setTimeout(res, 2000));
181-
const app = { _id: ObjectId("65c1f875a12e98a328d5eb9e"), timezone: "Europe/Istanbul" };
182-
const date = new Date("2024-02-07T12:00:00.000Z");
183-
let monthlyData = await getCrashDataByDate(app, "cr_u", date, "monthly");
184-
let dailyData = await getCrashDataByDate(app, "cr_u", date, "daily", ["4:02:0", "4:01:2"]);
185-
console.log(monthlyData, dailyData);
186-
console.log(await calculateMetricByDate(app, "# of crashes/errors", date, "daily"));
187-
})();
188-
*/

plugins/alerts/api/alertModules/dataPoints.js

Lines changed: 24 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,54 +6,40 @@ const log = require('../../../../api/utils/log.js')('alert:dataPoints');
66
const moment = require('moment-timezone');
77
const common = require('../../../../api/utils/common.js');
88
const commonLib = require("../parts/common-lib.js");
9-
const { ObjectId } = require('mongodb');
109

1110
const DATA_POINT_PROPERTY = "dp";
1211

13-
module.exports.check = async function({ alertConfigs: alert, done, scheduledTo: date }) {
14-
const selectedApp = alert.selectedApps[0];
15-
let apps;
16-
if (selectedApp === "all") {
17-
apps = await common.readBatcher.getMany("apps", {});
18-
}
19-
else {
20-
apps = [await common.readBatcher.getOne("apps", { _id: new ObjectId(selectedApp) })];
21-
}
12+
module.exports.check = async function({ alert, app, done, scheduledTo: date }) {
13+
let { period, compareType, compareValue } = alert;
14+
compareValue = Number(compareValue);
2215

23-
for (let app of apps) {
24-
if (!app) {
25-
log.e(`App ${alert.selectedApps[0]} couldn't be found`);
26-
continue;
27-
}
16+
const metricValue = await getDataPointByDate(app, date, period) || 0;
17+
log.d(alert._id, "value on", date, "is", metricValue);
2818

29-
let { period, compareType, compareValue } = alert;
30-
compareValue = Number(compareValue);
31-
32-
const metricValue = await getDataPointByDate(app, date, period) || 0;
33-
34-
if (compareType === commonLib.COMPARE_TYPE_ENUM.MORE_THAN) {
35-
if (metricValue > compareValue) {
36-
await commonLib.trigger({ alert, app, metricValue, date }, log);
37-
}
19+
if (compareType === commonLib.COMPARE_TYPE_ENUM.MORE_THAN) {
20+
if (metricValue > compareValue) {
21+
log.d(alert._id, "triggered because", metricValue, "is more than", compareValue);
22+
await commonLib.trigger({ alert, app, metricValue, date }, log);
23+
}
24+
}
25+
else {
26+
const before = moment(date).subtract(1, commonLib.PERIOD_TO_DATE_COMPONENT_MAP[period]).toDate();
27+
const metricValueBefore = await getDataPointByDate(app, before, period);
28+
log.d(alert._id, "value on", before, "is", metricValueBefore);
29+
if (!metricValueBefore) {
30+
return done();
3831
}
39-
else {
40-
const before = moment(date).subtract(1, commonLib.PERIOD_TO_DATE_COMPONENT_MAP[period]).toDate();
41-
const metricValueBefore = await getDataPointByDate(app, before, period);
42-
if (!metricValueBefore) {
43-
continue;
44-
}
4532

46-
const change = (metricValue / metricValueBefore - 1) * 100;
47-
const shouldTrigger = compareType === commonLib.COMPARE_TYPE_ENUM.INCREASED_BY
48-
? change >= compareValue
49-
: change <= -compareValue;
33+
const change = (metricValue / metricValueBefore - 1) * 100;
34+
const shouldTrigger = compareType === commonLib.COMPARE_TYPE_ENUM.INCREASED_BY
35+
? change >= compareValue
36+
: change <= -compareValue;
5037

51-
if (shouldTrigger) {
52-
await commonLib.trigger({ alert, app, date, metricValue, metricValueBefore }, log);
53-
}
38+
if (shouldTrigger) {
39+
log.d(alert._id, "triggered because", compareType, String(change) + "%");
40+
await commonLib.trigger({ alert, app, date, metricValue, metricValueBefore }, log);
5441
}
5542
}
56-
5743
done();
5844
};
5945

@@ -117,17 +103,3 @@ function dailySum(dailyData) {
117103
}
118104
return dailyValue;
119105
}
120-
/*
121-
(async function() {
122-
await new Promise(res => setTimeout(res, 2000));
123-
const app = { _id: ObjectId("65c1f875a12e98a328d5eb9e"), timezone: "Europe/Istanbul" };
124-
const date1 = new Date("2024-01-07T10:00:00.000Z");
125-
const date2 = new Date("2024-02-07T10:00:00.000Z");
126-
const date3 = new Date("2024-03-07T10:00:00.000Z");
127-
let monthlyData1 = await getDataPointByDate(app, date1, "monthly");
128-
let monthlyData2 = await getDataPointByDate(app, date2, "monthly");
129-
let monthlyData3 = await getDataPointByDate(app, date3, "monthly");
130-
console.log("monthly:", monthlyData1, monthlyData2, monthlyData3);
131-
console.log("all:", monthlyData1 + monthlyData2 + monthlyData3);
132-
})();
133-
*/

plugins/alerts/api/alertModules/events.js

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const log = require('../../../../api/utils/log.js')('alert:events');
66
const moment = require('moment-timezone');
77
const common = require('../../../../api/utils/common.js');
88
const commonLib = require("../parts/common-lib.js");
9-
const { ObjectId } = require('mongodb');
109

1110
const METRIC_TO_PROPERTY_MAP = {
1211
// these are directly being stored in db
@@ -20,13 +19,7 @@ const METRIC_TO_PROPERTY_MAP = {
2019

2120
const AVERAGE_METRICS = ["average sum", "average duration"];
2221

23-
module.exports.check = async({ alertConfigs: alert, done, scheduledTo: date }) => {
24-
const app = await common.readBatcher.getOne("apps", { _id: new ObjectId(alert.selectedApps[0]) });
25-
if (!app) {
26-
log.e(`App ${alert.selectedApps[0]} couldn't be found`);
27-
return done();
28-
}
29-
22+
module.exports.check = async({ alert, app, done, scheduledTo: date }) => {
3023
let { alertDataSubType, alertDataSubType2, period, compareType, compareValue, filterKey, filterValue } = alert;
3124
const metricProp = METRIC_TO_PROPERTY_MAP[alertDataSubType];
3225
let segments;
@@ -45,9 +38,11 @@ module.exports.check = async({ alertConfigs: alert, done, scheduledTo: date }) =
4538
}
4639
metricValue /= count;
4740
}
41+
log.d(alert._id, "value on", date, "is", metricValue);
4842

4943
if (compareType === commonLib.COMPARE_TYPE_ENUM.MORE_THAN) {
5044
if (metricValue > compareValue) {
45+
log.d(alert._id, "triggered because", metricValue, "is more than", compareValue);
5146
await commonLib.trigger({ alert, app, metricValue, date }, log);
5247
}
5348
}
@@ -67,13 +62,15 @@ module.exports.check = async({ alertConfigs: alert, done, scheduledTo: date }) =
6762
}
6863
metricValueBefore /= count;
6964
}
65+
log.d(alert._id, "value on", before, "is", metricValueBefore);
7066

7167
const change = (metricValue / metricValueBefore - 1) * 100;
7268
const shouldTrigger = compareType === commonLib.COMPARE_TYPE_ENUM.INCREASED_BY
7369
? change >= compareValue
7470
: change <= -compareValue;
7571

7672
if (shouldTrigger) {
73+
log.d(alert._id, "triggered because", compareType, String(change) + "%");
7774
await commonLib.trigger({ alert, app, date, metricValue, metricValueBefore }, log);
7875
}
7976
}
@@ -155,27 +152,3 @@ async function getEventMetricByDate(app, event, metric, date, period, segments)
155152
}
156153
return number;
157154
}
158-
/*
159-
(async function() {
160-
if (!require("cluster").isPrimary) {
161-
return;
162-
}
163-
await new Promise(res => setTimeout(res, 2000));
164-
const app = { _id: "67fff00d901abe2f8cc57646", timezone: "Europe/Istanbul" };
165-
const date = new Date("2025-02-02T12:47:19.247Z");
166-
const event = "Product Viewed";
167-
const prop = "c";
168-
169-
const hourly = await getEventMetricByDate(app, event, prop, date, "hourly");
170-
console.assert(hourly === 5, "hourly event data doesn't match");
171-
172-
const daily = await getEventMetricByDate(app, event, prop, date2, "daily", { "Delivery Type": "Express" });
173-
console.assert(daily === 22, "daily segmented event data doesn't match");
174-
175-
const monthly = await getEventMetricByDate(app, event, prop, date2, "monthly");
176-
console.assert(monthly === 5120, "monthly event data doesn't match");
177-
178-
const monthlySegmented = await getEventMetricByDate(app, event, prop, date2, "monthly", { "Delivery Type": "Express" });
179-
console.assert(monthlySegmented === 2535, "monthly segmented event data doesn't match");
180-
})();
181-
*/

plugins/alerts/api/alertModules/nps.js

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const log = require('../../../../api/utils/log.js')('alert:nps');
66
const moment = require('moment-timezone');
77
const common = require('../../../../api/utils/common.js');
88
const commonLib = require("../parts/common-lib.js");
9-
const { ObjectId } = require('mongodb');
109

1110
module.exports.triggerByEvent = triggerByEvent;
1211
/**
@@ -50,26 +49,23 @@ async function triggerByEvent(payload) {
5049
}
5150
}
5251

53-
module.exports.check = async function({ alertConfigs: alert, done, scheduledTo: date }) {
54-
const app = await common.readBatcher.getOne("apps", { _id: new ObjectId(alert.selectedApps[0]) });
55-
if (!app) {
56-
log.e(`App ${alert.selectedApps[0]} couldn't be found`);
57-
return done();
58-
}
59-
52+
module.exports.check = async function({ alert, app, done, scheduledTo: date }) {
6053
let { period, alertDataSubType2, compareType, compareValue, filterValue } = alert;
6154
compareValue = Number(compareValue);
6255

6356
const metricValue = await getResponsesByDate(app, alertDataSubType2, date, period, filterValue) || 0;
57+
log.d(alert._id, "value on", date, "is", metricValue);
6458

6559
if (compareType === commonLib.COMPARE_TYPE_ENUM.MORE_THAN) {
6660
if (metricValue > compareValue) {
61+
log.d(alert._id, "triggered because", metricValue, "is more than", compareValue);
6762
await commonLib.trigger({ alert, app, metricValue, date }, log);
6863
}
6964
}
7065
else {
7166
const before = moment(date).subtract(1, commonLib.PERIOD_TO_DATE_COMPONENT_MAP[period]).toDate();
7267
const metricValueBefore = await getResponsesByDate(app, alertDataSubType2, before, period, filterValue);
68+
log.d(alert._id, "value on", before, "is", metricValueBefore);
7369
if (!metricValueBefore) {
7470
return done();
7571
}
@@ -80,6 +76,7 @@ module.exports.check = async function({ alertConfigs: alert, done, scheduledTo:
8076
: change <= -compareValue;
8177

8278
if (shouldTrigger) {
79+
log.d(alert._id, "triggered because", compareType, String(change) + "%");
8380
await commonLib.trigger({ alert, app, date, metricValue, metricValueBefore }, log);
8481
}
8582
}
@@ -164,19 +161,3 @@ function sumOfAllResponses(scope, nps, score) {
164161
return numberOfResponses;
165162
}
166163

167-
/*
168-
(async function() {
169-
const app = {name: "test", _id: new ObjectId("6600901a71159e99a3434253"), timezone: "Europe/Istanbul", plugins: null };
170-
const nps = "6600909ed476e1837317dc52";
171-
const date = new Date("2024-09-16T12:00:00.000Z");
172-
173-
let data = await getResponsesByDate(app, nps, date, "monthly");
174-
console.log("monthly:", data);
175-
176-
data = await getResponsesByDate(app, nps, date, "daily");
177-
console.log("daily:", data);
178-
179-
data = await getResponsesByDate(app, nps, date, "hourly");
180-
console.log("hourly:", data);
181-
})();
182-
*/

0 commit comments

Comments
 (0)