mdbx: merge branch master into devel.

This commit is contained in:
Леонид Юрьев (Leonid Yuriev) 2022-07-23 11:56:17 +03:00
commit 5e565433f7
6 changed files with 107 additions and 72 deletions

View File

@ -569,7 +569,10 @@ else()
"${MDBX_SOURCE_DIR}/options.h" "${MDBX_SOURCE_DIR}/base.h" "${MDBX_SOURCE_DIR}/options.h" "${MDBX_SOURCE_DIR}/base.h"
"${MDBX_SOURCE_DIR}/internals.h" "${MDBX_SOURCE_DIR}/osal.h" "${MDBX_SOURCE_DIR}/internals.h" "${MDBX_SOURCE_DIR}/osal.h"
"${MDBX_SOURCE_DIR}/core.c" "${MDBX_SOURCE_DIR}/osal.c" "${MDBX_SOURCE_DIR}/core.c" "${MDBX_SOURCE_DIR}/osal.c"
"${MDBX_SOURCE_DIR}/lck-posix.c" "${MDBX_SOURCE_DIR}/lck-windows.c") "${MDBX_SOURCE_DIR}/lck-posix.c")
if(NOT APPLE)
list(APPEND LIBMDBX_SOURCES "${MDBX_SOURCE_DIR}/lck-windows.c")
endif()
include_directories("${MDBX_SOURCE_DIR}") include_directories("${MDBX_SOURCE_DIR}")
endif() endif()
endif(MDBX_AMALGAMATED_SOURCE) endif(MDBX_AMALGAMATED_SOURCE)

View File

@ -29,14 +29,22 @@ Not a release but preparation for changing feature set and API.
The stable bugfix release. The stable bugfix release.
It is planned that this will be the last release of the v0.11 branch. It is planned that this will be the last release of the v0.11 branch.
Acknowledgements:
- [Alex Sharov](https://github.com/AskAlexSharov) and Erigon team for reporting and testing.
- [Andrew Ashikhmin](https://gitflic.ru/user/yperbasis) for contributing.
New: New:
- Ability to customise `MDBX_LOCK_SUFFIX`, `MDBX_DATANAME`, `MDBX_LOCKNAME` just by predefine ones during build. - Ability to customise `MDBX_LOCK_SUFFIX`, `MDBX_DATANAME`, `MDBX_LOCKNAME` just by predefine ones during build.
- Added to [`mdbx::env_managed`](https://libmdbx.dqdkfa.ru/group__cxx__api.html#classmdbx_1_1env__managed)'s methods a few overloads with `const char* pathname` parameter (C++ API).
Fixes: Fixes:
- Fixed hang copy-with-compactification of a corrupted DB - Fixed hang copy-with-compactification of a corrupted DB
or in case the volume of output pages is a multiple of `MDBX_ENVCOPY_WRITEBUF`. or in case the volume of output pages is a multiple of `MDBX_ENVCOPY_WRITEBUF`.
- Fixed standalone non-CMake build on MacOS (`#include AvailabilityMacros.h>`).
- Fixed unexpected `MDBX_PAGE_FULL` error in rare cases with large database page sizes.
Minors: Minors:
@ -47,6 +55,7 @@ Minors:
- Minor refine/fix batch-get testcase for large page size. - Minor refine/fix batch-get testcase for large page size.
- Added `--pagesize NN` option to long-stotastic test script. - Added `--pagesize NN` option to long-stotastic test script.
- Updated Valgrind-suppressions file for modern GCC. - Updated Valgrind-suppressions file for modern GCC.
- Fixed `has no symbols` warning from Apple's ranlib.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------

View File

@ -3228,6 +3228,8 @@ public:
#endif /* Windows */ #endif /* Windows */
env &copy(const ::std::string &destination, bool compactify, env &copy(const ::std::string &destination, bool compactify,
bool force_dynamic_size = false); bool force_dynamic_size = false);
env &copy(const char *destination, bool compactify,
bool force_dynamic_size = false);
/// \brief Copy an environment to the specified file descriptor. /// \brief Copy an environment to the specified file descriptor.
env &copy(filehandle fd, bool compactify, bool force_dynamic_size = false); env &copy(filehandle fd, bool compactify, bool force_dynamic_size = false);
@ -3252,14 +3254,16 @@ public:
/// \brief Removes the environment's files in a proper and multiprocess-safe /// \brief Removes the environment's files in a proper and multiprocess-safe
/// way. /// way.
#ifdef MDBX_STD_FILESYSTEM_PATH #ifdef MDBX_STD_FILESYSTEM_PATH
static bool remove(const MDBX_STD_FILESYSTEM_PATH &, static bool remove(const MDBX_STD_FILESYSTEM_PATH &pathname,
const remove_mode mode = just_remove); const remove_mode mode = just_remove);
#endif /* MDBX_STD_FILESYSTEM_PATH */ #endif /* MDBX_STD_FILESYSTEM_PATH */
#if defined(_WIN32) || defined(_WIN64) || defined(DOXYGEN) #if defined(_WIN32) || defined(_WIN64) || defined(DOXYGEN)
static bool remove(const ::std::wstring &, static bool remove(const ::std::wstring &pathname,
const remove_mode mode = just_remove); const remove_mode mode = just_remove);
#endif /* Windows */ #endif /* Windows */
static bool remove(const ::std::string &, static bool remove(const ::std::string &pathname,
const remove_mode mode = just_remove);
static bool remove(const char *pathname,
const remove_mode mode = just_remove); const remove_mode mode = just_remove);
/// \brief Statistics for a database in the MDBX environment. /// \brief Statistics for a database in the MDBX environment.
@ -3497,15 +3501,17 @@ public:
/// \brief Open existing database. /// \brief Open existing database.
#ifdef MDBX_STD_FILESYSTEM_PATH #ifdef MDBX_STD_FILESYSTEM_PATH
env_managed(const MDBX_STD_FILESYSTEM_PATH &, const operate_parameters &, env_managed(const MDBX_STD_FILESYSTEM_PATH &pathname,
bool accede = true); const operate_parameters &, bool accede = true);
#endif /* MDBX_STD_FILESYSTEM_PATH */ #endif /* MDBX_STD_FILESYSTEM_PATH */
#if defined(_WIN32) || defined(_WIN64) || defined(DOXYGEN) #if defined(_WIN32) || defined(_WIN64) || defined(DOXYGEN)
env_managed(const ::std::wstring &, const operate_parameters &, env_managed(const ::std::wstring &pathname, const operate_parameters &,
bool accede = true); bool accede = true);
#endif /* Windows */ #endif /* Windows */
env_managed(const ::std::string &, const operate_parameters &, env_managed(const ::std::string &pathname, const operate_parameters &,
bool accede = true); bool accede = true);
explicit env_managed(const char *pathname, const operate_parameters &,
bool accede = true);
/// \brief Additional parameters for creating a new database. /// \brief Additional parameters for creating a new database.
struct create_parameters { struct create_parameters {
@ -3518,15 +3524,18 @@ public:
/// \brief Create new or open existing database. /// \brief Create new or open existing database.
#ifdef MDBX_STD_FILESYSTEM_PATH #ifdef MDBX_STD_FILESYSTEM_PATH
env_managed(const MDBX_STD_FILESYSTEM_PATH &, const create_parameters &, env_managed(const MDBX_STD_FILESYSTEM_PATH &pathname,
const operate_parameters &, bool accede = true); const create_parameters &, const operate_parameters &,
bool accede = true);
#endif /* MDBX_STD_FILESYSTEM_PATH */ #endif /* MDBX_STD_FILESYSTEM_PATH */
#if defined(_WIN32) || defined(_WIN64) || defined(DOXYGEN) #if defined(_WIN32) || defined(_WIN64) || defined(DOXYGEN)
env_managed(const ::std::wstring &, const create_parameters &, env_managed(const ::std::wstring &pathname, const create_parameters &,
const operate_parameters &, bool accede = true); const operate_parameters &, bool accede = true);
#endif /* Windows */ #endif /* Windows */
env_managed(const ::std::string &, const create_parameters &, env_managed(const ::std::string &pathname, const create_parameters &,
const operate_parameters &, bool accede = true); const operate_parameters &, bool accede = true);
explicit env_managed(const char *pathname, const create_parameters &,
const operate_parameters &, bool accede = true);
/// \brief Explicitly closes the environment and release the memory map. /// \brief Explicitly closes the environment and release the memory map.
/// ///

View File

@ -158,6 +158,7 @@
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
#include <AvailabilityMacros.h>
#ifndef MAC_OS_X_VERSION_MIN_REQUIRED #ifndef MAC_OS_X_VERSION_MIN_REQUIRED
#define MAC_OS_X_VERSION_MIN_REQUIRED 1070 /* Mac OS X 10.7, 2011 */ #define MAC_OS_X_VERSION_MIN_REQUIRED 1070 /* Mac OS X 10.7, 2011 */
#endif #endif

View File

@ -660,6 +660,7 @@ page_room(const MDBX_page *mp) {
return mp->mp_upper - mp->mp_lower; return mp->mp_upper - mp->mp_lower;
} }
/* Maximum free space in an empty page */
MDBX_NOTHROW_PURE_FUNCTION static __always_inline unsigned MDBX_NOTHROW_PURE_FUNCTION static __always_inline unsigned
page_space(const MDBX_env *env) { page_space(const MDBX_env *env) {
STATIC_ASSERT(PAGEHDRSZ % 2 == 0); STATIC_ASSERT(PAGEHDRSZ % 2 == 0);
@ -17630,6 +17631,7 @@ static int mdbx_node_move(MDBX_cursor *csrc, MDBX_cursor *cdst, bool fromleft) {
} break; } break;
default: default:
assert(false);
goto bailout; goto bailout;
} }
@ -19047,6 +19049,7 @@ static int mdbx_page_split(MDBX_cursor *mc, const MDBX_val *const newkey,
(newindx < nkeys) (newindx < nkeys)
? /* split at the middle */ (nkeys + 1) / 2 ? /* split at the middle */ (nkeys + 1) / 2
: /* split at the end (i.e. like append-mode ) */ nkeys - minkeys + 1; : /* split at the end (i.e. like append-mode ) */ nkeys - minkeys + 1;
mdbx_assert(env, split_indx >= minkeys && split_indx <= nkeys - minkeys + 1);
mdbx_cassert(mc, !IS_BRANCH(mp) || newindx > 0); mdbx_cassert(mc, !IS_BRANCH(mp) || newindx > 0);
/* It is reasonable and possible to split the page at the begin */ /* It is reasonable and possible to split the page at the begin */
@ -19144,11 +19147,6 @@ static int mdbx_page_split(MDBX_cursor *mc, const MDBX_val *const newkey,
goto done; goto done;
} }
} else { } else {
/* Maximum free space in an empty page */
const unsigned max_space = page_space(env);
const size_t new_size = IS_LEAF(mp) ? leaf_size(env, newkey, newdata)
: branch_size(env, newkey);
/* grab a page to hold a temporary copy */ /* grab a page to hold a temporary copy */
tmp_ki_copy = mdbx_page_malloc(mc->mc_txn, 1); tmp_ki_copy = mdbx_page_malloc(mc->mc_txn, 1);
if (unlikely(tmp_ki_copy == NULL)) { if (unlikely(tmp_ki_copy == NULL)) {
@ -19156,6 +19154,10 @@ static int mdbx_page_split(MDBX_cursor *mc, const MDBX_val *const newkey,
goto done; goto done;
} }
const unsigned max_space = page_space(env);
const size_t new_size = IS_LEAF(mp) ? leaf_size(env, newkey, newdata)
: branch_size(env, newkey);
/* prepare to insert */ /* prepare to insert */
for (unsigned j = i = 0; i < nkeys; ++i, ++j) { for (unsigned j = i = 0; i < nkeys; ++i, ++j) {
tmp_ki_copy->mp_ptrs[j] = 0; tmp_ki_copy->mp_ptrs[j] = 0;
@ -19168,34 +19170,35 @@ static int mdbx_page_split(MDBX_cursor *mc, const MDBX_val *const newkey,
tmp_ki_copy->mp_lower = 0; tmp_ki_copy->mp_lower = 0;
tmp_ki_copy->mp_upper = (indx_t)max_space; tmp_ki_copy->mp_upper = (indx_t)max_space;
/* When items are relatively large the split point needs /* Добавляемый узел может не поместиться в страницу-половину вместе
* to be checked, because being off-by-one will make the * с количественной половиной узлов из исходной страницы. В худшем случае,
* difference between success or failure in mdbx_node_add. * в страницу-половину с добавляемым узлом могут попасть самые больше узлы
* из исходной страницы, а другую половину только узлы с самыми короткими
* ключами и с пустыми данными. Поэтому, чтобы найти подходящую границу
* разреза требуется итерировать узлы и считая их объем.
* *
* It's also relevant if a page happens to be laid out * Однако, при простом количественном делении (без учета размера ключей
* such that one half of its nodes are all "small" and * и данных) на страницах-половинах будет примерно вдвое меньше узлов.
* the other half of its nodes are "large". If the new * Поэтому добавляемый узел точно поместится, если его размер не больше
* item is also "large" and falls on the half with * чем место "освобождающееся" от заголовков узлов, которые переедут
* "large" nodes, it also may not fit. * в другую страницу-половину. Кроме этого, как минимум по одному байту
* * будет в каждом ключе, в худшем случае кроме одного, который может быть
* As a final tweak, if the new item goes on the last * нулевого размера. */
* spot on the page (and thus, onto the new page), bias
* the split so the new page is emptier than the old page.
* This yields better packing during sequential inserts. */
if (nkeys < 32 || new_size > max_space / 16) { if (newindx == split_indx && split_indx + minkeys <= nkeys)
split_indx += 1;
mdbx_assert(env,
split_indx >= minkeys && split_indx <= nkeys - minkeys + 1);
const unsigned dim_nodes =
(newindx >= split_indx) ? split_indx : nkeys - split_indx;
const unsigned dim_used = (sizeof(indx_t) + NODESIZE + 1) * dim_nodes;
if (new_size >= dim_used) {
/* Find split point */ /* Find split point */
int dir; i = (newindx < split_indx) ? 0 : nkeys;
if (newindx <= split_indx) { int dir = (newindx < split_indx) ? 1 : -1;
i = 0;
dir = 1;
} else {
i = nkeys;
dir = -1;
}
size_t before = 0, after = new_size + page_used(env, mp); size_t before = 0, after = new_size + page_used(env, mp);
int best = split_indx; unsigned best_split = split_indx;
int best_offset = nkeys + 1; unsigned best_offset = INT_MAX;
mdbx_trace("seek separator from %u, step %i, default %u, new-idx %u, " mdbx_trace("seek separator from %u, step %i, default %u, new-idx %u, "
"new-size %zu", "new-size %zu",
@ -19208,8 +19211,8 @@ static int mdbx_page_split(MDBX_cursor *mc, const MDBX_val *const newkey,
(MDBX_node *)((char *)mp + tmp_ki_copy->mp_ptrs[i] + PAGEHDRSZ); (MDBX_node *)((char *)mp + tmp_ki_copy->mp_ptrs[i] + PAGEHDRSZ);
size = NODESIZE + node_ks(node) + sizeof(indx_t); size = NODESIZE + node_ks(node) + sizeof(indx_t);
if (IS_LEAF(mp)) if (IS_LEAF(mp))
size += F_ISSET(node_flags(node), F_BIGDATA) ? sizeof(pgno_t) size += (node_flags(node) & F_BIGDATA) ? sizeof(pgno_t)
: node_ds(node); : node_ds(node);
size = EVEN(size); size = EVEN(size);
} }
@ -19219,21 +19222,23 @@ static int mdbx_page_split(MDBX_cursor *mc, const MDBX_val *const newkey,
size, before, after, max_space); size, before, after, max_space);
if (before <= max_space && after <= max_space) { if (before <= max_space && after <= max_space) {
int offset = branchless_abs(split_indx - i); const unsigned split = i + (dir > 0);
if (offset >= best_offset) if (split >= minkeys && split <= nkeys + 1 - minkeys) {
break; const unsigned offset = branchless_abs(split_indx - split);
best_offset = offset; if (offset >= best_offset)
best = i; break;
best_offset = offset;
best_split = split;
}
} }
i += dir; i += dir;
} while (i < nkeys); } while (i < nkeys);
split_indx = best + (dir > 0); split_indx = best_split;
split_indx = (split_indx <= nkeys - minkeys + 1) ? split_indx
: nkeys - minkeys + 1;
split_indx = (split_indx >= minkeys) ? split_indx : minkeys;
mdbx_trace("chosen %u", split_indx); mdbx_trace("chosen %u", split_indx);
} }
mdbx_assert(env,
split_indx >= minkeys && split_indx <= nkeys - minkeys + 1);
sepkey.iov_len = newkey->iov_len; sepkey.iov_len = newkey->iov_len;
sepkey.iov_base = newkey->iov_base; sepkey.iov_base = newkey->iov_base;

View File

@ -214,12 +214,6 @@ MDBX_MAYBE_UNUSED PATH pchar_to_path(const char *c_str) {
return PATH(c_str); return PATH(c_str);
} }
template <> struct path_to_pchar<std::string> {
const char *const ptr;
path_to_pchar(const std::string &path) : ptr(path.c_str()) {}
operator const char *() const { return ptr; }
};
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
#ifndef WC_ERR_INVALID_CHARS #ifndef WC_ERR_INVALID_CHARS
@ -1271,17 +1265,21 @@ env &env::copy(const ::std::wstring &destination, bool compactify,
} }
#endif /* Windows */ #endif /* Windows */
env &env::copy(const ::std::string &destination, bool compactify, env &env::copy(const char *destination, bool compactify,
bool force_dynamic_size) { bool force_dynamic_size) {
const path_to_pchar<::std::string> utf8(destination);
error::success_or_throw( error::success_or_throw(
::mdbx_env_copy(handle_, utf8, ::mdbx_env_copy(handle_, destination,
(compactify ? MDBX_CP_COMPACT : MDBX_CP_DEFAULTS) | (compactify ? MDBX_CP_COMPACT : MDBX_CP_DEFAULTS) |
(force_dynamic_size ? MDBX_CP_FORCE_DYNAMIC_SIZE (force_dynamic_size ? MDBX_CP_FORCE_DYNAMIC_SIZE
: MDBX_CP_DEFAULTS))); : MDBX_CP_DEFAULTS)));
return *this; return *this;
} }
env &env::copy(const ::std::string &destination, bool compactify,
bool force_dynamic_size) {
return copy(destination.c_str(), compactify, force_dynamic_size);
}
env &env::copy(filehandle fd, bool compactify, bool force_dynamic_size) { env &env::copy(filehandle fd, bool compactify, bool force_dynamic_size) {
error::success_or_throw( error::success_or_throw(
::mdbx_env_copy2fd(handle_, fd, ::mdbx_env_copy2fd(handle_, fd,
@ -1314,10 +1312,13 @@ bool env::remove(const ::std::wstring &pathname, const remove_mode mode) {
} }
#endif /* Windows */ #endif /* Windows */
bool env::remove(const ::std::string &pathname, const remove_mode mode) { bool env::remove(const char *pathname, const remove_mode mode) {
const path_to_pchar<::std::string> utf8(pathname);
return error::boolean_or_throw( return error::boolean_or_throw(
::mdbx_env_delete(utf8, MDBX_env_delete_mode_t(mode))); ::mdbx_env_delete(pathname, MDBX_env_delete_mode_t(mode)));
}
bool env::remove(const ::std::string &pathname, const remove_mode mode) {
return remove(pathname.c_str(), mode);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -1418,35 +1419,42 @@ __cold env_managed::env_managed(const ::std::wstring &pathname,
} }
#endif /* Windows */ #endif /* Windows */
__cold env_managed::env_managed(const ::std::string &pathname, __cold env_managed::env_managed(const char *pathname,
const operate_parameters &op, bool accede) const operate_parameters &op, bool accede)
: env_managed(create_env()) { : env_managed(create_env()) {
setup(op.max_maps, op.max_readers); setup(op.max_maps, op.max_readers);
const path_to_pchar<::std::string> utf8(pathname);
error::success_or_throw( error::success_or_throw(
::mdbx_env_open(handle_, utf8, op.make_flags(accede), 0)); ::mdbx_env_open(handle_, pathname, op.make_flags(accede), 0));
if (op.options.nested_write_transactions && if (op.options.nested_write_transactions &&
!get_options().nested_write_transactions) !get_options().nested_write_transactions)
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE); MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
} }
__cold env_managed::env_managed(const ::std::string &pathname, __cold env_managed::env_managed(const char *pathname,
const env_managed::create_parameters &cp, const env_managed::create_parameters &cp,
const env::operate_parameters &op, bool accede) const env::operate_parameters &op, bool accede)
: env_managed(create_env()) { : env_managed(create_env()) {
setup(op.max_maps, op.max_readers); setup(op.max_maps, op.max_readers);
const path_to_pchar<::std::string> utf8(pathname);
set_geometry(cp.geometry); set_geometry(cp.geometry);
error::success_or_throw( error::success_or_throw(::mdbx_env_open(
::mdbx_env_open(handle_, utf8, op.make_flags(accede, cp.use_subdirectory), handle_, pathname, op.make_flags(accede, cp.use_subdirectory),
cp.file_mode_bits)); cp.file_mode_bits));
if (op.options.nested_write_transactions && if (op.options.nested_write_transactions &&
!get_options().nested_write_transactions) !get_options().nested_write_transactions)
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE); MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
} }
__cold env_managed::env_managed(const ::std::string &pathname,
const operate_parameters &op, bool accede)
: env_managed(pathname.c_str(), op, accede) {}
__cold env_managed::env_managed(const ::std::string &pathname,
const env_managed::create_parameters &cp,
const env::operate_parameters &op, bool accede)
: env_managed(pathname.c_str(), cp, op, accede) {}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
txn_managed txn::start_nested() { txn_managed txn::start_nested() {