OnCreateトリガーとonDeleteトリガーの両方でこの動作を時々観察しました。
両方の実行は、firestoreで作成された同じドキュメントに対して行われました。ドキュメントは1つしかないため、ハンドラーを2回トリガーする方法がわかりません。ハンドラー自体は非常に単純です。
module.exports = functions.firestore.document('notes/{noteId}').onCreate((event) => {
const db = admin.firestore();
const params = event.params;
const data = event.data.data();
// empty
});
これは常に発生するわけではありません。何が足りないのですか?
Cloud Firestoreトリガーを参照してください 制限と保証 :
関数呼び出しの配信は現在保証されていません。 CloudFirestoreとCloudFunctionsの統合が改善されるにつれて、「少なくとも1回」の配信を保証する予定です。ただし、ベータ版ではこれが常に当てはまるとは限りません。 これにより、1つのイベントに対して複数の呼び出しが発生する可能性もあります。したがって、最高品質の関数の場合は、関数がべき等になるように記述されていることを確認してください。
Firecastビデオ べき等を実装するためのヒントがあります。
私の場合、eventIdとtransactionを使用して、onCreateが2回トリガーされるのを防ぎます。
(eventIdをリストに保存し、関数が実際に頻繁にトリガーされるかどうかを確認する必要がある場合があります)
const functions = require('firebase-functions')
const admin = require('firebase-admin')
const db = admin.firestore()
exports = module.exports = functions.firestore.document('...').onCreate((snap, context) => {
const prize = 1000
const eventId = context.eventId
if (!eventId) {
return false
}
// increment money
const p1 = () => {
const ref = db.doc('...')
return db.runTransaction(t => {
return t.get(ref).then(doc => {
let money_total = 0
if (doc.exists) {
const eventIdLast = doc.data().event_id_last
if (eventIdLast === eventId) {
throw 'duplicated event'
}
const m0 = doc.data().money_total
if(m0 !== undefined) {
money_total = m0 + prize
}
} else {
money_total = prize
}
return t.set(ref, {
money_total: money_total,
event_id_last: eventId
}, {merge: true})
})
})
}
// will execute p2 p3 p4 if p1 success
const p2 = () => {
...
}
const p3 = () => {
...
}
const p4 = () => {
...
}
return p1().then(() => {
return Promise.all([p2(), p3(), p4()])
}).catch((error) => {
console.log(error)
})
})
@saranpolの回答に基づいて、今のところ以下を使用します。ただし、実際に重複するイベントIDを取得しているかどうかはまだ確認していません。
const alreadyTriggered = eventId => {
// Firestore doesn't support forward slash in ids and the eventId often has it
const validEventId = eventId.replace('/', '')
const firestore = firebase.firestore()
return firestore.runTransaction(async transaction => {
const ref = firestore.doc(`eventIds/${validEventId}`)
const doc = await transaction.get(ref)
if (doc.exists) {
console.error(`Already triggered function for event: ${validEventId}`)
return true
} else {
transaction.set(ref, {})
return false
}
})
}
// Usage
if (await alreadyTriggered(context.eventId)) {
return
}