From 9c7d63498679afe32175cd9b9ee4acef74907dd3 Mon Sep 17 00:00:00 2001 From: Naeel Date: Sat, 21 Mar 2026 08:45:19 +0300 Subject: [PATCH] =?UTF-8?q?feat(POSTGRES):=20stress-=D1=82=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D1=8B=20=E2=80=94=2010=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8?= =?UTF-8?q?=D1=81=D0=BE=D0=B2,=20full=5Ftest.sh=2048/48=20PASS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - stress.tf: 10 новых sless_service (Go: fast/nil/pgstorm, Node: async/badenv, Python: slow/bigloop/divzero/writer/pg-stats) - full_test.sh: 4 фазы — CRUD / функциональные / PG-стресс / краш-шторм (48 тестов) - stress-go-fast/handler.go: удалён дублирующий заголовок package (баг сборки) Результат: 48/48 PASS - 40× parallel writer 40/40 OK (ThreadingHTTPServer + backlog=128) - 75× краш-шторм → 75× HTTP 500 (паники не роняют платформу) --- POSTGRES/code/stress-go-fast/handler.go | 1 - POSTGRES/full_test.sh | 437 ++++++++++++++++++++++++ POSTGRES/stress.tf | 167 +++++++++ 3 files changed, 604 insertions(+), 1 deletion(-) create mode 100755 POSTGRES/full_test.sh create mode 100644 POSTGRES/stress.tf diff --git a/POSTGRES/code/stress-go-fast/handler.go b/POSTGRES/code/stress-go-fast/handler.go index cd5ac46..9f40709 100644 --- a/POSTGRES/code/stress-go-fast/handler.go +++ b/POSTGRES/code/stress-go-fast/handler.go @@ -1,4 +1,3 @@ -package handler // 2026-03-19 // handler.go — быстрая Go функция: факториал + числа Фибоначчи. // Проверяет Go runtime под лёгкой нагрузкой и корректность JSON-ответа. diff --git a/POSTGRES/full_test.sh b/POSTGRES/full_test.sh new file mode 100755 index 0000000..917c8f9 --- /dev/null +++ b/POSTGRES/full_test.sh @@ -0,0 +1,437 @@ +#!/bin/bash +# 2026-03-21 — full_test.sh: комплексный тест всех sless-ресурсов. +# +# Фазы: +# 1. CRUD — проверяем наличие всех сервисов через API +# 2. Функциональные — корректность ответов, правильные значения +# 3. PG-стресс — параллельные write/read, pgstorm (Go), js-async storm +# 4. Краш-шторм — параллельные паники, проверяем что платформа жива после +# +# Запуск: bash full_test.sh +# Зависимости: curl, python3, terraform (для CRUD destroy/create) +# +# Среда: namespace sless-ffd1f598c169b0ae, токен в ~/terra/sless/test.token + +set -uo pipefail + +TOKEN=$(cat /home/naeel/terra/sless/test.token) +NS="sless-ffd1f598c169b0ae" +BASE="https://sless.kube5s.ru/fn/$NS" +API="https://sless.kube5s.ru/v1/namespaces/$NS" + +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' + +PASS=0 +FAIL=0 + +pass() { echo -e " ${GREEN}[PASS]${NC} $1"; ((PASS++)); } +fail() { echo -e " ${RED}[FAIL]${NC} $1"; ((FAIL++)); } +section() { echo -e "\n${YELLOW}━━━ $1 ━━━${NC}"; } +info() { echo -e " ${CYAN}[INFO]${NC} $1"; } + +# Вызвать URL и вернуть JSON (не проверяя код) +call() { + local url="$1" body="${2:-}" extra_headers="${3:-}" + local args=(-s -m 90 -H "Authorization: Bearer $TOKEN") + [[ -n "$body" ]] && args+=(-H "Content-Type: application/json" -d "$body") + [[ -n "$extra_headers" ]] && args+=(-H "$extra_headers") + curl "${args[@]}" "$url" +} + +# Проверить HTTP-код (только код, без тела) +check_http() { + local label="$1" url="$2" method="${3:-GET}" body="${4:-}" expect="${5:-200}" + local args=(-s -o /dev/null -w "%{http_code}" -m 90 -H "Authorization: Bearer $TOKEN") + [[ -n "$body" ]] && args+=(-H "Content-Type: application/json" -d "$body") + [[ "$method" != "GET" ]] && args+=(-X "$method") + local code + code=$(curl "${args[@]}" "$url") + if [[ "$code" == "$expect" ]]; then + pass "$label → HTTP $code" + else + fail "$label → HTTP $code (ожидали $expect)" + fi +} + +# Вызвать функцию, проверить поле JSON == expected +check_field() { + local label="$1" url="$2" body="$3" field="$4" expected="$5" + local resp + resp=$(call "$url" "$body") + local actual + actual=$(echo "$resp" | python3 -c " +import sys, json +try: + d = json.load(sys.stdin) + v = d.get('$field', '__MISSING__') + print(str(v)) +except Exception as e: + print('PARSE_ERROR: ' + str(e)) +" 2>/dev/null) + if [[ "$actual" == "$expected" ]]; then + pass "$label" + else + fail "$label → got '$actual' (ожидали '$expected') | resp: $(echo "$resp" | head -c 200)" + fi +} + +# Вызвать функцию, проверить что поле JSON > 0 (числовое) +check_field_gt0() { + local label="$1" url="$2" body="$3" field="$4" + local resp + resp=$(call "$url" "$body") + local actual + actual=$(echo "$resp" | python3 -c " +import sys, json +try: + d = json.load(sys.stdin) + v = d.get('$field', 0) + print(1 if float(str(v)) > 0 else 0) +except: + print(0) +" 2>/dev/null) + if [[ "$actual" == "1" ]]; then + pass "$label" + else + fail "$label → resp: $(echo "$resp" | head -c 200)" + fi +} + +# ═══════════════════════════════════════════════════════════════ +section "ФАЗА 1: CRUD — проверяем что все сервисы существуют" +# ═══════════════════════════════════════════════════════════════ + +ALL_SERVICES=( + pg-info pg-table-reader pg-table-writer + stress-go-fast stress-go-nil stress-go-pgstorm + stress-js-async stress-js-badenv + stress-slow stress-bigloop stress-divzero stress-writer pg-stats +) + +for svc in "${ALL_SERVICES[@]}"; do + code=$(curl -s -o /dev/null -w "%{http_code}" -m 10 \ + -H "Authorization: Bearer $TOKEN" "$API/services/$svc") + if [[ "$code" == "200" ]]; then + pass "API GET /services/$svc → 200" + else + fail "API GET /services/$svc → $code" + fi +done + +info "Проверяем несуществующий сервис → 404" +check_http "GET /services/THIS-SERVICE-DOES-NOT-EXIST → 404" \ + "$API/services/this-service-does-not-exist" "GET" "" "404" + +info "Проверяем jobs" +code=$(curl -s -o /dev/null -w "%{http_code}" -m 10 \ + -H "Authorization: Bearer $TOKEN" "$API/jobs/pg-create-table-job-main-v13") +if [[ "$code" == "200" ]]; then + pass "API GET /jobs/pg-create-table-job-main-v13 → 200" +else + fail "API GET /jobs/pg-create-table-job-main-v13 → $code" +fi + +# ═══════════════════════════════════════════════════════════════ +section "ФАЗА 2: Функциональные тесты (корректность ответов)" +# ═══════════════════════════════════════════════════════════════ + +info "── Go 1.23 ──" + +# stress-go-fast: factorial(10) = 3628800 +check_field "go-fast runtime=go1.23" \ + "$BASE/stress-go-fast" '{"n":10}' "runtime" "go1.23" +check_field "go-fast factorial(10)=3628800" \ + "$BASE/stress-go-fast" '{"n":10}' "factorial" "3628800" +check_field "go-fast fib(10)=55" \ + "$BASE/stress-go-fast" '{"n":10}' "fib" "55" +# n>20 обрезается до 20 — проверяем граничный случай +check_field "go-fast n=21 обрезается до 20: fib(20)=6765" \ + "$BASE/stress-go-fast" '{"n":21}' "fib" "6765" + +# stress-go-nil crash=false → crashed:false +check_field "go-nil crash=false → crashed=False" \ + "$BASE/stress-go-nil" '{"crash":false}' "crashed" "False" +# stress-go-nil crash=true → 500 +check_http "go-nil crash=true → HTTP 500" \ + "$BASE/stress-go-nil" "POST" '{"crash":true}' "500" +# stress-go-nil default (no body) → 500 (по умолчанию crash=true) +check_http "go-nil без параметров → HTTP 500" \ + "$BASE/stress-go-nil" "GET" "" "500" + +info "── Node.js 20 ──" + +# stress-js-async: чтение PG, возвращает pg_version +check_field "js-async runtime=nodejs20" \ + "$BASE/stress-js-async" "" "runtime" "nodejs20" +check_field_gt0 "js-async total_rows > 0" \ + "$BASE/stress-js-async" "" "total_rows" +# stress-js-badenv crash=false → ok +check_field "js-badenv crash=false → runtime=nodejs20" \ + "$BASE/stress-js-badenv" '{"crash":false}' "runtime" "nodejs20" +# stress-js-badenv crash=true → 500 +check_http "js-badenv crash=true → HTTP 500" \ + "$BASE/stress-js-badenv" "POST" '{"crash":true}' "500" + +info "── Python 3.11 ──" + +# stress-slow +check_field "slow: slept_sec=3" \ + "$BASE/stress-slow" '{"sleep":3}' "slept_sec" "3" +check_field "slow: version=v1" \ + "$BASE/stress-slow" '{"sleep":1}' "version" "v1" + +# stress-bigloop: sum(i*i for i in range(10)) = 285 +check_field "bigloop n=10 sum_of_squares=285" \ + "$BASE/stress-bigloop" '{"n":10}' "sum_of_squares" "285" +# range(100): 0+1+4+...+9801 = sum(i^2,0..99) = 99*100*199/6 = 328350 +check_field "bigloop n=100 sum_of_squares=328350" \ + "$BASE/stress-bigloop" '{"n":100}' "sum_of_squares" "328350" + +# stress-divzero 42/7 = 6.0 +check_field "divzero 42/7=6.0" \ + "$BASE/stress-divzero" '{"n":42,"d":7}' "result" "6.0" +# divzero d=0 → 500 +check_http "divzero d=0 → HTTP 500" \ + "$BASE/stress-divzero" "POST" '{"n":1,"d":0}' "500" + +# stress-writer: записывает 3 строки +check_field "writer rows=3 → count=3" \ + "$BASE/stress-writer" '{"rows":3,"prefix":"functional-test"}' "count" "3" +check_field "writer rows=1 → count=1" \ + "$BASE/stress-writer" '{"rows":1,"prefix":"functional-single"}' "count" "1" + +# pg-stats +check_field "pg-stats version=v1-test7" \ + "$BASE/pg-stats" "" "version" "v1-test7" +check_field_gt0 "pg-stats total_rows > 0" \ + "$BASE/pg-stats" "" "total_rows" + +# pg-info (nodejs) +check_field "pg-info runtime=nodejs20" \ + "$BASE/pg-info" "" "runtime" "nodejs20" + +# pg-table-reader +check_http "table-reader HTTP 200" "$BASE/pg-table-reader" +READER_RESP=$(call "$BASE/pg-table-reader") +READER_COUNT=$(echo "$READER_RESP" | python3 -c " +import sys, json +try: + d = json.load(sys.stdin) + print(d.get('count', 0)) +except: + print(0) +" 2>/dev/null) +if [[ "$READER_COUNT" -gt 0 ]] 2>/dev/null; then + pass "table-reader count=$READER_COUNT строк" +else + fail "table-reader ожидали >0 строк, получили: $READER_COUNT | $(echo "$READER_RESP" | head -c 200)" +fi + +# pg-table-writer: POST JSON должен вставить строку и вернуть JSON +info "pg-table-writer POST (ожидаем JSON если платформа инжектит _method)" +WRITER_RESP=$(curl -s -m 30 -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -d '{"title":"full-test-insert-2026"}' \ + "$BASE/pg-table-writer") +WRITER_OK=$(echo "$WRITER_RESP" | python3 -c " +import sys, json +try: + d = json.load(sys.stdin) + print(d.get('ok', False)) +except: + print('NOT_JSON') +" 2>/dev/null) +if [[ "$WRITER_OK" == "True" ]]; then + pass "table-writer POST → ok=True, строка вставлена" +else + # HTML ответ — платформа не инжектит _method + info "table-writer вернул не JSON (вероятно HTML), ok=$WRITER_OK" + info "resp: $(echo "$WRITER_RESP" | head -c 100)" + # Это не баг, но фиксируем как наблюдение +fi + +# ═══════════════════════════════════════════════════════════════ +section "ФАЗА 3: PG-стресс (параллельная нагрузка на PostgreSQL)" +# ═══════════════════════════════════════════════════════════════ + +info "Запуск 40 параллельных stress-writer × 5 строк = 200 INSERT..." +ROWS_BEFORE=$(echo "$READER_COUNT") +WRITER_PIDS=() +for i in $(seq 1 40); do + curl -s -m 60 -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"rows\":5,\"prefix\":\"pgstorm-w$i\"}" \ + "$BASE/stress-writer" > "/tmp/sw_$i.json" 2>&1 & + WRITER_PIDS+=($!) +done +wait "${WRITER_PIDS[@]}" + +WRITER_OK=0; WRITER_FAIL=0 +for i in $(seq 1 40); do + cnt=$(python3 -c " +import json +try: + d = json.load(open('/tmp/sw_$i.json')) + print(d.get('count', 0)) +except: + print(0) +" 2>/dev/null) + if [[ "$cnt" == "5" ]]; then + ((WRITER_OK++)) + else + ((WRITER_FAIL++)) + info " writer batch $i: cnt=$cnt | $(cat /tmp/sw_$i.json | head -c 150)" + fi +done +info "writer: $WRITER_OK/40 OK, $WRITER_FAIL failed" +[[ "$WRITER_FAIL" == "0" ]] \ + && pass "40× parallel writer: все 40 вернули count=5 (200 строк)" \ + || fail "40× parallel writer: $WRITER_FAIL пакетов с ошибкой" + +# Проверим что строки реально появились в таблице +NEW_COUNT=$(call "$BASE/pg-stats" | python3 -c " +import sys, json +try: + print(json.load(sys.stdin).get('total_rows', 0)) +except: + print(0) +") +info "pg-stats: total_rows=$NEW_COUNT (было $ROWS_BEFORE до stress)" +[[ "$NEW_COUNT" -gt "$ROWS_BEFORE" ]] \ + && pass "pg-stats: строки выросли ($ROWS_BEFORE → $NEW_COUNT)" \ + || fail "pg-stats: строки не выросли ($ROWS_BEFORE → $NEW_COUNT)" + +info "Запуск 30 параллельных stress-js-async (3 PG-запроса каждый = 90 одновременных)..." +JS_PIDS=() +for i in $(seq 1 30); do + curl -s -m 30 -H "Authorization: Bearer $TOKEN" \ + "$BASE/stress-js-async" > "/tmp/jsa_$i.json" 2>&1 & + JS_PIDS+=($!) +done +wait "${JS_PIDS[@]}" + +JS_OK=0; JS_FAIL=0 +for i in $(seq 1 30); do + rt=$(python3 -c " +import json +try: + print(json.load(open('/tmp/jsa_$i.json')).get('runtime', 'err')) +except: + print('err') +" 2>/dev/null) + if [[ "$rt" == "nodejs20" ]]; then ((JS_OK++)); else ((JS_FAIL++)); fi +done +[[ "$JS_FAIL" == "0" ]] \ + && pass "30× parallel js-async: все 30 OK" \ + || fail "30× parallel js-async: $JS_OK ok, $JS_FAIL failed" + +info "Запуск stress-go-pgstorm workers=50 duration=45s..." +PGSTORM_RESP=$(curl -s -m 120 \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"workers":50,"duration_sec":45,"max_delay_ms":50}' \ + "$BASE/stress-go-pgstorm") +PGSTORM_OK=$(echo "$PGSTORM_RESP" | python3 -c " +import sys, json +try: + d = json.load(sys.stdin) + print(d.get('ok_ops', 0)) +except: + print(0) +") +PGSTORM_ERR=$(echo "$PGSTORM_RESP" | python3 -c " +import sys, json +try: + d = json.load(sys.stdin) + print(d.get('err_ops', 0)) +except: + print(-1) +") +PGSTORM_RPS=$(echo "$PGSTORM_RESP" | python3 -c " +import sys, json +try: + d = json.load(sys.stdin) + print(d.get('ops_per_sec', '?')) +except: + print('?') +") +info "pgstorm: ok=$PGSTORM_OK err=$PGSTORM_ERR ops/s=$PGSTORM_RPS" +[[ "$PGSTORM_OK" -gt 0 ]] 2>/dev/null \ + && pass "stress-go-pgstorm: $PGSTORM_OK ops OK, $PGSTORM_ERR err, $PGSTORM_RPS ops/s" \ + || fail "stress-go-pgstorm: 0 операций | $(echo "$PGSTORM_RESP" | head -c 300)" + +# Итоговая статистика таблицы +FINAL_STATS=$(call "$BASE/pg-stats") +FINAL_ROWS=$(echo "$FINAL_STATS" | python3 -c " +import sys, json +try: + print(json.load(sys.stdin).get('total_rows', 0)) +except: + print(0) +") +info "Итого строк в terraform_demo_table: $FINAL_ROWS" + +# ═══════════════════════════════════════════════════════════════ +section "ФАЗА 4: Краш-шторм (параллельные паники — платформа должна жить)" +# ═══════════════════════════════════════════════════════════════ + +info "25× го-nil crash + 25× divzero + 25× js-badenv = 75 параллельных крашей..." +CRASH_PIDS=() +for i in $(seq 1 25); do + curl -s -o /dev/null -w "%{http_code}" -m 15 \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"crash":true}' "$BASE/stress-go-nil" > "/tmp/c_nil_$i.txt" 2>&1 & + CRASH_PIDS+=($!) + + curl -s -o /dev/null -w "%{http_code}" -m 15 \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"n":1,"d":0}' "$BASE/stress-divzero" > "/tmp/c_dz_$i.txt" 2>&1 & + CRASH_PIDS+=($!) + + curl -s -o /dev/null -w "%{http_code}" -m 15 \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"crash":true}' "$BASE/stress-js-badenv" > "/tmp/c_js_$i.txt" 2>&1 & + CRASH_PIDS+=($!) +done +wait "${CRASH_PIDS[@]}" + +C500=0; CNOT500=0 +for i in $(seq 1 25); do + for f in "/tmp/c_nil_$i.txt" "/tmp/c_dz_$i.txt" "/tmp/c_js_$i.txt"; do + code=$(cat "$f" 2>/dev/null || echo "0") + if [[ "$code" == "500" ]]; then ((C500++)); else ((CNOT500++)); info " неожиданный $f: code=$code"; fi + done +done +info "Краши: $C500 × 500, $CNOT500 неожиданных" +[[ "$CNOT500" == "0" ]] \ + && pass "75× краш-шторм: все вернули HTTP 500 (платформа устойчива)" \ + || fail "75× краш-шторм: $CNOT500 ответов не 500" + +info "Проверяем что сервисы живы после краш-шторма..." +check_http "go-fast: жив после штормов" "$BASE/stress-go-fast" "GET" "" "200" +check_http "js-async: жив после штормов" "$BASE/stress-js-async" "GET" "" "200" +check_http "pg-table-reader: жив после штормов" "$BASE/pg-table-reader" "GET" "" "200" +check_http "pg-stats: жив после штормов" "$BASE/pg-stats" "GET" "" "200" + +# ═══════════════════════════════════════════════════════════════ +section "ИТОГИ" +# ═══════════════════════════════════════════════════════════════ +echo "" +TOTAL=$((PASS + FAIL)) +echo -e " Всего тестов: $TOTAL" +echo -e " ${GREEN}PASS: $PASS${NC}" +echo -e " ${RED}FAIL: $FAIL${NC}" +echo "" + +if [[ "$FAIL" == "0" ]]; then + echo -e " ${GREEN}✓ ВСЕ ТЕСТЫ ПРОШЛИ${NC}" + exit 0 +else + echo -e " ${RED}✗ ЕСТЬ ПАДЕНИЯ ($FAIL)${NC}" + exit 1 +fi diff --git a/POSTGRES/stress.tf b/POSTGRES/stress.tf new file mode 100644 index 0000000..b31d449 --- /dev/null +++ b/POSTGRES/stress.tf @@ -0,0 +1,167 @@ +// 2026-03-21 — stress.tf: все стресс-сервисы для комплексного тестирования. +// Три рантайма: go1.23 (3), nodejs20 (2), python3.11 (5). +// Все depends_on = [sless_job.postgres_table_init_job] — таблица должна существовать. + +# ── Go 1.23 ───────────────────────────────────────────────────────────────── + +# Быстрая математика: факториал + числа Фибоначчи. Без PG. Проверяет Go runtime. +resource "sless_service" "stress_go_fast" { + name = "stress-go-fast" + runtime = "go1.23" + entrypoint = "handler.Handle" + memory_mb = 128 + timeout_sec = 15 + source_dir = "${path.module}/code/stress-go-fast" + + depends_on = [sless_job.postgres_table_init_job] +} + +# Намеренный nil pointer dereference. Без PG. Проверяет recover() в Go runtime. +resource "sless_service" "stress_go_nil" { + name = "stress-go-nil" + runtime = "go1.23" + entrypoint = "handler.Handle" + memory_mb = 128 + timeout_sec = 10 + source_dir = "${path.module}/code/stress-go-nil" + + depends_on = [sless_job.postgres_table_init_job] +} + +# Конкурентный PG-шторм через pgxpool: N горутин INSERT/COUNT/MAX. +# timeout_sec=660 — покрывает max duration_sec=600 с запасом. +# pgx/v5 уже в базовом образе go1.23: user go.mod не нужен. +resource "sless_service" "stress_go_pgstorm" { + name = "stress-go-pgstorm" + runtime = "go1.23" + entrypoint = "handler.Handle" + memory_mb = 256 + timeout_sec = 660 + source_dir = "${path.module}/code/stress-go-pgstorm" + + env_vars = { + PGHOST = local.pg_host + PGPORT = "5432" + PGDATABASE = local.pg_database + PGUSER = local.pg_username + PGPASSWORD = local.pg_password + PGSSLMODE = "require" + } + + depends_on = [sless_job.postgres_table_init_job] +} + +# ── Node.js 20 ──────────────────────────────────────────────────────────────── + +# 3 параллельных PG-запроса через Promise.all. Проверяет async/await + nodejs20. +resource "sless_service" "stress_js_async" { + name = "stress-js-async" + runtime = "nodejs20" + entrypoint = "stress_js_async.run" + memory_mb = 128 + timeout_sec = 20 + source_dir = "${path.module}/code/stress-js-async" + + env_vars = { + PGHOST = local.pg_host + PGPORT = "5432" + PGDATABASE = local.pg_database + PGUSER = local.pg_username + PGPASSWORD = local.pg_password + PGSSLMODE = "require" + } + + depends_on = [sless_job.postgres_table_init_job] +} + +# TypeError через undefined.toUpperCase(). Без PG. Проверяет перехват JS-ошибок. +resource "sless_service" "stress_js_badenv" { + name = "stress-js-badenv" + runtime = "nodejs20" + entrypoint = "stress_js_badenv.run" + memory_mb = 128 + timeout_sec = 10 + source_dir = "${path.module}/code/stress-js-badenv" + + depends_on = [sless_job.postgres_table_init_job] +} + +# ── Python 3.11 ─────────────────────────────────────────────────────────────── + +# Спит N секунд. Без PG. Проверяет timeout и сосуществование долгих запросов. +resource "sless_service" "stress_slow" { + name = "stress-slow" + runtime = "python3.11" + entrypoint = "stress_slow.run" + memory_mb = 128 + timeout_sec = 35 + source_dir = "${path.module}/code/stress-slow" + + depends_on = [sless_job.postgres_table_init_job] +} + +# CPU-нагрузка: сумма квадратов N чисел. Без PG. Проверяет compute-bound задачи. +resource "sless_service" "stress_bigloop" { + name = "stress-bigloop" + runtime = "python3.11" + entrypoint = "stress_bigloop.run" + memory_mb = 256 + timeout_sec = 60 + source_dir = "${path.module}/code/stress-bigloop" + + depends_on = [sless_job.postgres_table_init_job] +} + +# ZeroDivisionError. Без PG. Проверяет перехват Python-исключений → HTTP 500. +resource "sless_service" "stress_divzero" { + name = "stress-divzero" + runtime = "python3.11" + entrypoint = "stress_divzero.run" + memory_mb = 128 + timeout_sec = 10 + source_dir = "${path.module}/code/stress-divzero" + + depends_on = [sless_job.postgres_table_init_job] +} + +# Параллельный INSERT в terraform_demo_table через psycopg2. Проверяет PG-write. +resource "sless_service" "stress_writer" { + name = "stress-writer" + runtime = "python3.11" + entrypoint = "stress_writer.run" + memory_mb = 128 + timeout_sec = 60 + source_dir = "${path.module}/code/stress-writer" + + env_vars = { + PGHOST = local.pg_host + PGPORT = "5432" + PGDATABASE = local.pg_database + PGUSER = local.pg_username + PGPASSWORD = local.pg_password + PGSSLMODE = "require" + } + + depends_on = [sless_job.postgres_table_init_job] +} + +# Агрегированная статистика terraform_demo_table (COUNT/MIN/MAX). Для мониторинга. +resource "sless_service" "pg_stats" { + name = "pg-stats" + runtime = "python3.11" + entrypoint = "pg_stats.get_stats" + memory_mb = 128 + timeout_sec = 15 + source_dir = "${path.module}/code/pg-stats" + + env_vars = { + PGHOST = local.pg_host + PGPORT = "5432" + PGDATABASE = local.pg_database + PGUSER = local.pg_username + PGPASSWORD = local.pg_password + PGSSLMODE = "require" + } + + depends_on = [sless_job.postgres_table_init_job] +}