docs: bench PV results 2026-05-20 PASS=20/20; misc updates

This commit is contained in:
“Naeel”
2026-05-20 08:30:26 +04:00
parent a2e1723e33
commit 005b4786c0
8 changed files with 785 additions and 3 deletions
+113
View File
@@ -0,0 +1,113 @@
# FIX: archivePruner удаляет архивы tenant namespaces
## Симптом
Node.js функции не специализируются → fetcher 404 при скачивании архива.
Storagesvc логи: `archive_pruner` удаляет архив через ~90 сек после создания функции.
## Причина
`pkg/storagesvc/archivePruner.go`, функция `getOrphanArchives`, строка 95:
```go
for _, namespace := range utils.DefaultNSResolver().Snapshot() {
pkgList, err := pruner.crdClient.CoreV1().Packages(namespace).List(...)
```
`DefaultNSResolver().Snapshot()` возвращает только namespaces зарегистрированные
через `AddNamespace()`. Storagesvc не слушает namespace events → tenant namespaces
(`fission-*`) туда не попадают → их Package CRD невидимы → все загруженные архивы
считаются orphan → удаляются.
## Правка
**Файл:** `pkg/storagesvc/archivePruner.go`
**Заменить** (строки 94125):
```go
// get all pkgs from kubernetes
for _, namespace := range utils.DefaultNSResolver().Snapshot() {
pkgList, err := pruner.crdClient.CoreV1().Packages(namespace).List(ctx, metav1.ListOptions{})
if err != nil {
pruner.logger.Error("error getting package list from kubernetes", zap.Error(err))
return
}
// extract archives referenced by these pkgs
for _, pkg := range pkgList.Items {
if pkg.Spec.Deployment.URL != "" {
archiveID, err = getQueryParamValue(pkg.Spec.Deployment.URL, "id")
if err != nil {
pruner.logger.Error("error extracting value of archiveID from deployment url",
zap.Error(err),
zap.String("url", pkg.Spec.Deployment.URL))
return
}
archivesRefByPkgs = append(archivesRefByPkgs, archiveID)
}
if pkg.Spec.Source.URL != "" {
archiveID, err = getQueryParamValue(pkg.Spec.Source.URL, "id")
if err != nil {
pruner.logger.Error("error extracting value of archiveID from source url",
zap.Error(err),
zap.String("url", pkg.Spec.Source.URL))
return
}
archivesRefByPkgs = append(archivesRefByPkgs, archiveID)
}
}
}
```
**На:**
```go
// get all pkgs from kubernetes across ALL namespaces (including tenant namespaces)
// Fix: DefaultNSResolver().Snapshot() returns only fission-registered namespaces,
// not tenant namespaces (fission-*). Use NamespaceAll to scan everything.
pkgList, err := pruner.crdClient.CoreV1().Packages(metav1.NamespaceAll).List(ctx, metav1.ListOptions{})
if err != nil {
pruner.logger.Error("error getting package list from kubernetes", zap.Error(err))
return
}
// extract archives referenced by these pkgs
for _, pkg := range pkgList.Items {
if pkg.Spec.Deployment.URL != "" {
archiveID, err = getQueryParamValue(pkg.Spec.Deployment.URL, "id")
if err != nil {
pruner.logger.Error("error extracting value of archiveID from deployment url",
zap.Error(err),
zap.String("url", pkg.Spec.Deployment.URL))
return
}
archivesRefByPkgs = append(archivesRefByPkgs, archiveID)
}
if pkg.Spec.Source.URL != "" {
archiveID, err = getQueryParamValue(pkg.Spec.Source.URL, "id")
if err != nil {
pruner.logger.Error("error extracting value of archiveID from source url",
zap.Error(err),
zap.String("url", pkg.Spec.Source.URL))
return
}
archivesRefByPkgs = append(archivesRefByPkgs, archiveID)
}
}
```
**Импорты** — удалить `"github.com/fission/fission/pkg/utils"` (больше не используется).
## После правки
```bash
cd ~/terra/fission-src
go build ./pkg/storagesvc/... # проверить компиляцию
# пересобрать storagesvc образ и передеплоить
docker build -t naeel/fission-storagesvc:<новый-тег> -f cmd/storagesvc/Dockerfile .
docker push naeel/fission-storagesvc:<новый-тег>
kubectl set image -n fission deploy/storagesvc storagesvc=naeel/fission-storagesvc:<новый-тег>
kubectl rollout status -n fission deploy/storagesvc
```
+56
View File
@@ -235,3 +235,59 @@
### Вывод
- Текущий prompt уже работает на коротких и простых кейсах.
- Следующий шаг — проверить его на более разнообразных задачах, чтобы убедиться, что шаблон не ломается за пределами приветствий.
---
## Итерация 9 (2026-05-19) — RUNTIME BUG DISCOVERY
### Источник
Реальный прогон `test_heavy.sh` на кластере. Обнаружены критические баги в PHP и Node.js runtime-контракте.
### БАГ C: PHP `return array` → пустой response
**Задача LLM:** написать PHP-функцию.
**Сгенерированный код (типичный):**
```php
<?php
function handler(array $ctx): array {
return ['statusCode' => 200, 'body' => json_encode(['tag' => 'ok'])];
}
```
**Результат invoke:** `{"status":200,"response_raw":""}` — тело пустое.
**Причина:** PHP env (`ghcr.io/fission/php-env:latest`, server.php) создаёт `$response = new Response()`,
вызывает функцию и возвращает оригинальный объект. Return value функции игнорируется.
**Правильный код:**
```php
<?php
function handler($ctx) {
$ctx["response"]->getBody()->write(json_encode(['tag' => 'ok']));
}
```
**Что изменено в промпте:**
- PHP секция переписана: запрет на `return array`, требование писать через `$ctx["response"]->getBody()->write(...)`.
- Добавлен пример с JSON-ответом и PSR-7 объектами.
---
### БАГ B: Node.js entrypoint `"main.js"` — split на точке → funcname="js"
**Проблема консоли, не пользовательского кода.**
Node-env `specializeV2` (строка 160 server.js) интерпретирует functionName через `split(".")`:
- `"main.js"``{filename:"main", funcname:"js"}` → ищет `module["js"]` → undefined → specialize 500.
- `"main"``{filename:"main", funcname:undefined}` → берёт `default export` → работает.
**Fix в консоли:** `console/internal/runtime/entrypoint.go`, `DefaultEntrypoint("nodejs")` = `"main"`.
**Влияние на промпт:** никакого — это внутренняя проблема консоли. Пользовательский Node.js код пишется как обычно.
---
### Итог итерации 9
- PHP промпт обновлён: PSR-7 `getBody()->write()` вместо `return array`.
- Node.js промпт уточнён: `module.exports = async function(ctx) {...}`, CJS стиль.
- Документация: `doc/RUNTIME_FUNCTION_GUIDE.md` — полный справочник контрактов.
+25 -3
View File
@@ -54,9 +54,31 @@
- Не ломай контракт функции выдуманными обертками или неподходящими типами.
### PHP
- Возвращай корректный PHP-код функции.
- Следи за открывающим тегом и синтаксисом массива/строк.
- Не выдумывай несуществующие `Fission\\Contract` классы или SDK, если задача про обычную функцию.
- **КРИТИЧНО:** PHP env НЕ читает return value функции. `return ['statusCode'=>200,'body'=>...]` — тело ответа будет ПУСТЫМ.
- Запись в ответ — ТОЛЬКО через `$ctx["response"]->getBody()->write(...)`:
```php
<?php
function handler($ctx) {
$ctx["response"]->getBody()->write(json_encode(['key' => 'value']));
}
```
- Сигнатура: `function handler($ctx)` — без type hint `array` и без `: array` return type.
- Доступ к запросу: `$ctx["request"]`, к логгеру: `$ctx["logger"]`.
- Открывающий тег `<?php` обязателен.
- Не выдумывай несуществующие `Fission\\Contract` классы или SDK.
- Альтернатива (только если нет доступа к request): простой `echo` без объявления функции handler:
```php
<?php
echo json_encode(['key' => 'value']);
```
### Node.js
- Пользовательский код оборачивается консолью в CJS wrapper. Писать как обычный CommonJS.
- Экспорт: `module.exports = async function(ctx) { return { status: 200, body: "..." }; }`
- Формат ответа: объект `{ status: число, body: строка }`.
- **НЕ** использовать `export default` — код запускается в CJS контексте.
- **НЕ** указывать `.js` в entrypoint — это внутренняя настройка консоли, не пользовательский код.
- Не вставляй ESM import/export обвязку в пользовательский код.
### Ruby
- Возвращай корректный Ruby-код функции.
+114
View File
@@ -0,0 +1,114 @@
#!/bin/bash
# Бенчмарк: upload, deploy, cold/hot start для простой функции
# Все результаты пишутся в test-results/bench-func
set -e
RESULTS_DIR="$(dirname "$0")/../test-results/bench-func"
FUNC_DIR="$(dirname "$0")"
FUNC_NAME="bench-hello"
ENV_NAME="bench-python-env"
NS="bench-func-$(date +%s)"
# Donor namespace с готовым RBAC
DONOR_NS=$(kubectl get ns --no-headers | awk '{print $1}' | grep -E '^fission-[a-f0-9]{16}$' | head -1)
LOGFILE="$RESULTS_DIR/bench_func_$(date +%Y-%m-%d_%H-%M-%S).log"
mkdir -p "$RESULTS_DIR"
{
echo "# Bench: $(date)"
echo "Namespace: $NS"
echo "Donor NS: $DONOR_NS"
} | tee "$LOGFILE"
# 1. Create namespace + copy RBAC from donor
echo -n "[1] Create namespace + RBAC... " | tee -a "$LOGFILE"
START_NS=$(date +%s%3N)
kubectl create ns "$NS" >/dev/null
# Копируем ServiceAccount-ы
for sa in fission-builder fission-fetcher; do
kubectl get sa "$sa" -n "$DONOR_NS" -o json 2>/dev/null \
| jq 'del(.metadata.resourceVersion,.metadata.uid,.metadata.creationTimestamp,.metadata.annotations,.metadata.ownerReferences) | .metadata.namespace = $ns' --arg ns "$NS" \
| kubectl apply -f - >/dev/null 2>&1 || true
done
# Копируем RoleBindings из donor
kubectl get rolebinding -n "$DONOR_NS" -o json 2>/dev/null \
| jq -r '.items[] | select(.roleRef.kind=="ClusterRole") | del(.metadata.resourceVersion,.metadata.uid,.metadata.creationTimestamp,.metadata.annotations,.metadata.ownerReferences) | .metadata.namespace = $ns' --arg ns "$NS" \
| kubectl apply -f - >/dev/null 2>&1 || true
END_NS=$(date +%s%3N)
echo "$((END_NS-START_NS)) ms" | tee -a "$LOGFILE"
# 2. Create environment
echo -n "[2] Create environment... " | tee -a "$LOGFILE"
START_ENV=$(date +%s%3N)
fission env create --name "$ENV_NAME" --image naeel/fission-python-env:v1.1 --namespace "$NS" >/dev/null 2>&1
END_ENV=$(date +%s%3N)
echo "$((END_ENV-START_ENV)) ms" | tee -a "$LOGFILE"
# 3. Upload function
echo -n "[3] Upload function... " | tee -a "$LOGFILE"
START_UP=$(date +%s%3N)
fission fn create --name "$FUNC_NAME" --env "$ENV_NAME" --code "$FUNC_DIR/hello.py" --namespace "$NS" >/dev/null 2>&1
END_UP=$(date +%s%3N)
echo "$((END_UP-START_UP)) ms" | tee -a "$LOGFILE"
# 4. Cold start invoke (retry до 60 раз по 2 сек = 2 мин максимум)
echo -n "[4] Cold start invoke... " | tee -a "$LOGFILE"
START_COLD=$(date +%s%3N)
COLD_OK=0
for i in {1..60}; do
RESULT=$(fission fn test --name "$FUNC_NAME" --namespace "$NS" 2>&1 || true)
if echo "$RESULT" | grep -qE "hello|200|statusCode"; then
COLD_OK=1
break
fi
sleep 2
done
END_COLD=$(date +%s%3N)
if [[ $COLD_OK -eq 1 ]]; then
echo "$((END_COLD-START_COLD)) ms" | tee -a "$LOGFILE"
else
echo "FAILED" | tee -a "$LOGFILE"
{
echo "[DEBUG] pods in $NS:"
kubectl get pods -n "$NS" 2>&1
echo "[DEBUG] events in $NS:"
kubectl get events -n "$NS" --sort-by=.metadata.creationTimestamp 2>&1 | tail -20
} >> "$LOGFILE"
exit 1
fi
# 5. Hot start invoke (pod уже прогрет, retry до 5 раз)
echo -n "[5] Hot start invoke... " | tee -a "$LOGFILE"
START_HOT=$(date +%s%3N)
HOT_OK=0
for i in {1..5}; do
RESULT=$(fission fn test --name "$FUNC_NAME" --namespace "$NS" 2>&1 || true)
if echo "$RESULT" | grep -qE "hello|200|statusCode"; then
HOT_OK=1; break
fi
sleep 2
done
END_HOT=$(date +%s%3N)
if [[ $HOT_OK -eq 1 ]]; then
echo "$((END_HOT-START_HOT)) ms" | tee -a "$LOGFILE"
else
echo "FAILED (pod не ответил)" | tee -a "$LOGFILE"
fi
# 6. Hot x5
echo "[6] Hot start x5:" | tee -a "$LOGFILE"
for i in {1..5}; do
START_H=$(date +%s%3N)
RESULT=$(fission fn test --name "$FUNC_NAME" --namespace "$NS" 2>&1 || true)
END_H=$(date +%s%3N)
if echo "$RESULT" | grep -qE "hello|200|statusCode"; then
echo " invoke #$i: $((END_H-START_H)) ms" | tee -a "$LOGFILE"
else
echo " invoke #$i: FAILED" | tee -a "$LOGFILE"
fi
done
echo "---" | tee -a "$LOGFILE"
echo "DONE. Log: $LOGFILE"
+5
View File
@@ -0,0 +1,5 @@
def main(event, context):
return {
"statusCode": 200,
"body": "hello, world!"
}
+339
View File
@@ -0,0 +1,339 @@
# Руководство: контракт функций в Fission runtime
Дата: 2026-05-19
Версия консоли: v1.3.94+
Источник: дизассемблирование реальных env-образов в кластере + отладка тестов
---
## Содержание
1. [PHP](#php)
2. [Node.js](#nodejs)
3. [Python](#python)
4. [Ruby](#ruby)
5. [Найденные баги и исправления](#баги)
---
## PHP
### Образ
```
ghcr.io/fission/php-env:latest
```
Сервер: `ReactPHP` на порту `:8888`, файл `/app/server.php`.
### Entrypoint
```
main.php::handler
```
Формат: `filename::functionName`. Двойное двоеточие — разделитель файла и функции.
### ⚠️ ГЛАВНЫЙ БАГ (2026-05-19)
**`return ['statusCode' => 200, 'body' => ...]` — PHP env ИГНОРИРУЕТ return value функции.**
Почему: server.php создаёт `$response = new Response()` (пустой PSR-7 объект),
вызывает функцию и возвращает этот же объект без чтения результата вызова:
```php
// server.php — реальный код PHP env:
if (function_exists($userFunction)) {
$response = new Response();
ob_end_clean();
$userFunction(['request' =>$request, 'response' => $response, 'logger' => $logger]);
return $response; // ← возвращает оригинальный объект, не результат вызова
}
```
### Правильный формат — PSR-7
Функция должна **писать** в `$ctx["response"]->getBody()`:
```php
<?php
function handler($ctx) {
$response = $ctx["response"];
$response->getBody()->write(json_encode([
'status' => 'ok',
'value' => 42,
]));
}
```
Доступные ключи контекста:
- `$ctx["request"]``Psr\Http\Message\ServerRequestInterface`
- `$ctx["response"]``Psr\Http\Message\ResponseInterface` (RingCentral\Psr7)
- `$ctx["logger"]``Monolog\Logger`
### Backwards compatibility (echo)
Если функция `handler` НЕ объявлена, PHP env перехватывает весь вывод через `ob_start()` и возвращает его как тело ответа:
```php
<?php
// Работает, но нет доступа к request/response/logger
echo json_encode(['key' => 'value']);
```
Использовать только для самых простых случаев без работы с запросом.
### Примеры
**Минимальный (PSR-7):**
```php
<?php
function handler($ctx) {
$ctx["response"]->getBody()->write("Hello from PHP");
}
```
**С JSON-ответом:**
```php
<?php
function handler($ctx) {
$result = ['sum' => 1 + 2, 'lang' => 'php'];
$ctx["response"]->getBody()->write(json_encode($result));
}
```
**С чтением тела запроса:**
```php
<?php
function handler($ctx) {
$body = json_decode((string)$ctx["request"]->getBody(), true);
$name = $body['name'] ?? 'World';
$ctx["response"]->getBody()->write("Hello, $name!");
}
```
**CPU-нагрузка:**
```php
<?php
function fib($n) {
if ($n < 2) return $n;
return fib($n-1) + fib($n-2);
}
function handler($ctx) {
$ctx["response"]->getBody()->write('result:' . fib(28));
}
```
### Что НЕ работает
```php
// ❌ НЕПРАВИЛЬНО — return игнорируется
function handler(array $ctx): array {
return ['statusCode' => 200, 'body' => 'hello'];
}
// ❌ НЕПРАВИЛЬНО — echo не работает если функция объявлена
function handler($ctx) {
echo "hello"; // ob_start уже закрыт перед вызовом
}
```
---
## Node.js
### Образ
```
ghcr.io/fission/node-env:latest
```
Сервер: Express на порту `:8888`, файл `/usr/src/app/server.js`.
### Entrypoint
```
main
```
**Без расширения `.js`.**
### ⚠️ ГЛАВНЫЙ БАГ (2026-05-19)
**`functionName = "main.js"` ломает специализацию.**
Почему: server.js делает `split(".")` для любого functionName содержащего точку:
```js
// server.js — реальный код node-env:
if (req.body.functionName && req.body.functionName.includes('.')) {
// ПРЕДПОЛАГАЕТСЯ формат: 'file.function'
const entrypoint = req.body.functionName.split(".");
filename = entrypoint[0]; // "main"
funcname = entrypoint[1]; // "js" ← ищет экспорт с именем "js"!
}
```
`"main.js"``filename="main"`, `funcname="js"` → после загрузки модуля ищет `module["js"]` → undefined → specialize 500.
**Правильно:** `functionName = "main"``funcname = undefined` → берёт `default export` = `module.exports`.
### Формат кода
Консоль оборачивает пользовательский код в CJS wrapper. Пользователь пишет:
```js
// Простая функция, экспортирует async handler:
module.exports = async function(ctx) {
return { status: 200, body: "Hello from Node.js" };
};
```
Или через именованный экспорт:
```js
async function handler(ctx) {
return { status: 200, body: "ok" };
}
module.exports = handler;
```
Формат возвращаемого объекта:
```js
{ status: 200, body: "строка или JSON" }
```
### Примеры
**Минимальный:**
```js
module.exports = async function(ctx) {
return { status: 200, body: "hello-node" };
};
```
**С JSON:**
```js
module.exports = async function(ctx) {
const result = { lang: "nodejs", fib25: 75025 };
return { status: 200, body: JSON.stringify(result) };
};
```
**CPU-нагрузка:**
```js
function fib(n) { return n < 2 ? n : fib(n-1) + fib(n-2); }
module.exports = async function(ctx) {
return { status: 200, body: "fib35:" + fib(35) };
};
```
---
## Python
### Образ
```
naeel/fission-python-env:v1.1
```
(НЕ использовать официальный `ghcr.io/fission/python-env` — не поддерживает `def main(event, context)`)
### Entrypoint
```
main.main
```
Формат: `module.function`.
### Формат функции
```python
def main(event, context):
return {
"status": 200,
"body": "Hello from Python"
}
```
### Примеры
```python
import json
def main(event, context):
result = {"lang": "python", "sum": sum(range(100))}
return {"status": 200, "body": json.dumps(result)}
```
---
## Ruby
### Образ
```
ghcr.io/fission/ruby-env:latest
```
### Entrypoint
```
handler
```
Имя метода в загруженном файле.
### Формат функции
```ruby
def handler(event, context)
{ statusCode: 200, body: "Hello from Ruby" }
end
```
### Примеры
```ruby
require 'json'
def handler(event, context)
result = { lang: "ruby", value: (1..10).reduce(:+) }
{ statusCode: 200, body: result.to_json }
end
```
---
## Баги
### БАГ A — ExpiryReaper race condition (исправлен в v1.3.93)
**Симптом:** PHP/последний язык в цикле CREATE теряет Package — удаляется ExpiryReaper'ом.
**Причина:** `activeFunctions` snapshot снимается ДО создания Function. Reaper проверяет orphan packages по устаревшему снимку → последний Package = orphan → удалён.
**Коммит:** `72e5d50`
**Файл:** `console/internal/cloud/tenant.go`
**Fix:** свежий LIST functions (`freshFunctions`) перед orphan-check вместо кешированного снимка.
---
### БАГ B — Node.js entrypoint `"main.js"` ломает specializeV2 (исправлен в v1.3.94)
**Симптом:** Node.js функция не специализируется, invoke timeout.
**Причина:** node-env `specializeV2` делает `split(".")` по любой точке в functionName. `"main.js"``funcname="js"` → ищет `module["js"]` → undefined → specialize 500 → бесконечный retry.
**Коммит:** `090a7af`
**Файл:** `console/internal/runtime/entrypoint.go`
**Fix:** entrypoint Node.js = `"main"` (без расширения). funcname=undefined → берёт `default export`.
---
### БАГ C — PHP `return array` игнорируется (исправлен в test_heavy.sh, v1.3.94)
**Симптом:** PHP invoke возвращает `status=200` но `response_raw=""`.
**Причина:** PHP env создаёт пустой `$response` объект, вызывает функцию, возвращает оригинальный объект — return value функции не читается.
**Коммит:** `042b1e5`
**Файл:** `scripts/test_heavy.sh`
**Fix:** PHP код в тестах переписан на PSR-7: `$ctx["response"]->getBody()->write(...)`.
---
## Сводная таблица: правильный контракт
| Язык | Entrypoint | Формат функции | return/write |
|--------|---------------------|-------------------------------------------------------|---------------------------|
| Python | `main.main` | `def main(event, context):` | `return {"status":200,...}`|
| Node.js| `main` | `module.exports = async function(ctx) {...}` | `return { status, body }` |
| PHP | `main.php::handler` | `function handler($ctx) {...}` | `$ctx["response"]->getBody()->write(...)` |
| Ruby | `handler` | `def handler(event, context)` | `{ statusCode: 200, body: ... }` |
+40
View File
@@ -0,0 +1,40 @@
# Bench Results — PV Storage — 2026-05-20
**RUNID:** 9250986
**Дата:** Wed May 20 07:23:0607:27:07 MSK 2026
**Storage:** PVC (PV)
**API:** https://fission.kube5s.ru/console/api
**SUB:** bench@bench.local
**Итог: PASS=20 FAIL=0 TOTAL=20**
## Таблица результатов
| LANG | SIZE | UPLOAD(ms) | COLD(ms) | HOT_AVG(ms) | STATUS |
|--------|---------|-----------|---------|------------|--------|
| python | tiny | 308 | 4384 | 771 | OK |
| python | small | 161 | 1947 | 1860 | OK |
| python | medium | 2751 | 1387 | 1183 | OK |
| python | large | 2764 | 1382 | 1184 | OK |
| nodejs | tiny | 3150 | 8011 | 1175 | OK |
| nodejs | small | 2764 | 1526 | 1198 | OK |
| nodejs | medium | 2745 | 2597 | 1185 | OK |
| nodejs | large | 2737 | 2603 | 1314 | OK |
| ruby | tiny | 3148 | 2105 | 1209 | OK |
| ruby | small | 2768 | 1339 | 1194 | OK |
| ruby | medium | 2759 | **81549** | 1183 | OK (аномалия cold, 3-й прогон подряд) |
| ruby | large | 2743 | 1390 | 787 | OK |
| php | tiny | 1547 | 2546 | 94 | OK |
| php | small | 904 | 1106 | 1213 | OK |
| php | medium | 2744 | 1571 | 1198 | OK ✓ FIXED |
| php | large | 2732 | 1508 | 1151 | OK ✓ FIXED |
| python | archive | 2759 | 1392 | 1178 | OK |
| nodejs | archive | 3177 | 1545 | 1197 | OK |
| ruby | archive | 2780 | 1376 | 1187 | OK |
| php | archive | 2785 | 1416 | 1172 | OK |
## Что было исправлено
PHP medium/large — заменены arrow functions `fn()=>` на традиционный синтаксис `function(){}`.
PHP env в кластере < 7.4, arrow functions добавлены в PHP 7.4.
Лог на VM: `/home/naeel/terra/fission/test-results/bench-func/bench_full_2026-05-20_07-23-06.log`
+93
View File
@@ -1,5 +1,98 @@
# Progress Log
---
## 2026-05-19 (вечер) — Node.js require fix (v1.3.95) + суровые тесты
### Ветка: `feat/console-pv-storage` (git commit `fe8870a`)
---
### Исправленный баг: Node.js `require` недоступен внутри функции
**Симптом:** Любая Node.js функция, использующая `require(...)`, падала с:
```
ERR_MODULE_NOT_FOUND: Cannot find module '/userfunc/deployarchive/main'
```
**Реальная причина:** В `console/internal/runtime/nodejs.go` wrapper генерировался как:
```js
(new Function('module', 'exports', code))(__mod, __mod.exports);
```
`new Function` выполняется в изолированном глобальном scope — `require` там не существует.
Вызов `require('crypto')` внутри кода → `ReferenceError` → ESM fallback → `import('file:///userfunc/deployarchive/main')` без `.js` → ERR_MODULE_NOT_FOUND.
**Fix** (`console/internal/runtime/nodejs.go`):
```js
// было:
(new Function('module', 'exports', code))(__mod, __mod.exports);
// стало:
(new Function('module', 'exports', 'require', code))(__mod, __mod.exports, require);
```
**Также исправлено** (`console/internal/api/package.go`):
Regex `nodeJSWrapperPattern` обновлён — принимает оба формата (старый без `require` и новый с `require`), backward compatible.
**Unit-тест** (`console/internal/runtime/nodejs_test.go`):
Убраны устаревшие ESM-проверки, добавлены актуальные CJS-проверки.
**Образ:** `naeel/fission-console:v1.3.95`
---
### Прогоны `test_heavy.sh`
Два прогона подряд с теми же условиями (namespace `fission-c3511430abe5675b`):
| Секция | af2f674f | be04cc1b | Вывод |
|--------|---------|---------|-------|
| S1 CREATE (4 яз) | PHP 502 → FAIL | **4/4 PASS** | PHP 502 в первом — transient |
| S1 invoke "не стабилизировался" | FAIL×4 | FAIL×4 | баг теста — WAIT_SEC < cold start |
| S2 invoke "не стабилизировался" | FAIL×4 | FAIL×4 | то же |
| S3 BIG RESPONSE size=46/48 | FAIL×2 | FAIL×2 | баг теста — проверяет поле `raw` (truncated), не реальный ответ |
| S4 invoke "не стабилизировался" | FAIL×4 | FAIL×4 | баг теста — WAIT_SEC < cold start |
| S5 RAPID 12/12 | PASS | PASS | ✓ |
| S6 Python PARALLEL 5/5 | PASS | PASS | ✓ |
| S6 Node.js PARALLEL 5/5 | **2/5 FAIL** | **2/5 FAIL** | pool size=1 не хватает на 5 параллельных — стабильно |
| S6 Ruby PARALLEL 5/5 | PASS | PASS | ✓ |
| S6 PHP PARALLEL 5/5 | 1/5 FAIL | **PASS** | первый — transient |
| S7 Python SLOW(6s) | PASS | PASS | ✓ |
| S7 Node.js SLOW(6s) | PASS (TRY=2) | PASS (TRY=1) | ✓ require fix работает |
| S8 Python MEMORY(20MB) | PASS | PASS | ✓ |
| S8 Node.js MEMORY(20MB) | PASS | PASS | ✓ |
| **ИТОГ** | **PASS=29 FAIL=17** | **PASS=31 FAIL=15** | |
Логи: `test-results/2026-05-19_*` (два файла)
---
### Анализ стабильных FAIL=15 (оба прогона)
#### 1. S1+S2+S4 "не стабилизировался" (12 FAIL)
**Причина:** `WAIT_SEC` в `test_heavy.sh` меньше реального cold start.
Функции реально работают — проверено прямыми SSH-запросами через `sleep + curl`.
**Что нужно:** увеличить `WAIT_SEC` или добавить retry-loop с адекватным таймаутом.
#### 2. S3 BIG RESPONSE (2 FAIL)
**Причина:** тест проверяет `response_raw length` — это длина JSON-поля `raw` из ответа консоли.
Консоль обрезает `raw` до ~46-48 символов. Реальный ответ функции = 50KB (status=200 подтверждено).
**Что нужно:** либо фиксить тест (проверять Content-Length/реальный body), либо фиксить консоль (не обрезать `raw`).
#### 3. S6 Node.js PARALLEL 2/5 (1 FAIL)
**Причина:** pool size=1 (один pod). При 5 параллельных запросах 2 таймаутятся пока ждут специализацию нового пода.
Python/Ruby справляются 5/5 — у них специализация быстрее.
**Что нужно:** либо увеличить pool size в Node.js environment, либо уменьшить параллельность в тесте до 3.
---
### Что делать дальше с тестами
- [ ] Исправить `test_heavy.sh` S1/S2/S4: увеличить `WAIT_SEC` до 120-180s или добавить retry-poll
- [ ] Исправить `test_heavy.sh` S3: проверять реальный ответ, не поле `raw`
- [ ] Решить S6 Node.js PARALLEL: увеличить pool или снизить параллельность
---
## 2026-05-19 — PV Storage Migration + Fetcher v1.23.0
### Ветка: `feat/console-pv-storage` (git commit `2aadae3`, pushed)