如何将损坏的 Firestore 数据库恢复到故障点?

How would you recover a corrupt Firestore database to the point of failure?

Firestore 的 exportimport 服务使您能够将损坏的数据库恢复到所需恢复时间之前的某个时间点。但是你如何将数据库从那里前滚到实际的故障点?

您是否必须制定自己的恢复流程,并结合 transaction-logging 和 运行 措施来推动这一进程?

作为您可能需要它的原因的一个例子,假设您刚刚被告知您的 IT 员工的一名初级成员认为他们正在从公司的 Firestore 数据库中删除 collection 测试数据已经居然删了公司的Customer_Detailscollection.

您有上次自动导出 运行 的 collection 的副本 - 但现在已经有两周时间了,您的公司一直在通过其在线 t运行action 站点以快速的速度。这些客户没有纸质记录。你沉了!

在您之前的工作中,您使用了一个自动创建日志文件的 SQL 数据库,这些日志文件允许您通过从转储中重新加载然后滚动它来将损坏的数据库恢复到故障点向前。 Firestore 似乎没有同等的设施。如果是这样,您如何设计您的系统来复制此功能?

您所谓的 Cloud Firestore triggers 无需更新客户端代码即可处理 Cloud Firestore 中的事件。

您可以尝试将新更新写入 Firestore。如果成功,您的 .then 可以解决承诺并且函数可以结束。如果失败,请向 .catch 添加一些行以使用 change.before.data()

更新文档

下面是示例代码:

const admin = require('firebase-admin');
const functions = require('firebase-functions');

admin.initializeApp();

const firestore = admin.firestore();

exports.myFunction = functions.firestore
  .document('myCollection/{myDocumentId}')
  .onUpdate((change, context) => {

  const newValue = change.after.data();
  const oldValue = change.before.data();

  let myChanges = {
    // Update a value
  };

  return change.after.ref.update(myChanges)
    .then(() => {
      console.log('Document updated successfully');
      return Promise.resolve();
    })
    .catch(() => {
      // The update failed and the document needs to be rolled back
      return change.before.ref.set(oldValue);
    })
    .then(() => {
      console.log('Document was rolled back');
      return Promise.resolve();
    })
    .catch(err => {
      console.error(err);
      return Promise.reject(err);
    });
});

然后文档将 return 到它以前的值。


对于 Firestore 备份:

触发 Cloud Firestore 备份的方法有多种:

  1. Datastore Import/Export UI
  2. Firebase CLI
  3. exportDocuments API
  4. Scheduled data exports

您还可以使用 Cloud Workflows 来开发自己的恢复过程。

创建您自己的恢复过程的步骤:

  1. Create Cloud Storage Bucket(如果你已经有这些可以跳过。)

  2. 创建 Cloud Workflow 定义以执行 Firestore 导出 API 调用。

    • 要定义工作流程,请转到 Cloud Workflows 页面。
      • 如果您还没有为您的项目启用云工作流,系统将提示您启用云工作流API。确保在启用 API 后再次打开控制台。
      • 在云工作流仪表板上,点击创建。
      • 设置工作流名称和描述:例如firestoreExportDocuments
      • 选择地区:例如us-central1。点击下一步。
      • 您会注意到有一个预选的服务帐户,记住这一点。它可能有以下形式 xxxxxxxxxxx-compute@developer.gserviceaccount.com
      • 在下一页上,粘贴以下代码段,在第 5 行编辑存储桶的路径。
        - initialize:
            assign:
              - project: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
              - firestoreDatabaseId: (default)
              - firestoreBackupBucket: gs://my_bucket_for_backups
        - exportFirestoreDatabaseAll:
            call: http.post
            args:
              url: ${"https://firestore.googleapis.com/v1/projects/"+project+"/databases/"+firestoreDatabaseId+":exportDocuments"}
              auth:
                type: OAuth2
              body:
                outputUriPrefix: ${firestoreBackupBucket}
            result: result
        - returnResult:
            return: ${result}
        
      • 单击“部署”。您的工作流将得到部署。
  3. 设置 IAM 权限以执行 Cloud Workflow。

    • 转到 IAM 权限页面,并从列表中识别服务帐户。从右侧菜单中选择“编辑”选项。
      • 添加以下权限:
        • Cloud Datastore 导入导出管理员:拥有管理导入和导出的完全访问权限。
        • 存储 Object 创建者:在 GCS 中创建 objects 的权限。
        • 工作流调用者:访问执行工作流和管理执行。

    此时,您可以执行您的工作流程。当工作流能够触发 Firestore export/backup 进程时,工作流状态将显示成功状态。

  4. 通过 Cloud Scheduler 设置调用

    • 转到 Cloud Scheduler 页面,如果出现提示,请启用 API,然后重新访问“计划程序”页面。
    • 要创建计划作业,请点击创建作业:
      • 为您的日程安排程序使用名称和说明。例如:workflows-datastore-export-backup
      • 对于午夜触发器使用此语法:0 0 * * *您可以生成 cron 计划 here.
      • 在目标选择器中选择:HTTP 作为方法选择 POST
      • 输入以下内容URL: https://workflowexecutions.googleapis.com/v1beta/projects/<PROJECT-ID>/locations/us-central1/workflows/<WORKFLOW_NAME>/executions
      • 替换占位符。
      • 在显示更多部分,配置 Auth header: OAuth,并添加您以前使用的服务帐户,以及范围使用: https://www.googleapis.com/auth/cloud-platform
      • 保留其他选择的默认选择。
  5. 运行 调度器.

输出在您选择的 Cloud Storage 存储桶上,格式不可自读,文件被分成许多部分,并且有一个类似于架构和格式的清单文件。要 import/restore 备份到 Firestore/Datastore 实例,您可以从 Datastore Import/Export UI.

手动执行此操作

更新:

您也可以像您提到的那样使用 Scheduled data exports。我把它包含在上面的选项中。

根据给定的场景,一旦您在 Firestore 中删除文档,文档将永远消失。目前,没有取消删除功能,但有一种方法可以实现撤消功能。如果您有删除文档的功能,则必须修改删除功能以不实际删除文档,而只是更新 UI 以删除视图。您必须将删除安排在稍后进行。撤消选项只会恢复已删除文档的可见性并取消延迟删除。

此外,强烈建议考虑向文档添加一个新的布尔字段以标记它是否已删除,并在传递给 FirebaseUI 的查询的过滤器中使用它。该字段必须出现在每个文档中才能正确过滤。 UI 完成后,您将不得不弄清楚如何实际删除您在 UI.

中标记为删除的所有文档

如果您愿意,我还建议您创建一个 Feature Request有这种功能。