backend-manager 5.9.26 → 5.9.27

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "5.9.26",
3
+ "version": "5.9.27",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "files": [
@@ -3,6 +3,11 @@ const moment = require('moment');
3
3
  // Must match the SEND_AT_LIMIT in email.js
4
4
  const SEND_AT_LIMIT = 71;
5
5
 
6
+ // Permanent failures (bad data, deleted user) are removed immediately.
7
+ // Temporary failures (network, SendGrid outage) retry up to MAX_RETRIES
8
+ // before being removed. With the 10-minute cron cycle, 5 retries ≈ 50 minutes.
9
+ const MAX_RETRIES = 5;
10
+
6
11
  /**
7
12
  * Email queue processor cron job
8
13
  *
@@ -32,25 +37,35 @@ module.exports = async ({ Manager, assistant, context, libraries }) => {
32
37
  assistant.log(`Processing ${snapshot.size} queued email(s)...`);
33
38
 
34
39
  const email = Manager.Email(assistant);
40
+ let sent = 0;
41
+ let dropped = 0;
42
+ let retried = 0;
35
43
 
36
- const results = await Promise.allSettled(snapshot.docs.map(async (doc) => {
37
- const { settings } = doc.data();
44
+ await Promise.allSettled(snapshot.docs.map(async (doc) => {
45
+ const data = doc.data();
46
+ const { settings } = data;
38
47
  const emailId = doc.id;
48
+ const retries = data.retries || 0;
39
49
 
40
- const result = await email.send(settings);
41
- assistant.log(`Queued email ${emailId} ${result.status}`);
42
-
43
- await doc.ref.delete();
44
- }));
45
-
46
- const sent = results.filter(r => r.status === 'fulfilled').length;
47
- const failed = results.filter(r => r.status === 'rejected').length;
50
+ try {
51
+ const result = await email.send(settings);
52
+ assistant.log(`Queued email ${emailId} ${result.status}`);
53
+ await doc.ref.delete();
54
+ sent++;
55
+ } catch (e) {
56
+ const isPermanent = e.code >= 400 && e.code < 500;
48
57
 
49
- for (const r of results) {
50
- if (r.status === 'rejected') {
51
- assistant.error(`Failed to send queued email: ${r.reason?.message}`, r.reason);
58
+ if (isPermanent || retries >= MAX_RETRIES) {
59
+ assistant.error(`Dropping queued email ${emailId} after ${retries} retries: ${e.message}`);
60
+ await doc.ref.delete();
61
+ dropped++;
62
+ } else {
63
+ assistant.warn(`Queued email ${emailId} failed (retry ${retries + 1}/${MAX_RETRIES}): ${e.message}`);
64
+ await doc.ref.set({ retries: retries + 1, lastError: e.message }, { merge: true });
65
+ retried++;
66
+ }
52
67
  }
53
- }
68
+ }));
54
69
 
55
- assistant.log(`Completed! (${sent} sent, ${failed} failed)`);
70
+ assistant.log(`Completed! (${sent} sent, ${dropped} dropped, ${retried} retried)`);
56
71
  };