Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
async_stress_test.cpp File Reference
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include <vector>
#include <thread>
#include <future>
#include <atomic>
#include <chrono>
#include "database/backends/sqlite_backend.h"
#include "database/core/database_backend.h"
#include "database/query_builder.h"
Include dependency graph for async_stress_test.cpp:

Go to the source code of this file.

Classes

class  AsyncStressTest
 Test fixture for async operation stress tests. More...
 

Functions

 TEST_F (AsyncStressTest, HighConcurrencyInserts)
 Tests database behavior under high concurrent insert load.
 
 TEST_F (AsyncStressTest, MixedReadWriteWorkload)
 Tests database under mixed read/write workload.
 
 TEST_F (AsyncStressTest, ConcurrentQueryBuilderUsage)
 Tests that query builders work correctly under concurrent use.
 
 TEST_F (AsyncStressTest, RapidQueryExecution)
 Tests rapid sequential query execution.
 
 TEST_F (AsyncStressTest, SystemRemainResponsiveAfterLoad)
 Tests that database remains responsive after heavy load.
 
 TEST_F (AsyncStressTest, NoDataCorruptionUnderConcurrency)
 Verifies data integrity after concurrent operations.
 
 TEST_F (AsyncStressTest, GracefulHandlingOfErrors)
 Tests that errors are handled gracefully under concurrent load.
 

Function Documentation

◆ TEST_F() [1/7]

TEST_F ( AsyncStressTest ,
ConcurrentQueryBuilderUsage  )

Tests that query builders work correctly under concurrent use.

Test
ConcurrentQueryBuilderUsage

Definition at line 223 of file async_stress_test.cpp.

223 {
224#ifdef USE_SQLITE
225 constexpr int NUM_THREADS = 8;
226 constexpr int QUERIES_PER_THREAD = 100;
227
228 std::atomic<int> build_success{0};
229 std::atomic<int> build_failure{0};
230 std::vector<std::thread> threads;
231
232 for (int t = 0; t < NUM_THREADS; ++t) {
233 threads.emplace_back([&, t]() {
234 for (int i = 0; i < QUERIES_PER_THREAD; ++i) {
235 try {
236 // Each thread uses its own builder (not shared)
237 query_builder builder(database_types::sqlite);
238 auto query = builder
239 .select({"*"})
240 .from("stress_test")
241 .where("thread_id", "=", static_cast<int64_t>(t))
242 .limit(10)
243 .build();
244
245 if (!query.empty()) {
246 build_success++;
247 } else {
248 build_failure++;
249 }
250 } catch (...) {
251 build_failure++;
252 }
253 }
254 });
255 }
256
257 for (auto& t : threads) {
258 t.join();
259 }
260
261 int total = build_success + build_failure;
262 EXPECT_EQ(total, NUM_THREADS * QUERIES_PER_THREAD);
263 EXPECT_EQ(build_failure.load(), 0)
264 << "Query builder failures under concurrent use";
265#else
266 GTEST_SKIP() << "SQLite not available";
267#endif
268}
Universal query builder that adapts to different database types.

References database::query_builder::build(), database::query_builder::limit(), database::query_builder::select(), and database::query_builder::where().

Here is the call graph for this function:

◆ TEST_F() [2/7]

TEST_F ( AsyncStressTest ,
GracefulHandlingOfErrors  )

Tests that errors are handled gracefully under concurrent load.

Test
GracefulHandlingOfErrors

Definition at line 409 of file async_stress_test.cpp.

409 {
410#ifdef USE_SQLITE
411 std::atomic<bool> running{true};
412 std::atomic<int> errors_caught{0};
413 std::vector<std::thread> threads;
414
415 // Spawn threads that will generate some errors
416 for (int t = 0; t < 4; ++t) {
417 threads.emplace_back([&]() {
418 while (running) {
419 try {
420 // Some valid queries
421 db_->select_query("SELECT * FROM stress_test LIMIT 1");
422
423 // Some invalid queries (should return error)
424 auto result = db_->select_query("SELECT * FROM nonexistent_table");
425 if (!result.is_ok()) {
426 errors_caught++;
427 }
428 } catch (...) {
429 errors_caught++;
430 }
431 }
432 });
433 }
434
435 std::this_thread::sleep_for(std::chrono::milliseconds(500));
436 running = false;
437
438 for (auto& t : threads) {
439 t.join();
440 }
441
442 // System should still be functional
443 auto result = db_->select_query("SELECT 1 as test");
444 EXPECT_TRUE(result.is_ok() && !result.value().empty()) << "System non-functional after error handling test";
445#else
446 GTEST_SKIP() << "SQLite not available";
447#endif
448}

◆ TEST_F() [3/7]

TEST_F ( AsyncStressTest ,
HighConcurrencyInserts  )

Tests database behavior under high concurrent insert load.

Test
HighConcurrencyInserts

Spawns multiple threads performing concurrent inserts. Measures success rate and performance.

Definition at line 75 of file async_stress_test.cpp.

75 {
76#ifdef USE_SQLITE
77 constexpr int NUM_THREADS = 10;
78 constexpr int OPS_PER_THREAD = 50;
79
80 std::atomic<int> success_count{0};
81 std::atomic<int> failure_count{0};
82 std::vector<std::future<void>> futures;
83
84 auto start = std::chrono::high_resolution_clock::now();
85
86 for (int t = 0; t < NUM_THREADS; ++t) {
87 futures.push_back(std::async(std::launch::async, [&, t]() {
88 for (int i = 0; i < OPS_PER_THREAD; ++i) {
89 std::string query = "INSERT INTO stress_test (thread_id, value) VALUES (" +
90 std::to_string(t) + ", 'value_" +
91 std::to_string(t * OPS_PER_THREAD + i) + "')";
92 try {
93 auto result = db_->execute_query(query);
94 if (result.is_ok()) {
95 success_count++;
96 } else {
97 failure_count++;
98 }
99 } catch (...) {
100 failure_count++;
101 }
102 }
103 }));
104 }
105
106 for (auto& f : futures) {
107 f.wait();
108 }
109
110 auto duration = std::chrono::high_resolution_clock::now() - start;
111 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
112
113 // Report results
114 int total = success_count + failure_count;
115 double success_rate = (total > 0) ? (100.0 * success_count / total) : 0;
116
117 std::cout << "High Concurrency Insert Test:\n"
118 << " Threads: " << NUM_THREADS << "\n"
119 << " Ops per thread: " << OPS_PER_THREAD << "\n"
120 << " Successful: " << success_count << "\n"
121 << " Failed: " << failure_count << "\n"
122 << " Success rate: " << success_rate << "%\n"
123 << " Duration: " << ms.count() << "ms\n";
124
125 // At least some operations should succeed
126 EXPECT_GT(success_count.load(), 0)
127 << "No successful operations under concurrent load";
128#else
129 GTEST_SKIP() << "SQLite not available";
130#endif
131}

◆ TEST_F() [4/7]

TEST_F ( AsyncStressTest ,
MixedReadWriteWorkload  )

Tests database under mixed read/write workload.

Test
MixedReadWriteWorkload

Multiple reader and writer threads operating simultaneously.

Definition at line 139 of file async_stress_test.cpp.

139 {
140#ifdef USE_SQLITE
141 constexpr int NUM_WRITERS = 3;
142 constexpr int NUM_READERS = 5;
143 constexpr int DURATION_MS = 2000;
144
145 std::atomic<bool> running{true};
146 std::atomic<int> write_ops{0};
147 std::atomic<int> read_ops{0};
148 std::atomic<int> write_errors{0};
149 std::atomic<int> read_errors{0};
150
151 std::vector<std::thread> threads;
152
153 // Writers
154 for (int i = 0; i < NUM_WRITERS; ++i) {
155 threads.emplace_back([&, i]() {
156 int counter = 0;
157 while (running) {
158 try {
159 std::string query = "INSERT INTO stress_test (thread_id, value) VALUES (" +
160 std::to_string(i) + ", 'write_" +
161 std::to_string(counter++) + "')";
162 auto result = db_->execute_query(query);
163 if (result.is_ok()) {
164 write_ops++;
165 } else {
166 write_errors++;
167 }
168 } catch (...) {
169 write_errors++;
170 }
171 }
172 });
173 }
174
175 // Readers
176 for (int i = 0; i < NUM_READERS; ++i) {
177 threads.emplace_back([&]() {
178 while (running) {
179 try {
180 auto result = db_->select_query("SELECT COUNT(*) FROM stress_test");
181 if (result.is_ok() && !result.value().empty()) {
182 read_ops++;
183 } else {
184 read_errors++;
185 }
186 } catch (...) {
187 read_errors++;
188 }
189 }
190 });
191 }
192
193 // Run for specified duration
194 std::this_thread::sleep_for(std::chrono::milliseconds(DURATION_MS));
195 running = false;
196
197 for (auto& t : threads) {
198 t.join();
199 }
200
201 std::cout << "Mixed Read/Write Workload:\n"
202 << " Writers: " << NUM_WRITERS << ", Readers: " << NUM_READERS << "\n"
203 << " Duration: " << DURATION_MS << "ms\n"
204 << " Write ops: " << write_ops << " (errors: " << write_errors << ")\n"
205 << " Read ops: " << read_ops << " (errors: " << read_errors << ")\n";
206
207 // Both reads and writes should have occurred
208 EXPECT_GT(write_ops.load(), 0) << "No write operations completed";
209 EXPECT_GT(read_ops.load(), 0) << "No read operations completed";
210#else
211 GTEST_SKIP() << "SQLite not available";
212#endif
213}

◆ TEST_F() [5/7]

TEST_F ( AsyncStressTest ,
NoDataCorruptionUnderConcurrency  )

Verifies data integrity after concurrent operations.

Test
NoDataCorruptionUnderConcurrency

Definition at line 351 of file async_stress_test.cpp.

351 {
352#ifdef USE_SQLITE
353 constexpr int NUM_THREADS = 4;
354 constexpr int OPS_PER_THREAD = 25;
355 const int EXPECTED_ROWS = NUM_THREADS * OPS_PER_THREAD;
356
357 std::vector<std::thread> threads;
358 std::atomic<int> actual_inserts{0};
359
360 for (int t = 0; t < NUM_THREADS; ++t) {
361 threads.emplace_back([&, t]() {
362 for (int i = 0; i < OPS_PER_THREAD; ++i) {
363 std::string unique_value = "thread_" + std::to_string(t) +
364 "_op_" + std::to_string(i);
365 std::string query = "INSERT INTO stress_test (thread_id, value) VALUES (" +
366 std::to_string(t) + ", '" + unique_value + "')";
367 auto insert_result = db_->execute_query(query);
368 if (insert_result.is_ok()) {
369 actual_inserts++;
370 }
371 }
372 });
373 }
374
375 for (auto& t : threads) {
376 t.join();
377 }
378
379 // Verify row count
380 auto result = db_->select_query("SELECT COUNT(*) as cnt FROM stress_test");
381 ASSERT_TRUE(result.is_ok() && !result.value().empty());
382
383 int row_count = 0;
384 if (!result.value().empty() && result.value()[0].count("cnt") > 0) {
385 auto& cnt_value = result.value()[0].at("cnt");
386 if (std::holds_alternative<int64_t>(cnt_value)) {
387 row_count = static_cast<int>(std::get<int64_t>(cnt_value));
388 } else if (std::holds_alternative<std::string>(cnt_value)) {
389 row_count = std::stoi(std::get<std::string>(cnt_value));
390 }
391 }
392
393 // All successful inserts should be present
394 EXPECT_EQ(row_count, actual_inserts.load())
395 << "Data corruption: row count mismatch";
396#else
397 GTEST_SKIP() << "SQLite not available";
398#endif
399}
#define ASSERT_TRUE(condition, message)

References ASSERT_TRUE.

◆ TEST_F() [6/7]

TEST_F ( AsyncStressTest ,
RapidQueryExecution  )

Tests rapid sequential query execution.

Test
RapidQueryExecution

Definition at line 274 of file async_stress_test.cpp.

274 {
275#ifdef USE_SQLITE
276 constexpr int NUM_QUERIES = 1000;
277
278 auto start = std::chrono::high_resolution_clock::now();
279
280 int success = 0;
281 int failure = 0;
282
283 for (int i = 0; i < NUM_QUERIES; ++i) {
284 try {
285 std::string query = "INSERT INTO stress_test (thread_id, value) VALUES (0, 'rapid_" +
286 std::to_string(i) + "')";
287 auto result = db_->execute_query(query);
288 if (result.is_ok()) {
289 success++;
290 } else {
291 failure++;
292 }
293 } catch (...) {
294 failure++;
295 }
296 }
297
298 auto duration = std::chrono::high_resolution_clock::now() - start;
299 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
300
301 double qps = (ms.count() > 0) ? (1000.0 * success / ms.count()) : 0;
302
303 std::cout << "Rapid Query Execution:\n"
304 << " Queries: " << NUM_QUERIES << "\n"
305 << " Success: " << success << ", Failure: " << failure << "\n"
306 << " Duration: " << ms.count() << "ms\n"
307 << " Throughput: " << qps << " queries/sec\n";
308
309 EXPECT_GT(success, 0);
310 EXPECT_EQ(failure, 0) << "Failures during rapid sequential execution";
311#else
312 GTEST_SKIP() << "SQLite not available";
313#endif
314}

References database::success.

◆ TEST_F() [7/7]

TEST_F ( AsyncStressTest ,
SystemRemainResponsiveAfterLoad  )

Tests that database remains responsive after heavy load.

Test
SystemRemainResponsiveAfterLoad

Definition at line 324 of file async_stress_test.cpp.

324 {
325#ifdef USE_SQLITE
326 // Generate load
327 for (int i = 0; i < 100; ++i) {
328 db_->execute_query("INSERT INTO stress_test (thread_id, value) VALUES (" +
329 std::to_string(i % 10) + ", 'load_test')");
330 }
331
332 // Now test responsiveness
333 auto start = std::chrono::high_resolution_clock::now();
334 auto result = db_->select_query("SELECT COUNT(*) FROM stress_test");
335 auto duration = std::chrono::high_resolution_clock::now() - start;
336 auto us = std::chrono::duration_cast<std::chrono::microseconds>(duration);
337
338 EXPECT_TRUE(result.is_ok() && !result.value().empty()) << "System unresponsive after load";
339 EXPECT_LT(us.count(), 100000) << "Query took too long after load: " << us.count() << "us";
340
341 std::cout << "Post-load query response time: " << us.count() << "us\n";
342#else
343 GTEST_SKIP() << "SQLite not available";
344#endif
345}