Выбор между onSnapshot и облачными функциями в Firebase

Вопрос или проблема

useEffect(()=>{
    if(!user){
      return;
    }
    const creationTime = user.metadata?.creationTime;

    if (!creationTime) {
      setLoading(false);
      return;
    }

    const userCreationDate = new Date(creationTime);
    const currentDate = new Date();
    const diffTime = Math.abs(currentDate.getTime() - userCreationDate.getTime());
    const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));

    const db = getFirestore(app);
    const userRef = doc(db, 'users', user.uid);

    if (diffDays < 7) {
      setIsSubscribed(true);
      setLoading(false);
      return;
    }

    const unsubscribeSnapshot = onSnapshot(userRef, (userDoc) => {
      console.log('В unsubscribeSnapshot');
      if (userDoc.exists()) {
        const userData = userDoc.data();
        const subscribed = userData.isSubscribed;

        if (!subscribed) {
          router.push('/signUp');
          setLoading(false);
          return;
        }
        setIsSubscribed(subscribed);
      } else {
        setIsSubscribed(false);
        router.push('/signUp');
      }
      setLoading(false);
    });

    setUnsubscribeSnapshot(() => unsubscribeSnapshot);

    return () => {
      if (unsubscribeSnapshot) {
        unsubscribeSnapshot();
      }
    };
  },[router, user]);

import { FirebaseApp } from 'firebase/app';
import { getFirestore, doc, setDoc } from 'firebase/firestore';
import { getAuth } from 'firebase/auth';

const updateSubscriptionStatus = async (
  app: FirebaseApp,
  isSubscribed: boolean,
  customerId: string,
) => {
  const auth = getAuth(app);
  const userId = auth.currentUser?.uid;
  if (!userId) {
    console.error('Пользователь не аутентифицирован');
    throw new Error('Пользователь не аутентифицирован');
  }

  const db = getFirestore(app);
  const userRef = doc(db, 'users', userId);
  await setDoc(userRef, { isSubscribed, stripeCustomerId: customerId }, { merge: true });
};

export default updateSubscriptionStatus;

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!stripe || !elements) {
      return;
    }
    setIsLoading(true);

    const result = (await stripe.confirmPayment({
      elements,
      redirect: 'if_required',
    })) as PaymentIntentResult;

    if (result.error) {
      setMessage(result.error.message || 'Произошла неожиданная ошибка.');
    } else if (result.paymentIntent && result.paymentIntent.status === 'succeeded') {
      try {
        await updateSubscriptionStatus(app, true, customerId);
        setMessage('Платеж успешен!');
        router.push('/thanks');
      } catch (error) {
        setMessage('Не удалось обновить статус подписки.');
        console.error(error);
      }
    } else {
      setMessage('Обработка платежа.');
    }
    setIsLoading(false);
  };

В настоящее время я управляю системой подписки, в которой каждый пользователь получает семидневный бесплатный пробный период. В течение этого пробного периода локальная переменная isSubscribed устанавливается в значение true, что позволяет пользователю получать доступ ко всем страницам. После окончания пробного периода isSubscribed устанавливается в значение false, и соответствующая переменная в Firestore создается и обновляется до значения false. Это ограничивает доступ ко всем страницам, и пользователь перенаправляется на страницу регистрации.

После завершения пользователем процесса регистрации и настройки его ежемесячной подписки как переменная isSubscribed в Firestore, так и локальная переменная isSubscribed обновляются до значения true, восстанавливая доступ к страницам.

Я также настроил веб-хук Stripe API для автоматического обновления статуса isSubscribed в Firestore на основе успешности или неудачи ежемесячных платежей, что, в свою очередь, обновляет локальную переменную isSubscribed.

Мой вопрос в том, следует ли использовать Cloud Functions вместо onSnapshot, учитывая, что у меня будет примерно 1000 пользователей, каждый из которых пройдет семидневный бесплатный пробный период, и статус их isSubscribed будет определяться их ежемесячными платежами.

В настоящее время я использую onSnapshot, и он работает хорошо, но меня беспокоит его производительность и затраты.

Ответ или решение

При принятии решения между использованием onSnapshot из Firestore и облачными функциями для управления подписками пользователей, необходимо учитывать несколько факторов, таких как производительность, стоимость и архитектура вашего приложения. Давайте разберем каждый из элементов этого выбора.

Использование onSnapshot

onSnapshot предоставляет возможность прослушивать изменения в реальном времени для документа или коллекции в Firestore. Это означает, что когда данные о пользователе в базе данных обновляются, приложение автоматически получает эти изменения без необходимости вручную запрашивать данные.

Преимущества:

  1. Реальное время: Вы можете сразу же отобразить изменения в состоянии подписки и предоставлении доступа для пользователей.
  2. Упрощение логики: У вас будет меньше логики для обработки обновлений данных, так как Firestore сам управляет этими потоками.

Недостатки:

  1. Производительность: Каждый раз, когда вызывается onSnapshot, происходит подписка на отдельный документ. Если у вас 1000 пользователей, это может привести к значительному количеству одновременных соединений с Firestore и увеличить нагрузку на клиентское приложение.
  2. Стоимость: Использование onSnapshot может привести к большим затратам, если пользователи часто обновляют свои данные или если данные часто изменяются в Firestore, так как вы будете платить за каждый документ, который прочитан и изменён.

Использование облачных функций

Облачные функции позволяют выполнять серверный код в ответ на события, такие как HTTP-запросы или изменения в Firestore. Вы можете использовать их для управления состоянием подписки каждого пользователя и обновления данных в Firestore при получении уведомлений от Stripe.

Преимущества:

  1. Эффективность: Облачные функции работают на сервере, что означает меньшую нагрузку на клиентскую часть вашего приложения, особенно с увеличением числа пользователей.
  2. Контроль и масштабируемость: Вы можете более гибко обрабатывать большой объём данных и события. Например, изменить бизнес-логику при разных условиях, таких как успешная или неудачная оплата.
  3. Сокращение затрат: Облачные функции могут быть более экономичной моделью, так как возможность платите только за выполнение функции, а не за каждое изменение в документе Firestore.

Недостатки:

  1. Сложность реализации: Требуется дополнительная логика для реализации взаимодействия облачных функций с Firestore и Stripe.
  2. Задержка: В некоторых случаях обработка событий через облачные функции может занять больше времени, чем использование onSnapshot, особенно если требуются дополнительные запросы к Firestore.

Рекомендации

С учетом всех вышеперечисленных факторов, если у вас 1000 пользователей с потенциально частыми изменениями состояния подписки, рекомендую рассмотреть использование облачных функций. Это не только поможет упростить управление состоянием подписки, но и уменьшит нагрузку на клиентскую часть и потенциальные затраты.

Вы можете использовать облачные функции для обработки вызовов от Stripe и обновления поля isSubscribed в Firestore. После этого вы можете периодически запрашивать состояние подписки на клиенте или использовать статические методы для получения состояния при загрузке.

Кроме того, если в вашем приложении крайне важно мгновенное обновление данных, можно рассмотреть комбинированный подход: использовать onSnapshot для уведомлений о более рутинных изменениях и облачные функции для более критических операций.

Заключение

Переход на облачные функции может повысить производительность и снизить затраты по мере роста числа пользователей. Поэтому, учитывая особенности вашего приложения и количество пользователей, использование облачных функций представляется более целесообразным решением на долгосрочную перспективу.

Оцените материал
Добавить комментарий

Капча загружается...