backend-manager 5.11.4 → 5.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -39,6 +39,9 @@ const moment = require('moment');
|
|
|
39
39
|
const pushid = require('pushid');
|
|
40
40
|
const notification = require('../../../libraries/notification.js');
|
|
41
41
|
const { getNextFutureOccurrence } = require('../../../libraries/email/constants.js');
|
|
42
|
+
// firebase-admin v13 dropped the admin.firestore.FieldValue static — import
|
|
43
|
+
// the modular way (same pattern as admin/send-email.js).
|
|
44
|
+
const { FieldValue } = require('firebase-admin/firestore');
|
|
42
45
|
|
|
43
46
|
// How long a 'processing' lease is honored before the campaign of a crashed or
|
|
44
47
|
// timed-out run is reclaimed for retry. Must comfortably exceed the function
|
|
@@ -147,7 +150,7 @@ module.exports = async ({ Manager, assistant, libraries }) => {
|
|
|
147
150
|
await doc.ref.set({
|
|
148
151
|
status: 'pending',
|
|
149
152
|
sendAt: nextSendAt,
|
|
150
|
-
generatorAttempts:
|
|
153
|
+
generatorAttempts: FieldValue.delete(),
|
|
151
154
|
metadata: { updated: stamp() },
|
|
152
155
|
}, { merge: true });
|
|
153
156
|
|
|
@@ -216,7 +219,7 @@ module.exports = async ({ Manager, assistant, libraries }) => {
|
|
|
216
219
|
await doc.ref.set({
|
|
217
220
|
status: 'pending',
|
|
218
221
|
sendAt: nextSendAt,
|
|
219
|
-
generatorAttempts:
|
|
222
|
+
generatorAttempts: FieldValue.delete(),
|
|
220
223
|
metadata: { updated: stamp() },
|
|
221
224
|
}, { merge: true });
|
|
222
225
|
|
|
@@ -226,7 +229,7 @@ module.exports = async ({ Manager, assistant, libraries }) => {
|
|
|
226
229
|
await doc.ref.set({
|
|
227
230
|
status: success ? 'sent' : 'failed',
|
|
228
231
|
results: campaignResults,
|
|
229
|
-
generatorAttempts:
|
|
232
|
+
generatorAttempts: FieldValue.delete(),
|
|
230
233
|
metadata: { updated: stamp() },
|
|
231
234
|
}, { merge: true });
|
|
232
235
|
|
package/src/manager/index.js
CHANGED
|
@@ -1127,11 +1127,11 @@ Manager.prototype.setupFunctions = function (exporter, options) {
|
|
|
1127
1127
|
.onRun((context) => self.EventMiddleware({ context }).run(`${cron}/daily.js`));
|
|
1128
1128
|
|
|
1129
1129
|
// Frequent cron runs the inline newsletter generator (AI structure + section
|
|
1130
|
-
// images + article + uploads) — needs the v1 max timeout
|
|
1131
|
-
//
|
|
1132
|
-
//
|
|
1130
|
+
// images + article + uploads) — needs the v1 max timeout. If a run times out
|
|
1131
|
+
// or OOMs, the campaign lease reclaim in marketing-campaigns.js retries it
|
|
1132
|
+
// safely.
|
|
1133
1133
|
exporter.bm_cronFrequent =
|
|
1134
|
-
fn({memory: '
|
|
1134
|
+
fn({memory: '256MB', timeoutSeconds: 540})
|
|
1135
1135
|
.pubsub.schedule('*/10 * * * *')
|
|
1136
1136
|
.onRun((context) => self.EventMiddleware({ context }).run(`${cron}/frequent.js`));
|
|
1137
1137
|
};
|
|
@@ -255,7 +255,11 @@ module.exports = {
|
|
|
255
255
|
const unknownGenerator = await firestore.get('marketing-campaigns/_test-unknown-generator');
|
|
256
256
|
const unknownType = await firestore.get('marketing-campaigns/_test-unknown-type');
|
|
257
257
|
|
|
258
|
-
|
|
258
|
+
// 'sent'/'failed' — NOT merely !== 'pending': the claim flips the
|
|
259
|
+
// doc to 'processing' first, and proceeding on that mid-flight
|
|
260
|
+
// state races the send (oneoff-campaign-processed then reads
|
|
261
|
+
// 'processing' and fails).
|
|
262
|
+
return ['sent', 'failed'].includes(oneoff?.status)
|
|
259
263
|
&& recurring?.sendAt !== state.pastSendAt
|
|
260
264
|
&& recurringStale?.sendAt !== state.staleSendAt
|
|
261
265
|
&& ['sent', 'failed'].includes(staleProcessing?.status)
|
|
@@ -47,7 +47,7 @@ module.exports = {
|
|
|
47
47
|
|
|
48
48
|
{
|
|
49
49
|
name: 'no-wait-gets-clobbered',
|
|
50
|
-
async run({ Manager, assert }) {
|
|
50
|
+
async run({ Manager, assert, skip }) {
|
|
51
51
|
const admin = Manager.libraries.admin;
|
|
52
52
|
const testUid = '_test-race-no-wait';
|
|
53
53
|
const testEmail = '_test.race-no-wait@test.com';
|
|
@@ -76,7 +76,16 @@ module.exports = {
|
|
|
76
76
|
const doc = await userRef.get();
|
|
77
77
|
const survived = doc.exists && !!(doc.data()?.api?.clientId);
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
// The race resolves either way: when the emulator happens to finish
|
|
80
|
+
// the on-delete BEFORE the re-create's onCreate writes the doc, the
|
|
81
|
+
// dangerous late-clobber ordering never materializes that run —
|
|
82
|
+
// scheduler luck, not a regression. Only the clobber outcome is
|
|
83
|
+
// assertable; the benign ordering skips.
|
|
84
|
+
if (survived) {
|
|
85
|
+
await admin.auth().deleteUser(testUid).catch(() => {});
|
|
86
|
+
await pollUntilGone(userRef);
|
|
87
|
+
return skip('on-delete completed before the re-create this run — race did not manifest');
|
|
88
|
+
}
|
|
80
89
|
|
|
81
90
|
await admin.auth().deleteUser(testUid).catch(() => {});
|
|
82
91
|
await pollUntilGone(userRef);
|