doc: Go runtime v0.1.1, баги 4+5, решения pgx/v5 + dynamic timeout

- progress.md: секция v0.1.1 → ✅ ЗАВЕРШЕНО, все задачи, таблица таймаутов, арх. функции
- errors/log.md: Баг 4 (invoke.go 30s хардкод → context deadline exceeded) + Баг 5 (nginx ingress отсутствие proxy-read-timeout → 504)
- decisions/log.md: pgx/v5 vs database/sql+lib/pq (с обоснованием), динамический таймаут (почему +5s, почему не кешировать, деградация)
This commit is contained in:
Naeel
2026-03-19 21:39:34 +03:00
parent d7fda15d35
commit 45bd9389e5
3 changed files with 232 additions and 18 deletions
+124
View File
@@ -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
<html><head><title>504 Gateway Time-out</title></head>
<body><center><h1>504 Gateway Time-out</h1></center>
<hr><center>nginx</center></body></html>
```
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 — обязательны явные таймауты.