Top.Mail.Ru
Руководство по разрешению проблем | Tarantool
 
Tarantool Cartridge / Руководство по разрешению проблем
Tarantool Cartridge / Руководство по разрешению проблем

Руководство по разрешению проблем

Для начала обратитесь к аналогичному руководству по разрешению проблем в документации Tarantool. Ниже рассматриваются проблемы, присущие Tarantool Cartridge.

Примеры:

  • Missing .xlog file between LSN 5137088 {1: 240379, 2: 4750534, 5: 146175} and 5137379 {1: 240379, 2: 4750825, 5: 146175} which means that master lost one or more of their xlog files, please check it
  • Duplicate key exists in unique index "primary" in space "T1" with old tuple

Решение:

Если вы не знаете, как справиться с возникшими конфликтами и проблемами репликации, попробуйте повторно настроить реплику.

(!) Перед повторной настройкой убедитесь, что ваши данные находятся в безопасности на мастере.

  1. Остановите экземпляр
  2. Удалите снимки и файлы в формате xlog
  3. Сохраните конфигурацию на уровне кластера (каталог config)
  4. Перезапустите экземпляр

Примеры:

  • NetboxConnectError: "localhost:3302": Connection refused;
  • Prepare2pcError: Instance state is OperationError, can't apply config in this state.

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

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

Решение:

  1. Подключитесь к консоли рабочего экземпляра.

    tarantoolctl connect unix/:/var/run/tarantool/<app-name>.<instance-name>.control
    
  2. Проверьте, что происходит.

    cartridge = require('cartridge')
    report = {}
    for _, srv in pairs(cartridge.admin_get_servers()) do
        report[srv.uuid] = {uri = srv.uri, status = srv.status, message = srv.message}
    end
    return report
    
  3. Если вы готовы продолжить, запустите следующий фрагмент кода. Он отключит все нерабочие экземпляры. После этого вы сможете использовать веб-интерфейс как обычно.

    disable_list = {}
    for uuid, srv in pairs(report) do
        if srv.status ~= 'healthy' then
           table.insert(disable_list, uuid)
        end
    end
    return cartridge.admin_disable_servers(disable_list)
    
  4. Если необходимо вернуть отключенные экземпляры, включите их:

    cartridge = require('cartridge')
    enable_list = {}
    for _, srv in pairs(cartridge.admin_get_servers()) do
        if srv.disabled then
           table.insert(enable_list, srv.uuid)
        end
    end
    return cartridge.admin_enable_servers(enable_list)
    

Пример:

../../../_images/stuck-connecting-fullmesh.png

Главная проблема: после перезапуска экземпляр пытается подключиться ко всем своим репликам и остается в состоянии ConnectingFullmesh до тех пор, пока ему это не удастся. Если он не может (из-за недоступности URI реплики или по любой другой причине), то он завис навсегда.

Решение:

Для параметра replication_connect_quorum задайте значение «ноль». Это может сделать двумя способами:

  • Перезапустить экземпляр, задав соответствующий параметр (в переменных окружения или в конфигурационном файле экземпляра);

  • Без перезапуска выполнить:

    echo "box.cfg({replication_connect_quorum = 0})" | tarantoolctl connect \
    unix/:/var/run/tarantool/<app-name>.<instance-name>.control
    

Главная проблема: Параметр advertise_uri сохраняется в конфигурации на уровне кластера. Даже если изменить его при перезапуске, остальная часть кластера продолжит использовать старое значение, и кластер может вести себя непредсказуемо.

Решение:

Нужно обновить конфигурацию на уровне кластера.

  1. Убедитесь, что все экземпляры запущены и не зависли в состоянии ConnectingFullmesh (см. проблему выше).

  2. Убедитесь, что все экземпляры нашли друг друга (т.е. в веб-интерфейсе выглядят рабочими).

  3. Выполните следующий фрагмент кода в консоли Tarantool. Он подготовит патч для конфигурации на уровне кластера.

    cartridge = require('cartridge')
    members = require('membership').members()
    
    edit_list = {}
    changelog = {}
    for _, srv in pairs(cartridge.admin_get_servers()) do
        for _, m in pairs(members) do
            if m.status == 'alive'
            and m.payload.uuid == srv.uuid
            and m.uri ~= srv.uri
            then
                table.insert(edit_list, {uuid = srv.uuid, uri = m.uri})
                table.insert(changelog, string.format('%s -> %s (%s)', srv.uri, m.uri, m.payload.alias))
                break
            end
        end
    end
    return changelog
    

    В результате вы увидите сводную таблицу такого типа:

    localhost:3301> return changelog
    ---
    - - localhost:13301 -> localhost:3301 (srv-1)
      - localhost:13302 -> localhost:3302 (srv-2)
      - localhost:13303 -> localhost:3303 (srv-3)
      - localhost:13304 -> localhost:3304 (srv-4)
      - localhost:13305 -> localhost:3305 (srv-5)
    ...
    
  4. Наконец, примените патч:

    cartridge.admin_edit_topology({servers = edit_list})
    

Предупреждение

Обратите внимание, что это довольно рискованно (убедитесь, что вы знаете, что делаете). Вам пригодится информация о структуре конфигурации на уровне кластера и «нормальном» API управления.

Если вы все же решили перезагрузить конфигурацию вручную, это можно сделать в консоли Tarantool:

function reload_clusterwide_config()
    local changelog = {}

    local ClusterwideConfig = require('cartridge.clusterwide-config')
    local confapplier = require('cartridge.confapplier')

    -- load config from filesystem
    table.insert(changelog, 'Loading new config...')

    local cfg, err = ClusterwideConfig.load('./config')
    if err ~= nil then
        return changelog, string.format('Failed to load new config: %s', err)
    end

    -- check instance state
    table.insert(changelog, 'Checking instance config state...')

    local roles_configured_state = 'RolesConfigured'
    local connecting_fullmesh_state = 'ConnectingFullmesh'

    local state = confapplier.wish_state(roles_configured_state, 10)

    if state == connecting_fullmesh_state then
        return changelog, string.format(
            'Failed to reach %s config state. Stuck in %s. ' ..
                'Call "box.cfg({replication_connect_quorum = 0})" in instance console and try again',
            roles_configured_state, state
        )
    end

    if state ~= roles_configured_state then
        return changelog, string.format(
            'Failed to reach %s config state. Stuck in %s',
            roles_configured_state, state
        )
    end

    -- apply config changes
    table.insert(changelog, 'Applying config changes...')

    cfg:lock()
    local ok, err = confapplier.apply_config(cfg)
    if err ~= nil then
        return changelog, string.format('Failed to apply new config: %s', err)
    end

    table.insert(changelog, 'Cluster-wide configuration was successfully updated')

    return changelog
end

reload_clusterwide_config()

Этот фрагмент кода перезагрузит конфигурацию на отдельном экземпляре. Все остальные экземпляры продолжат работать в прежнем режиме.

Примечание

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

Начиная с версии 2.3.0, в интерфейсе командной строки Cartridge можно использовать команду repair.

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

Примечание

cartridge repair исправляет кластерные конфигурационные файлы экземпляров приложений, размещенных НА ЛОКАЛЬНОЙ МАШИНЕ. Это означает, что в обязанности пользователя входит запуск cartridge repair на всех машинах.

Примечание

Недостаточно применить новую конфигурацию — экземпляр должен перезагрузить конфигурацию. Если у вашего приложения cartridge >= 2.0.0, вы можете просто использовать флаг --reload, чтобы загрузить конфигурацию. В противном случае нужно перезапустить экземпляры или перезагрузить конфигурацию вручную.

Чтобы изменить advertise_URI экземпляра, нужно выполнить следующие действия:

  1. Запустите экземпляр с новым advertise_URI. Самый простой способ — изменить значение advertise_uri в конфигурационном файле экземпляра).

  2. Убедитесь, что экземпляры запущены и не зависли в состоянии ConnectingFullmesh (см. проблему выше).

  3. Получите UUID экземпляра:

    • откройте вкладку server details в веб-интерфейсе;
    • вызовите cartridge repair list-topology --name <app-name> и найдите нужный экземпляр:
    • получите box.info().uuid экземпляра:
    echo "return box.info().uuid" | tarantoolctl connect \
    unix/:/var/run/tarantool/<app-name>.<instance-name>.control
    
  4. Теперь нужно обновить URI, который передает этот экземпляр, в конфигурационных файлах на уровне кластера на каждой машине. Запустите cartridge repair set-advertise-uri с флагом --dry-run на каждой машине, чтобы проверить изменения конфигурации на уровне кластера, вычисленные cartridge-cli:

    cartridge repair set-advertise-uri \
      --name myapp \
      --dry-run \
      <instance-uuid> <new-advertise-uri>
    
  5. Запустите cartridge repair set-advertise-uri без флага --dry-run на каждой машине, чтобы применить изменения конфигурации, вычисленные cartridge-cli. Если у вашего приложения cartridge >= 2.0.0, вы можете указать флаг --reload, чтобы загрузить новую кластерную конфигурацию на экземпляры. В противном случае нужно перезапустить экземпляры или перезагрузить конфигурацию вручную.

    cartridge repair set-advertise-uri \
      --name myapp \
      --verbose \
      --reload \
      <instance-uuid> <new-advertise-uri>
    

Поменять лидера в наборе реплик можно с помощью команды cartridge repair.

  1. Получите UUID набора реплик и UUID нового лидера (в веб-интерфейсе или с помощью команды cartridge repair list-topology --name <app-name>).

  2. Теперь нужно обновить конфигурационные файлы на уровне кластера на каждой машине. Запустите cartridge repair set-leader с флагом --dry-run на каждой машине, чтобы проверить изменения конфигурации на уровне кластера, вычисленные cartridge-cli:

    cartridge repair set-leader \
      --name myapp \
      --dry-run \
      <replicaset-uuid> <instance-uuid>
    
  3. Запустите cartridge repair set-advertise-uri без флага --dry-run на каждой машине, чтобы применить изменения конфигурации, вычисленные cartridge-cli. Если у вашего приложения cartridge >= 2.0.0, вы можете указать флаг --reload, чтобы загрузить новую кластерную конфигурацию на экземпляры. В противном случае нужно перезапустить экземпляры или перезагрузить конфигурацию вручную.

    cartridge repair set-leader \
      --name myapp \
      --verbose \
      --reload \
      <replicaset-uuid> <instance-uuid>
    

Удалить экземпляр из кластера можно с помощью команды cartridge repair.

  1. Получите UUID экземпляра:

    • откройте вкладку server details в веб-интерфейсе;
    • вызовите cartridge repair list-topology --name <app-name> и найдите нужный экземпляр:
    • получите box.info().uuid экземпляра:
    echo "return box.info().uuid" | tarantoolctl connect \
    unix/:/var/run/tarantool/<app-name>.<instance-name>.control
    
  2. Теперь нужно обновить конфигурационные файлы на уровне кластера на каждой машине. Запустите cartridge repair remove-instance с флагом --dry-run на каждой машине, чтобы проверить изменения конфигурации на уровне кластера, вычисленные cartridge-cli:

    cartridge repair remove-instance \
      --name myapp \
      --dry-run \
      <replicaset-uuid>
    
  3. Запустите cartridge repair remove-instance без флага --dry-run на каждой машине, чтобы применить изменения конфигурации, вычисленные cartridge-cli. Если у вашего приложения cartridge >= 2.0.0, вы можете указать флаг --reload, чтобы загрузить новую кластерную конфигурацию на экземпляры. В противном случае нужно перезапустить экземпляры или перезагрузить конфигурацию вручную.

    cartridge repair set-leader \
      --name myapp \
      --verbose \
      --reload \
      <replicaset-uuid> <instance-uuid>