IOREDIS - Ошибка при попытке перехода с Redis на KeyDB - PullRequest
6 голосов
/ 16 июня 2020

Мы использовали Redis долгое время, пока не пришли к выводу, что переход на KeyDB может быть хорошим выбором с учетом его функций.

Среда

OS: Centos 7
NodeJs: v12.18.0
Redis: v6.0.5
Targeted KeyDB: v0.0.0 (git:1069d0b4) //  keydb-cli -v showed this. Installed Using Docker.
ioredis: v4.17.3
pm2: v4.2.1 // used for clustering my application. 

Фон

Ссылаясь на документацию KeyDB, KeyDB совместим с последней версией Redis .

KeyDB остается полностью совместимым с API и протоколом модулей Redis. Таким образом, миграция с Redis на KeyDB очень проста и будет аналогична тому, что вы ожидаете при переходе с Redis на Redis. https://docs.keydb.dev/docs/migration/

На той же странице они предоставляют список клиентов Redis, совместимых с KeyDB. Список содержит ioredis, который я использую.

KeyDB совместим со всеми клиентами Redis, перечисленными здесь , поэтому это не должно вызывать беспокойства. Просто используйте свой клиент так же, как и с Redis. https://docs.keydb.dev/docs/migration/

Проблема

Как сказано в документации. Я смогу легко перейти на KeyDB за несколько часов. Что ж, это не так! По крайней мере не для меня! Я провел последние 3 дня в поисках решения на inte rnet. Я пришел к выводу, что надо писать в stackoverflow :)

Вопрос как-то интересный. Клиент на самом деле работает с KeyDb, и процесс фактически устанавливает и извлекает ключи (не уверен, но может потерять некоторые данные во время ошибки.). Но в 10% случаев это дает мне следующую ошибку, и через некоторое время продолжает работать. Поскольку я использую Redis для хранения сессий и прочего в своей производственной среде; Я не могу рискнуть проигнорировать такую ​​настойчивую ошибку.

error:  message=write EPIPE, stack=Error: write EPIPE
./app-error-1.log:37:    at WriteWrap.onWriteComplete [as oncomplete] (internal/stream_base_commons.js:92:16), errno=EPIPE, code=EPIPE, syscall=write

Я проверил почти все inte rnet на предмет этой ошибки, но никто не дал ни решения, ни объяснения того, что происходит не так.

К счастью, процесс «иногда» показывает ошибку стек за ошибку. Он указывает на lib/redis/index.ts:711 внутри кодов ioredis. Я понятия не имею, что он делает.

(stream || this.stream).write(command.toWritable());

https://github.com/luin/ioredis/blob/master/lib/redis/index.ts#L711

Я обнаружил некоторые проблемы в репозитории ioredis github, в которых упоминается некоторая ошибка EPIPE. Но большинство из них касалось обработки ошибок, и все они помечены как устраненные.

Я также обнаружил несколько общих ошибок EPIPE в Google (большинство из них касалось socket.io, который я не использую.)

Заключение

Что не так?

1 Ответ

0 голосов
/ 26 июня 2020

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

Обратите внимание, что это не канонический ответ. Но это скорее обходной путь.

Я начинаю рассказывать о том, что происходит.

Мы пытались выполнить миграцию с сервера Redis, на котором размещено около 600 000 ключей. Стандартный процесс миграции занимал много времени для переноса такого количества ключей из Redis в keyDB. Итак, я нашел другое решение.

Наша KeyDB работает на 2 серверах Active-Active реплик. Я предоставлю ссылку тем, кто интересуется, как работает эта система.

https://medium.com/faun/failover-redis-like-cluster-from-two-masters-with-keydb-9ab8e806b66c

Решение было воссоздавать наши данные Redis с использованием некоторой агрегации базы данных MongoDB и выполнение некоторых пакетных операций на KeyDB.

Вот симуляция (не совсем в качестве источника. Также я не проверял синтаксические ошибки)

const startPoint =
            (Number.parseInt(process.env.NODE_APP_INSTANCE) || 0) * 40000;
let skip = 0 + startPoint;
let limit = 1000;
let results = await SomeMongooseSchema.find({someQueries}).limit(1000).skip(skip);

let counter = 0;
while (results.length){
   if(counter > 39) break;
   for(const res of results){
      const item = {
         key: '',
         value: ''
      };
      // do some build ups on item
      ...
      // end n
      app.ioRedisClient.set(item.key, item.value);
   }
   counter++;
   skip = i * limit + startPoint;
   results = await SomeMongooseSchema.find({someQueries}).limit(limit).skip(skip);
}

Запуск этого кода в 16 процессах с использованием pm2 устанавливает все ключи в keyDB примерно за 45 минут. (по сравнению с 4-5 часами)

   pm2 start app.js -i 16 

Когда мы запускаем код на сервере Redis. Он работает, но дает следующую ошибку на KeyDB.

error:  message=write EPIPE, stack=Error: write EPIPE
./app-error-1.log:37:    at WriteWrap.onWriteComplete [as oncomplete] (internal/stream_base_commons.js:92:16), errno=EPIPE, code=EPIPE, syscall=write

Сначала я начал с настройки кода, создав транзакцию, вместо того, чтобы настраивать каждый ключ отдельно. и установил интервал в 1 секунду между каждыми 1000 операциями. код изменился следующим образом.

const startPoint =
            (Number.parseInt(process.env.NODE_APP_INSTANCE) || 0) * 40000;
let skip = 0 + startPoint;
let limit = 1000;
let results = await SomeMongooseSchema.find({someQueries}).limit(1000).skip(skip);

const batch = app.ioredisClient.multi();
let counter = 0;
while (results.length){
   if(counter > 39) break;
   for(const res of results){
      const item = {
         key: '',
         value: ''
      };
      // do some build ups on item
      ...
      // end n
      batch.set(item.key, item.value);
   }
   counter++;
   await batch.exec();
   await sleep();
   skip = i * limit + startPoint;
   results = await SomeMongooseSchema.find({someQueries}).limit(limit).skip(skip);
}

Это снизило частоту ошибок до 20 минут. Но ошибка все еще сохранялась.

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

https://download.keydb.dev/packages/rpm/centos7/x86_64/

Сделал это, и это сработало. Все ошибки исчезли, и их удалось успешно перенести за 20 минут.

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

...