Руководство по разрешению проблем | Tarantool
Документация на русском языке
поддерживается сообществом
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. Подключитесь к консоли рабочего экземпляра.

    tt connect /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})" | tt connect \
    /var/run/tarantool/<app-name>.<instance-name>.control -f -
    

Главная проблема: Параметр 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')

    -- загрузите конфигурацию из файловой системы
    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

    -- проверьте состояние экземпляра
    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

    -- примените изменения конфигурации
    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" | tt connect \
    /var/run/tarantool/<app-name>.<instance-name>.control -f -
    
  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" | tt connect \
    /var/run/tarantool/<app-name>.<instance-name>.control -f -
    
  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>
    

When updating on Cartridge 2.7.7/2.7.8 while trying to promote a replica you can see an error like this: Cannot appoint non-electable instance.

This is a known bug in Cartridge 2.7.7/2.7.8, which is fixed in Cartridge 2.7.9.

To fix this issue, you need to update Cartridge to version 2.7.9 or higher.

Or you can use the following workaround:

require('cartridge.lua-api.topology').set_electable_servers({uuid1, uuid2, ... uuidN}) -- list all of your uuids here

Before v2.7.4, an unconfigured instance was bound to the 0.0.0.0 interface. Given that 0.0.0.0 accepts connections on any IP address assigned to the machine, this might impose additional security risks.

With v2.7.4 version, an unconfigured instance resolves the advertise_uri host and binds to it. You can check that the instance’s advertise_uri is resolved to a network interface (not loopback) as follows:

dig +short place_advertise_uri_here

There are the following known „Address already in use“ errors:

  • CartridgeCfgError: Socket bind error (<port>/udp): Address already in use
  • HttpInitError: <…> Can’t create tcp_server: Address already in use
  • RemoteControlError: Can’t start server on <host>:<port>: Address already in use

You can see these errors in Tarantool logs only. The reason causing these errors is that Tarantool cannot use the binary (for example, 3301) or HTTP (for example, 8081) port. As a result, the instance cannot be configured and fails with an error.

To fix an error, follow the steps below:

  1. Determine which port is busy.

  2. Use the lsof command to determine the application that uses this port:

    sudo lsof -i :<port>
    

    Note that without sudo you can see only the current user’s processes.

  3. Determine the connection type, for example:

    # An outgoing connection on the 50858 port
    TCP 192.168.100.17:50858->google.com:https (ESTABLISHED)
    
    # Waiting for incoming requests on the 3301 port.
    TCP localhost:3301 (LISTEN)
    
  4. For an outgoing connection, you need to adjust the port range used to choose the local port, for example:

    echo "32768 61000" > /proc/sys/net/ipv4/ip_local_port_range
    /etc/rc.d/init.d/network restart
    
  5. For an incoming connection, do one of the following:

    • For Google Chrome, etcd, nginx, or other application, you need to adjust settings to release a busy port.
    • For Tarantool, you might have an incorrect cluster topology or there might be several clusters running simultaneously. In this case, please contact Tarantool support.
Нашли ответ на свой вопрос?
Обратная связь