Pull to refresh

Comments 11

Следующим шагом будут HaaS и AaaS — Hashmap-as-a-Service и Array-as-a-Service. С REST-интерфейсом, конечно же, куда ж без того.
Redis расшифровывается как REmote DIctionary, например. Так что уже есть.
А сможете сделать возможность собирать только вычислительную часть (без сервера и сохранения на диск) в виде библиотеки? Тогда можно будет, например, интегрировать фильтр с nginx или своим серверным приложением.
Да, все функции, связанные с фильтром Блума уже отделены.

Можно просто добавить файлы bf_* и md6_* в директорию с исходниками модуля и заинкудить заголовочные файлы bf_*.h.

Можно собрать полноценную библиотеку .so и далее пользоваться ею:
$ gcc -shared -fPIC bf_*.c md6_*.c -o libbloom.so
$ cat > libtest.c
#include "bf_types.h"
#include "bf_ops.h"
#include <stdio.h>

void main() {
    if (bf_create(1,1))
        puts("OK");
}
<CTRL+D>
$ gcc -L. -o libtest libtest.c -lbloom
$ LD_LIBRARY_PATH=. ./libtest
OK


В случае с nginx проблема только в том, что у каждого воркера своя память, поэтому придётся создавать структуру в мастер-процессе в общей для всех воркеров памяти: или IPC SHM, или mmap с флагом MAP_ANONYMOUS. Второй вариант может быть быстрее и гарантирует, что память будет освобождена при любом завершении процесса. Однако он не является частью стандарта POSIX. Первый вариант требует очистки, так как сегменты общей памяти IPC не связаны с конкретным процессом и продолжают существовать после его выхода. Судя по исходникам nginx и модулей, которые используют общую память, в nginx принят первый подход и есть внутреннее API для получения общей зоны.

Для использования общей памяти между процессами придётся сделать версии bf_create() и bf_destroy(), использующие общую память.
Redis ограничивает длину битового поля в 512 мегабайт. Все реализации, которые я смог найти (1, 2) рассчитаны на использование только одного ключа Redis. Это ограничивает размер фильтра.

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

Для redis есть http_api, например такое. Можно поставить его и написать процедуру на lua в которой будет вся логика.
HTTP намного более громоздкий чем родной redis-а, соотвественно часть времени будет тратится на парсинг и формирования запросов.
Ну и самое главное:
1) В redis есть журнал операций — соотвественно если инстанс редиса прибить он восстановит из журнала все последние записи. В вашем случае могут потерятся записи за последние 300 секунд.
2) В redis имеется репликация, если один инстанс ляжет — приложение может сходить в другой.
Ну а так вроде всё норм, только ещё бы batch запросы добавить.
Почему бы просто не запатчить redis с целью поддержки более длинных битовых полей?

Эта работа ведётся с 2012го года. Там есть много ограничений, от формата хранения строк до сообщений между кластерами и хранением. Даже когда эта работа будет проделана, всё равно потребуется сделать на стороне хэшера ширину хэша больше 32 бит. Поэтому, раз уж всё равно подстраиваться, лучше использовать несколько ключей редиса.

1) В redis есть журнал операций — соотвественно если инстанс редиса прибить он восстановит из журнала все последние записи. В вашем случае могут потерятся записи за последние 300 секунд.
По умолчанию в Redis-е AOF выключен, но да — функция стоящая. В моём случае реализовать её довольно просто, не нужно даже точно помнить позицию, откуда проигрывать — есть только одна операция и она идемпотентная. Меня сдерживает от реализации только тот факт, что при постоянном большом наплыве добавляемых элементов и длительной просадке дисковой производительности возникнет ситуация, что или будет разрастаться буфер записи бинлога, или придётся задерживать операции добавления. Так можно получить SIGKILL от OOM Killer-а в ситуации, когда в общем-то можно нормально работать.
2) В redis имеется репликация, если один инстанс ляжет — приложение может сходить в другой.
Не так просто, чтобы записать в слэйв что-то, нужно сначала разорвать его репликацию с упавшим мастером.

Принимая это всё во внимание, можно надёжно организовать кластеризацию параллельной записью в сразу несколько фильтров. При чтении результатом будет логическое ИЛИ от ответов всех фильтров. Такой подход даёт высокие гарантии, не ломает предсказуемость потребления памяти фильтрами и ничего не стоит для приложения — для параллельных запросов с таймаутами есть простые инструменты.
Принимая это всё во внимание, можно надёжно организовать кластеризацию параллельной записью в сразу несколько фильтров.

Плохой вариант. Что будет если сначала лежал сервер A, а потом лежал сервер B? В итоге в сервер A никогда не доедут записи из сервера B, происходящие в тот момент когда A лежал, и наоборот. Параллельная запись всегда приводит к рассинхронам.
Равное состояние фильтров не является самоцелью, они представляют интерес только в совокупности.
Попробую ещё раз объяснить ситуацию. В случае если есть две реплики и они ложатся поочередно ваш агрегированный bloom фильтр будет давать неверные ответы. Цепь событий следующая:
  • ложится нода A
  • приходит запись 'key1'. Её хеши попадают только в ноду B
  • поднимается нода A
  • ложится нода B
  • приходит запрос на проверку наличия записи 'key1'. Нода A отвечает что бит нулевой. Нода B лежит и делать «ИЛИ» не с чем.

Этот сценарий вполне реальный когда поочередно ложатся разные дата центры к примеру.
Про batch-запросы: на данный момент можно использовать HTTP pipelining:
$ cat > batch.txt
GET /check?e=123 HTTP/1.1
Host: localhost

GET /check?e=456 HTTP/1.1
Host: localhost

$ nc 0 8889 < batch.txt 
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Date: Wed, 06 Jul 2016 14:53:23 GMT
Content-Length: 8

MISSING
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Date: Wed, 06 Jul 2016 14:53:23 GMT
Content-Length: 8

MISSING


Но лучше, конечно, внутри одного запроса — это я учту.
В итоге на каждый запрос по 120 байт ответа (вместо одного бита в идеале). Уж лучше хоть какой-то батчинг (а ещё лучше кастомный протокол).
Sign up to leave a comment.

Articles