Реализация кеширования в памяти вместе с загрузчиками данных GraphQL

Я собираюсь добавить в свое приложение кеш в памяти, например redis, и у меня возникли некоторые проблемы с пониманием того, как все это сочетается друг с другом.

const postLoader = new DataLoader(async keys => {
    // ... sql code to get multiple posts by ids
})

const postRepository = {
    async get(id) {
        let post = await cachingImplementation.get("post:" + id)

        if (!post) {
            post = await postLoader.load(id)
        }

        return post
    }
}

Я понимаю необходимость пакетных запросов к базе данных, но применим ли тот же принцип к запросам к серверу Redis?

В этом сценарии, если я запустил метод postRepository.get 10 раз за один тик, мне пришлось бы сделать 10 разных запросов к серверу redis.

Это проблема? Следует ли переместить фактический источник выборки (кеш или базу данных) в резолвер загрузчика данных, чтобы он вместо непосредственного выполнения кода sql сначала просматривал кеш, а затем - базу данных.

Например

cache = {
  1: ...,
  2: ...
}

Если я прошу сообщения с идентификаторами 1,2,3, в кеше их будет только два. Поэтому мне пришлось бы отфильтровать существующие и запросить базу данных для оставшихся, или просто проверить, соответствует ли запрошенный идентификатор длине возвращаемых строк, и если это не так, запросить базу данных для всего.

Каковы недостатки обоих подходов? Есть ли предпочтительное решение?


person Diyan Slavov    schedule 25.07.2020    source источник


Ответы (1)


  1. Основная стоимость выполнения нескольких запросов к любой базе данных по сравнению с объединением этих запросов в один - это работа в сети. Вы не избавитесь от этих затрат, используя базу данных в памяти. Поэтому я предлагаю вам использовать кеш внутри загрузчика данных.
  2. Постепенное заполнение кеша - это способ пойти, но не забудьте указать дату истечения срока действия ключей Redis, потому что вы можете довольно скоро получить нехватку памяти, если кешируете каждую сущность.

В зависимости от вашего приложения кеш уровня API может быть для вас лучшим вариантом. Если вы используете GraphQL, проверьте Apollo Server Caching Doc.

В нашей системе мы вызываем загрузчик данных, уровень агрегации API. Здесь вы хотите иметь кеш уровня агрегации API. Я предлагаю вам обобщить этот кеш независимо от модели данных и использовать функцию более высокого порядка всякий раз, когда вы хотите кэшировать загрузчик данных.

const memo = (type, loadData) => {
  return async (keys) => {
    { cacheData, notFoundKeys } = loadFromRedis(type, keys);
    let data = cacheData;
    if (notFoundKeys.length > 0) {
      loadedData = await loadData(notFoundKeys);
      populateCache(type, notFoundKeys, data);
      data = addLoadedData(cacheData, loadedData);
    }
    return data;
  }
}

const postLoaderMemoized = memo('post', async keys => {
  // ... sql code to get multiple posts by ids
})

const postLoader = new DataLoader(postLoaderMemoized)
person HosseinAgha    schedule 25.07.2020
comment
Я просмотрел документы Apollo для кеширования, но они, похоже, не предоставляют другого механизма истечения срока, кроме тайм-аутов. Приложение, над которым я работаю, позволяет пользователям создавать сообщения и другие объекты и редактировать их, поэтому истечение срока действия не подходит, по крайней мере, на первый взгляд. У меня есть необходимость вручную сделать кеш недействительным из-за некоторых мутаций. Есть ли у них API для этого, которого я пропустил? - person Diyan Slavov; 25.07.2020
comment
Таким образом, кеш Redis (загрузчик данных) может быть для вас хорошим вариантом (мое решение выше предназначено для кеша Redis). Я только что упомянул кеш Apollo, поскольку иногда он лучше подходит для некоторых приложений. Я все еще думаю, что вам нужно использовать истечения срока действия Redis в дополнение к недействительности кеша внутри ваших мутаций. хранение всех просмотренных сообщений в Redis может довольно скоро заполнить память вашего компьютера, если у вас не будет ограниченного количества сообщений. - person HosseinAgha; 25.07.2020
comment
Да, я буду использовать тайм-ауты истечения срока действия, но обычно они будут дольше, чем я бы рассмотрел с Apollo Caching. Знаете ли вы какие-либо шаблоны, которые позволили бы немного разделить проблемы, потому что я не особенно люблю смешивать запросы кеша с запросами к базе данных, кажется, они слишком тесно связаны друг с другом. - person Diyan Slavov; 25.07.2020
comment
Мы делаем запросы к базе данных на уровне доступа к данным, наши функции уровня платформы (приложения) затем вызываем уровень данных для доступа к базе данных, и, наконец, мы вызываем функции уровня платформы из < i> Уровень агрегации API (загрузчики данных) и, наконец, уровень API вызывает загрузчики данных (а иногда и уровень платформы напрямую). Вы можете поместить кеш Redis перед любым из этих уровней, но мне нравится кеш уровня агрегации API, использующий функции высшего порядка (в вашем случае). Я также предлагаю вам взглянуть на prisma, если вы используете SQL и вам нужен более организованный уровень доступа к данным. - person HosseinAgha; 25.07.2020