test: 8 стресс-функций (py/go/js), crash-тесты, stress_test.sh — 32 строки в PG
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
# 2026-03-19
|
||||
# stress_bigloop.py — CPU-интенсивная функция: считает сумму квадратов N чисел.
|
||||
# Проверяет поведение под нагрузкой (большая и средняя итерация).
|
||||
|
||||
import time
|
||||
|
||||
_VERSION = "v1"
|
||||
|
||||
|
||||
def run(event):
|
||||
n = int(event.get("n", 500_000))
|
||||
start = time.monotonic()
|
||||
total = sum(i * i for i in range(n))
|
||||
elapsed = round(time.monotonic() - start, 4)
|
||||
return {
|
||||
"version": _VERSION,
|
||||
"n": n,
|
||||
"sum_of_squares": total,
|
||||
"elapsed_sec": elapsed,
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# 2026-03-19
|
||||
# stress_divzero.py — намеренно делит на ноль (ZeroDivisionError).
|
||||
# Проверяет: платформа перехватывает панику, возвращает HTTP 500, не роняет под.
|
||||
|
||||
_VERSION = "v1"
|
||||
|
||||
|
||||
def run(event):
|
||||
numerator = int(event.get("n", 42))
|
||||
denominator = int(event.get("d", 0)) # по умолчанию 0 — намеренный краш
|
||||
# ZeroDivisionError: проверяем что платформа обрабатывает исключения
|
||||
result = numerator / denominator
|
||||
return {"version": _VERSION, "result": result}
|
||||
@@ -0,0 +1,47 @@
|
||||
// 2026-03-19
|
||||
// handler.go — быстрая Go функция: факториал + числа Фибоначчи.
|
||||
// Проверяет Go runtime под лёгкой нагрузкой и корректность JSON-ответа.
|
||||
//
|
||||
// Entrypoint: handler.Handle
|
||||
|
||||
package handler
|
||||
|
||||
import "fmt"
|
||||
|
||||
// factorial — рекурсивный факториал (для небольших n <= 20).
|
||||
func factorial(n int) uint64 {
|
||||
if n <= 1 {
|
||||
return 1
|
||||
}
|
||||
return uint64(n) * factorial(n-1)
|
||||
}
|
||||
|
||||
// fib — итеративное число Фибоначчи.
|
||||
func fib(n int) int {
|
||||
if n <= 1 {
|
||||
return n
|
||||
}
|
||||
a, b := 0, 1
|
||||
for i := 2; i <= n; i++ {
|
||||
a, b = b, a+b
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Handle — точка входа рантайма.
|
||||
func Handle(event map[string]interface{}) interface{} {
|
||||
n := 10
|
||||
if v, ok := event["n"].(float64); ok {
|
||||
n = int(v)
|
||||
if n > 20 {
|
||||
n = 20
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"runtime": "go1.23",
|
||||
"version": "v1",
|
||||
"n": n,
|
||||
"factorial": fmt.Sprintf("%d", factorial(n)),
|
||||
"fib": fib(n),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// 2026-03-19
|
||||
// handler.go — намеренный nil pointer dereference в Go.
|
||||
// Проверяет что Go runtime recover() перехватывает панику и платформа возвращает 500.
|
||||
// Entrypoint: handler.Handle
|
||||
package handler
|
||||
|
||||
func Handle(event map[string]interface{}) interface{} {
|
||||
crash := true
|
||||
if v, ok := event["crash"].(bool); ok {
|
||||
crash = v
|
||||
}
|
||||
if crash {
|
||||
var p *string
|
||||
_ = *p // panic: намеренный nil pointer для stress-теста
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"runtime": "go1.23",
|
||||
"version": "v1",
|
||||
"crashed": false,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "stress-js-async",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"pg": "^8.11.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// 2026-03-19
|
||||
// stress_js_async.js — делает 3 параллельных запроса к PG через Promise.all.
|
||||
// Проверяет nodejs20 runtime под умеренной нагрузкой и async/await.
|
||||
//
|
||||
// Entrypoint: stress_js_async.run
|
||||
|
||||
'use strict';
|
||||
|
||||
const { Client } = require('pg');
|
||||
|
||||
exports.run = async (event) => {
|
||||
const client = new Client({
|
||||
host: process.env.PGHOST,
|
||||
port: parseInt(process.env.PGPORT || '5432'),
|
||||
database: process.env.PGDATABASE,
|
||||
user: process.env.PGUSER,
|
||||
password: process.env.PGPASSWORD,
|
||||
ssl: process.env.PGSSLMODE === 'require' ? { rejectUnauthorized: false } : false,
|
||||
});
|
||||
await client.connect();
|
||||
try {
|
||||
const [ver, cnt, max] = await Promise.all([
|
||||
client.query('SELECT version() AS v'),
|
||||
client.query('SELECT COUNT(*) AS cnt FROM terraform_demo_table'),
|
||||
client.query('SELECT MAX(id) AS max_id FROM terraform_demo_table'),
|
||||
]);
|
||||
return {
|
||||
runtime: 'nodejs20',
|
||||
version: 'v1',
|
||||
pg_version: ver.rows[0].v.split(' ').slice(0, 2).join(' '),
|
||||
total_rows: parseInt(cnt.rows[0].cnt, 10),
|
||||
max_id: max.rows[0].max_id,
|
||||
};
|
||||
} finally {
|
||||
await client.end();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "stress-js-badenv",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// 2026-03-19
|
||||
// stress_js_badenv.js — читает несуществующую переменную env и падает.
|
||||
// Проверяет: платформа перехватывает TypeError/undefined, возвращает 500.
|
||||
//
|
||||
// Entrypoint: stress_js_badenv.run
|
||||
|
||||
'use strict';
|
||||
|
||||
exports.run = async (event) => {
|
||||
const crash = event.crash !== false; // по умолчанию crash=true
|
||||
if (crash) {
|
||||
// Читаем несуществующий env, пытаемся вызвать .toUpperCase() на undefined
|
||||
const val = process.env.THIS_VAR_DOES_NOT_EXIST_AT_ALL;
|
||||
return { shout: val.toUpperCase() }; // TypeError: Cannot read properties of undefined
|
||||
}
|
||||
return { runtime: 'nodejs20', version: 'v1', crashed: false };
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
# 2026-03-19
|
||||
# stress_slow.py — долгая функция: спит N секунд (по умолчанию 8).
|
||||
# Проверяет что timeout-механизм и параллельные запросы не блокируют друг друга.
|
||||
|
||||
import time
|
||||
import os
|
||||
|
||||
_VERSION = "v1"
|
||||
|
||||
|
||||
def run(event):
|
||||
secs = int(event.get("sleep", 8))
|
||||
time.sleep(secs)
|
||||
return {
|
||||
"version": _VERSION,
|
||||
"slept_sec": secs,
|
||||
"pid": os.getpid(),
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
psycopg2-binary==2.9.9
|
||||
@@ -0,0 +1,39 @@
|
||||
# 2026-03-19
|
||||
# stress_writer.py — пишет N строк в terraform_demo_table (по умолчанию 5).
|
||||
# Проверяет параллельные INSERT'ы и устойчивость соединения с PG при нагрузке.
|
||||
|
||||
import os
|
||||
import psycopg2
|
||||
import time
|
||||
|
||||
_VERSION = "v1"
|
||||
|
||||
|
||||
def run(event):
|
||||
n = int(event.get("rows", 5))
|
||||
prefix = event.get("prefix", "stress")
|
||||
|
||||
conn = psycopg2.connect(
|
||||
host=os.environ["PGHOST"],
|
||||
port=int(os.environ.get("PGPORT", "5432")),
|
||||
dbname=os.environ["PGDATABASE"],
|
||||
user=os.environ["PGUSER"],
|
||||
password=os.environ["PGPASSWORD"],
|
||||
sslmode=os.environ.get("PGSSLMODE", "require"),
|
||||
)
|
||||
inserted = []
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
for i in range(n):
|
||||
title = f"{prefix}-{int(time.time()*1000)}-{i}"
|
||||
cur.execute(
|
||||
"INSERT INTO terraform_demo_table (title) VALUES (%s) RETURNING id",
|
||||
(title,),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
inserted.append({"id": row[0], "title": title})
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
return {"version": _VERSION, "inserted": inserted, "count": len(inserted)}
|
||||
@@ -194,3 +194,184 @@ output "table_writer_url" {
|
||||
value = sless_trigger.postgres_table_writer_http.url
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# STRESS-ТЕСТЫ: 8 функций для проверки устойчивости платформы.
|
||||
# Python: slow, divzero, bigloop, writer
|
||||
# Go: fast, nil-panic
|
||||
# NodeJS: async-parallel, badenv
|
||||
# =============================================================================
|
||||
|
||||
# --- [1] Python: долгая (sleep N сек) ---
|
||||
resource "sless_function" "stress_slow" {
|
||||
name = "stress-slow"
|
||||
runtime = "python3.11"
|
||||
entrypoint = "stress_slow.run"
|
||||
memory_mb = 128
|
||||
timeout_sec = 30
|
||||
|
||||
env_vars = {}
|
||||
|
||||
source_dir = "${path.module}/code/stress-slow"
|
||||
depends_on = [sless_job.postgres_table_init_job]
|
||||
}
|
||||
resource "sless_trigger" "stress_slow_http" {
|
||||
name = "stress-slow-http"
|
||||
type = "http"
|
||||
function = sless_function.stress_slow.name
|
||||
enabled = true
|
||||
}
|
||||
|
||||
# --- [2] Python: деление на ноль ---
|
||||
resource "sless_function" "stress_divzero" {
|
||||
name = "stress-divzero"
|
||||
runtime = "python3.11"
|
||||
entrypoint = "stress_divzero.run"
|
||||
memory_mb = 128
|
||||
timeout_sec = 10
|
||||
|
||||
env_vars = {}
|
||||
|
||||
source_dir = "${path.module}/code/stress-divzero"
|
||||
depends_on = [sless_job.postgres_table_init_job]
|
||||
}
|
||||
resource "sless_trigger" "stress_divzero_http" {
|
||||
name = "stress-divzero-http"
|
||||
type = "http"
|
||||
function = sless_function.stress_divzero.name
|
||||
enabled = true
|
||||
}
|
||||
|
||||
# --- [3] Python: CPU bigloop ---
|
||||
resource "sless_function" "stress_bigloop" {
|
||||
name = "stress-bigloop"
|
||||
runtime = "python3.11"
|
||||
entrypoint = "stress_bigloop.run"
|
||||
memory_mb = 256
|
||||
timeout_sec = 30
|
||||
|
||||
env_vars = {}
|
||||
|
||||
source_dir = "${path.module}/code/stress-bigloop"
|
||||
depends_on = [sless_job.postgres_table_init_job]
|
||||
}
|
||||
resource "sless_trigger" "stress_bigloop_http" {
|
||||
name = "stress-bigloop-http"
|
||||
type = "http"
|
||||
function = sless_function.stress_bigloop.name
|
||||
enabled = true
|
||||
}
|
||||
|
||||
# --- [4] Python: массовая запись в PG ---
|
||||
resource "sless_function" "stress_writer" {
|
||||
name = "stress-writer"
|
||||
runtime = "python3.11"
|
||||
entrypoint = "stress_writer.run"
|
||||
memory_mb = 128
|
||||
timeout_sec = 30
|
||||
|
||||
env_vars = {
|
||||
PGHOST = local.pg_host
|
||||
PGPORT = "5432"
|
||||
PGDATABASE = local.pg_database
|
||||
PGUSER = local.pg_username
|
||||
PGPASSWORD = local.pg_password
|
||||
PGSSLMODE = "require"
|
||||
}
|
||||
|
||||
source_dir = "${path.module}/code/stress-writer"
|
||||
depends_on = [sless_job.postgres_table_init_job]
|
||||
}
|
||||
resource "sless_trigger" "stress_writer_http" {
|
||||
name = "stress-writer-http"
|
||||
type = "http"
|
||||
function = sless_function.stress_writer.name
|
||||
enabled = true
|
||||
}
|
||||
|
||||
# --- [5] Go: быстрая математика (факториал + Фибоначчи) ---
|
||||
resource "sless_function" "stress_go_fast" {
|
||||
name = "stress-go-fast"
|
||||
runtime = "go1.23"
|
||||
entrypoint = "handler.Handle"
|
||||
memory_mb = 64
|
||||
timeout_sec = 10
|
||||
|
||||
env_vars = {}
|
||||
|
||||
source_dir = "${path.module}/code/stress-go-fast"
|
||||
depends_on = [sless_job.postgres_table_init_job]
|
||||
}
|
||||
resource "sless_trigger" "stress_go_fast_http" {
|
||||
name = "stress-go-fast-http"
|
||||
type = "http"
|
||||
function = sless_function.stress_go_fast.name
|
||||
enabled = true
|
||||
}
|
||||
|
||||
# --- [6] Go: nil pointer panic ---
|
||||
resource "sless_function" "stress_go_nil" {
|
||||
name = "stress-go-nil"
|
||||
runtime = "go1.23"
|
||||
entrypoint = "handler.Handle"
|
||||
memory_mb = 64
|
||||
timeout_sec = 10
|
||||
|
||||
env_vars = {}
|
||||
|
||||
source_dir = "${path.module}/code/stress-go-nil"
|
||||
depends_on = [sless_job.postgres_table_init_job]
|
||||
}
|
||||
resource "sless_trigger" "stress_go_nil_http" {
|
||||
name = "stress-go-nil-http"
|
||||
type = "http"
|
||||
function = sless_function.stress_go_nil.name
|
||||
enabled = true
|
||||
}
|
||||
|
||||
# --- [7] NodeJS: 3 параллельных запроса к PG через Promise.all ---
|
||||
resource "sless_function" "stress_js_async" {
|
||||
name = "stress-js-async"
|
||||
runtime = "nodejs20"
|
||||
entrypoint = "stress_js_async.run"
|
||||
memory_mb = 128
|
||||
timeout_sec = 15
|
||||
|
||||
env_vars = {
|
||||
PGHOST = local.pg_host
|
||||
PGPORT = "5432"
|
||||
PGDATABASE = local.pg_database
|
||||
PGUSER = local.pg_username
|
||||
PGPASSWORD = local.pg_password
|
||||
PGSSLMODE = "require"
|
||||
}
|
||||
|
||||
source_dir = "${path.module}/code/stress-js-async"
|
||||
depends_on = [sless_job.postgres_table_init_job]
|
||||
}
|
||||
resource "sless_trigger" "stress_js_async_http" {
|
||||
name = "stress-js-async-http"
|
||||
type = "http"
|
||||
function = sless_function.stress_js_async.name
|
||||
enabled = true
|
||||
}
|
||||
|
||||
# --- [8] NodeJS: TypeError на несуществующей env-переменной ---
|
||||
resource "sless_function" "stress_js_badenv" {
|
||||
name = "stress-js-badenv"
|
||||
runtime = "nodejs20"
|
||||
entrypoint = "stress_js_badenv.run"
|
||||
memory_mb = 128
|
||||
timeout_sec = 10
|
||||
|
||||
env_vars = {}
|
||||
|
||||
source_dir = "${path.module}/code/stress-js-badenv"
|
||||
depends_on = [sless_job.postgres_table_init_job]
|
||||
}
|
||||
resource "sless_trigger" "stress_js_badenv_http" {
|
||||
name = "stress-js-badenv-http"
|
||||
type = "http"
|
||||
function = sless_function.stress_js_badenv.name
|
||||
enabled = true
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
# 2026-03-19 — stress test script: параллельный запуск всех 8 стресс-функций
|
||||
BASE="https://sless.kube5s.ru/fn/sless-ffd1f598c169b0ae"
|
||||
|
||||
echo "=== РАУНД 1: первый холодный запуск ==="
|
||||
curl -s -m 35 "$BASE/stress-slow" -d '{"sleep":3}' -H "Content-Type:application/json" > /tmp/r_slow.json &
|
||||
curl -s -m 10 "$BASE/stress-divzero" > /tmp/r_divzero.json &
|
||||
curl -s -m 40 "$BASE/stress-bigloop" -d '{"n":1000000}' -H "Content-Type:application/json"> /tmp/r_bigloop.json &
|
||||
curl -s -m 35 "$BASE/stress-writer" -d '{"rows":3,"prefix":"batch1"}' -H "Content-Type:application/json" > /tmp/r_writer.json &
|
||||
curl -s -m 15 "$BASE/stress-go-fast" -d '{"n":15}' -H "Content-Type:application/json" > /tmp/r_go_fast.json &
|
||||
curl -s -m 10 "$BASE/stress-go-nil" > /tmp/r_go_nil.json &
|
||||
curl -s -m 20 "$BASE/stress-js-async" > /tmp/r_js_async.json &
|
||||
curl -s -m 10 "$BASE/stress-js-badenv" > /tmp/r_js_badenv.json &
|
||||
wait
|
||||
|
||||
echo "[slow]: $(cat /tmp/r_slow.json)"
|
||||
echo "[divzero]: $(cat /tmp/r_divzero.json)"
|
||||
echo "[bigloop]: $(cat /tmp/r_bigloop.json)"
|
||||
echo "[writer]: $(cat /tmp/r_writer.json)"
|
||||
echo "[go-fast]: $(cat /tmp/r_go_fast.json)"
|
||||
echo "[go-nil]: $(cat /tmp/r_go_nil.json)"
|
||||
echo "[js-async]: $(cat /tmp/r_js_async.json)"
|
||||
echo "[js-badenv]:$(cat /tmp/r_js_badenv.json)"
|
||||
|
||||
echo ""
|
||||
echo "=== РАУНД 2: повторный (горячий кэш) ==="
|
||||
curl -s -m 15 "$BASE/stress-bigloop" -d '{"n":2000000}' -H "Content-Type:application/json" > /tmp/r2_bigloop.json &
|
||||
curl -s -m 10 "$BASE/stress-go-fast" -d '{"n":20}' -H "Content-Type:application/json" > /tmp/r2_go_fast.json &
|
||||
curl -s -m 20 "$BASE/stress-js-async" > /tmp/r2_async.json &
|
||||
curl -s -m 35 "$BASE/stress-writer" -d '{"rows":10,"prefix":"batch2"}' -H "Content-Type:application/json" > /tmp/r2_writer.json &
|
||||
wait
|
||||
echo "[bigloop-2M]: $(cat /tmp/r2_bigloop.json)"
|
||||
echo "[go-fast-20]: $(cat /tmp/r2_go_fast.json)"
|
||||
echo "[js-async-2]: $(cat /tmp/r2_async.json)"
|
||||
echo "[writer-10]: $(cat /tmp/r2_writer.json)"
|
||||
|
||||
echo ""
|
||||
echo "=== РАУНД 3: crash функции с неверными параметрами ==="
|
||||
curl -s -m 10 "$BASE/stress-divzero" -d '{"n":100,"d":0}' -H "Content-Type:application/json" > /tmp/r3_dz.json &
|
||||
curl -s -m 10 "$BASE/stress-go-nil" -d '{"crash":true}' -H "Content-Type:application/json" > /tmp/r3_nil.json &
|
||||
curl -s -m 10 "$BASE/stress-js-badenv" -d '{"crash":true}' -H "Content-Type:application/json" > /tmp/r3_bad.json &
|
||||
# divzero с нормальным делителем — должен вернуть результат
|
||||
curl -s -m 10 "$BASE/stress-divzero" -d '{"n":42,"d":7}' -H "Content-Type:application/json" > /tmp/r3_ok.json &
|
||||
# go-nil без краша — должен вернуть ok
|
||||
curl -s -m 10 "$BASE/stress-go-nil" -d '{"crash":false}' -H "Content-Type:application/json" > /tmp/r3_nil_ok.json &
|
||||
wait
|
||||
echo "[divzero crash]: $(cat /tmp/r3_dz.json)"
|
||||
echo "[go-nil crash]: $(cat /tmp/r3_nil.json)"
|
||||
echo "[js-badenv crash]: $(cat /tmp/r3_bad.json)"
|
||||
echo "[divzero ok 42/7]: $(cat /tmp/r3_ok.json)"
|
||||
echo "[go-nil ok]: $(cat /tmp/r3_nil_ok.json)"
|
||||
|
||||
echo ""
|
||||
echo "=== ИТОГ: количество строк в таблице ==="
|
||||
curl -s -m 15 "$BASE/pg-table-reader"
|
||||
echo ""
|
||||
echo "=== DONE ==="
|
||||
Reference in New Issue
Block a user