1#include "catch.hpp"
2#include "duckdb/common/file_system.hpp"
3#include "test_helpers.hpp"
4
5using namespace duckdb;
6using namespace std;
7
8TEST_CASE("Test that database size does not grow after many checkpoints", "[storage][.]") {
9 constexpr idx_t VALUE_COUNT = 10000;
10 idx_t expected_sum = 0;
11
12 FileSystem fs;
13 auto config = GetTestConfig();
14 unique_ptr<DuckDB> database;
15 unique_ptr<QueryResult> result;
16 auto storage_database = TestCreatePath("dbsize_test");
17
18 // make sure the database does not exist
19 DeleteDatabase(storage_database);
20 {
21 // create a database and insert values
22 DuckDB db(storage_database);
23 Connection con(db);
24 REQUIRE_NO_FAIL(con.Query("BEGIN TRANSACTION;"));
25 REQUIRE_NO_FAIL(con.Query("CREATE TABLE test(a INTEGER);"));
26 for (idx_t i = 0; i < VALUE_COUNT; i++) {
27 REQUIRE_NO_FAIL(con.Query("INSERT INTO test VALUES (" + to_string(i) + ");"));
28 expected_sum += i;
29 }
30 REQUIRE_NO_FAIL(con.Query("COMMIT;"));
31 result = con.Query("SELECT SUM(a) FROM test");
32 REQUIRE(CHECK_COLUMN(result, 0, {Value::BIGINT(expected_sum)}));
33 }
34 // force a checkpoint by reloading
35 {
36 DuckDB db(storage_database, config.get());
37 Connection con(db);
38 }
39
40 // get the size of the database
41 int64_t size;
42 {
43 auto handle = fs.OpenFile(storage_database, FileFlags::READ);
44 size = fs.GetFileSize(*handle);
45 REQUIRE(size >= 0);
46 }
47 // now reload the database a bunch of times, and everytime we reload update all the values
48 for (idx_t i = 0; i < 20; i++) {
49 DuckDB db(storage_database, config.get());
50 Connection con(db);
51 // verify the current count
52 result = con.Query("SELECT SUM(a) FROM test");
53 REQUIRE(CHECK_COLUMN(result, 0, {Value::BIGINT(expected_sum)}));
54 // update the table
55 REQUIRE_NO_FAIL(con.Query("UPDATE test SET a=a+1;"));
56 expected_sum += VALUE_COUNT;
57 // verify the current count again
58 result = con.Query("SELECT SUM(a) FROM test");
59 REQUIRE(CHECK_COLUMN(result, 0, {Value::BIGINT(expected_sum)}));
60 }
61 // get the new file size
62 int64_t new_size;
63 {
64 auto handle = fs.OpenFile(storage_database, FileFlags::READ);
65 new_size = fs.GetFileSize(*handle);
66 REQUIRE(new_size >= 0);
67 }
68 // require that the size did not grow more than factor 3
69 // we allow the database file to grow somewhat because there will be empty blocks inside the file after many
70 // checkpoints however this should never be more than factor ~2.5 the original database size
71 REQUIRE(new_size <= size * 3);
72 DeleteDatabase(storage_database);
73}
74