---
metadata:
  - name: generator
    content: Diplodoc Platform v5.50.6
alternate:
  - https://ytsaurus.tech/docs/en/user-guide/problems/jobshell-and-slowjobs.md
  - https://ytsaurus.tech/docs/ru/user-guide/problems/jobshell-and-slowjobs.md
---
> **Documentation Index:** Fetch the complete configuration index at https://ytsaurus.tech/docs/ru/llms.txt

<!-- source: ru/_includes/user-guide/problems/jobshell-and-slowjobs.md -->
# Диагностика медленных джобов

В данном разделе приведено описание диагностики медленных джобов с использованием инструмента `Job Shell`.

Время от времени может случаться так, что один или несколько джобов операции выполняются долго и без видимого прогресса. Чтобы разобраться в причине, можно воспользоваться инструментом `Job Shell`. С помощью данного инструмента можно открыть консоль непосредственно в окружении джоба от имени того же пользователя, от которого выполняется джоб. После этого можно исследовать работу джоба с помощью вызовов **gdb** или **strace**.

## Запуск Job Shell

Для запуска Job Shell необходимо выполнить команду `yt --proxy <cluster name> run-job-shell <job id>`, подставив вместо `<...>` имя кластера и идентификатор джоба. Идентификатор джоба можно узнать на странице операции, на вкладке **Jobs**.

Подробное описание запуска Job Shell:

```bash
$ yt --proxy cluster-name run-job-shell fcebe6b9-e06ab2e1-3fe0384-d0b66453
Use ^F to terminate shell.

Job environment:
TMPDIR=/yt/disk2/cluster-data/slots/11/sandbox
PYTHONPATH=/yt/disk2/cluster-data/slots/11/sandbox
PYTHONUSERBASE=/yt/disk2/cluster-data/slots/11/sandbox/.python-site-packages
PYTHON_EGG_CACHE=/yt/disk2/cluster-data/slots/11/sandbox/.python-eggs
HOME=/yt/disk2/cluster-data/slots/11/sandbox
YT_OPERATION_ID=78b048b5-8055fabe-3fe03e8-d5b74b2a
YT_JOB_INDEX=120
YT_JOB_ID=fcebe6b9-e06ab2e1-3fe0384-d0b66453
YT_START_ROW_INDEX=202133578

UID          PID    PPID  C STIME TTY          TIME CMD
19911     257386  239675  0 02:24 pts/1    00:00:00 /bin/bash
19911     257397  257386  0 02:24 pts/1    00:00:00  \_ ps -fu 19911 --forest
19911     240043  239675  0 02:21 ?        00:00:00 /bin/bash -c :; ./my_mapper
19911     240073  240043 91 02:21 ?        00:02:37  \_ ./my_mapper

yt_slot_11@n1757-sas:~$
```

В выводе команды отображена различная информация о джобе:

- путь к рабочей директории джоба — /yt/disk2/cluster-data/slots/11/sandbox;
- имя пользователя, от которого запущен джоб — yt_slot_11;
- список процессов, которые выполняются от имени текущего пользователя.

Первый и второй процессы из приведённого примера относятся к Job Shell, третий процесс — сессия командной оболочки bash, в которой запускается исследуемый джоб, четвёртый — это непосредственно пользовательский процесс.

## Анализ пользовательского процесса

В первую очередь следует запустить команду `top -p <PID>`, указав в качестве аргумента идентификатор пользовательского процесса. Ниже приведен пример и результат вызова:

```bash
yt_slot_11@n1757-sas:~$ top -p 240073
top - 02:30:52 up 6 days,  7:11,  0 users,  load average: 41.26, 37.59, 39.23
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
Cpu(s): 48.9%us,  5.6%sy,  0.0%ni, 31.1%id, 14.3%wa,  0.0%hi,  0.2%si,  0.0%st
Mem:  132001796k total, 130937776k used,  1064020k free,   296488k buffers
Swap:        0k total,        0k used,        0k free, 107424768k cached

    PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 240073 yt_slot_  20   0  4164  632  552 R   98  0.0   8:08.05 my_mapper
```

По использованию CPU (48.9%) в приведенном примере видно, что пользовательский процесс выполняет вычисления и, вероятно, причина медленной работы джоба в пользовательском коде. Следует подключиться к процессу джоба с помощью **gdb** и попытаться понять, что он в данный момент делает.

Если потребление CPU близко к нулю, то это скорее всего означает, что процесс завис на системном вызове. Стоит вызвать **strace** на пользовательский процесс.
Пример вызова:

```bash
yt_slot_11@n1757-sas:~$ strace -p 240073
Process 240073 attached - interrupt to quit
write(4
```

Если процесс постоянно производит системные вызовы (а не висит на каком-то одном), то будьте готовы, что указанная выше команда выдаст на консоль сразу поток информации. В такой ситуации может быть полезен вариант ``strace -p <PID> 2>&1 | head``, позволяющий увидеть лишь один вызов.

Также если ваш код многопоточный, то может быть полезно добавить опцию ``-f``, позволяющую перехватывать системные вызовы изо всех потоков.

Если процесс завис на системном вызове read или **write**, стоит обратить внимание на значение первого аргумента, который обозначает файловый дескриптор потока. Нулевое значение аргумента для системного вызова **read** и небольшое целое число для системного вызова **write** (1, 4, 7), означает, что по каким-то причинам служебный процесс YTsaurus Job Proxy не отдаёт или не принимает очередную порцию данных в направлении к пользовательскому процессу или от него. В таком случае следует сообщить о проблеме администратору системы, приложив детали о проведенной вами диагностике. Подробнее о проведении диагностики можно прочитать в разделе [How To Report](https://ytsaurus.tech/docs/ru/user-guide/problems/howtoreport.md).

В случае зависаний на системных вызовах **futex**, **connect**, **open**, **stat** и др. причина не связана с работой  `Job Proxy` и системы YTsaurus в целом. Проблему необходимо искать в пользовательском коде. Стоит воспользоваться **gdb**, чтобы понять чем вызвана блокировка. Например, если пользовательский код многопоточный и в нём по каким-то причинам возникает блокировка, то вывод **strace** может выглядеть так:

```bash
yt_slot_11@n1757-sas:~$ strace -p 240073 2>&1 | head
Process 240073 attached - interrupt to quit
futex(0x7ffff79b3e00, FUTEX_WAIT, 2, NULL
```

Возможна ситуация, когда пользовательский процесс не потребляет CPU, а **strace** не показывает никаких системных вызовов. Часто это означает, что код использует **memory mapping** и производит чтение (или запись) из файла, находящегося на диске. При этом происходит **major page fault** и процесс ожидает, пока операционная система загрузит данные с диска в память.

{% note warning "Внимание" %}

Использование **memory mapping** в джобах **категорически не рекомендуется**, а джобы, совершающие такие действия, сильно нагружают диски случайными чтениями и записями. Такие джобы также можно распознать по высоким значениям [статистик](https://ytsaurus.tech/docs/ru/user-guide/problems/jobstatistics.md) `/user_job/block_io/[io_read,io_write]`. Помните, что обычный жесткий диск способен совершить не более 100 операций со случайным доступом в секунду, в то время как на узле кластера одновременно работают несколько десятков джобов.

{% endnote %}

Частным случаем большого **memory mapped** файла, читаемого в джобах, является исполняемый файл операции и его зависимости, загружаемые динамическим линковщиком во время запуска операции. Код загрузчика написан так, что возникающие при этом **page faults** обычно не составляют проблемы. Стоит помнить, что уменьшение размеров исполняемого файла и его зависимостей всегда положительно сказывается на производительности. В частности, с точки зрения производительности лучше использовать исполняемые файлы без отладочных символов.

## Именованные Job Shells

Иногда пользовательские процессы могут создавать внутри джобов свои подконтейнеры.

Чтобы иметь возможность подключаться и отлаживать джобы во вложенных контейнерах, в YT есть специальные именованные Job Shells. Их можно указать в спеке операции с привязкой к конкретному контейнеру.

Для этого в спеке операции можно указать `job_shells`:

```yson
spec = {
    "job_shells" = [
        {
            "name" = "inner";
            "subcontainer" = "/N";
            "owners" = ["user1", "user2", "admin"];
        };
        {
            "name" = "default";
            "subcontainer" = "";
            "owners" = ["admin"];
        };
    ];
};
```

Использование именованных Job Shells практически ничем не отличается от обычных. Если нужно подключиться к конкретному Job Shell, нужно указать параметр `--shell-name`:

```bash
$ yt --proxy cluster-name run-job-shell fcebe6b9-e06ab2e1-3fe0384-d0b66453 --shell-name N
```

По умолчанию запускается Job Shell с именем `default`.

## Права на запуск Job Shell

Для запуска Job Shell нужно иметь права на чтение (**read**) и управление (**manage**) операцией.

Для этого при запуске операции в спецификации нужно указывать [ACL](https://ytsaurus.tech/docs/ru/user-guide/storage/access-control.md), описывающий пользователей и группы, которые могут выполнять действия над операцией и джобами.
По умолчанию список ACL пустой, права на операцию выдаются только пользователю, запустившему операцию, а также администраторам системы YTsaurus.

Пример настройки ACL в Python:
```python
spec={"acl": [{"permissions": ["read", "manage"], "subjects": ["user1", "user2"], "action": "allow"}]}
```
Пользователь из переменных окружения YT_TOKEN/YT_TOKEN_PATH (authenticated_user) добавляется автоматически.

Для изменения прав доступа к уже работающей операции нужно использовать команду `update_operation_parameters` с новым ACL.

Чтобы иметь доступ до именованных Job Shells, нужно также указать им правильные настройки `owners`. `owners` – это упрощенная версия `acl`, который представляет собой список субъектов (пользователей или групп), которым будет разрешен доступ до Job Shell.

```yson
"owners" = ["user1", "user2"];
```

Если вам захочется поменять `owners` какого-то Job Shell уже запущенной операции, можно воспользоваться командой [update_operation_parameters](https://ytsaurus.tech/docs/ru/api/commands.md#update_operation_parameters--update_operation_parameters), указав новый список `owners` конкретного Job Shell:

```yson
PARAMETERS {"operation_id" = "<op-id>"; "parameters" = {"options_per_job_shell" = {"shell_name": {"owners": ["user3"]}}}}
```
<!-- endsource: ru/_includes/user-guide/problems/jobshell-and-slowjobs.md -->