297 lines
12 KiB
Lua
297 lines
12 KiB
Lua
-- tests/load_test.lua
|
||
-- Комплексное нагрузочное тестирование всех компонентов
|
||
|
||
local function run_load_test()
|
||
print("╔══════════════════════════════════════════════════════════════╗")
|
||
print("║ LOAD TEST SUITE ║")
|
||
print("╚══════════════════════════════════════════════════════════════╝")
|
||
|
||
local test_results = {}
|
||
|
||
-- Функция для замера производительности
|
||
local function measure_performance(name, fn, iterations)
|
||
print(string.format("\n📊 Measuring: %s (%d iterations)", name, iterations))
|
||
|
||
local times = {}
|
||
local errors = 0
|
||
|
||
for i = 1, iterations do
|
||
local start = os.clock()
|
||
local success, err = pcall(fn)
|
||
local duration = os.clock() - start
|
||
|
||
if success then
|
||
table.insert(times, duration)
|
||
else
|
||
errors = errors + 1
|
||
print(string.format(" Error #%d: %s", i, tostring(err)))
|
||
end
|
||
end
|
||
|
||
if #times == 0 then
|
||
print(string.format(" ❌ All %d iterations failed", iterations))
|
||
return nil
|
||
end
|
||
|
||
-- Вычисляем статистику
|
||
table.sort(times)
|
||
local min = times[1]
|
||
local max = times[#times]
|
||
local sum = 0
|
||
for _, t in ipairs(times) do sum = sum + t end
|
||
local avg = sum / #times
|
||
local p50 = times[math.floor(#times * 0.5)]
|
||
local p95 = times[math.floor(#times * 0.95)]
|
||
local p99 = times[math.floor(#times * 0.99)]
|
||
|
||
print(string.format([[
|
||
Results:
|
||
Success rate: %.1f%% (%d/%d)
|
||
Min: %.3f ms
|
||
Max: %.3f ms
|
||
Avg: %.3f ms
|
||
P50: %.3f ms
|
||
P95: %.3f ms
|
||
P99: %.3f ms
|
||
]], (iterations - errors) / iterations * 100, iterations - errors, iterations,
|
||
min * 1000, max * 1000, avg * 1000, p50 * 1000, p95 * 1000, p99 * 1000))
|
||
|
||
return {
|
||
success_rate = (iterations - errors) / iterations,
|
||
min_ms = min * 1000,
|
||
max_ms = max * 1000,
|
||
avg_ms = avg * 1000,
|
||
p50_ms = p50 * 1000,
|
||
p95_ms = p95 * 1000,
|
||
p99_ms = p99 * 1000
|
||
}
|
||
end
|
||
|
||
-- Подготовка
|
||
print("\n🔧 Preparing test environment...")
|
||
db.execute("CREATE DATABASE IF NOT EXISTS load_test")
|
||
db.execute("USE load_test")
|
||
|
||
db.execute([[
|
||
CREATE COLLECTION IF NOT EXISTS load_collection
|
||
WITH SETTINGS {
|
||
max_documents = 1000000,
|
||
auto_index_id = true
|
||
}
|
||
]])
|
||
|
||
-- Тест 1: Вставка документов
|
||
local insert_results = measure_performance("INSERT", function()
|
||
local doc = {
|
||
_id = string.format("doc_%d_%d", os.time(), math.random(1, 1000000)),
|
||
timestamp = os.time() * 1000,
|
||
value = math.random(1, 10000),
|
||
data = string.rep("x", 100)
|
||
}
|
||
db.execute(string.format(
|
||
"INSERT INTO load_collection VALUES (%s)",
|
||
db.json_encode(doc)
|
||
))
|
||
end, 1000)
|
||
|
||
-- Тест 2: Чтение по ID
|
||
local read_results = measure_performance("SELECT BY ID", function()
|
||
db.execute(string.format("SELECT * FROM load_collection WHERE _id = 'doc_%d'",
|
||
math.random(1, 1000)))
|
||
end, 1000)
|
||
|
||
-- Тест 3: Обновление
|
||
local update_results = measure_performance("UPDATE", function()
|
||
db.execute(string.format([[
|
||
UPDATE load_collection
|
||
SET value = %d, updated_at = %d
|
||
WHERE _id = 'doc_%d'
|
||
]], math.random(1, 10000), os.time() * 1000, math.random(1, 1000)))
|
||
end, 1000)
|
||
|
||
-- Тест 4: Удаление
|
||
local delete_results = measure_performance("DELETE", function()
|
||
db.execute(string.format("DELETE FROM load_collection WHERE _id = 'temp_%d'",
|
||
math.random(1, 100)))
|
||
end, 500)
|
||
|
||
-- Тест 5: Сложный запрос с фильтром
|
||
local query_results = measure_performance("COMPLEX QUERY", function()
|
||
db.execute([[
|
||
SELECT * FROM load_collection
|
||
WHERE value > 5000
|
||
AND value < 8000
|
||
LIMIT 50
|
||
]])
|
||
end, 500)
|
||
|
||
-- Тест 6: Агрегация
|
||
local agg_results = measure_performance("AGGREGATION", function()
|
||
db.execute([[
|
||
SELECT
|
||
COUNT(*) as total,
|
||
AVG(value) as avg_value,
|
||
MIN(value) as min_value,
|
||
MAX(value) as max_value
|
||
FROM load_collection
|
||
WHERE value > 1000
|
||
]])
|
||
end, 200)
|
||
|
||
-- Тест 7: Пакетная вставка
|
||
local batch_results = measure_performance("BATCH INSERT", function()
|
||
local docs = {}
|
||
for i = 1, 10 do
|
||
table.insert(docs, {
|
||
_id = string.format("batch_%d_%d", os.time(), i),
|
||
value = i
|
||
})
|
||
end
|
||
db.execute(string.format(
|
||
"INSERT INTO load_collection VALUES %s",
|
||
db.json_encode(docs)
|
||
))
|
||
end, 100)
|
||
|
||
-- Тест 8: Транзакция
|
||
local tx_results = measure_performance("TRANSACTION", function()
|
||
db.execute("BEGIN TRANSACTION")
|
||
db.execute("INSERT INTO load_collection VALUES {_id = 'tx1', value = 1}")
|
||
db.execute("INSERT INTO load_collection VALUES {_id = 'tx2', value = 2}")
|
||
db.execute("COMMIT")
|
||
end, 100)
|
||
|
||
-- Тест 9: Поиск по индексу
|
||
db.execute("CREATE INDEX idx_value ON load_collection(value)")
|
||
|
||
local index_results = measure_performance("INDEX SEARCH", function()
|
||
db.execute("SELECT * FROM load_collection WHERE value = 5000")
|
||
end, 500)
|
||
|
||
-- Тест 10: Сканирование с лимитом
|
||
local scan_results = measure_performance("SCAN WITH LIMIT", function()
|
||
db.execute("SELECT * FROM load_collection LIMIT 1000")
|
||
end, 100)
|
||
|
||
-- Сводка
|
||
print("\n╔══════════════════════════════════════════════════════════════╗")
|
||
print("║ PERFORMANCE SUMMARY ║")
|
||
print("╚══════════════════════════════════════════════════════════════╝")
|
||
|
||
local function print_summary(name, results)
|
||
if results then
|
||
local status = results.success_rate > 0.99 and "✅" or "⚠️"
|
||
print(string.format("%s %-15s: avg=%.2fms, p95=%.2fms, success=%.1f%%",
|
||
status, name, results.avg_ms, results.p95_ms, results.success_rate * 100))
|
||
else
|
||
print(string.format("❌ %-15s: FAILED", name))
|
||
end
|
||
end
|
||
|
||
print_summary("INSERT", insert_results)
|
||
print_summary("SELECT", read_results)
|
||
print_summary("UPDATE", update_results)
|
||
print_summary("DELETE", delete_results)
|
||
print_summary("QUERY", query_results)
|
||
print_summary("AGGREGATE", agg_results)
|
||
print_summary("BATCH", batch_results)
|
||
print_summary("TRANSACTION", tx_results)
|
||
print_summary("INDEX SEARCH", index_results)
|
||
print_summary("SCAN", scan_results)
|
||
|
||
-- Общий вердикт
|
||
local success_rate = (insert_results and insert_results.success_rate or 0) +
|
||
(read_results and read_results.success_rate or 0) +
|
||
(update_results and update_results.success_rate or 0) +
|
||
(delete_results and delete_results.success_rate or 0) +
|
||
(query_results and query_results.success_rate or 0) +
|
||
(agg_results and agg_results.success_rate or 0) +
|
||
(batch_results and batch_results.success_rate or 0) +
|
||
(tx_results and tx_results.success_rate or 0) +
|
||
(index_results and index_results.success_rate or 0) +
|
||
(scan_results and scan_results.success_rate or 0)
|
||
|
||
success_rate = success_rate / 10 * 100
|
||
|
||
print("\n╔══════════════════════════════════════════════════════════════╗")
|
||
print("║ FINAL VERDICT ║")
|
||
print("╚══════════════════════════════════════════════════════════════╝")
|
||
|
||
if success_rate > 99 then
|
||
print("✅ EXCELLENT: All operations performing optimally")
|
||
elseif success_rate > 95 then
|
||
print("✅ GOOD: Most operations performing well")
|
||
elseif success_rate > 90 then
|
||
print("⚠️ ACCEPTABLE: Some operations need optimization")
|
||
else
|
||
print("❌ POOR: Significant performance issues detected")
|
||
end
|
||
|
||
print(string.format("Overall success rate: %.1f%%", success_rate))
|
||
|
||
-- Очистка
|
||
print("\n🧹 Cleaning up...")
|
||
db.execute("DROP DATABASE IF EXISTS load_test")
|
||
|
||
return success_rate > 90
|
||
end
|
||
|
||
-- Запуск всех тестов
|
||
local function run_all_tests()
|
||
print("🚀 STARTING FULL TEST SUITE\n")
|
||
|
||
local test_modules = {
|
||
{name = "AUTOSCALING", path = "autoscaling_test.lua"},
|
||
{name = "BACKPRESSURE", path = "backpressure_test.lua"},
|
||
{name = "RUNTIME_LIMITS", path = "runtime_limits_test.lua"},
|
||
{name = "BACKUP", path = "backup_test.lua"},
|
||
{name = "SCHEMA_MIGRATION", path = "schema_migration_test.lua"},
|
||
{name = "LOAD_TEST", path = nil, fn = run_load_test}
|
||
}
|
||
|
||
local results = {}
|
||
|
||
for _, module in ipairs(test_modules) do
|
||
print("\n" .. string.rep("=", 70))
|
||
print(string.format("▶️ RUNNING %s TESTS", module.name))
|
||
print(string.rep("=", 70))
|
||
|
||
local result
|
||
if module.fn then
|
||
result = module.fn()
|
||
else
|
||
-- Загружаем и выполняем тестовый модуль
|
||
local chunk, err = loadfile(module.path)
|
||
if chunk then
|
||
local test_fn = chunk()
|
||
result = test_fn()
|
||
else
|
||
print("❌ Failed to load test module: " .. tostring(err))
|
||
result = false
|
||
end
|
||
end
|
||
|
||
table.insert(results, {name = module.name, passed = result})
|
||
end
|
||
|
||
-- Итоговый отчёт
|
||
print("\n" .. string.rep("=", 70))
|
||
print("🏁 FINAL TEST SUMMARY")
|
||
print(string.rep("=", 70))
|
||
|
||
local passed_count = 0
|
||
for _, result in ipairs(results) do
|
||
local status = result.passed and "✅ PASSED" or "❌ FAILED"
|
||
print(string.format(" %-20s: %s", result.name, status))
|
||
if result.passed then passed_count = passed_count + 1 end
|
||
end
|
||
|
||
print(string.rep("-", 70))
|
||
print(string.format(" TOTAL: %d/%d tests passed", passed_count, #results))
|
||
print(string.rep("=", 70))
|
||
|
||
return passed_count == #results
|
||
end
|
||
|
||
-- Запуск
|
||
return run_all_tests() |