Skip to content

Commit

Permalink
box: do not reset region on select
Browse files Browse the repository at this point in the history
A read only transaction should not invalidate the fiber region.

The problem was found in context of calling a Lua function from SQL on
tarantool 2.* (#5427).

Another problem (#5623) was fixed by this commit on tarantool 2.*. Since
2.2.0-469-g707e58a3f ('box: rework box_lua_{call, eval} to use input
port') arguments for a C stored procedure is allocated on a fiber
region. The region is truncated at the end of the call. When the
procedure performs a read only transaction, fiber_gc() may be called
and so the region_truncate() call will fail on assertion (on Debug
build).

Despite that all found cases are specific for tarantoo 2.*, it is
possible to be hit by the problem on 1.10 as well. It is enough to
allocate something on a region (explicitly or implicitly) and perform
any read only transaction.

So it looks worthful to backport the fix to 1.10 as well.

(cherry picked from commit d0d668f)

The original commit message:

 | sql: do not reset region on select
 |
 | Prior to this patch, region on fiber was reset during select(), get(),
 | count(), max(), or min(). This would result in an error if one of these
 | operations was used in a user-defined function in SQL. After this patch,
 | these functions truncate region instead of resetting it.
 |
 | Closes #5427
  • Loading branch information
ImeevMA authored and Totktonada committed Dec 28, 2020
1 parent f92bf2d commit 96916f8
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 15 deletions.
5 changes: 3 additions & 2 deletions src/box/box.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1070,7 +1070,8 @@ box_select(uint32_t space_id, uint32_t index_id,
});

struct txn *txn;
if (txn_begin_ro_stmt(space, &txn) != 0)
struct txn_ro_savepoint svp;
if (txn_begin_ro_stmt(space, &txn, &svp) != 0)
return -1;

struct iterator *it = index_create_iterator(index, type,
Expand Down Expand Up @@ -1104,7 +1105,7 @@ box_select(uint32_t space_id, uint32_t index_id,
txn_rollback_stmt();
return -1;
}
txn_commit_ro_stmt(txn);
txn_commit_ro_stmt(txn, &svp);
return 0;
}

Expand Down
25 changes: 15 additions & 10 deletions src/box/index.cc
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,14 @@ box_index_get(uint32_t space_id, uint32_t index_id, const char *key,
return -1;
/* Start transaction in the engine. */
struct txn *txn;
if (txn_begin_ro_stmt(space, &txn) != 0)
struct txn_ro_savepoint svp;
if (txn_begin_ro_stmt(space, &txn, &svp) != 0)
return -1;
if (index_get(index, key, part_count, result) != 0) {
txn_rollback_stmt();
return -1;
}
txn_commit_ro_stmt(txn);
txn_commit_ro_stmt(txn, &svp);
/* Count statistics. */
rmean_collect(rmean_box, IPROTO_SELECT, 1);
if (*result != NULL)
Expand Down Expand Up @@ -280,13 +281,14 @@ box_index_min(uint32_t space_id, uint32_t index_id, const char *key,
return -1;
/* Start transaction in the engine. */
struct txn *txn;
if (txn_begin_ro_stmt(space, &txn) != 0)
struct txn_ro_savepoint svp;
if (txn_begin_ro_stmt(space, &txn, &svp) != 0)
return -1;
if (index_min(index, key, part_count, result) != 0) {
txn_rollback_stmt();
return -1;
}
txn_commit_ro_stmt(txn);
txn_commit_ro_stmt(txn, &svp);
if (*result != NULL)
tuple_bless(*result);
return 0;
Expand All @@ -312,13 +314,14 @@ box_index_max(uint32_t space_id, uint32_t index_id, const char *key,
return -1;
/* Start transaction in the engine. */
struct txn *txn;
if (txn_begin_ro_stmt(space, &txn) != 0)
struct txn_ro_savepoint svp;
if (txn_begin_ro_stmt(space, &txn, &svp) != 0)
return -1;
if (index_max(index, key, part_count, result) != 0) {
txn_rollback_stmt();
return -1;
}
txn_commit_ro_stmt(txn);
txn_commit_ro_stmt(txn, &svp);
if (*result != NULL)
tuple_bless(*result);
return 0;
Expand All @@ -345,14 +348,15 @@ box_index_count(uint32_t space_id, uint32_t index_id, int type,
return -1;
/* Start transaction in the engine. */
struct txn *txn;
if (txn_begin_ro_stmt(space, &txn) != 0)
struct txn_ro_savepoint svp;
if (txn_begin_ro_stmt(space, &txn, &svp) != 0)
return -1;
ssize_t count = index_count(index, itype, key, part_count);
if (count < 0) {
txn_rollback_stmt();
return -1;
}
txn_commit_ro_stmt(txn);
txn_commit_ro_stmt(txn, &svp);
return count;
}

Expand Down Expand Up @@ -381,15 +385,16 @@ box_index_iterator(uint32_t space_id, uint32_t index_id, int type,
if (key_validate(index->def, itype, key, part_count))
return NULL;
struct txn *txn;
if (txn_begin_ro_stmt(space, &txn) != 0)
struct txn_ro_savepoint svp;
if (txn_begin_ro_stmt(space, &txn, &svp) != 0)
return NULL;
struct iterator *it = index_create_iterator(index, itype,
key, part_count);
if (it == NULL) {
txn_rollback_stmt();
return NULL;
}
txn_commit_ro_stmt(txn);
txn_commit_ro_stmt(txn, &svp);
rmean_collect(rmean_box, IPROTO_SELECT, 1);
return it;
}
Expand Down
23 changes: 20 additions & 3 deletions src/box/txn.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,20 @@ struct txn_savepoint {
struct stailq_entry *stmt;
};

/**
* Read-only transaction savepoint object. After completing a read-only
* transaction, we must clear the region. However, if we just reset the region,
* we may corrupt the data that was placed in the region before the read-only
* transaction began. To avoid this, we should use truncation. This structure
* contains the information required for truncation.
*/
struct txn_ro_savepoint {
/** Region used during this transaction. */
struct region *region;
/** Savepoint for region. */
size_t region_used;
};

extern double too_long_threshold;

struct txn {
Expand Down Expand Up @@ -236,24 +250,27 @@ txn_begin_in_engine(struct engine *engine, struct txn *txn);
* select.
*/
static inline int
txn_begin_ro_stmt(struct space *space, struct txn **txn)
txn_begin_ro_stmt(struct space *space, struct txn **txn,
struct txn_ro_savepoint *svp)
{
*txn = in_txn();
if (*txn != NULL) {
struct engine *engine = space->engine;
return txn_begin_in_engine(engine, *txn);
}
svp->region = &fiber()->gc;
svp->region_used = region_used(svp->region);
return 0;
}

static inline void
txn_commit_ro_stmt(struct txn *txn)
txn_commit_ro_stmt(struct txn *txn, struct txn_ro_savepoint *svp)
{
assert(txn == in_txn());
if (txn) {
/* nothing to do */
} else {
fiber_gc();
region_truncate(svp->region, svp->region_used);
}
}

Expand Down

0 comments on commit 96916f8

Please sign in to comment.