1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/db/binlog/Binlog.h"
8 #include "td/db/binlog/ConcurrentBinlog.h"
9 #include "td/db/BinlogKeyValue.h"
10 #include "td/db/DbKey.h"
11 #include "td/db/SeqKeyValue.h"
12 #include "td/db/SqliteConnectionSafe.h"
13 #include "td/db/SqliteDb.h"
14 #include "td/db/SqliteKeyValueAsync.h"
15 #include "td/db/SqliteKeyValueSafe.h"
16
17 #include "td/actor/actor.h"
18 #include "td/actor/ConcurrentScheduler.h"
19
20 #include "td/utils/benchmark.h"
21 #include "td/utils/common.h"
22 #include "td/utils/format.h"
23 #include "td/utils/logging.h"
24 #include "td/utils/SliceBuilder.h"
25 #include "td/utils/Status.h"
26 #include "td/utils/StringBuilder.h"
27
28 #include <memory>
29
30 template <class KeyValueT>
31 class TdKvBench final : public td::Benchmark {
32 td::ConcurrentScheduler sched;
33 td::string name_;
34
35 public:
TdKvBench(td::string name)36 explicit TdKvBench(td::string name) {
37 name_ = std::move(name);
38 }
39
get_description() const40 td::string get_description() const final {
41 return name_;
42 }
43
44 class Main final : public td::Actor {
45 public:
Main(int n)46 explicit Main(int n) : n_(n) {
47 }
48
49 private:
loop()50 void loop() final {
51 KeyValueT::destroy("test_tddb").ignore();
52
53 class Worker final : public Actor {
54 public:
55 Worker(int n, td::string db_name) : n_(n) {
56 kv_.init(db_name).ensure();
57 }
58
59 private:
60 void loop() final {
61 for (int i = 0; i < n_; i++) {
62 kv_.set(td::to_string(i % 10), td::to_string(i));
63 }
64 td::Scheduler::instance()->finish();
65 }
66 int n_;
67 KeyValueT kv_;
68 };
69 td::create_actor_on_scheduler<Worker>("Worker", 0, n_, "test_tddb").release();
70 }
71 int n_;
72 };
73
start_up_n(int n)74 void start_up_n(int n) final {
75 sched.init(1);
76 sched.create_actor_unsafe<Main>(1, "Main", n).release();
77 }
78
run(int n)79 void run(int n) final {
80 sched.start();
81 while (sched.run_main(10)) {
82 // empty
83 }
84 sched.finish();
85 }
86
tear_down()87 void tear_down() final {
88 }
89 };
90
91 template <bool is_encrypted = false>
92 class SqliteKVBench final : public td::Benchmark {
93 td::SqliteDb db;
get_description() const94 td::string get_description() const final {
95 return PSTRING() << "SqliteKV " << td::tag("is_encrypted", is_encrypted);
96 }
start_up()97 void start_up() final {
98 td::string path = "testdb.sqlite";
99 td::SqliteDb::destroy(path).ignore();
100 if (is_encrypted) {
101 db = td::SqliteDb::change_key(path, true, td::DbKey::password("cucumber"), td::DbKey::empty()).move_as_ok();
102 } else {
103 db = td::SqliteDb::open_with_key(path, true, td::DbKey::empty()).move_as_ok();
104 }
105 db.exec("PRAGMA encoding=\"UTF-8\"").ensure();
106 db.exec("PRAGMA synchronous=NORMAL").ensure();
107 db.exec("PRAGMA journal_mode=WAL").ensure();
108 db.exec("PRAGMA temp_store=MEMORY").ensure();
109 db.exec("DROP TABLE IF EXISTS KV").ensure();
110 db.exec("CREATE TABLE IF NOT EXISTS KV (k BLOB PRIMARY KEY, v BLOB)").ensure();
111 }
run(int n)112 void run(int n) final {
113 auto stmt = db.get_statement("REPLACE INTO KV (k, v) VALUES(?1, ?2)").move_as_ok();
114 db.exec("BEGIN TRANSACTION").ensure();
115 for (int i = 0; i < n; i++) {
116 auto key = td::to_string(i % 10);
117 auto value = td::to_string(i);
118 stmt.bind_blob(1, key).ensure();
119 stmt.bind_blob(2, value).ensure();
120 stmt.step().ensure();
121 CHECK(!stmt.can_step());
122 stmt.reset();
123
124 if (i % 10 == 0) {
125 db.exec("COMMIT TRANSACTION").ensure();
126 db.exec("BEGIN TRANSACTION").ensure();
127 }
128 }
129 db.exec("COMMIT TRANSACTION").ensure();
130 }
131 };
132
init_db(td::SqliteDb & db)133 static td::Status init_db(td::SqliteDb &db) {
134 TRY_STATUS(db.exec("PRAGMA encoding=\"UTF-8\""));
135 TRY_STATUS(db.exec("PRAGMA journal_mode=WAL"));
136
137 TRY_STATUS(db.exec("PRAGMA synchronous=NORMAL"));
138 TRY_STATUS(db.exec("PRAGMA temp_store=MEMORY"));
139 // TRY_STATUS(db.exec("PRAGMA secure_delete=1"));
140
141 return td::Status::OK();
142 }
143
144 class SqliteKeyValueAsyncBench final : public td::Benchmark {
145 public:
get_description() const146 td::string get_description() const final {
147 return "SqliteKeyValueAsync";
148 }
start_up()149 void start_up() final {
150 do_start_up().ensure();
151 scheduler_->start();
152 }
run(int n)153 void run(int n) final {
154 auto guard = scheduler_->get_main_guard();
155
156 for (int i = 0; i < n; i++) {
157 auto key = td::to_string(i % 10);
158 auto value = td::to_string(i);
159 sqlite_kv_async_->set(key, value, td::Auto());
160 }
161 }
tear_down()162 void tear_down() final {
163 scheduler_->run_main(0.1);
164 {
165 auto guard = scheduler_->get_main_guard();
166 sqlite_kv_async_.reset();
167 sqlite_kv_safe_.reset();
168 sql_connection_->close_and_destroy();
169 }
170
171 scheduler_->finish();
172 scheduler_.reset();
173 }
174
175 private:
176 td::unique_ptr<td::ConcurrentScheduler> scheduler_;
177 std::shared_ptr<td::SqliteConnectionSafe> sql_connection_;
178 std::shared_ptr<td::SqliteKeyValueSafe> sqlite_kv_safe_;
179 td::unique_ptr<td::SqliteKeyValueAsyncInterface> sqlite_kv_async_;
180
do_start_up()181 td::Status do_start_up() {
182 scheduler_ = td::make_unique<td::ConcurrentScheduler>();
183 scheduler_->init(1);
184
185 auto guard = scheduler_->get_main_guard();
186
187 td::string sql_db_name = "testdb.sqlite";
188 td::SqliteDb::destroy(sql_db_name).ignore();
189
190 sql_connection_ = std::make_shared<td::SqliteConnectionSafe>(sql_db_name, td::DbKey::empty());
191 auto &db = sql_connection_->get();
192 TRY_STATUS(init_db(db));
193
194 sqlite_kv_safe_ = std::make_shared<td::SqliteKeyValueSafe>("common", sql_connection_);
195 sqlite_kv_async_ = create_sqlite_key_value_async(sqlite_kv_safe_, 0);
196
197 return td::Status::OK();
198 }
199 };
200
201 class SeqKvBench final : public td::Benchmark {
get_description() const202 td::string get_description() const final {
203 return "SeqKvBench";
204 }
205
206 td::SeqKeyValue kv;
run(int n)207 void run(int n) final {
208 for (int i = 0; i < n; i++) {
209 kv.set(PSLICE() << i % 10, PSLICE() << i);
210 }
211 }
212 };
213
214 template <bool is_encrypted = false>
215 class BinlogKeyValueBench final : public td::Benchmark {
get_description() const216 td::string get_description() const final {
217 return PSTRING() << "BinlogKeyValue " << td::tag("is_encrypted", is_encrypted);
218 }
219
220 td::BinlogKeyValue<td::Binlog> kv;
start_up()221 void start_up() final {
222 td::SqliteDb::destroy("test_binlog").ignore();
223 kv.init("test_binlog", is_encrypted ? td::DbKey::password("cucumber") : td::DbKey::empty()).ensure();
224 }
run(int n)225 void run(int n) final {
226 for (int i = 0; i < n; i++) {
227 kv.set(td::to_string(i % 10), td::to_string(i));
228 }
229 }
230 };
231
main()232 int main() {
233 SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
234 bench(BinlogKeyValueBench<true>());
235 bench(BinlogKeyValueBench<false>());
236 bench(SqliteKVBench<false>());
237 bench(SqliteKVBench<true>());
238 bench(SqliteKeyValueAsyncBench());
239 bench(TdKvBench<td::BinlogKeyValue<td::Binlog>>("BinlogKeyValue<Binlog>"));
240 bench(TdKvBench<td::BinlogKeyValue<td::ConcurrentBinlog>>("BinlogKeyValue<ConcurrentBinlog>"));
241 bench(SeqKvBench());
242 }
243