Skip to content

Commit a59be4d

Browse files
Ehesplaurenzlong
authored andcommitted
[multiple] Migrate to Firestore transactions for writes (#65)
* [firestore-translate-text] Move write to transaction * [firestore-shorten-urls-bitly] Move write to transaction * [firestore-send-email] Move write to transaction * [delete-user-data] Move write to transaction
1 parent bf18fbe commit a59be4d

File tree

8 files changed

+105
-49
lines changed

8 files changed

+105
-49
lines changed

delete-user-data/functions/lib/index.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,13 @@ const clearFirestoreData = (firestorePaths, uid) => __awaiter(this, void 0, void
116116
try {
117117
const isRecursive = config_1.default.firestoreDeleteMode === 'recursive';
118118
if (!isRecursive) {
119+
const firestore = admin.firestore();
119120
logs.firestorePathDeleting(path, false);
120-
yield admin
121-
.firestore()
122-
.doc(path)
123-
.delete();
121+
// Wrapping in transaction to allow for automatic retries (#48)
122+
yield firestore.runTransaction((transaction => {
123+
transaction.delete(firestore.doc(path));
124+
return Promise.resolve();
125+
}));
124126
logs.firestorePathDeleted(path, false);
125127
}
126128
else {

delete-user-data/functions/src/index.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,14 @@ const clearFirestoreData = async (firestorePaths: string, uid: string) => {
121121
const isRecursive = config.firestoreDeleteMode === 'recursive';
122122

123123
if (!isRecursive) {
124+
const firestore = admin.firestore();
124125
logs.firestorePathDeleting(path, false);
125-
await admin
126-
.firestore()
127-
.doc(path)
128-
.delete();
126+
127+
// Wrapping in transaction to allow for automatic retries (#48)
128+
await firestore.runTransaction((transaction => {
129+
transaction.delete(firestore.doc(path));
130+
return Promise.resolve();
131+
}));
129132
logs.firestorePathDeleted(path, false);
130133
} else {
131134
logs.firestorePathDeleting(path, true);

firestore-send-email/functions/lib/index.js

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,18 @@ function validateFieldArray(field, array) {
5858
}
5959
function processCreate(snap) {
6060
return __awaiter(this, void 0, void 0, function* () {
61-
return snap.ref.update({
62-
delivery: {
63-
startTime: admin.firestore.FieldValue.serverTimestamp(),
64-
state: "PENDING",
65-
attempts: 0,
66-
error: null,
67-
},
68-
});
61+
// Wrapping in transaction to allow for automatic retries (#48)
62+
return admin.firestore().runTransaction((transaction => {
63+
transaction.update(snap.ref, {
64+
delivery: {
65+
startTime: admin.firestore.FieldValue.serverTimestamp(),
66+
state: "PENDING",
67+
attempts: 0,
68+
error: null,
69+
},
70+
});
71+
return Promise.resolve();
72+
}));
6973
});
7074
}
7175
function preparePayload(payload) {
@@ -211,7 +215,11 @@ function deliver(payload, ref) {
211215
update["delivery.error"] = e.toString();
212216
logs.deliveryError(ref, e);
213217
}
214-
return ref.update(update);
218+
// Wrapping in transaction to allow for automatic retries (#48)
219+
return admin.firestore().runTransaction((transaction => {
220+
transaction.update(ref, update);
221+
return Promise.resolve();
222+
}));
215223
});
216224
}
217225
function processWrite(change) {
@@ -233,18 +241,26 @@ function processWrite(change) {
233241
return null;
234242
case "PROCESSING":
235243
if (payload.delivery.leaseExpireTime.toMillis() < Date.now()) {
236-
return change.after.ref.update({
237-
"delivery.state": "ERROR",
238-
error: "Message processing lease expired.",
239-
});
244+
// Wrapping in transaction to allow for automatic retries (#48)
245+
return admin.firestore().runTransaction((transaction => {
246+
transaction.update(change.after.ref, {
247+
"delivery.state": "ERROR",
248+
error: "Message processing lease expired.",
249+
});
250+
return Promise.resolve();
251+
}));
240252
}
241253
return null;
242254
case "PENDING":
243255
case "RETRY":
244-
yield change.after.ref.update({
245-
"delivery.state": "PROCESSING",
246-
"delivery.leaseExpireTime": admin.firestore.Timestamp.fromMillis(Date.now() + 60000),
247-
});
256+
// Wrapping in transaction to allow for automatic retries (#48)
257+
yield admin.firestore().runTransaction((transaction => {
258+
transaction.update(change.after.ref, {
259+
"delivery.state": "PROCESSING",
260+
"delivery.leaseExpireTime": admin.firestore.Timestamp.fromMillis(Date.now() + 60000),
261+
});
262+
return Promise.resolve();
263+
}));
248264
return deliver(payload, change.after.ref);
249265
}
250266
});

firestore-send-email/functions/src/index.ts

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,18 @@ function validateFieldArray(field: string, array?: string[]) {
8686
}
8787

8888
async function processCreate(snap: FirebaseFirestore.DocumentSnapshot) {
89-
return snap.ref.update({
90-
delivery: {
91-
startTime: admin.firestore.FieldValue.serverTimestamp(),
92-
state: "PENDING",
93-
attempts: 0,
94-
error: null,
95-
},
96-
});
89+
// Wrapping in transaction to allow for automatic retries (#48)
90+
return admin.firestore().runTransaction((transaction => {
91+
transaction.update(snap.ref, {
92+
delivery: {
93+
startTime: admin.firestore.FieldValue.serverTimestamp(),
94+
state: "PENDING",
95+
attempts: 0,
96+
error: null,
97+
},
98+
});
99+
return Promise.resolve();
100+
}));
97101
}
98102

99103
async function preparePayload(payload: QueuePayload): Promise<QueuePayload> {
@@ -277,7 +281,11 @@ async function deliver(
277281
logs.deliveryError(ref, e);
278282
}
279283

280-
return ref.update(update);
284+
// Wrapping in transaction to allow for automatic retries (#48)
285+
return admin.firestore().runTransaction((transaction => {
286+
transaction.update(ref, update);
287+
return Promise.resolve();
288+
}));
281289
}
282290

283291
async function processWrite(change) {
@@ -302,20 +310,28 @@ async function processWrite(change) {
302310
return null;
303311
case "PROCESSING":
304312
if (payload.delivery.leaseExpireTime.toMillis() < Date.now()) {
305-
return change.after.ref.update({
306-
"delivery.state": "ERROR",
307-
error: "Message processing lease expired.",
308-
});
313+
// Wrapping in transaction to allow for automatic retries (#48)
314+
return admin.firestore().runTransaction((transaction => {
315+
transaction.update(change.after.ref, {
316+
"delivery.state": "ERROR",
317+
error: "Message processing lease expired.",
318+
});
319+
return Promise.resolve();
320+
}));
309321
}
310322
return null;
311323
case "PENDING":
312324
case "RETRY":
313-
await change.after.ref.update({
314-
"delivery.state": "PROCESSING",
315-
"delivery.leaseExpireTime": admin.firestore.Timestamp.fromMillis(
316-
Date.now() + 60000
317-
),
318-
});
325+
// Wrapping in transaction to allow for automatic retries (#48)
326+
await admin.firestore().runTransaction((transaction => {
327+
transaction.update(change.after.ref, {
328+
"delivery.state": "PROCESSING",
329+
"delivery.leaseExpireTime": admin.firestore.Timestamp.fromMillis(
330+
Date.now() + 60000
331+
),
332+
});
333+
return Promise.resolve();
334+
}));
319335
return deliver(payload, change.after.ref);
320336
}
321337
}

firestore-shorten-urls-bitly/functions/lib/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ const shortenUrl = (snapshot) => __awaiter(this, void 0, void 0, function* () {
119119
});
120120
const updateShortUrl = (snapshot, url) => __awaiter(this, void 0, void 0, function* () {
121121
logs.updateDocument(snapshot.ref.path);
122-
yield snapshot.ref.update(config_1.default.shortUrlFieldName, url);
122+
// Wrapping in transaction to allow for automatic retries (#48)
123+
yield admin.firestore().runTransaction((transaction => {
124+
transaction.update(snapshot.ref, config_1.default.shortUrlFieldName, url);
125+
return Promise.resolve();
126+
}));
123127
logs.updateDocumentComplete(snapshot.ref.path);
124128
});

firestore-shorten-urls-bitly/functions/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,11 @@ const updateShortUrl = async (
139139
): Promise<void> => {
140140
logs.updateDocument(snapshot.ref.path);
141141

142-
await snapshot.ref.update(config.shortUrlFieldName, url);
142+
// Wrapping in transaction to allow for automatic retries (#48)
143+
await admin.firestore().runTransaction((transaction => {
144+
transaction.update(snapshot.ref, config.shortUrlFieldName, url);
145+
return Promise.resolve();
146+
}));
143147

144148
logs.updateDocumentComplete(snapshot.ref.path);
145149
};

firestore-translate-text/functions/lib/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ const translateString = (string, targetLanguage) => __awaiter(this, void 0, void
153153
});
154154
const updateTranslations = (snapshot, translations) => __awaiter(this, void 0, void 0, function* () {
155155
logs.updateDocument(snapshot.ref.path);
156-
yield snapshot.ref.update(config_1.default.outputFieldName, translations);
156+
// Wrapping in transaction to allow for automatic retries (#48)
157+
yield admin.firestore().runTransaction((transaction) => {
158+
transaction.update(snapshot.ref, config_1.default.outputFieldName, translations);
159+
return Promise.resolve();
160+
});
157161
logs.updateDocumentComplete(snapshot.ref.path);
158162
});

firestore-translate-text/functions/src/index.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,10 @@ const translateString = async (
185185
try {
186186
logs.translateInputString(string, targetLanguage);
187187

188-
const [translatedString] = await translate.translate(string, targetLanguage);
188+
const [translatedString] = await translate.translate(
189+
string,
190+
targetLanguage
191+
);
189192

190193
logs.translateStringComplete(string, targetLanguage);
191194

@@ -202,7 +205,11 @@ const updateTranslations = async (
202205
): Promise<void> => {
203206
logs.updateDocument(snapshot.ref.path);
204207

205-
await snapshot.ref.update(config.outputFieldName, translations);
208+
// Wrapping in transaction to allow for automatic retries (#48)
209+
await admin.firestore().runTransaction((transaction) => {
210+
transaction.update(snapshot.ref, config.outputFieldName, translations);
211+
return Promise.resolve();
212+
});
206213

207214
logs.updateDocumentComplete(snapshot.ref.path);
208215
};

0 commit comments

Comments
 (0)