使用 Firebase 进行缓存

Looker 数据洞察拥有自己的报告缓存系统。创建连接器时,您可以实现自定义缓存,以加快报告生成速度并避免 APR 速率限制。

例如,您正在创建一个连接器,用于为特定邮政编码提供过去 7 天的历史天气数据。您的连接器变得非常流行,但用于从中提取数据的外部 API 具有严格的速率限制。该 API 每天仅更新一次数据,因此对于特定的邮政编码,无需在一天内多次提取相同的数据。使用此解决方案指南,您可以为每个邮政编码实现每日缓存。

要求

  • 一个 Firebase 实时数据库。如果您无权访问任何实时数据库,请创建一个 Google Cloud Platform (GCP) 项目,并按照入门指南创建自己的 Firebase Realtime Database 实例。
  • 一个 GCP 服务帐号,用于从 Firebase Realtime Database 读写数据。
  • 一个可从来源中提取数据的社区连接器。

限制

  • 此解决方案无法与 Looker Studio 高级服务搭配使用。使用 Looker Studio 高级服务时,您在 Apps 脚本中的连接器代码无权访问数据。因此,您无法使用 Apps 脚本缓存数据。
  • 报告编辑者和查看者无法重置该特定缓存。

解决方案

实现服务账号

  1. 在 Google Cloud 项目中创建一个服务账号
  2. 确保此服务帐号在 Cloud 项目中具有 BigQuery 访问权限。
    • 必需的身份和访问权限管理 (IAM) 角色:Firebase Admin
  3. 下载 JSON 文件以获取服务账号密钥。将文件的内容存储在连接器项目的脚本属性中。添加密钥后,脚本属性应类似于 Apps 脚本界面中的以下内容:
    在脚本属性中保存服务账号密钥
  4. 在 Apps 脚本项目中添加适用于 Apps 脚本的 OAuth2 库。
  5. 为服务账号实现必需的 OAuth2 代码:
firestore-cache/src/firebase.js
var SERVICE_ACCOUNT_CREDS = 'SERVICE_ACCOUNT_CREDS';
var SERVICE_ACCOUNT_KEY = 'private_key';
var SERVICE_ACCOUNT_EMAIL = 'client_email';
var BILLING_PROJECT_ID = 'project_id';

var scriptProperties = PropertiesService.getScriptProperties();

/**
 * Copy the entire credentials JSON file from creating a service account in GCP.
 * Service account should have `Firebase Admin` IAM role.
 */
function getServiceAccountCreds() {
  return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}

function getOauthService() {
  var serviceAccountCreds = getServiceAccountCreds();
  var serviceAccountKey = serviceAccountCreds[SERVICE_ACCOUNT_KEY];
  var serviceAccountEmail = serviceAccountCreds[SERVICE_ACCOUNT_EMAIL];

  return OAuth2.createService('FirebaseCache')
    .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
    .setTokenUrl('https://accounts.google.com/o/oauth2/token')
    .setPrivateKey(serviceAccountKey)
    .setIssuer(serviceAccountEmail)
    .setPropertyStore(scriptProperties)
    .setCache(CacheService.getScriptCache())
    .setScope([
      'https://www.googleapis.com/auth/userinfo.email',
      'https://www.googleapis.com/auth/firebase.database'
    ]);
}

实现代码以便从 Firebase 中读取和向其中写入数据

您将使用 Firebase Database REST API 对 Firebase 实时数据库执行读写操作。以下代码实现了访问该 API 所需的方法。

实现 getData()

无缓存的现有 getData() 代码的结构应大致如下所示:

firestore-cache/src/without-caching.js
/*
 * This file is only to demonstrate how the `getData()` fucntion would look
 * like without the Firebase Realtime Database caching. It is not a part of
 * the connector code and should not be included in Apps Script / Clasp.
 */

function getData(request) {
  var requestedFields = getFields().forIds(
    request.fields.map(function(field) {
      return field.name;
    })
  );

  var fetchedData = fetchAndParseData(request);
  var data = getFormattedData(fetchedData, requestedFields);

  return {
    schema: requestedFields.build(),
    rows: data
  };
}

要在您的 getData() 代码中使用缓存,请按以下步骤操作:

  1. 确定应缓存数据的“区块”或“单元”。
  2. 创建一个唯一的键,以在缓存中存储最小的数据单元。
    对于示例实现,configparams 中的 zipcode 被用作键。
    可选:对于每位用户缓存,创建一个包含基本键和用户身份的复合键。实现示例:
    js var baseKey = getBaseKey(request); var userEmail = Session.getEffectiveUser().getEmail(); var hasheduserEmail = getHashedValue(userEmail); var compositeKey = baseKey + hasheduserEmail;

  3. 如果缓存数据存在,请检查缓存是否为最新数据。
    在该示例中,特定邮政编码的缓存数据会与当前日期一同保存。从缓存中检索数据时,系统会根据当前日期检查缓存的日期。

    var cacheForZipcode = {
      data: <data being cached>,
      ymd: <current date in YYYYMMDD format>
    }
    
  4. 如果缓存的数据不存在或不是最新的,则从来源提取数据并将其存储在缓存中。

在以下示例中,main.js 包含实现了缓存的 getData() 代码。

示例代码

其他资源

Chrome 用户体验连接器为数千名用户打造了一个基于约 20 GB 的 BigQuery 表格的信息中心。此连接器将 Firebase Realtime Database 与 Apps 脚本缓存服务结合使用,提供了一种双层缓存方法。如需了解实现详情,请参阅代码