From f7f0119668c3694660ecc95744e741fde6640a2f Mon Sep 17 00:00:00 2001 From: Vladimir Davydov <vdavydov@tarantool.org> Date: Thu, 6 Jun 2024 14:17:04 +0300 Subject: [PATCH] vinyl: fix crash in index drop if there is DML request reading from it A DML request (insert, replace, update) can yield while reading from the disk in order to check unique constraints. In the meantime the index can be dropped. The DML request can't crash in this case thanks to commit d3e1236956515 ("vinyl: abort affected transactions when space is removed from cache"), but the DDL operation can because: - It unreferences the index in `alter_space_commit`, which may result in dropping the LSM tree with `vy_lsm_delete`. - `vy_lsm_delete` may yield in `vy_range_tree_free_cb` while waiting for disk readers to complete. - Yielding in commit triggers isn't allowed (crashes). We already fixed a similar issue when `index.get` crashed if raced with index drop, see commit 75f03a50df4a ("vinyl: fix crash if space is dropped while space.get is reading from it"). Let's fix this issue in the same way - by taking a reference to the LSM tree while checking unique constraints. To do that it's enough to move `vy_lsm_ref` from `vinyl_index_get` to `vy_get`. Also, let's replace `vy_slice_wait_pinned` with an assertion checking that the slice pin count is 0 in `vy_range_tree_free_cb` because `vy_lsm_delete` must not yield. Closes #10094 NO_DOC=bug fix (cherry picked from commit bde28f0faa0945cc555fb055b8866a575593907c) --- .../gh-10094-vy-index-drop-crash-fix.md | 4 ++ src/box/vinyl.c | 23 +++++----- src/box/vy_lsm.c | 2 +- .../gh_10094_index_drop_test.lua | 44 +++++++++++++++++++ 4 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 changelogs/unreleased/gh-10094-vy-index-drop-crash-fix.md create mode 100644 test/vinyl-luatest/gh_10094_index_drop_test.lua diff --git a/changelogs/unreleased/gh-10094-vy-index-drop-crash-fix.md b/changelogs/unreleased/gh-10094-vy-index-drop-crash-fix.md new file mode 100644 index 0000000000..f02db6f5fc --- /dev/null +++ b/changelogs/unreleased/gh-10094-vy-index-drop-crash-fix.md @@ -0,0 +1,4 @@ +## bugfix/vinyl + +* Fixed a bug when a DDL operation dropping a unique index could crash + if performed concurrently with DML requests (gh-10094). diff --git a/src/box/vinyl.c b/src/box/vinyl.c index 135b3e030d..cd76d065aa 100644 --- a/src/box/vinyl.c +++ b/src/box/vinyl.c @@ -1410,6 +1410,11 @@ vy_get(struct vy_lsm *lsm, struct vy_tx *tx, * space.index.get({key}). */ assert(tx == NULL || tx->state == VINYL_TX_READY); + /* + * Make sure the LSM tree isn't deleted while we are + * reading from it. + */ + vy_lsm_ref(lsm); int rc; struct vy_entry partial, entry; @@ -1425,15 +1430,15 @@ vy_get(struct vy_lsm *lsm, struct vy_tx *tx, * Use point lookup for a full key. */ if (tx != NULL && vy_tx_track_point(tx, lsm, key) != 0) - return -1; + goto fail; if (vy_point_lookup(lsm, tx, rv, key, &partial) != 0) - return -1; + goto fail; if (lsm->index_id > 0 && partial.stmt != NULL) { rc = vy_get_by_secondary_tuple(lsm, tx, rv, partial, &entry); tuple_unref(partial.stmt); if (rc != 0) - return -1; + goto fail; } else { entry = partial; } @@ -1459,7 +1464,7 @@ vy_get(struct vy_lsm *lsm, struct vy_tx *tx, vy_read_iterator_cache_add(&itr, entry); vy_read_iterator_close(&itr); if (rc != 0) - return -1; + goto fail; out: *result = entry.stmt; @@ -1474,7 +1479,11 @@ vy_get(struct vy_lsm *lsm, struct vy_tx *tx, } if (*result != NULL) vy_stmt_counter_acct_tuple(&lsm->stat.get, *result); + vy_lsm_unref(lsm); return 0; +fail: + vy_lsm_unref(lsm); + return -1; } /** @@ -3856,14 +3865,8 @@ vinyl_index_get(struct index *index, const char *key, tx = &tx_autocommit; vy_tx_create(env->xm, tx); } - /* - * Make sure the LSM tree isn't deleted while we are - * reading from it. - */ - vy_lsm_ref(lsm); int rc = vy_get_by_raw_key(lsm, tx, vy_tx_read_view(tx), key, part_count, ret); - vy_lsm_unref(lsm); if (tx == &tx_autocommit) vy_tx_destroy(tx); if (rc != 0) diff --git a/src/box/vy_lsm.c b/src/box/vy_lsm.c index 51150e45fb..82631f2c32 100644 --- a/src/box/vy_lsm.c +++ b/src/box/vy_lsm.c @@ -256,7 +256,7 @@ vy_range_tree_free_cb(vy_range_tree_t *t, struct vy_range *range, void *arg) (void)arg; struct vy_slice *slice; rlist_foreach_entry(slice, &range->slices, in_range) - vy_slice_wait_pinned(slice); + assert(slice->pin_count == 0); vy_range_delete(range); return NULL; } diff --git a/test/vinyl-luatest/gh_10094_index_drop_test.lua b/test/vinyl-luatest/gh_10094_index_drop_test.lua new file mode 100644 index 0000000000..8e260148db --- /dev/null +++ b/test/vinyl-luatest/gh_10094_index_drop_test.lua @@ -0,0 +1,44 @@ +local server = require('luatest.server') +local t = require('luatest') + +local g = t.group() + +g.before_all(function(cg) + t.tarantool.skip_if_not_debug() + cg.server = server:new() + cg.server:start() + cg.server:exec(function() + box.cfg{vinyl_cache = 0} + end) +end) + +g.after_all(function(cg) + cg.server:drop() +end) + +g.after_each(function(cg) + cg.server:exec(function() + if box.space.test ~= nil then + box.space.test:drop() + end + box.error.injection.set('ERRINJ_VY_READ_PAGE_DELAY', false) + end) +end) + +g.test_index_drop = function(cg) + cg.server:exec(function() + local fiber = require('fiber') + local s = box.schema.create_space('test', {engine = 'vinyl'}) + s:create_index('pk') + s:create_index('sk', {parts = {{2, 'unsigned'}}}) + s:insert{1, 10} + box.snapshot() + box.error.injection.set('ERRINJ_VY_READ_PAGE_DELAY', true) + fiber.create(function() + s:insert{2, 10} + end) + s.index.sk:drop() + box.error.injection.set('ERRINJ_VY_READ_PAGE_DELAY', false) + t.assert_equals(s:select({}, {fullscan = true}), {{1, 10}}) + end) +end -- GitLab