# Сон и пробуждение Galaxy-приложения

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

[Что останавливается](#что-останавливается-во-сне) · [Поля состояния](#поля-status-и-reachability-отвечают-на-разные-вопросы) · [Чем поднять](#чем-поднять-спящее-приложение) · [Логи](#логи-спящего-приложения) · [Окно по расписанию](#окно-пробуждения-по-расписанию)

## Что останавливается во сне

Приложение живёт в контейнере на общем хосте — галактике. Спать могут обе части, и это разные состояния.

| Что спит | Как это выглядит | Что нужно, чтобы поднять |
|---|---|---|
| Контейнер приложения, галактика работает | Приложение не отвечает, соседние приложения той же галактики работают | Запуск контейнера — секунды |
| Галактика вместе со всеми контейнерами | Не отвечает ни одно приложение этой галактики | Загрузка машины галактики, затем запуск контейнера — минуты |

Постоянный том `/data` сон переживает. Пробуждение запускает **тот же** контейнер, а не создаёт новый, поэтому строки, которые приложение записало в свой поток вывода до сна, доступны и после пробуждения. Полностью новый контейнер появляется только при повторной [загрузке кода](./deploy/deploy.md) — вот она журнал предыдущего контейнера не сохраняет.

## Поля status и reachability отвечают на разные вопросы

Поле `status` в ответе [`GET /v1/infra/servers/:id`](./servers/get.md) — это состояние учётной записи приложения на платформе. Пробуждение переводит запись в `running` в конце, когда контейнер уже запущен, поэтому во время подъёма галактики запись остаётся в `sleeping` — на холодной машине это занимает минуты. Обратное расхождение тоже возможно: запись может числиться в `running`, пока контейнер уже не работает.

Поэтому рядом идёт блок `reachability` — он отвечает на вопрос «может ли приложение ответить прямо сейчас». Блок приходит только у приложения, `kind` равен `GALAXY_APP`, у остальных типов серверов там `null`.

```json
{
  "success": true,
  "data": {
    "id": "e22bb297-8a0a-4597-9b42-47d059cd1090",
    "kind": "GALAXY_APP",
    "status": "sleeping",
    "reachability": {
      "effectiveStatus": "running",
      "hostStatus": "running",
      "hostTunnel": "CONNECTED",
      "container": "running",
      "forwarder": "active",
      "probe": "ok",
      "probedAt": "2026-08-12T13:24:58.117Z"
    }
  }
}
```

Показаны поля, относящиеся к сну. Полный ответ — [`GET /v1/infra/servers/:id`](./servers/get.md).

| Поле | Описание |
|------|---------|
| `effectiveStatus` | Сводный ответ о доступности — `running`, `sleeping`, `waking`, `unreachable` или `unknown`. Как он выводится, показано в таблице ниже |
| `hostStatus` | Состояние галактики-носителя в том же наборе значений, что и `status` сервера |
| `hostTunnel` | Соединение галактики с платформой — `CONNECTED`, `DISCONNECTED` или `NONE` |
| `container` | Контейнер приложения — `running`, `stopped` или `unknown`. Значение `unknown` означает, что платформа не смогла посмотреть, а не что контейнер остановлен |
| `forwarder` | Маршрутизация к контейнеру на хосте — `active`, `inactive` или `unknown`. Во сне останавливается вместе с контейнером |
| `probe` | Чем закончился живой опрос хоста — `ok`, `host-down` (галактика спит или недоступна, опрос не отправлялся), `host-busy` (хост занят другой операцией), `failed` или `not-attempted` |
| `probedAt` | Время опроса по стандарту ISO 8601. Значение `null` означает, что опроса не было |

Как складывается `effectiveStatus`:

| Состояние | `effectiveStatus` |
|---|---|
| Галактика загружается | `waking` |
| Галактика спит | `sleeping` |
| Галактика работает, контейнер остановлен | `sleeping` |
| Галактика работает, контейнер работает, маршрутизация активна | `running` |
| Галактика работает, контейнер работает, маршрутизация остановлена | `unreachable` |
| Галактика в ошибке либо потеряла соединение с платформой | `unreachable` |
| Посмотреть не удалось | `unknown` |

**Важно:** живой опрос хоста платформа делает только когда галактика работает и соединена с платформой. Спящую галактику чтение состояния не будит, поэтому у спящей галактики `container` и `forwarder` всегда `unknown`, а `probe` равен `host-down`. Результат опроса платформа держит несколько секунд и отдаёт из памяти: опрос занимает канал команд, общий для всех приложений галактики, поэтому частый опрос состояния его не занимает.

## Чем поднять спящее приложение

| Способ | Что делает |
|---|---|
| [`POST /v1/infra/servers/:id/wake`](./lifecycle/wake.md) | Поднимает приложение через хост. При необходимости запускает галактику, затем стартует контейнер |
| [`POST /v1/infra/servers/:id/start`](./lifecycle/start.md) | То же самое. Отдельного смысла у двух вызовов для приложения нет |
| [Загрузка кода](./deploy/deploy.md) | Поднимает галактику сама и оставляет приложение работающим |
| Обращение к адресу приложения | Запрос через шлюз поднимает приложение и до готовности отвечает кодом `BH_SERVER_WAKING` с заголовком `Retry-After` — подробности в [Авторизации в приложении на Black Hole](./app-runtime.md) |
| [Доставка события портала](./event-subscriptions.md) | Платформа поднимает приложение перед доставкой |

Ответ на вызов пробуждения приходит сразу, а не по готовности: у приложения нет своей облачной машины, и параметр `?wait=true` ожидания здесь не даёт. Готовность проверяйте опросом [`GET /v1/infra/servers/:id`](./servers/get.md) по полю `reachability.effectiveStatus` — значение `waking` означает, что галактика ещё загружается.

Сколько ждать: подъём холодной галактики платформа ждёт до 15 минут и столько же готова ждать от вызывающего. Точное значение бюджета приходит в ответе на чтение логов спящего приложения, поле `recovery.coldBootBudgetSeconds`. Приложение на уже работающей галактике стартует за секунды.

Запрет пробуждения на хосте блокирует оба вызова, и `/start` его не снимает. Коды отказов — в таблице жизненного цикла на странице [Galaxy-приложение](./galaxy.md).

## Логи спящего приложения

Чтение [`GET /v1/infra/servers/:id/logs`](./deploy/logs.md) не будит ни приложение, ни галактику. Пока галактика спит или недоступна, ответ приходит со статусом `200`, пустым массивом `data.logs` и двумя диагностическими полями.

```json
{
  "success": true,
  "data": {
    "logs": [],
    "lines": [],
    "hint": "Galaxy host is asleep or unreachable, so the container log cannot be read right now. …",
    "recovery": {
      "reason": "The galaxy host carrying this container is not RUNNING + CONNECTED. …",
      "recoveryAction": "POST /v1/infra/servers/e22bb297-8a0a-4597-9b42-47d059cd1090/wake",
      "logsPreserved": true,
      "coldBootBudgetSeconds": 900,
      "poll": "GET /v1/infra/servers/e22bb297-8a0a-4597-9b42-47d059cd1090 (read `reachability`)",
      "wakeSchedule": { "available": false, "code": "ALWAYS_ON_CONFLICT" }
    }
  }
}
```

| Поле | Описание |
|------|---------|
| `hint` | Та же подсказка обычным текстом, одной строкой |
| `recovery.reason` | Почему журнал сейчас не читается |
| `recovery.recoveryAction` | Вызов, которым поднять приложение |
| `recovery.logsPreserved` | Значение `true` означает, что строки, записанные до сна, доступны после пробуждения |
| `recovery.coldBootBudgetSeconds` | Бюджет подъёма холодной галактики в секундах |
| `recovery.poll` | Чем проверять готовность вместо повторного чтения логов |
| `recovery.wakeSchedule` | Доступно ли этому приложению окно пробуждения по расписанию. Поле `available` — ответ, поле `code` при отказе несёт тот же код, которым ответит создание окна |

Порядок действий: поднять приложение вызовом из `recovery.recoveryAction`, дождаться `reachability.effectiveStatus` равного `running`, повторить чтение логов. Повторять чтение логов в цикле, пока приложение спит, смысла нет — ответ не изменится.

## Окно пробуждения по расписанию

Окно пробуждения поднимает приложение по расписанию, без вызова руками. Доступно оно не каждому приложению, и проверять это следует по ответу платформы, а не по общему правилу: поле `recovery.wakeSchedule` в ответе на чтение логов спящего приложения даёт готовый ответ для конкретного приложения, а при отказе — его код.

Один отказ стоит знать заранее. Приложение, которое не засыпает по простою само, платформа считает работающим постоянно, и окно ему отклоняется кодом `ALWAYS_ON_CONFLICT`. Так создаются агенты и боты в галактике: их поднимает не входящий запрос, а собственный опрос портала, поэтому таймер простоя им не назначается. Такое приложение поднимают вызовом пробуждения.

Список окон, создание, изменение и удаление — [Пробуждение по расписанию](./wake-schedules.md).

## Смотрите также

- [Galaxy-приложение](./galaxy.md)
- [Разбудить сервер](./lifecycle/wake.md)
- [Логи сервера](./deploy/logs.md)
- [Получить сервер](./servers/get.md)
- [Пробуждение по расписанию](./wake-schedules.md)
- [Авторизация в приложении на Black Hole](./app-runtime.md)
