1#include "catch.hpp"
2#include "duckdb/common/file_system.hpp"
3#include "duckdb.hpp"
4#include "duckdb/main/appender.hpp"
5#include "test_helpers.hpp"
6
7#include <signal.h>
8#include <sys/mman.h>
9#include <unistd.h>
10
11using namespace duckdb;
12using namespace std;
13
14TEST_CASE("Test transactional integrity when facing process aborts", "[persistence][.]") {
15 FileSystem fs;
16
17 // shared memory to keep track of insertions
18 size_t *count = (size_t *)mmap(NULL, sizeof(size_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
19
20 string db_folder_parent = TestCreatePath("llstorage");
21 if (fs.DirectoryExists(db_folder_parent)) {
22 fs.RemoveDirectory(db_folder_parent);
23 }
24 fs.CreateDirectory(db_folder_parent);
25
26 string db_folder = fs.JoinPath(db_folder_parent, "dbfolder");
27 {
28 DuckDB db(db_folder);
29 Connection con(db);
30 con.Query("CREATE TABLE a (i INTEGER)");
31 }
32
33 // fork away a child to be mercilessy shot in a bit
34 pid_t pid = fork();
35
36 if (pid == 0) { // child process
37 DuckDB db(db_folder);
38 Connection con(db);
39 while (true) {
40 con.Query("INSERT INTO a VALUES(42)");
41 (*count)++;
42 }
43 } else if (pid > 0) { // parent process
44 // wait until child has inserted at least 1000 rows
45 while (*count < 1000) {
46 usleep(100);
47 }
48 if (kill(pid, SIGKILL) != 0) {
49 FAIL();
50 }
51 unique_ptr<DuckDB> db;
52 // it may take some time for the OS to reclaim the lock
53 // loop and wait until the database is successfully started again
54 for (size_t i = 0; i < 1000; i++) {
55 usleep(10000);
56 try {
57 db = make_unique<DuckDB>(db_folder);
58 } catch (...) {
59 }
60 if (db) {
61 break;
62 }
63 }
64 Connection con(*db);
65 auto res = con.Query("SELECT COUNT(*) FROM a");
66 // there may be an off-by-one if we kill exactly between query and count increment
67 REQUIRE(abs((int64_t)(res->GetValue(0, 0).GetValue<int64_t>() - *count)) < 2);
68 } else {
69 FAIL();
70 }
71}
72