Upload files to "tests"
This commit is contained in:
342
tests/backpressure_test.lua
Normal file
342
tests/backpressure_test.lua
Normal file
@@ -0,0 +1,342 @@
|
||||
-- tests/backpressure_test.lua
|
||||
-- Тестирование механизма backpressure при перегрузке
|
||||
|
||||
local function setup_backpressure_test()
|
||||
print("=== Setup Backpressure Test ===")
|
||||
|
||||
db.execute("CREATE DATABASE IF NOT EXISTS backpressure_test")
|
||||
db.execute("USE backpressure_test")
|
||||
|
||||
db.execute([[
|
||||
CREATE COLLECTION IF NOT EXISTS test_collection
|
||||
WITH SETTINGS {
|
||||
max_documents = 10000000
|
||||
}
|
||||
]])
|
||||
|
||||
-- Включаем backpressure с агрессивными настройками для теста
|
||||
db.execute([[
|
||||
SET BACKPRESSURE CONFIG {
|
||||
enabled = true,
|
||||
cpu_threshold = 0.50,
|
||||
memory_threshold = 0.60,
|
||||
queue_size_threshold = 100,
|
||||
connection_threshold = 50,
|
||||
low_delay_ms = 50,
|
||||
medium_reject_prob = 30,
|
||||
high_reject_prob = 70
|
||||
}
|
||||
]])
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_backpressure_level_none()
|
||||
print("\n=== Test 1: Backpressure Level NONE ===")
|
||||
|
||||
-- При нормальной нагрузке
|
||||
local start_time = os.clock()
|
||||
local success_count = 0
|
||||
local reject_count = 0
|
||||
|
||||
for i = 1, 100 do
|
||||
local success, err = pcall(function()
|
||||
return db.execute(string.format([[
|
||||
INSERT INTO test_collection VALUES {
|
||||
_id = "doc_%d",
|
||||
value = %d,
|
||||
timestamp = %d
|
||||
}
|
||||
]], i, i, os.time() * 1000))
|
||||
end)
|
||||
|
||||
if success then
|
||||
success_count = success_count + 1
|
||||
else
|
||||
reject_count = reject_count + 1
|
||||
if string.find(err, "backpressure") then
|
||||
print("Unexpected rejection: " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local duration = os.clock() - start_time
|
||||
|
||||
print(string.format("Success: %d, Rejected: %d, Duration: %.3fs",
|
||||
success_count, reject_count, duration))
|
||||
|
||||
if reject_count == 0 then
|
||||
print("✅ No rejections under normal load")
|
||||
return true
|
||||
else
|
||||
print("❌ Unexpected rejections: " .. reject_count)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function simulate_backpressure_condition()
|
||||
print("Simulating high load condition...")
|
||||
|
||||
-- Симулируем высокую нагрузку
|
||||
local threads = {}
|
||||
for i = 1, 100 do
|
||||
table.insert(threads, coroutine.create(function()
|
||||
for j = 1, 100 do
|
||||
local success = pcall(function()
|
||||
db.execute(string.format([[
|
||||
SELECT * FROM test_collection WHERE value > %d
|
||||
]], math.random(1, 50)))
|
||||
end)
|
||||
if not success then
|
||||
coroutine.yield(false)
|
||||
end
|
||||
db.sleep(0.001)
|
||||
end
|
||||
coroutine.yield(true)
|
||||
end))
|
||||
end
|
||||
|
||||
-- Запускаем все потоки
|
||||
for _, thread in ipairs(threads) do
|
||||
coroutine.resume(thread)
|
||||
end
|
||||
|
||||
-- Ждём, пока сработает backpressure
|
||||
db.sleep(5)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_backpressure_level_medium()
|
||||
print("\n=== Test 2: Backpressure Level MEDIUM ===")
|
||||
|
||||
simulate_backpressure_condition()
|
||||
|
||||
local status = db.execute("SHOW BACKPRESSURE STATUS")
|
||||
local bp_status = db.json_decode(status)
|
||||
|
||||
print(string.format("Current backpressure level: %s", bp_status.current_level))
|
||||
print(string.format("Write allowed: %s", tostring(bp_status.write_allowed)))
|
||||
print(string.format("Read allowed: %s", tostring(bp_status.read_allowed)))
|
||||
print(string.format("Reject probability: %d%%", bp_status.reject_probability or 0))
|
||||
|
||||
-- Тестируем процент отказов
|
||||
local total_requests = 500
|
||||
local rejected = 0
|
||||
|
||||
for i = 1, total_requests do
|
||||
local success, err = pcall(function()
|
||||
return db.execute("INSERT INTO test_collection VALUES {_id = 'test_" .. i .. "', value = " .. i .. "}")
|
||||
end)
|
||||
|
||||
if not success and string.find(err or "", "rejected") then
|
||||
rejected = rejected + 1
|
||||
end
|
||||
end
|
||||
|
||||
local reject_rate = (rejected / total_requests) * 100
|
||||
print(string.format("Actual reject rate: %.1f%% (expected: ~%d%%)",
|
||||
reject_rate, bp_status.reject_probability or 30))
|
||||
|
||||
-- Проверяем, что reject_rate в разумных пределах
|
||||
local expected_reject = bp_status.reject_probability or 30
|
||||
if math.abs(reject_rate - expected_reject) < 20 then
|
||||
print("✅ Rejection rate within acceptable range")
|
||||
return true
|
||||
else
|
||||
print("❌ Rejection rate outside expected range")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function test_backpressure_with_delay()
|
||||
print("\n=== Test 3: Backpressure with Delay ===")
|
||||
|
||||
-- Замеряем время выполнения при backpressure
|
||||
local start_time = os.clock()
|
||||
local delayed_requests = 0
|
||||
|
||||
for i = 1, 50 do
|
||||
local req_start = os.clock()
|
||||
db.execute("SELECT * FROM test_collection LIMIT 10")
|
||||
local req_end = os.clock()
|
||||
|
||||
if req_end - req_start > 0.05 then -- больше 50ms задержки
|
||||
delayed_requests = delayed_requests + 1
|
||||
end
|
||||
end
|
||||
|
||||
local total_duration = os.clock() - start_time
|
||||
local avg_delay = total_duration / 50 * 1000 -- в миллисекундах
|
||||
|
||||
print(string.format("Avg request time: %.2f ms", avg_delay))
|
||||
print(string.format("Requests with >50ms delay: %d/%d", delayed_requests, 50))
|
||||
|
||||
if avg_delay > 40 then
|
||||
print("✅ Backpressure delays detected as expected")
|
||||
return true
|
||||
else
|
||||
print("⚠️ Lower than expected delays")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function test_backpressure_recovery()
|
||||
print("\n=== Test 4: Backpressure Recovery ===")
|
||||
|
||||
-- Даём системе восстановиться
|
||||
print("Waiting for system recovery...")
|
||||
db.sleep(30)
|
||||
|
||||
local status = db.execute("SHOW BACKPRESSURE STATUS")
|
||||
local bp_status = db.json_decode(status)
|
||||
|
||||
print(string.format("Recovered backpressure level: %s", bp_status.current_level))
|
||||
|
||||
-- Проверяем, что запросы снова проходят
|
||||
local success_count = 0
|
||||
for i = 1, 100 do
|
||||
local success = pcall(function()
|
||||
return db.execute("INSERT INTO test_collection VALUES {_id = 'recovery_" .. i .. "', value = " .. i .. "}")
|
||||
end)
|
||||
if success then success_count = success_count + 1 end
|
||||
end
|
||||
|
||||
print(string.format("Successful writes after recovery: %d/100", success_count))
|
||||
|
||||
if success_count > 95 then
|
||||
print("✅ System recovered successfully")
|
||||
return true
|
||||
else
|
||||
print("❌ Recovery incomplete: " .. success_count .. " successes")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function test_backpressure_metrics()
|
||||
print("\n=== Test 5: Backpressure Metrics ===")
|
||||
|
||||
local metrics = db.execute("SHOW BACKPRESSURE METRICS")
|
||||
local metrics_data = db.json_decode(metrics)
|
||||
|
||||
print("Backpressure metrics:")
|
||||
for key, value in pairs(metrics_data) do
|
||||
print(string.format(" %s: %s", key, tostring(value)))
|
||||
end
|
||||
|
||||
local required_fields = {
|
||||
"current_level", "write_allowed", "read_allowed",
|
||||
"rejected_count", "delayed_count", "cpu_threshold"
|
||||
}
|
||||
|
||||
for _, field in ipairs(required_fields) do
|
||||
if metrics_data[field] == nil then
|
||||
print("❌ Missing metric: " .. field)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
print("✅ All required metrics present")
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_backpressure_burst_handling()
|
||||
print("\n=== Test 6: Burst Handling ===")
|
||||
|
||||
-- Генерируем burst запросов
|
||||
local burst_size = 1000
|
||||
local start_time = os.clock()
|
||||
local results = {}
|
||||
|
||||
-- Используем coroutines для параллельных запросов
|
||||
local handlers = {}
|
||||
for i = 1, burst_size do
|
||||
table.insert(handlers, coroutine.create(function()
|
||||
local success = pcall(function()
|
||||
return db.execute(string.format([[
|
||||
INSERT INTO test_collection VALUES {
|
||||
_id = "burst_%d_%d",
|
||||
value = %d,
|
||||
timestamp = %d
|
||||
}
|
||||
]], os.time(), i, i, os.time() * 1000)
|
||||
end)
|
||||
coroutine.yield(success)
|
||||
end))
|
||||
end
|
||||
|
||||
-- Запускаем все handlers
|
||||
for _, handler in ipairs(handlers) do
|
||||
coroutine.resume(handler)
|
||||
end
|
||||
|
||||
-- Собираем результаты
|
||||
local success_count = 0
|
||||
for _, handler in ipairs(handlers) do
|
||||
local success = coroutine.resume(handler)
|
||||
if success then
|
||||
local result = coroutine.status(handler) == "dead" and true or false
|
||||
if result then success_count = success_count + 1 end
|
||||
end
|
||||
end
|
||||
|
||||
local duration = os.clock() - start_time
|
||||
local qps = burst_size / duration
|
||||
|
||||
print(string.format("Burst: %d requests in %.2fs (%.0f QPS)",
|
||||
burst_size, duration, qps))
|
||||
print(string.format("Successful: %d, Failed: %d",
|
||||
success_count, burst_size - success_count))
|
||||
|
||||
if qps > 100 then
|
||||
print("✅ Backpressure handling burst correctly")
|
||||
return true
|
||||
else
|
||||
print("⚠️ Lower than expected QPS")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function cleanup_backpressure()
|
||||
print("\n=== Cleanup ===")
|
||||
db.execute("DROP DATABASE IF EXISTS backpressure_test")
|
||||
-- Сбрасываем backpressure настройки
|
||||
db.execute("SET BACKPRESSURE CONFIG DEFAULT")
|
||||
print("Cleanup completed")
|
||||
end
|
||||
|
||||
local function run_all_backpressure_tests()
|
||||
print("╔══════════════════════════════════════════════════════════════╗")
|
||||
print("║ BACKPRESSURE TESTS SUITE ║")
|
||||
print("╚══════════════════════════════════════════════════════════════╝")
|
||||
|
||||
setup_backpressure_test()
|
||||
|
||||
local results = {
|
||||
test_backpressure_level_none(),
|
||||
test_backpressure_level_medium(),
|
||||
test_backpressure_with_delay(),
|
||||
test_backpressure_recovery(),
|
||||
test_backpressure_metrics(),
|
||||
test_backpressure_burst_handling()
|
||||
}
|
||||
|
||||
print("\n╔══════════════════════════════════════════════════════════════╗")
|
||||
print("║ TEST RESULTS ║")
|
||||
print("╚══════════════════════════════════════════════════════════════╝")
|
||||
|
||||
local passed = 0
|
||||
for i, result in ipairs(results) do
|
||||
local status = result and "✅ PASSED" or "⚠️ SKIPPED/FAILED"
|
||||
print(string.format("Test %d: %s", i, status))
|
||||
if result then passed = passed + 1 end
|
||||
end
|
||||
|
||||
print(string.format("\nTotal: %d/%d tests passed", passed, #results))
|
||||
|
||||
cleanup_backpressure()
|
||||
|
||||
return passed == #results
|
||||
end
|
||||
|
||||
return run_all_backpressure_tests()
|
||||
Reference in New Issue
Block a user