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 region_truncate() will fail on assertion (on Debug build).

Despite that all found cases are specific for tarantool 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 a
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 29, 2020
1 parent 723fcb1 commit 107ad3f
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 107ad3f

Please sign in to comment.