首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >onUpdate与事务文档锁

onUpdate与事务文档锁
EN

Stack Overflow用户
提问于 2020-11-05 17:47:45
回答 1查看 66关注 0票数 0

我有一个案例,一个用户想注册一个课程。

如果课程已满,则用户可在等候名单上登记。如果为此课程打开了一个空闲插槽,该用户将自动注册。为了实现等待列表自动化,我使用了一个带有.onUpdate触发器的云函数(CF),它可以工作。

CF示例:课程已满。参与者取消注册。CF启动并检查课程中是否有空位,以及等候名单上的用户是否尚未注册。如果是这样,则CF从课程中的等待列表中注册用户。

同时,在UI中,当一个插槽可用时,我允许已经在等候名单上的同一个用户注册该课程。通过注册课程,我把他从等候名单上除名。

对于这个实现,我使用了一个transaction,它只在用户尚未在课程中注册时才能完成。

由于某些原因,用户有时会注册两次。

我的问题是: onUpdate函数是否锁定文档?我是否需要在transaction函数中使用onUpdate来侦听同一文档的更改?对我来说,=>这样做是没有意义的。

处理这个错误的最简单的方法是让用户先从等候名单中注销,然后注册课程。不过,我想了解如何才能实现上述逻辑。

在颤振交易:

代码语言:javascript
复制
@override
  Future<void> selfSetCourseParticipantWithStashedCreditPoint(
      String courseEventId, Participant participantToAdd) async {
    try {
      UsedCreditPointInfo participantsCreditPoint =
          participantToAdd.usedCreditPointInfo;
      await _firestore.runTransaction((transaction) async {
        // 1) Check if the customer does still have an available credit point left
        final userRef = _usersCollection.doc(participantToAdd.uid);
        var userDocumentSnapshot = await transaction.get(userRef);
        var stashedCreditPoints =
            userDocumentSnapshot.data()['${participantToAdd.creditPointPath}'];
        var creditPointExists = false;
        for (var usedCreditPointInfoMap in stashedCreditPoints) {
          var usedCreditPoint =
              UsedCreditPointInfo.fromMap(usedCreditPointInfoMap);
          if (usedCreditPoint == participantsCreditPoint) {
            creditPointExists = true;
            break;
          }
        }
        // if the credit point does not exist, then the transaction must fail
        if (!creditPointExists) return null;

        final courseRef = _courseEventsCollection.doc(courseEventId);
        final courseEventDocumentSnapshot = await transaction.get(courseRef);
        // 2) if the courseEvent document exists I have to check first if there are slots left
        var currentCourseEvent = CourseEvent.fromEntity(
            CourseEventEntity.fromSnapshot(courseEventDocumentSnapshot));
        //todo: add the remaining requirements, e.g., registration date limit
        if (currentCourseEvent.participants.length >=
            currentCourseEvent.maxParticipants) {
          return null;
        }

        if (currentCourseEvent.waitingList != null) {
          var waitingList = currentCourseEvent.waitingList;
          for (var i = 0; i < waitingList.length; i++) {
            final waitingListParticipant = waitingList[i];
            if (waitingListParticipant.uid == participantToAdd.uid) {
              waitingList.removeAt(i);
              break;
            }
          }
        }

        // add only if the participant is not already included to the list
        if (!currentCourseEvent.participants.contains(participantToAdd)) {
          currentCourseEvent = currentCourseEvent.copyWith(
            participants: List.from(currentCourseEvent.participants)
              ..add(participantToAdd),
          );

          // the user document exists at this point, thus update is sufficient
          transaction.update(
            userRef,
            {
              '${participantToAdd.creditPointPath}': FieldValue.arrayRemove(
                  [participantToAdd.usedCreditPointInfo?.toMap()])
            },
          );
          return transaction.set(
              courseRef, currentCourseEvent.toEntity().toMap());
        }
      });
    } catch (e, s) {
      return handleFirestoreError(e, s);
    }
  }

一段我的CF:

代码语言:javascript
复制
export const notifyOnCourseEventUpdateAndWaitingListRegistration = functions.firestore
  .document("CourseEvents/{courseEvent}")
  .onUpdate(async (change, context) => {
    return firestore
      .runTransaction(async (transaction) => {
        const courseEventRef = firestore
          .collection("CourseEvents")
          .doc(change.after.data().firebaseId);
        const courseEventSnapshot = await transaction.get(courseEventRef);
        const courseEvent = courseEventSnapshot.data() as CourseEvent;

        const currentCourseEvent =  change.after.data() as CourseEvent;
        const currentParticipants = currentCourseEvent.participants;
        const previousCourseEvent = change.before.data() as CourseEvent;
        const previousParticipants = previousCourseEvent.participants;
        const currentMaxParticipants = currentCourseEvent.maxParticipants;
        const currentWaitingList = currentCourseEvent.waitingList;
        ...
      })
    });
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-11-05 21:09:43

onUpdate函数是否锁定文档?

不是的。

,我是否需要onUpdate函数中的事务来侦听同一文档的更改?

我不太清楚这个问题在问什么,但无论如何,如果您希望更新两段并发运行的代码,以安全地更新同一个文档而不覆盖彼此,那么您就需要一个事务。

事务不“监听”文档中的更改。当检测到冲突时,它们只是通过重试事务处理程序函数来确保两个客户端在对同一个文档进行事务处理时不能相互覆盖。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64702630

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档