Как улучшить производительность запроса кеша FireStore - PullRequest
0 голосов
/ 22 октября 2019

Я разрабатываю PWA, который отображает список транзакций (транзакция - это объект с ~ 10 полями). Я использую Firestore для хранения и обновлений в реальном времени, и у меня также включено постоянство .

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

В начале приложения я хочу убедиться, что данные загружены, поэтому я использую один раз кэш запрос для получения транзакций. Я ожидал бы, что запрос будет почти мгновенным, но на ноутбуке требуется около 1 секунды, чтобы получить исходные данные (и у меня также есть другая коллекция, которую я извлекаю из кэша, и она разрешается через ~ 2 секунды послезапрос транзакций). Для мобильных устройств это занимает около ~ 9 секунд ( загрузка на мобильный , загрузка на ноутбук )

Я хочу свое приложениечтобы чувствовать себя мгновенно, но мне нужно несколько секунд, пока данные на месте. Обратите внимание, что я не выполняю никаких дополнительных запросов (я просто хочу загрузить данные в память).

Я что-то не так делаю? Я прочитал документы Firestore, но я неНе думаю, что объем данных, которые у меня есть в кеше, должен вызывать такую ​​плохую производительность.

ОБНОВЛЕНИЕ: Даже если я ограничу первоначальный запрос только 20 документами. Для их получения все еще требуется ~ 2 секунды.
ОБНОВЛЕНИЕ 2: Код выглядит следующим образом:

export const initializeFirestore = (): Thunk => (dispatch) => {
  const initialQueries: Array<Promise<unknown>> = []
  getQueries().forEach((query) => {
    const q = query.createFirestoneQuery()
    initialQueries.push(
      q
        .get({
          source: 'cache',
        })
        .then((snapshot) =>
          dispatch(firestoneChangeAction(query, snapshot, true)),
        ),
    )
    q.onSnapshot((change) => {
      dispatch(firestoneChangeAction(query, change))
    })
  })

  console.log('Now I am just waiting for initial data...')
  return Promise.all(initialQueries)
}

Ответы [ 2 ]

1 голос
/ 22 октября 2019

Вас может заинтересовать продуманный подход, представленный инженерами Firebase во время сессии «Ускоренные веб-приложения с Firebase» на саммите Firebase 2019 (видео можно посмотреть здесь: https://www.youtube.com/watch?v=DHbVyRLkX4c).

В двух словахих идея состоит в том, чтобы использовать API-интерфейс REST Firestore для выполнения первого запроса к базе данных (для которой не требуется загружать какой-либо SDK) и параллельно динамически импортировать Web SDK, чтобы использовать его для последующих запросов.

Репозиторий github находится здесь: https://github.com/hsubox76/fireconf-demo


Я вставляю ниже содержимого файла ключа js (https://github.com/hsubox76/fireconf-demo/blob/master/src/dynamic.js) для дальнейшего использования.

import { firebaseConfigDynamic as firebaseConfig } from "./shared/firebase-config";
import { renderPage, logPerformance } from "./shared/helpers";

let firstLoad = false;

// Firestore REST URL for "current" collection.
const COLLECTION_URL =
  `https://firestore.googleapis.com/v1/projects/exchange-rates-adcf6/` +
  `databases/(default)/documents/current`;


// STEPS
// 1) Fetch REST data
// 2) Render data
// 3) Dynamically import Firebase components
// 4) Subscribe to Firestore



// HTTP GET from Firestore REST endpoint.
fetch(COLLECTION_URL)
  .then(res => res.json())
  .then(json => {
    // Format JSON data into a tabular format.
    const stocks = formatJSONStocks(json);

    // Measure time between navigation start and now (first data loaded)
    performance && performance.measure("initialDataLoadTime");

    // Render using initial REST data.
    renderPage({
      title: "Dynamic Loading (no Firebase loaded)",
      tableData: stocks
    });

    // Import Firebase library.
    dynamicFirebaseImport().then(firebase => {
      firebase.initializeApp(firebaseConfig);
      firebase.performance(); // Use Firebase Performance - 1 line
      subscribeToFirestore(firebase);
    });
  });

/**
 * FUNCTIONS
 */

// Dynamically imports firebase/app, firebase/firestore, and firebase/performance.
function dynamicFirebaseImport() {
  const appImport = import(
    /* webpackChunkName: "firebase-app-dynamic" */
    "firebase/app"
  );
  const firestoreImport = import(
    /* webpackChunkName: "firebase-firestore-dynamic" */
    "firebase/firestore"
  );
  const performanceImport = import(
    /* webpackChunkName: "firebase-performance-dynamic" */
    "firebase/performance"
  );
  return Promise.all([appImport, firestoreImport, performanceImport]).then(
    ([dynamicFirebase]) => {
      return dynamicFirebase;
    }
  );
}

// Subscribe to "current" collection with `onSnapshot()`.
function subscribeToFirestore(firebase) {
  firebase
    .firestore()
    .collection(`current`)
    .onSnapshot(snap => {
      if (!firstLoad) {
        // Measure time between navigation start and now (first data loaded)
        performance && performance.measure("realtimeDataLoadTime");
        // Log to console for internal development
        logPerformance();
        firstLoad = true;
      }
      const stocks = formatSDKStocks(snap);
      renderPage({
        title: "Dynamic Loading (Firebase now loaded)",
        tableData: stocks
      });
    });
}

// Format stock data in JSON format (returned from REST endpoint)
function formatJSONStocks(json) {
  const stocks = [];
  json.documents.forEach(doc => {
    const pathParts = doc.name.split("/");
    const symbol = pathParts[pathParts.length - 1];
    stocks.push({
      symbol,
      value: doc.fields.closeValue.doubleValue || 0,
      delta: doc.fields.delta.doubleValue || 0,
      timestamp: parseInt(doc.fields.timestamp.integerValue)
    });
  });
  return stocks;
}

// Format stock data in Firestore format (returned from `onSnapshot()`)
function formatSDKStocks(snap) {
  const stocks = [];
  snap.forEach(docSnap => {
    if (!docSnap.data()) return;
    const symbol = docSnap.id;
    const value = docSnap.data().closeValue;
    stocks.push({
      symbol,
      value,
      delta: docSnap.data().delta,
      timestamp: docSnap.data().timestamp
    });
  });
  return stocks;
}
0 голосов
/ 22 октября 2019

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

Для первого запроса в вашем приложении будет включено время, необходимое для полной инициализации SDK, что может включать асинхронную работу, выходящую за рамки простого запроса. ,Также имейте в виду, что чтение и сортировка данных с локального диска не обязательно являются «быстрыми», и что для больших объемов документов чтение из локального дискового кэша может быть даже более дорогим, чем время, необходимое для извлечения тех же документов. сеть.

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

Если вы считаете, что то, что вы испытываете, является ошибкой, то, пожалуйста, сообщите об ошибкеотчет на GitHub .

...