Firestore get() 似乎随机挂起

问题描述 投票:0回答:1

我在 Flutter-web 上。

我有一个 Firestore 集合

schools
,每所学校都有一个
pupils
集合。 我有大约 5000 所学校和 500000 名学生。

我想拜访每一位学生并写道

  await visitSchoolPupils(
      onSchoolPupil: (s, school, pupil) async {
        //print("School ${school.schoolName}, pupil ${pupil.userName}");
        return true;
      });

哪里

  Future<void> visitSchoolPupils({required Future<bool> Function(DocumentSnapshot s, School school, Pupil pupil) onSchoolPupil}) async {
    int start = DateTime.now().millisecondsSinceEpoch;
    int pupilCtr = 0;
    await visitSchools(
        onSchool: (s, school) async {
          print(">>>DEBUG onSchool ${school.schoolId}");
          try {
            //pick all pupils
            QuerySnapshot pupilSnapshot = await s.reference.collection(keyPupils).get();
            List<Future> pupilTasks = [];
            for (DocumentSnapshot doc in pupilSnapshot.docs) {
              JsonMap pupilData = (doc.data()) as JsonMap;
              Pupil pupil = Pupil.fromJson(pupilData);
              pupilTasks.add(onSchoolPupil(doc, school, pupil));

              // Execute in batches
              if (pupilTasks.length >= 5) {
                await Future.wait(pupilTasks);
                pupilTasks.clear();
              }
            }
            await Future.wait(pupilTasks);
            pupilTasks.clear();
            print("<<<DEBUG onSchool ${school.schoolId}");
            return true;
          } catch (e, trace) {
            print("pupil tasks1 $e $trace");
            return false;
          }
        });
    print("DEBUG visitSchoolPupils: READY took ${DateTime.now().millisecondsSinceEpoch - start} ms for $pupilCtr pupils");
  }

Future<void> visitSchools({required Future<bool> Function(DocumentSnapshot s, School school) onSchool}) async {
    int start = DateTime.now().millisecondsSinceEpoch;
    QuerySnapshot snapshot = await FirebaseFirestore.instance.collection(keySchools).get();
    int ctr=0;
    int nr = snapshot.docs.length;
    print("DEBUG visitSchools: START $nr schools");
    //for all schools
    List<Future> tasks = [];
    for (DocumentSnapshot s in snapshot.docs) {
      JsonMap schoolData = (s.data()) as JsonMap;
      School school = School.fromJson(schoolData);

      ctr++;
      if(ctr%100==0)print("DEBUG[${DateTime.now().millisecondsSinceEpoch - start}] visitSchools[$ctr] ${school.schoolId}");
      tasks.add(onSchool(s, school));

      // Execute in batches of 10 or on final iteration
      if (tasks.length >= 5) {
        await Future.wait(tasks);
        tasks.clear();
      }
    }
    await Future.wait(tasks);
    tasks.clear();
    print("DEBUG visitSchools: READY took ${DateTime.now().millisecondsSinceEpoch - start} ms");
  }

请注意,我在列表中添加了 5 个 future,并使用 Future.wait 一起等待它们(以加快速度)

此代码有时会挂起。 我注意到 5 个

onSchool
已经开始,但似乎没有一个完成。

当我仅向任务和瞳孔任务添加一个 Future 时,代码运行良好。

问题似乎是随机的,有时我会遇到 1000 所学校,有时会遇到 4000 所学校。是否存在我忽略的“线程安全”问题?我正在积累的记忆? 当某些 future 已经完成时,Future.wait 会出现问题吗?

关于如何调试此问题/改进此代码有什么建议吗?

flutter firebase dart google-cloud-firestore
1个回答
0
投票

每次您在前端应用程序中从 Firestore 加载数百或数千个文档时,您都做错了。

我建议将每个文档视为一个单独的文件。我怀疑您是否曾经从服务器加载数百个单独的文件,因此您也不应该加载那么多文档。

相反,请考虑您的应用程序实际需要从这些文档向用户显示哪些信息,然后仅加载该数据。

  1. 例如,您可能会显示学校名称列表。不要加载所有学校文档来显示名称,而是创建一个仅包含学校名称的集合 - 每个文档包含多个学校名称,并具有某种哈希机制。

  2. 接下来,考虑您是否真的立即需要数百或数千个学校名称。如果您在列表中显示它们,则一次可能无法容纳超过数十个(在移动设备上)或数百个(桌面设备),因此这就是您应该加载的全部内容。您可以使用

    count()
    查询确定有多少个学校名称(以确定列表的长度),然后使用基于游标的分页仅获取前N个名称并按需加载其余名称.

  3. 如果结合前两种方法,您可以按照学校名称文档中所需的任何排序顺序存储名称 - 然后仅加载其中的一两个。

如果您不熟悉 NoSQL 数据建模,我建议您阅读 NoSQL 数据建模技术 并观看 了解 Cloud Firestore。现在花几个小时在这些上,将为您节省更多时间。


需要考虑的其他一些(非数据建模)更改:

© www.soinside.com 2019 - 2024. All rights reserved.