diff --git a/doc/decisions/log.md b/doc/decisions/log.md
index 771ab7c..8b494f3 100644
--- a/doc/decisions/log.md
+++ b/doc/decisions/log.md
@@ -861,3 +861,72 @@ for _, k := range keys { envVars = append(envVars, corev1.EnvVar{Name: k, Value:
**Тесты:** 2 теста в `controllers/function_controller_unit_test.go`
(4 env vars → алфавитный порядок после SLESS_ENTRYPOINT; пустой Env → только SLESS_ENTRYPOINT).
+
+---
+
+## 2026-03-19 — pgx/v5 как PG-драйвер для Go функций (vs database/sql + lib/pq)
+
+**Контекст:** Go runtime v0.1.1 — добавляем прямой доступ к PostgreSQL из функций.
+Нужно выбрать: database/sql + lib/pq, или чистый pgx/v5?
+
+**Решение:** Использовать `github.com/jackc/pgx/v5` напрямую, без обёртки database/sql.
+
+**Причины:**
+
+1. **pgxpool из коробки** — `pgxpool.New()` без дополнительных пакетов. lib/pq требует `sql.Open` + настройку пула через `db.SetMaxOpenConns` и т.д.
+
+2. **Нативный протокол PostgreSQL** — pgx реализует wire protocol напрямую, без CGO.
+ lib/pq тоже pure Go, но pgx быстрее (~20% в бенчмарках) и активнее поддерживается.
+
+3. **Контекст-нативность** — `pgxpool.Pool.Query(ctx, ...)` — context как первый аргумент везде.
+ В database/sql контекст пришёл только в Go 1.8 как `QueryContext` — неудобный retrofit.
+
+4. **Сканирование строк** — `pgx.CollectRows`, `pgx.ForEachRow` — удобнее чем `rows.Scan`.
+
+5. **Экосистема** — pgx — де-факто стандарт в Go+PG проектах (используется в pgx, pgvector, ent).
+
+**Что добавлено в рантайм:**
+```
+github.com/jackc/pgx/v5 v5.7.2
+github.com/jackc/pgpassfile v1.0.0 // indirect
+github.com/jackc/pgservicefile v0.0.0-... // indirect
+github.com/jackc/puddle/v2 v2.2.2 // indirect (connection pool)
+golang.org/x/crypto v0.31.0 // indirect (scram auth)
+golang.org/x/sync v0.10.0 // indirect
+golang.org/x/text v0.21.0 // indirect
+```
+
+**go mod download** добавлен в Dockerfile до COPY server.go — слой с зависимостями кешируется отдельно.
+Пересборка функции (только изменение handler.go) не перекачивает ~15MB зависимостей.
+
+---
+
+## 2026-03-19 — Динамический таймаут в invoke.go из Function.Spec.TimeoutSec
+
+**Контекст:** invoke.go проксирует HTTP-запросы к подам функций. До этого — глобальный `http.Client{Timeout: 30s}`.
+
+**Проблема:** 30s — константа времени написания кода. Функции с `timeout_sec=700` (stress-тесты, batch-задачи) падают с `context deadline exceeded` раньше чем успевают завершиться.
+
+**Решение:** Перед каждым вызовом читать `Function.Spec.TimeoutSec` из k8s и создавать `http.Client` с таймаутом = `TimeoutSec + 5s`.
+
+**Почему +5s буфер:**
+- Нельзя ставить ровно `TimeoutSec` — есть сетевые задержки, TLS handshake, время на DNS резолв внутри кластера.
+- 5s достаточно для любых сетевых задержек в локальном k8s кластере.
+- Если функция реально завис на TimeoutSec — runtime сам должен прервать работу (это ответственность функции, не прокси).
+
+**Почему не кешировать http.Client:**
+- Каждый вызов может прийти к разной функции с разным TimeoutSec.
+- http.Client создаётся дёшево — только структура с одним полем Timeout.
+- Кеш потребовал бы sync.Map или mutex — лишняя сложность без измеримой пользы.
+
+**Деградация при недоступности k8s:**
+```go
+if err := h.K8s.Get(r.Context(), client.ObjectKey{...}, fn); err == nil {
+ timeoutSec = fn.Spec.TimeoutSec
+}
+// если Get упал — timeoutSec=0 → invokeHTTPClient вернёт 30s (дефолт)
+```
+Это осознанный выбор: если мы не можем прочитать функцию — мы не знаем её таймаут,
+используем разумный дефолт вместо возврата ошибки.
+
+**Коммит:** `d7fda15`, оператор `v0.1.40`
diff --git a/doc/errors/log.md b/doc/errors/log.md
index e924a7f..303b221 100644
--- a/doc/errors/log.md
+++ b/doc/errors/log.md
@@ -631,3 +631,127 @@ Attribute runtime value must be one of: ["nodejs20" "python3.11" "go1.21"], got:
### Версии
- Оператор: `naeel/sless-operator:v0.1.13`
- Провайдер: `terra.k8c.ru/naeel/sless v0.1.7`
+
+---
+
+## 2026-03-19 — Баг 4: Хардкодный 30s таймаут в invoke.go → context deadline exceeded
+
+### Симптом
+
+Вызов функции `stress-go-pgstorm` с `duration_sec=30` возвращал:
+```json
+{"error": "function unreachable: ... context deadline exceeded"}
+```
+При этом под был `Running`, логи показывали нормальную работу pgxpool.
+
+### Корневая причина
+
+В `internal/api/handler/invoke.go` (строка 24) был глобальный http.Client:
+```go
+var httpClient = &http.Client{Timeout: 30 * time.Second}
+```
+Функция реально отрабатывала ровно 30 секунд (duration_sec=30) + накладные расходы
+на pgxpool.New() и первый коннект к БД ≈ 1-2 секунды.
+Итого запрос превышал 30s → оператор разрывал соединение раньше чем функция успевала ответить.
+
+### Почему так было написано
+
+При создании invoke.go в марте 2026 таймаут 30s считался "достаточным для холодного
+старта". Длительные функции тогда не планировались. Когда появились batch/stress задачи
+с timeout_sec=600-700 — баг стал критическим.
+
+### Решение
+
+Убрать глобальный `httpClient`. Перед каждым вызовом:
+1. Получить Function CRD из k8s: `h.K8s.Get(ctx, ObjectKey{name, ns}, fn)`
+2. Прочитать `fn.Spec.TimeoutSec`
+3. Создать `http.Client{Timeout: TimeoutSec*time.Second + 5*time.Second}`
+4. Если функция не найдена (Get вернул ошибку) — дефолт 30s
+
+```go
+func invokeHTTPClient(timeoutSec int32) *http.Client {
+ t := time.Duration(timeoutSec)*time.Second + 5*time.Second
+ if timeoutSec <= 0 {
+ t = 30 * time.Second
+ }
+ return &http.Client{Timeout: t}
+}
+```
+
+### Файл
+
+`internal/api/handler/invoke.go` — исправлено в коммите `d7fda15`
+Оператор пересобран: `naeel/sless-operator:v0.1.40`
+
+### Урок
+
+**Никогда не хардкодить таймауты** в прокси-слое. Таймаут всегда должен браться
+из конфигурации вызываемого ресурса. `Function.Spec.TimeoutSec` существует именно для этого.
+
+---
+
+## 2026-03-19 — Баг 5: nginx ingress proxy-read-timeout не задан → 504 Gateway Time-out
+
+### Симптом
+
+После фикса invoke.go (баг 4) — повторный вызов `stress-go-pgstorm` вернул:
+```html
+
504 Gateway Time-out
+504 Gateway Time-out
+
nginx
+```
+curl exit code 5 (не 0), процесс завершился через ~60 секунд после старта запроса.
+
+### Корневая причина
+
+У ingress `sless-operator` не было аннотации `proxy-read-timeout`.
+nginx ingress controller использует дефолт **60 секунд** если аннотация отсутствует.
+
+```yaml
+# Было — аннотаций timeout нет вообще:
+annotations:
+ kubernetes.io/ingress.class: nginx
+ nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
+ nginx.ingress.kubernetes.io/ssl-redirect: "true"
+```
+
+Цепочка: клиент → nginx (60s timeout) → оператор (705s) → функция (600s).
+Nginx оборвал соединение на 60-й секунде, хотя и оператор и функция были живы.
+
+### Диагностика
+
+Проверили аннотации всех ingress в ns sless:
+- `nodered` ingress: `proxy-read-timeout: "3600"` ✅ (кто-то правильно настроил)
+- `sless-funcs-ingress`: нет timeout аннотаций
+- `sless-operator`: нет timeout аннотаций ← **виновник**
+
+### Решение
+
+1. `kubectl annotate` для мгновенного применения:
+```bash
+kubectl annotate ingress sless-operator -n sless \
+ nginx.ingress.kubernetes.io/proxy-read-timeout="900" \
+ nginx.ingress.kubernetes.io/proxy-send-timeout="900" --overwrite
+```
+
+2. Сохранить в манифест `deployments/k8s/operator.yaml`:
+```yaml
+nginx.ingress.kubernetes.io/proxy-read-timeout: "900"
+nginx.ingress.kubernetes.io/proxy-send-timeout: "900"
+```
+
+### Почему 900s
+
+- function timeout_sec = 700 → оператор ждёт 705s
+- nginx должен ждать дольше чем оператор → 900s с запасом
+- Не ставим 3600s как у nodered — избыточно для функций
+
+### Файл
+
+`deployments/k8s/operator.yaml` — обновлено в коммите `d7fda15`
+
+### Урок
+
+При развёртывании нового ingress **всегда явно задавать** `proxy-read-timeout`
+и `proxy-send-timeout`. Nginx дефолт 60s подходит только для быстрых API.
+Для любых операций дольше 30s — обязательны явные таймауты.
diff --git a/doc/progress.md b/doc/progress.md
index d620f14..bb73cf3 100644
--- a/doc/progress.md
+++ b/doc/progress.md
@@ -1,45 +1,66 @@
# Прогресс разработки
-Последнее обновление: 2026-03-19 21:00
+Последнее обновление: 2026-03-19 22:30
---
-## 2026-03-19 — Go runtime v0.1.1: pgx/v5 + stress-go-pgstorm ⏳ В РАБОТЕ
+## 2026-03-19 — Go runtime v0.1.1: pgx/v5 + stress-go-pgstorm ✅ ЗАВЕРШЕНО
### Цель
-Новая функция `stress-go-pgstorm` — Go код со 100 горутинами, которые ~10 минут долбят
-PostgreSQL напрямую через `pgxpool`. Проверяем: Go runtime под нагрузкой, connection pool
-под конкурентными запросами, устойчивость кластера.
+Новая функция `stress-go-pgstorm` — Go код со 100 горутинами, которые 10 минут
+долбят PostgreSQL напрямую через `pgxpool`. Проверяем: Go runtime под нагрузкой,
+connection pool под конкурентными запросами, устойчивость кластера.
+
+**Попутно выявлены и исправлены два системных бага:**
+- Хардкодный таймаут 30s в invoke.go (прокси оператора)
+- Отсутствие proxy-read-timeout на nginx ingress sless-operator
### Задачи
| # | Задача | Статус | Заметки |
|---|--------|--------|---------|
| 1 | `runtimes/go1.23/go.mod` — добавить `pgx/v5 v5.7.2` | ✅ | go mod tidy на remote, go.sum сгенерирован (28 строк) |
-| 2 | `runtimes/go1.23/go.sum` — создан | ✅ | Все indirect deps: pgpassfile, pgservicefile, puddle/v2, crypto, sync, text |
-| 3 | `runtimes/go1.23/Dockerfile` — добавить `go mod download` | ⏳ | |
-| 4 | Собрать `naeel/sless-runtime-go1.23:v0.1.1`, запушить | ⏳ | |
-| 5 | `internal/builder/context.go` — обновить тег `go1.23` → v0.1.1 | ⏳ | |
-| 6 | Пересобрать оператор `v0.1.39`, задеплоить | ⏳ | |
-| 7 | `code/stress-go-pgstorm/handler.go` — горутины + pgxpool | ⏳ | |
-| 8 | `resources.tf` — новая функция + trigger | ⏳ | |
-| 9 | `terraform apply`, тест | ⏳ | |
-| 10 | Коммит | ⏳ | |
+| 2 | `runtimes/go1.23/Dockerfile` — `go mod download` кешируем deps | ✅ | Один stage `golang:1.23-alpine`, COPY go.mod+go.sum перед server.go |
+| 3 | Собрать `naeel/sless-runtime-go1.23:v0.1.1`, запушить | ✅ | docker build + push, sha256 verified |
+| 4 | `internal/builder/context.go` — тег `go1.23` v0.1.0 → v0.1.1 | ✅ | строка `return "naeel/sless-runtime-go1.23:v0.1.1", nil` |
+| 5 | `internal/api/handler/invoke.go` — динамический таймаут | ✅ | Таймаут = Function.Spec.TimeoutSec + 5s (был хардкод 30s) |
+| 6 | Пересобрать и задеплоить оператор `v0.1.40` | ✅ | rodlout complete |
+| 7 | `deployments/k8s/operator.yaml` — nginx proxy-read-timeout=900s | ✅ | kubectl annotate + манифест обновлён |
+| 8 | `code/stress-go-pgstorm/handler.go` — горутины + pgxpool | ✅ | 100 горутин, INSERT/COUNT/MAX, параметры workers/duration_sec/max_delay_ms |
+| 9 | `resources.tf` — новая функция + trigger | ✅ | timeout_sec=700, memory_mb=256, все PG env vars |
+| 10 | terraform apply — 2 ресурса добавлено | ✅ | apply complete: 2 added |
+| 11 | Запуск 10-минутного стресс-теста | ⏳ | Идёт прямо сейчас: workers=100, duration_sec=600, max_delay_ms=300 |
+| 12 | Коммит d7fda15 | ✅ | feat: Go runtime v0.1.1 (pgx/v5)... |
### Архитектура функции stress-go-pgstorm
```
Handle(event) → запускает N горутин (default 100)
каждая горутина в цикле duration_sec (default 600):
- - случайная задержка 0-500ms
+ - случайная задержка 0-300ms (max_delay_ms)
- чередующиеся операции: INSERT / SELECT COUNT / SELECT MAX
- - логирует ошибки, считает ok/err
- финал → {workers, duration_sec, total_ops, ok_ops, err_ops, ops_per_sec}
+ - считает ok/err атомарно через sync/atomic
+ WaitGroup.Wait() → возвращает итог
+ возвращает: {runtime, version, workers, duration_sec, elapsed_sec,
+ total_ops, ok_ops, err_ops, ops_per_sec}
+
pgxpool.New() — connection pool, MaxConns=20
env: PGHOST, PGPORT, PGDATABASE, PGUSER, PGPASSWORD, PGSSLMODE
```
----
+### Цепочка таймаутов (после фиксов)
+
+| Слой | Таймаут | Где задаётся |
+|------|---------|--------------|
+| curl --max-time | 720s | клиент |
+| nginx ingress proxy-read-timeout | 900s | аннотация ingress sless-operator |
+| operator http.Client (invoke.go) | TimeoutSec+5s = 705s | Function.Spec.TimeoutSec=700 |
+| function runtime (Go) | duration_sec = 600s | параметр в JSON body |
+
+### Что было сломано до этой сессии
+
+1. invoke.go: `var httpClient = &http.Client{Timeout: 30 * time.Second}` — хардкод в строке 24
+2. nginx ingress sless-operator: аннотации proxy-read-timeout не было → nginx дефолт 60s → 504
## 2026-03-19 — Tests 3-7: E2E прогон POSTGRES + 8 стресс-функций