mdbxmou 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/.github/workflows/ci.yml +32 -0
  2. package/.github/workflows/publish.yml +27 -0
  3. package/.gitmodules +3 -0
  4. package/CMakeLists.txt +53 -0
  5. package/LICENSE +201 -0
  6. package/README.md +639 -0
  7. package/build.js +11 -0
  8. package/deps/libmdbx/.clang-format +3 -0
  9. package/deps/libmdbx/.cmake-format.yaml +3 -0
  10. package/deps/libmdbx/.le.ini +40 -0
  11. package/deps/libmdbx/CMakeLists.txt +1269 -0
  12. package/deps/libmdbx/COPYRIGHT +159 -0
  13. package/deps/libmdbx/ChangeLog.md +2786 -0
  14. package/deps/libmdbx/GNUmakefile +950 -0
  15. package/deps/libmdbx/LICENSE +177 -0
  16. package/deps/libmdbx/Makefile +16 -0
  17. package/deps/libmdbx/NOTICE +39 -0
  18. package/deps/libmdbx/README.md +863 -0
  19. package/deps/libmdbx/TODO.md +43 -0
  20. package/deps/libmdbx/cmake/compiler.cmake +1221 -0
  21. package/deps/libmdbx/cmake/profile.cmake +58 -0
  22. package/deps/libmdbx/cmake/utils.cmake +524 -0
  23. package/deps/libmdbx/conanfile.py +323 -0
  24. package/deps/libmdbx/docs/Doxyfile.in +2734 -0
  25. package/deps/libmdbx/docs/_preface.md +47 -0
  26. package/deps/libmdbx/docs/_restrictions.md +248 -0
  27. package/deps/libmdbx/docs/_starting.md +245 -0
  28. package/deps/libmdbx/docs/_toc.md +34 -0
  29. package/deps/libmdbx/docs/header.html +96 -0
  30. package/deps/libmdbx/example/CMakeLists.txt +6 -0
  31. package/deps/libmdbx/example/README.md +1 -0
  32. package/deps/libmdbx/example/example-mdbx.c +154 -0
  33. package/deps/libmdbx/example/sample-bdb.txt +77 -0
  34. package/deps/libmdbx/mdbx.h +6655 -0
  35. package/deps/libmdbx/mdbx.h++ +6428 -0
  36. package/deps/libmdbx/packages/buildroot/0001-package-libmdbx-new-package-library-database.patch +173 -0
  37. package/deps/libmdbx/src/alloy.c +54 -0
  38. package/deps/libmdbx/src/api-cold.c +543 -0
  39. package/deps/libmdbx/src/api-copy.c +912 -0
  40. package/deps/libmdbx/src/api-cursor.c +754 -0
  41. package/deps/libmdbx/src/api-dbi.c +315 -0
  42. package/deps/libmdbx/src/api-env.c +1434 -0
  43. package/deps/libmdbx/src/api-extra.c +165 -0
  44. package/deps/libmdbx/src/api-key-transform.c +197 -0
  45. package/deps/libmdbx/src/api-misc.c +286 -0
  46. package/deps/libmdbx/src/api-opts.c +575 -0
  47. package/deps/libmdbx/src/api-range-estimate.c +365 -0
  48. package/deps/libmdbx/src/api-txn-data.c +454 -0
  49. package/deps/libmdbx/src/api-txn.c +921 -0
  50. package/deps/libmdbx/src/atomics-ops.h +364 -0
  51. package/deps/libmdbx/src/atomics-types.h +97 -0
  52. package/deps/libmdbx/src/audit.c +109 -0
  53. package/deps/libmdbx/src/bits.md +34 -0
  54. package/deps/libmdbx/src/chk.c +1796 -0
  55. package/deps/libmdbx/src/cogs.c +309 -0
  56. package/deps/libmdbx/src/cogs.h +506 -0
  57. package/deps/libmdbx/src/coherency.c +170 -0
  58. package/deps/libmdbx/src/config.h.in +88 -0
  59. package/deps/libmdbx/src/cursor.c +2396 -0
  60. package/deps/libmdbx/src/cursor.h +391 -0
  61. package/deps/libmdbx/src/dbi.c +717 -0
  62. package/deps/libmdbx/src/dbi.h +142 -0
  63. package/deps/libmdbx/src/debug_begin.h +36 -0
  64. package/deps/libmdbx/src/debug_end.h +15 -0
  65. package/deps/libmdbx/src/dpl.c +486 -0
  66. package/deps/libmdbx/src/dpl.h +134 -0
  67. package/deps/libmdbx/src/dxb.c +1335 -0
  68. package/deps/libmdbx/src/env.c +607 -0
  69. package/deps/libmdbx/src/essentials.h +125 -0
  70. package/deps/libmdbx/src/gc-get.c +1345 -0
  71. package/deps/libmdbx/src/gc-put.c +970 -0
  72. package/deps/libmdbx/src/gc.h +40 -0
  73. package/deps/libmdbx/src/global.c +474 -0
  74. package/deps/libmdbx/src/internals.h +585 -0
  75. package/deps/libmdbx/src/layout-dxb.h +288 -0
  76. package/deps/libmdbx/src/layout-lck.h +289 -0
  77. package/deps/libmdbx/src/lck-posix.c +859 -0
  78. package/deps/libmdbx/src/lck-windows.c +607 -0
  79. package/deps/libmdbx/src/lck.c +174 -0
  80. package/deps/libmdbx/src/lck.h +110 -0
  81. package/deps/libmdbx/src/logging_and_debug.c +250 -0
  82. package/deps/libmdbx/src/logging_and_debug.h +159 -0
  83. package/deps/libmdbx/src/man1/mdbx_chk.1 +106 -0
  84. package/deps/libmdbx/src/man1/mdbx_copy.1 +95 -0
  85. package/deps/libmdbx/src/man1/mdbx_drop.1 +48 -0
  86. package/deps/libmdbx/src/man1/mdbx_dump.1 +101 -0
  87. package/deps/libmdbx/src/man1/mdbx_load.1 +105 -0
  88. package/deps/libmdbx/src/man1/mdbx_stat.1 +86 -0
  89. package/deps/libmdbx/src/mdbx.c++ +1837 -0
  90. package/deps/libmdbx/src/meta.c +656 -0
  91. package/deps/libmdbx/src/meta.h +168 -0
  92. package/deps/libmdbx/src/mvcc-readers.c +414 -0
  93. package/deps/libmdbx/src/node.c +365 -0
  94. package/deps/libmdbx/src/node.h +102 -0
  95. package/deps/libmdbx/src/ntdll.def +1246 -0
  96. package/deps/libmdbx/src/options.h +534 -0
  97. package/deps/libmdbx/src/osal.c +3485 -0
  98. package/deps/libmdbx/src/osal.h +587 -0
  99. package/deps/libmdbx/src/page-get.c +483 -0
  100. package/deps/libmdbx/src/page-iov.c +185 -0
  101. package/deps/libmdbx/src/page-iov.h +34 -0
  102. package/deps/libmdbx/src/page-ops.c +744 -0
  103. package/deps/libmdbx/src/page-ops.h +142 -0
  104. package/deps/libmdbx/src/pnl.c +236 -0
  105. package/deps/libmdbx/src/pnl.h +146 -0
  106. package/deps/libmdbx/src/preface.h +990 -0
  107. package/deps/libmdbx/src/proto.h +105 -0
  108. package/deps/libmdbx/src/refund.c +212 -0
  109. package/deps/libmdbx/src/sort.h +484 -0
  110. package/deps/libmdbx/src/spill.c +431 -0
  111. package/deps/libmdbx/src/spill.h +74 -0
  112. package/deps/libmdbx/src/table.c +107 -0
  113. package/deps/libmdbx/src/tls.c +551 -0
  114. package/deps/libmdbx/src/tls.h +43 -0
  115. package/deps/libmdbx/src/tools/chk.c +673 -0
  116. package/deps/libmdbx/src/tools/copy.c +166 -0
  117. package/deps/libmdbx/src/tools/drop.c +199 -0
  118. package/deps/libmdbx/src/tools/dump.c +515 -0
  119. package/deps/libmdbx/src/tools/load.c +831 -0
  120. package/deps/libmdbx/src/tools/stat.c +516 -0
  121. package/deps/libmdbx/src/tools/wingetopt.c +87 -0
  122. package/deps/libmdbx/src/tools/wingetopt.h +30 -0
  123. package/deps/libmdbx/src/tree-ops.c +1554 -0
  124. package/deps/libmdbx/src/tree-search.c +140 -0
  125. package/deps/libmdbx/src/txl.c +99 -0
  126. package/deps/libmdbx/src/txl.h +26 -0
  127. package/deps/libmdbx/src/txn.c +1083 -0
  128. package/deps/libmdbx/src/unaligned.h +205 -0
  129. package/deps/libmdbx/src/utils.c +32 -0
  130. package/deps/libmdbx/src/utils.h +76 -0
  131. package/deps/libmdbx/src/version.c.in +44 -0
  132. package/deps/libmdbx/src/walk.c +290 -0
  133. package/deps/libmdbx/src/walk.h +20 -0
  134. package/deps/libmdbx/src/windows-import.c +152 -0
  135. package/deps/libmdbx/src/windows-import.h +128 -0
  136. package/deps/libmdbx/test/CMakeLists.txt +317 -0
  137. package/deps/libmdbx/test/append.c++ +237 -0
  138. package/deps/libmdbx/test/base.h++ +92 -0
  139. package/deps/libmdbx/test/battery-tmux.sh +64 -0
  140. package/deps/libmdbx/test/cases.c++ +118 -0
  141. package/deps/libmdbx/test/chrono.c++ +134 -0
  142. package/deps/libmdbx/test/chrono.h++ +85 -0
  143. package/deps/libmdbx/test/config.c++ +643 -0
  144. package/deps/libmdbx/test/config.h++ +334 -0
  145. package/deps/libmdbx/test/copy.c++ +62 -0
  146. package/deps/libmdbx/test/dead.c++ +39 -0
  147. package/deps/libmdbx/test/dump-load.sh +40 -0
  148. package/deps/libmdbx/test/extra/crunched_delete.c++ +409 -0
  149. package/deps/libmdbx/test/extra/cursor_closing.c++ +410 -0
  150. package/deps/libmdbx/test/extra/dbi.c++ +229 -0
  151. package/deps/libmdbx/test/extra/doubtless_positioning.c++ +253 -0
  152. package/deps/libmdbx/test/extra/dupfix_addodd.c +94 -0
  153. package/deps/libmdbx/test/extra/dupfix_multiple.c++ +311 -0
  154. package/deps/libmdbx/test/extra/early_close_dbi.c++ +137 -0
  155. package/deps/libmdbx/test/extra/hex_base64_base58.c++ +118 -0
  156. package/deps/libmdbx/test/extra/maindb_ordinal.c++ +61 -0
  157. package/deps/libmdbx/test/extra/open.c++ +96 -0
  158. package/deps/libmdbx/test/extra/pcrf/README.md +2 -0
  159. package/deps/libmdbx/test/extra/pcrf/pcrf_test.c +380 -0
  160. package/deps/libmdbx/test/extra/probe.c++ +10 -0
  161. package/deps/libmdbx/test/extra/txn.c++ +407 -0
  162. package/deps/libmdbx/test/extra/upsert_alldups.c +193 -0
  163. package/deps/libmdbx/test/fork.c++ +263 -0
  164. package/deps/libmdbx/test/hill.c++ +447 -0
  165. package/deps/libmdbx/test/jitter.c++ +197 -0
  166. package/deps/libmdbx/test/keygen.c++ +393 -0
  167. package/deps/libmdbx/test/keygen.h++ +130 -0
  168. package/deps/libmdbx/test/log.c++ +358 -0
  169. package/deps/libmdbx/test/log.h++ +91 -0
  170. package/deps/libmdbx/test/main.c++ +706 -0
  171. package/deps/libmdbx/test/nested.c++ +318 -0
  172. package/deps/libmdbx/test/osal-unix.c++ +647 -0
  173. package/deps/libmdbx/test/osal-windows.c++ +440 -0
  174. package/deps/libmdbx/test/osal.h++ +41 -0
  175. package/deps/libmdbx/test/stochastic.sh +690 -0
  176. package/deps/libmdbx/test/stub/LICENSE +24 -0
  177. package/deps/libmdbx/test/stub/README.md +8 -0
  178. package/deps/libmdbx/test/stub/pthread_barrier.c +104 -0
  179. package/deps/libmdbx/test/stub/pthread_barrier.h +77 -0
  180. package/deps/libmdbx/test/test.c++ +1551 -0
  181. package/deps/libmdbx/test/test.h++ +298 -0
  182. package/deps/libmdbx/test/tmux.conf +3 -0
  183. package/deps/libmdbx/test/try.c++ +30 -0
  184. package/deps/libmdbx/test/ttl.c++ +240 -0
  185. package/deps/libmdbx/test/utils.c++ +203 -0
  186. package/deps/libmdbx/test/utils.h++ +326 -0
  187. package/deps/libmdbx/test/valgrind_suppress.txt +536 -0
  188. package/lib/mdbx_evn_async.js +211 -0
  189. package/lib/mdbx_worker.js +195 -0
  190. package/lib/nativemou.js +6 -0
  191. package/package.json +38 -0
  192. package/src/async/envmou_close.cpp +34 -0
  193. package/src/async/envmou_close.hpp +32 -0
  194. package/src/async/envmou_copy_to.cpp +29 -0
  195. package/src/async/envmou_copy_to.hpp +38 -0
  196. package/src/async/envmou_keys.cpp +201 -0
  197. package/src/async/envmou_keys.hpp +50 -0
  198. package/src/async/envmou_open.cpp +38 -0
  199. package/src/async/envmou_open.hpp +33 -0
  200. package/src/async/envmou_query.cpp +167 -0
  201. package/src/async/envmou_query.hpp +53 -0
  202. package/src/dbimou.cpp +522 -0
  203. package/src/dbimou.hpp +82 -0
  204. package/src/env_arg0.hpp +24 -0
  205. package/src/envmou.cpp +445 -0
  206. package/src/envmou.hpp +116 -0
  207. package/src/modulemou.cpp +113 -0
  208. package/src/querymou.cpp +177 -0
  209. package/src/querymou.hpp +93 -0
  210. package/src/txnmou.cpp +254 -0
  211. package/src/txnmou.hpp +122 -0
  212. package/src/typemou.hpp +239 -0
  213. package/src/valuemou.hpp +194 -0
  214. package/test/async.js +67 -0
  215. package/test/e3.js +38 -0
  216. package/test/e4.js +89 -0
  217. package/test/e5.js +162 -0
  218. package/test/test-batch-ops.js +243 -0
  219. package/test/test-cursor-mode.js +84 -0
  220. package/test/test-multi-mode.js +87 -0
@@ -0,0 +1,298 @@
1
+ /// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
2
+ /// \copyright SPDX-License-Identifier: Apache-2.0
3
+
4
+ #pragma once
5
+
6
+ #include "base.h++"
7
+ #include "chrono.h++"
8
+ #include "config.h++"
9
+ #include "keygen.h++"
10
+ #include "log.h++"
11
+ #include "osal.h++"
12
+ #include "utils.h++"
13
+
14
+ #include <deque>
15
+ #include <set>
16
+ #include <stack>
17
+ #include <tuple>
18
+
19
+ #ifndef HAVE_cxx17_std_string_view
20
+ #if __cplusplus >= 201703L && __has_include(<string_view>)
21
+ #include <string_view>
22
+ #define HAVE_cxx17_std_string_view 1
23
+ #else
24
+ #define HAVE_cxx17_std_string_view 0
25
+ #endif
26
+ #endif /* HAVE_cxx17_std_string_view */
27
+
28
+ #if HAVE_cxx17_std_string_view
29
+ #include <string_view>
30
+ #endif
31
+
32
+ bool test_execute(const actor_config &config);
33
+ std::string thunk_param(const actor_config &config);
34
+ void testcase_setup(const char *casename, const actor_params &params, unsigned &last_space_id);
35
+ void configure_actor(unsigned &last_space_id, const actor_testcase testcase, const char *space_id_cstr,
36
+ actor_params params);
37
+ void keycase_setup(const char *casename, actor_params &params);
38
+
39
+ namespace global {
40
+
41
+ extern const char thunk_param_prefix[];
42
+ extern std::vector<actor_config> actors;
43
+ extern std::unordered_map<unsigned, actor_config *> events;
44
+ extern std::unordered_map<mdbx_pid_t, actor_config *> pid2actor;
45
+ extern std::set<std::string> databases;
46
+ extern unsigned nactors;
47
+ extern chrono::time start_monotonic;
48
+ extern chrono::time deadline_monotonic;
49
+ extern bool singlemode;
50
+
51
+ namespace config {
52
+ extern unsigned timeout_duration_seconds;
53
+ extern bool dump_config;
54
+ extern bool cleanup_before;
55
+ extern bool cleanup_after;
56
+ extern bool failfast;
57
+ extern bool progress_indicator;
58
+ extern bool console_mode;
59
+ extern bool geometry_jitter;
60
+ } /* namespace config */
61
+
62
+ } /* namespace global */
63
+
64
+ //-----------------------------------------------------------------------------
65
+
66
+ struct db_deleter /* : public std::unary_function<void, MDBX_env *> */ {
67
+ void operator()(MDBX_env *env) const { mdbx_env_close(env); }
68
+ };
69
+
70
+ struct txn_deleter /* : public std::unary_function<void, MDBX_txn *> */ {
71
+ void operator()(MDBX_txn *txn) const {
72
+ int rc = mdbx_txn_abort(txn);
73
+ if (rc)
74
+ log_trouble(__func__, "mdbx_txn_abort()", rc);
75
+ }
76
+ };
77
+
78
+ struct cursor_deleter /* : public std::unary_function<void, MDBX_cursor *> */ {
79
+ void operator()(MDBX_cursor *cursor) const { mdbx_cursor_close(cursor); }
80
+ };
81
+
82
+ using scoped_db_guard = std::unique_ptr<MDBX_env, db_deleter>;
83
+ using scoped_txn_guard = std::unique_ptr<MDBX_txn, txn_deleter>;
84
+ using scoped_cursor_guard = std::unique_ptr<MDBX_cursor, cursor_deleter>;
85
+
86
+ //-----------------------------------------------------------------------------
87
+
88
+ class testcase;
89
+
90
+ class registry {
91
+ struct record {
92
+ actor_testcase id = ac_none;
93
+ std::string name;
94
+ bool (*review_params)(actor_params &, unsigned space_id) = nullptr;
95
+ testcase *(*constructor)(const actor_config &, const mdbx_pid_t) = nullptr;
96
+ };
97
+ std::unordered_map<std::string, const record *> name2id;
98
+ std::unordered_map<int, const record *> id2record;
99
+ static bool add(const record *item);
100
+ static registry *instance();
101
+
102
+ public:
103
+ template <class TESTCASE> struct factory : public record {
104
+ factory(const actor_testcase id, const char *name) {
105
+ this->id = id;
106
+ this->name = name;
107
+ review_params = TESTCASE::review_params;
108
+ constructor = [](const actor_config &config, const mdbx_pid_t pid) -> testcase * {
109
+ return new TESTCASE(config, pid);
110
+ };
111
+ add(this);
112
+ }
113
+ };
114
+ static bool review_actor_params(const actor_testcase id, actor_params &params, const unsigned space_id);
115
+ static testcase *create_actor(const actor_config &config, const mdbx_pid_t pid);
116
+ };
117
+
118
+ #define REGISTER_TESTCASE(NAME) \
119
+ static registry::factory<testcase_##NAME> gRegister_##NAME(ac_##NAME, MDBX_STRINGIFY(NAME))
120
+
121
+ class testcase {
122
+ protected:
123
+ using data_view = mdbx::slice;
124
+ static inline data_view iov2dataview(const MDBX_val &v) {
125
+ return (v.iov_base && v.iov_len) ? data_view(static_cast<const char *>(v.iov_base), v.iov_len) : data_view();
126
+ }
127
+ static inline data_view iov2dataview(const keygen::buffer &b) { return iov2dataview(b->value); }
128
+
129
+ using Item = std::pair<::mdbx::buffer<>, ::mdbx::buffer<>>;
130
+
131
+ static MDBX_val dataview2iov(const data_view &v) {
132
+ MDBX_val r;
133
+ r.iov_base = (void *)v.data();
134
+ r.iov_len = v.size();
135
+ return r;
136
+ }
137
+ struct ItemCompare {
138
+ const testcase *context;
139
+ ItemCompare(const testcase *owner) : context(owner) { /* The context->txn_guard may be empty/null here */ }
140
+
141
+ bool operator()(const Item &a, const Item &b) const {
142
+ MDBX_val va = dataview2iov(a.first), vb = dataview2iov(b.first);
143
+ assert(context->txn_guard.get() != nullptr);
144
+ int cmp = mdbx_cmp(context->txn_guard.get(), context->dbi, &va, &vb);
145
+ if (cmp == 0 && (context->config.params.table_flags & MDBX_DUPSORT) != 0) {
146
+ va = dataview2iov(a.second);
147
+ vb = dataview2iov(b.second);
148
+ cmp = mdbx_dcmp(context->txn_guard.get(), context->dbi, &va, &vb);
149
+ }
150
+ return cmp < 0;
151
+ }
152
+ };
153
+
154
+ // for simplify the set<pair<key,value>>
155
+ // is used instead of multimap<key,value>
156
+ using SET = std::set<Item, ItemCompare>;
157
+
158
+ const actor_config &config;
159
+ const mdbx_pid_t pid;
160
+
161
+ MDBX_dbi dbi{0};
162
+ scoped_db_guard db_guard;
163
+ scoped_txn_guard txn_guard;
164
+ scoped_cursor_guard cursor_guard;
165
+ bool signalled{false};
166
+ bool need_speculum_assign{false};
167
+
168
+ uint64_t nops_completed{0};
169
+ chrono::time start_timestamp;
170
+ keygen::buffer key;
171
+ keygen::buffer data;
172
+ keygen::maker keyvalue_maker;
173
+
174
+ struct {
175
+ MDBX_canary canary;
176
+ } last;
177
+
178
+ SET speculum{ItemCompare(this)}, speculum_committed{ItemCompare(this)};
179
+ #ifndef SPECULUM_CURSORS
180
+ #define SPECULUM_CURSORS 1
181
+ #endif /* SPECULUM_CURSORS */
182
+ #if SPECULUM_CURSORS
183
+ scoped_cursor_guard speculum_cursors[5 + 1];
184
+ void speculum_prepare_cursors(const Item &item);
185
+ bool speculum_check_cursor(const char *where, const char *stage, const testcase::SET::const_iterator &it,
186
+ int cursor_err, const MDBX_val &cursor_key, const MDBX_val &cursor_data,
187
+ MDBX_cursor *cursor) const;
188
+ bool speculum_check_cursor(const char *where, const char *stage, const testcase::SET::const_iterator &it,
189
+ MDBX_cursor *cursor, const MDBX_cursor_op op) const;
190
+ void speculum_render(const testcase::SET::const_iterator &it, const MDBX_cursor *ref) const;
191
+ #endif /* SPECULUM_CURSORS */
192
+ bool speculum_check_iterator(const char *where, const char *stage, const testcase::SET::const_iterator &it,
193
+ const MDBX_val &k, const MDBX_val &v, MDBX_cursor *cursor) const;
194
+
195
+ void verbose(const char *where, const char *stage, const testcase::SET::const_iterator &it) const;
196
+ void verbose(const char *where, const char *stage, const MDBX_val &k, const MDBX_val &v,
197
+ int err = MDBX_SUCCESS) const;
198
+
199
+ bool is_same(const Item &a, const Item &b) const;
200
+ bool is_same(const SET::const_iterator &it, const MDBX_val &k, const MDBX_val &v) const;
201
+
202
+ bool speculum_verify();
203
+ bool check_batch_get();
204
+ int insert(const keygen::buffer &akey, const keygen::buffer &adata, MDBX_put_flags_t flags);
205
+ int replace(const keygen::buffer &akey, const keygen::buffer &new_value, const keygen::buffer &old_value,
206
+ MDBX_put_flags_t flags, bool hush_keygen_mistakes = true);
207
+ int remove(const keygen::buffer &akey, const keygen::buffer &adata);
208
+
209
+ static int hsr_callback(const MDBX_env *env, const MDBX_txn *txn, mdbx_pid_t pid, mdbx_tid_t tid, uint64_t laggard,
210
+ unsigned gap, size_t space, int retry) MDBX_CXX17_NOEXCEPT;
211
+
212
+ MDBX_env_flags_t actual_env_mode{MDBX_ENV_DEFAULTS};
213
+ bool is_nested_txn_available() const { return (actual_env_mode & MDBX_WRITEMAP) == 0; }
214
+ void kick_progress(bool active) const;
215
+ void db_prepare();
216
+ void db_open();
217
+ void db_close();
218
+ virtual void txn_begin(bool readonly, MDBX_txn_flags_t flags = MDBX_TXN_READWRITE);
219
+ int breakable_commit();
220
+ virtual void txn_end(bool abort);
221
+ int breakable_restart();
222
+ void txn_restart(bool abort, bool readonly, MDBX_txn_flags_t flags = MDBX_TXN_READWRITE);
223
+ void cursor_open(MDBX_dbi handle);
224
+ void cursor_close();
225
+ void cursor_renew();
226
+ void txn_inject_writefault(void);
227
+ void txn_inject_writefault(MDBX_txn *txn);
228
+ bool txn_probe_parking();
229
+
230
+ void fetch_canary();
231
+ void update_canary(uint64_t increment);
232
+ bool checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check, MDBX_val expected_valued);
233
+ unsigned txn_underutilization_x256(MDBX_txn *txn) const;
234
+
235
+ using tablename_buf = char[32];
236
+ const char *db_tablename(tablename_buf &buffer, const char *suffix = "") const;
237
+ MDBX_dbi db_table_open(bool create, bool expect_failure = false);
238
+ void db_table_drop(MDBX_dbi handle);
239
+ void db_table_clear(MDBX_dbi handle, MDBX_txn *txn = nullptr);
240
+ void db_table_close(MDBX_dbi handle);
241
+ int db_open__begin__table_create_open_clean(MDBX_dbi &handle);
242
+ bool is_handle_created_in_current_txn(const MDBX_dbi handle, MDBX_txn *txn);
243
+
244
+ bool wait4start();
245
+ void report(size_t nops_done);
246
+ void signal();
247
+ bool should_continue(bool check_timeout_only = false) const;
248
+
249
+ bool MDBX_PRINTF_ARGS(2, 3) failure(const char *fmt, ...) const;
250
+ void generate_pair(const keygen::serial_t serial, keygen::buffer &out_key, keygen::buffer &out_value,
251
+ keygen::serial_t data_age) {
252
+ keyvalue_maker.pair(serial, out_key, out_value, data_age, false);
253
+ }
254
+
255
+ void generate_pair(const keygen::serial_t serial) { keyvalue_maker.pair(serial, key, data, 0, true); }
256
+
257
+ bool mode_readonly() const { return (config.params.mode_flags & MDBX_RDONLY) ? true : false; }
258
+
259
+ public:
260
+ testcase(const actor_config &config, const mdbx_pid_t pid) : config(config), pid(pid) {
261
+ start_timestamp.reset();
262
+ memset(&last, 0, sizeof(last));
263
+ }
264
+
265
+ static bool review_params(actor_params &params, unsigned space_id) {
266
+ // silently fix key/data length for fixed-length modes
267
+ params.prng_seed += bleach32(space_id);
268
+ if ((params.table_flags & MDBX_INTEGERKEY) && params.keylen_min != params.keylen_max)
269
+ params.keylen_min = params.keylen_max;
270
+ if ((params.table_flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) && params.datalen_min != params.datalen_max)
271
+ params.datalen_min = params.datalen_max;
272
+ return true;
273
+ }
274
+
275
+ virtual bool setup();
276
+ virtual bool run() { return true; }
277
+ virtual bool teardown();
278
+ virtual ~testcase() {}
279
+ };
280
+
281
+ //-----------------------------------------------------------------------------
282
+
283
+ class testcase_ttl : public testcase {
284
+ using inherited = testcase;
285
+
286
+ protected:
287
+ struct {
288
+ unsigned max_window_size{0};
289
+ unsigned max_step_size{0};
290
+ } sliding;
291
+ unsigned edge2window(uint64_t edge);
292
+ unsigned edge2count(uint64_t edge);
293
+
294
+ public:
295
+ testcase_ttl(const actor_config &config, const mdbx_pid_t pid) : inherited(config, pid) {}
296
+ bool setup() override;
297
+ bool run() override;
298
+ };
@@ -0,0 +1,3 @@
1
+ setw -g aggressive-resize on
2
+ set-option -g remain-on-exit on
3
+ # set-option -g remain-on-exit-format FAILED
@@ -0,0 +1,30 @@
1
+ /// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
2
+ /// \copyright SPDX-License-Identifier: Apache-2.0
3
+
4
+ #include "test.h++"
5
+
6
+ class testcase_try : public testcase {
7
+ public:
8
+ testcase_try(const actor_config &config, const mdbx_pid_t pid) : testcase(config, pid) {}
9
+ bool run() override;
10
+ };
11
+ REGISTER_TESTCASE(try);
12
+
13
+ bool testcase_try::run() {
14
+ db_open();
15
+ assert(!txn_guard);
16
+
17
+ MDBX_txn *txn = nullptr;
18
+ MDBX_txn *txn2 = nullptr;
19
+ int rc = mdbx_txn_begin(db_guard.get(), nullptr, MDBX_TXN_READWRITE, &txn);
20
+ if (unlikely(rc != MDBX_SUCCESS))
21
+ failure_perror("mdbx_txn_begin(MDBX_TXN_TRY)", rc);
22
+ else {
23
+ rc = mdbx_txn_begin(db_guard.get(), nullptr, MDBX_TXN_TRY, &txn2);
24
+ if (unlikely(rc != MDBX_BUSY))
25
+ failure_perror("mdbx_txn_begin(MDBX_TXN_TRY)", rc);
26
+ }
27
+
28
+ txn_guard.reset(txn);
29
+ return true;
30
+ }
@@ -0,0 +1,240 @@
1
+ /// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
2
+ /// \copyright SPDX-License-Identifier: Apache-2.0
3
+
4
+ #include "test.h++"
5
+ #include <cmath>
6
+ #include <deque>
7
+
8
+ /* LY: тест "эмуляцией time-to-live":
9
+ * - организуется "скользящее окно", которое двигается вперед вдоль
10
+ * числовой оси каждую транзакцию.
11
+ * - по переднему краю "скользящего окна" записи добавляются в таблицу,
12
+ * а по заднему удаляются.
13
+ * - количество добавляемых/удаляемых записей псевдослучайно зависит
14
+ * от номера транзакции, но с экспоненциальным распределением.
15
+ * - размер "скользящего окна" также псевдослучайно зависит от номера
16
+ * транзакции с "отрицательным" экспоненциальным распределением
17
+ * MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний
18
+ * край и удаляются записи позади него.
19
+ *
20
+ * Таким образом имитируется поведение таблицы с TTL: записи стохастически
21
+ * добавляются и удаляются, но изредка происходит массивное удаление.
22
+ */
23
+ REGISTER_TESTCASE(ttl);
24
+
25
+ unsigned testcase_ttl::edge2count(uint64_t edge) {
26
+ const double rnd = u64_to_double1(prng64_map1_white(edge));
27
+ const unsigned count = unsigned(std::lrint(std::pow(sliding.max_step_size, rnd)));
28
+ // average value: (X - 1) / ln(X), where X = sliding.max_step_size
29
+ return count;
30
+ }
31
+
32
+ unsigned testcase_ttl::edge2window(uint64_t edge) {
33
+ const double rnd = u64_to_double1(bleach64(edge));
34
+ const unsigned size = sliding.max_window_size - unsigned(std::lrint(std::pow(sliding.max_window_size, rnd)));
35
+ // average value: Y - (Y - 1) / ln(Y), where Y = sliding.max_window_size
36
+ return size;
37
+ }
38
+
39
+ static inline double estimate(const double x, const double y) {
40
+ /* среднее кол-во операций N = X' * Y', где X' и Y' средние значения
41
+ * размера окна и кол-ва добавляемых за один шаг записей:
42
+ * X' = (X - 1) / ln(X), где X = sliding.max_step_size
43
+ * Y' = Y - (Y - 1) / ln(Y), где Y = sliding.max_window_size */
44
+ return (x - 1) / std::log(x) * (y - (y - 1) / std::log(y));
45
+ }
46
+
47
+ bool testcase_ttl::setup() {
48
+ const unsigned window_top_lower = 7 /* нижний предел для верхней границы диапазона, в котором будет
49
+ стохастически колебаться размер окна */
50
+ ;
51
+ const unsigned count_top_lower = 7 /* нижний предел для верхней границы диапазона, в котором будет
52
+ стохастически колебаться кол-во записей добавляемых на одном шаге */
53
+ ;
54
+
55
+ /* для параметризации используем подходящие параметры,
56
+ * которые не имеют здесь смысла в первоначальном значении. */
57
+ const double ratio = double(config.params.batch_read ? config.params.batch_read : 1) /
58
+ double(config.params.batch_write ? config.params.batch_write : 1);
59
+
60
+ /* проще найти двоичным поиском (вариация метода Ньютона) */
61
+ double hi = config.params.test_nops, lo = 1;
62
+ double x = std::sqrt(hi + lo) / ratio;
63
+ while (hi > lo) {
64
+ const double n = estimate(x, x * ratio);
65
+ if (n > config.params.test_nops)
66
+ hi = x - 1;
67
+ else
68
+ lo = x + 1;
69
+ x = (hi + lo) / 2;
70
+ }
71
+
72
+ sliding.max_step_size = unsigned(std::lrint(x));
73
+ if (sliding.max_step_size < count_top_lower)
74
+ sliding.max_step_size = count_top_lower;
75
+ sliding.max_window_size = unsigned(std::lrint(x * ratio));
76
+ if (sliding.max_window_size < window_top_lower)
77
+ sliding.max_window_size = window_top_lower;
78
+
79
+ while (estimate(sliding.max_step_size, sliding.max_window_size) > config.params.test_nops * 2.0) {
80
+ if (ratio * sliding.max_step_size > sliding.max_window_size) {
81
+ if (sliding.max_step_size < count_top_lower)
82
+ break;
83
+ sliding.max_step_size = sliding.max_step_size * 7 / 8;
84
+ } else {
85
+ if (sliding.max_window_size < window_top_lower)
86
+ break;
87
+ sliding.max_window_size = sliding.max_window_size * 7 / 8;
88
+ }
89
+ }
90
+
91
+ log_verbose("come up window_max %u from `batch_read`", sliding.max_window_size);
92
+ log_verbose("come up step_max %u from `batch_write`", sliding.max_step_size);
93
+ return inherited::setup();
94
+ }
95
+
96
+ bool testcase_ttl::run() {
97
+ int err = db_open__begin__table_create_open_clean(dbi);
98
+ if (unlikely(err != MDBX_SUCCESS)) {
99
+ log_notice("ttl: bailout-prepare due '%s'", mdbx_strerror(err));
100
+ return false;
101
+ }
102
+
103
+ uint64_t seed = prng64_map2_white(prng_state) + config.space_id;
104
+ keyvalue_maker.setup(config.params, 0 /* thread_number */);
105
+ key = keygen::alloc(config.params.keylen_max);
106
+ data = keygen::alloc(config.params.datalen_max);
107
+ const MDBX_put_flags_t insert_flags =
108
+ (config.params.table_flags & MDBX_DUPSORT) ? MDBX_NODUPDATA : MDBX_NODUPDATA | MDBX_NOOVERWRITE;
109
+
110
+ std::deque<std::pair<uint64_t, unsigned>> fifo;
111
+ uint64_t serial = 0;
112
+ bool rc = false;
113
+ unsigned clear_wholetable_passed = 0;
114
+ unsigned clear_stepbystep_passed = 0;
115
+ unsigned dbfull_passed = 0;
116
+ unsigned loops = 0;
117
+ bool keyspace_overflow = false;
118
+ while (true) {
119
+ const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */;
120
+
121
+ const unsigned window_width = (!should_continue() || flipcoin_x4()) ? 0 : edge2window(salt);
122
+ unsigned head_count = edge2count(salt);
123
+ log_debug("ttl: step #%" PRIu64 " (serial %" PRIu64 ", window %u, count %u) salt %" PRIu64, nops_completed, serial,
124
+ window_width, head_count, salt);
125
+
126
+ if (window_width || flipcoin()) {
127
+ clear_stepbystep_passed += window_width == 0;
128
+ while (fifo.size() > window_width) {
129
+ uint64_t tail_serial = fifo.back().first;
130
+ const unsigned tail_count = fifo.back().second;
131
+ log_trace("ttl: pop-tail (serial %" PRIu64 ", count %u)", tail_serial, tail_count);
132
+ fifo.pop_back();
133
+ for (unsigned n = 0; n < tail_count; ++n) {
134
+ log_trace("ttl: remove-tail %" PRIu64, tail_serial);
135
+ generate_pair(tail_serial);
136
+ err = remove(key, data);
137
+ if (unlikely(err != MDBX_SUCCESS)) {
138
+ if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
139
+ log_notice("ttl: tail-bailout due '%s'", mdbx_strerror(err));
140
+ goto bailout;
141
+ }
142
+ failure_perror("mdbx_del(tail)", err);
143
+ }
144
+ if (unlikely(!keyvalue_maker.increment(tail_serial, 1)))
145
+ failure("ttl: unexpected key-space overflow on the tail");
146
+ }
147
+ report(tail_count);
148
+ }
149
+ } else {
150
+ log_trace("ttl: purge state");
151
+ db_table_clear(dbi);
152
+ fifo.clear();
153
+ clear_wholetable_passed += 1;
154
+ report(1);
155
+ }
156
+
157
+ err = breakable_restart();
158
+ if (unlikely(err != MDBX_SUCCESS)) {
159
+ log_notice("ttl: bailout at commit due '%s'", mdbx_strerror(err));
160
+ break;
161
+ }
162
+ if (!speculum_verify()) {
163
+ log_notice("ttl: bailout after tail-trim");
164
+ return false;
165
+ }
166
+
167
+ if (!keyspace_overflow && (should_continue() || !clear_wholetable_passed || !clear_stepbystep_passed)) {
168
+ unsigned underutilization_x256 = txn_underutilization_x256(txn_guard.get());
169
+ if (dbfull_passed > underutilization_x256) {
170
+ log_notice("ttl: skip head-grow to avoid one more dbfull (was %u, "
171
+ "underutilization %.2f%%)",
172
+ dbfull_passed, underutilization_x256 / 2.560);
173
+ continue;
174
+ }
175
+ fifo.push_front(std::make_pair(serial, head_count));
176
+ retry:
177
+ for (unsigned n = 0; n < head_count; ++n) {
178
+ log_trace("ttl: insert-head %" PRIu64, serial);
179
+ generate_pair(serial);
180
+ err = insert(key, data, insert_flags);
181
+ if (unlikely(err != MDBX_SUCCESS)) {
182
+ if ((err == MDBX_TXN_FULL || err == MDBX_MAP_FULL) && config.params.ignore_dbfull) {
183
+ log_notice("ttl: head-insert skip due '%s'", mdbx_strerror(err));
184
+ txn_restart(true, false);
185
+ serial = fifo.front().first;
186
+ fifo.front().second = head_count = n;
187
+ dbfull_passed += 1;
188
+ goto retry;
189
+ }
190
+ failure_perror("mdbx_put(head)", err);
191
+ }
192
+
193
+ if (unlikely(!keyvalue_maker.increment(serial, 1))) {
194
+ log_notice("ttl: unexpected key-space overflow");
195
+ keyspace_overflow = true;
196
+ txn_restart(true, false);
197
+ serial = fifo.front().first;
198
+ fifo.front().second = head_count = n;
199
+ goto retry;
200
+ }
201
+ }
202
+ err = breakable_restart();
203
+ if (unlikely(err != MDBX_SUCCESS)) {
204
+ log_notice("ttl: head-commit skip due '%s'", mdbx_strerror(err));
205
+ serial = fifo.front().first;
206
+ fifo.pop_front();
207
+ }
208
+ if (!speculum_verify()) {
209
+ log_notice("ttl: bailout after head-grow");
210
+ return false;
211
+ }
212
+ loops += 1;
213
+ } else if (fifo.empty()) {
214
+ log_notice("ttl: done %u whole loops, %" PRIu64 " ops, %" PRIu64 " items", loops, nops_completed, serial);
215
+ rc = true;
216
+ break;
217
+ } else {
218
+ log_notice("ttl: done, wait for empty, skip head-grow");
219
+ }
220
+ }
221
+
222
+ bailout:
223
+ if (!rc && err == MDBX_MAP_FULL && config.params.ignore_dbfull)
224
+ rc = true;
225
+ txn_end(true);
226
+ if (dbi) {
227
+ if (config.params.drop_table && !mode_readonly()) {
228
+ txn_begin(false);
229
+ db_table_drop(dbi);
230
+ err = breakable_commit();
231
+ if (unlikely(err != MDBX_SUCCESS)) {
232
+ log_notice("ttl: bailout-clean due '%s'", mdbx_strerror(err));
233
+ if (err != MDBX_MAP_FULL || !config.params.ignore_dbfull)
234
+ rc = false;
235
+ }
236
+ } else
237
+ db_table_close(dbi);
238
+ }
239
+ return rc;
240
+ }