Скажем, я реализовал два триггера, которые прослушивают созданных и удаленных пользователей в базе данных реального времени, чтобы отслеживать количество пользователей:
import * as functions from "firebase-functions"
import * as admin from "firebase-admin"
admin.initializeApp()
const db = admin.database()
const userRef = functions.runWith({failurePolicy: true}).database.ref("users/{userId}")
export const onUserCreate = userRef.onCreate(() =>
db.ref("nUsers").set(admin.database.ServerValue.increment(1)))
export const onUserDelete = userRef.onDelete(() =>
db.ref("nUsers").set(admin.database.ServerValue.increment(-1)))
Насколько я понимаю, поскольку эти функции гарантированно срабатывают ровно один раз, с включенным failurePolicy
это приведет к в конечном итоге согласованному счету, даже если события происходят не по порядку.
Но что, если я хочу обновить свое приложение до облачных функций 2-го поколения? Если новая функция будет развернута первой, есть риск, что пользователи будут учтены дважды. Если сначала удалить старую функцию, есть риск, что пользователи вообще не будут учитываться. Есть ли способ удалить одну функцию и добавить другую как единую атомарную операцию?
Я могу придумать два других решения:
Любые лучшие решения?
@DougStevenson В этом посте в основном говорится об использовании идентификатора события в качестве ключа идемпотентности. Здесь это невозможно, поскольку я предполагаю, что версия gen2 будет иметь другой идентификатор события. Идентификатор пользователя может быть еще одним возможным ключом идемпотентности, но он не работает, если пользователи создаются и добавляются несколько раз (идентификатор — это просто идентификатор пользователя Firebase Auth, поэтому он будет использоваться повторно).
Вам не нужно предполагать - вы можете попробовать это сами. Создайте проект с обоими типами функций и посмотрите, запускаются ли они с одним и тем же идентификатором или нет. Если они не совпадают, вы можете синтезировать свой собственный уникальный идентификатор, используя хэш каждого события, используя полезные данные, которые не меняются.
Могу подтвердить, что две системы событий создают разные идентификаторы событий. Я не думаю, что это решаемо без изменения структуры пользовательских данных или алгоритма подсчета. В этой последовательности: [1. Алиса создана, 2. Алиса удалена, 3. Алиса создана] невозможно различить события 1 и 3. Тем не менее, я хочу, чтобы счет увеличивался оба раза. Это работает только с одной функцией, потому что сам триггер происходит только один раз, но и с gen2 это невозможно.
Вы можете добавить случайную строку к каждой полезной нагрузке и использовать ее, чтобы убедиться, что хэш уникален для каждого события.
Это был бы вариант, просто назначьте случайную строку пользователю при его создании. Похоже, что метка времени события по крайней мере идентична для gen1 и gen2, поэтому, возможно, «Пользователь X удален во время Y» достаточно уникален. Я смотрел на себя немного слепо, пытаясь найти идеальное решение, но я думаю, что это сводится к проблеме двух генералов, поэтому точное решение, вероятно, невозможно.
Завершив эту миграцию, я попытаюсь дать ответ на самом деле.
Мой вопрос немного неверный, поскольку он предполагает, что Google Cloud несет ответственность за выполнение этой атомарной операции. Нет. Предположение было основано на официальных образцах, таких как this, которые используют ту же реализацию счетчика.
Это вводит в заблуждение, так как на самом деле это не очень хорошая реализация, если вам нужен постоянный подсчет. Есть много способов, которыми ваша функция может быть вызвана несколько раз, включая переключение на 2-е поколение, переименование вашей функции или перемещение ее в другой регион. Вместо этого вам нужно найти способ обеспечить идемпотентность самостоятельно. Этот пост в блоге — хороший источник вдохновения. Однако обратите внимание, что использование описанного здесь идентификатора события не работает, поскольку он отличается для облачных функций 1-го и 2-го поколений.
В моем случае мне удалось реализовать систему аренды, где уникальный токен представляет собой хэш идентификатора пользователя и метку времени создания или удаления. Временная метка, по-видимому, соответствует между 1-м и 2-м поколениями. В зависимости от того, как структурированы ваши данные, в вашем случае может быть лучший способ решить эту проблему.
Идемпотентность уже является лучшей практикой для облачных функций, независимо от того, что вам нужно сделать. Как минимум, это включает в себя обеспечение того, чтобы одно и то же событие не вступало в силу дважды. cloud.google.com/blog/products/serverless/…