Улучшения в Manticore Search 2.7: управление локальными индексами

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

Индексы могут быть большими или даже огромными, и они совместно используются рабочими процессами. На многоядерном ЦП можно одновременно запускать множество запросов, которые будут распределяться по ядрам с использованием одного и того же индекса. Это просто и понятно. Однако иногда требуется обновить индексы. Бесшовная ротация хорошо работала со старыми рабочими процессами fork: мы просто загружаем новые файлы индекса, оставляя работающие форки обслуживать старыми. И в один момент новые запросы переходят к форкам, которые уже используют вновь загруженный индекс. Таким образом, нет задержек при обслуживании: вы просто плавно переходите от старого индекса к новому. Рабочие процессы, использовавшие старую версию, завершаются (или аварийно завершаются), и, наконец, предыдущий индекс освобождается.

В случае потоковых рабочих процессов использовался механизм простых RW-блокировок - ‘блокировки с общим и монопольным доступом’, где многие совместные рабочие процессы (читатели) могут одновременно обращаться к ресурсу, но один монопольный рабочий процесс (писатель) может его изменять. Таким образом, индексы всегда были либо ‘общими’ между запросами, либо ‘заблокированными’ при ротации. Они загружались в начале и освобождались в конце. Когда выполнялась ротация, один поток загружал новый индекс, но ему требовался монопольный доступ к дескриптору активного индекса. Однако рабочие процессы запросов имеют совместный доступ, и никто не мешает им получать новые задачи. Это означает, что при высокой нагрузке невозможно выполнить “бесшовную” ротацию из-за последнего ее этапа - атомарной замены старого индекса на новый.

В новой модели, представленной в версии 2.7, индексы теперь существуют между потоками независимо, как в старом случае с ‘fork’, так как они теперь не ‘общие’ и не ‘заблокированные’, а просто неизменяемые. Это упрощает работу: рабочий процесс просто не заботится о ‘блокировке’ или ‘совместном использовании’ индекса, он просто использует его. Когда требуется ротация (загрузка нового индекса), демон просто выполняет ее без какой-либо заботы о выполняющихся запросах (и их потоки также не заботятся о “писателе”). Наконец, ротация просто переключает указатель активного индекса на вновь загруженный, и все работает как с форками: старые запросы по-прежнему используют предыдущий индекс, новые направляются на только что загруженный. Единственная ситуация, где по-прежнему используется монопольная блокировка - при выполнении операций ОБНОВЛЕНИЯ.

Еще одно преимущество новой модели заключается в том, что мы можем изменять тип индекса “на лету” при перезагрузке конфигурации. Раньше изменение типа индекса не всегда было возможным простым внесением изменений в файл конфигурации и попыткой перезагрузки (по сигналу HUP), например, для переключения с “простого” индекса на “распределенный” или с “распределенного” на “шаблонный”. Теперь при перезагрузке новой конфигурации, где все от старого индекса - это его имя, демон сначала анализирует новый. Затем, если его можно использовать немедленно (что характерно для шаблонов и распределенных индексов) - он обменивает их бесшовно. В случае, когда новые индексы ‘тяжелые’ (т.е. требуют предварительного кэширования в ОЗУ, что может занять минуты) - процесс ротации откладывается до их загрузки. «Отложенный» означает, что вызывающая сторона видит возврат ‘ok’, ‘(все проротировано)’. Но фактические рабочие процессы будут по-прежнему направлять все новые запросы к старым индексам, пока новые не будут окончательно загружены и активны.

Установить Manticore Search

Установить Manticore Search