diff --git a/src/box/lua/xlog.c b/src/box/lua/xlog.c index bd30187ae96790ac690cf8a90c77f3e4143cf2d9..971a26afedf9aa4c7a86009797b09f6f15564544 100644 --- a/src/box/lua/xlog.c +++ b/src/box/lua/xlog.c @@ -221,15 +221,19 @@ lbox_xlog_parser_iterate(struct lua_State *L) lua_pushnumber(L, row.tm); lua_settable(L, -3); /* timestamp */ } - if (row.txn_id != row.lsn || !row.is_commit) { - lua_pushstring(L, "txn_id"); - lua_pushnumber(L, row.txn_id); - lua_settable(L, -3); /* txn_id */ + if (row.tsn != row.lsn || !row.is_commit) { + lua_pushstring(L, "tsn"); + lua_pushnumber(L, row.tsn); + lua_settable(L, -3); /* transaction identifier */ } - if (row.is_commit && row.txn_id != row.lsn) { + if (row.is_commit && row.tsn != row.lsn) { lua_pushstring(L, "commit"); lua_pushboolean(L, true); - lua_settable(L, -3); /* txn_commit flag */ + /* + * is_commit, set for last row in multi-statement + * transaction + */ + lua_settable(L, -3); } lua_settable(L, -3); /* HEADER */ diff --git a/src/box/txn.c b/src/box/txn.c index 7f4e85b4786a9bf6d1eac36fab7870d4743ea15e..7900fb3ab2b6d2598ef976deb234b0d60e2dbdf1 100644 --- a/src/box/txn.c +++ b/src/box/txn.c @@ -131,7 +131,7 @@ txn_rollback_to_svp(struct txn *txn, struct stailq_entry *svp) struct txn * txn_begin(bool is_autocommit) { - static int64_t txn_id = 0; + static int64_t tsn = 0; assert(! in_txn()); struct txn *txn = region_alloc_object(&fiber()->gc, struct txn); if (txn == NULL) { @@ -145,7 +145,7 @@ txn_begin(bool is_autocommit) txn->has_triggers = false; txn->is_aborted = false; txn->in_sub_stmt = 0; - txn->id = ++txn_id; + txn->id = ++tsn; txn->signature = -1; txn->engine = NULL; txn->engine_tx = NULL; diff --git a/src/box/wal.c b/src/box/wal.c index 235a6f84099ca81f1a3593e18d9c67d7f94e271b..3faad9c3d2859e087b14c54afb336c85fc0f564b 100644 --- a/src/box/wal.c +++ b/src/box/wal.c @@ -895,16 +895,16 @@ wal_assign_lsn(struct vclock *vclock_diff, struct vclock *base, struct xrow_header **row, struct xrow_header **end) { - int64_t txn_id = 0; + int64_t tsn = 0; /** Assign LSN to all local rows. */ for ( ; row < end; row++) { if ((*row)->replica_id == 0) { (*row)->lsn = vclock_inc(vclock_diff, instance_id) + vclock_get(base, instance_id); (*row)->replica_id = instance_id; - /* Use the lsn of the first local row as txn_id. */ - txn_id = txn_id == 0 ? (*row)->lsn : txn_id; - (*row)->txn_id = txn_id; + /* Use lsn of the first local row as transaction id. */ + tsn = tsn == 0 ? (*row)->lsn : tsn; + (*row)->tsn = tsn; (*row)->is_commit = row == end - 1; } else { vclock_follow(vclock_diff, (*row)->replica_id, diff --git a/src/box/xrow.c b/src/box/xrow.c index 46e7c7b63accbd2e456ff1c072098bddadffd17e..107009c90a76a6450dc63826e089a3807616fdd4 100644 --- a/src/box/xrow.c +++ b/src/box/xrow.c @@ -137,7 +137,7 @@ xrow_header_decode(struct xrow_header *header, const char **pos, break; case IPROTO_TSN: has_tsn = true; - header->txn_id = mp_decode_uint(pos); + header->tsn = mp_decode_uint(pos); break; case IPROTO_FLAGS: flags = mp_decode_uint(pos); @@ -157,7 +157,7 @@ xrow_header_decode(struct xrow_header *header, const char **pos, header->is_commit = true; } /* Restore transaction id from lsn and transaction serial number. */ - header->txn_id = header->lsn - header->txn_id; + header->tsn = header->lsn - header->tsn; /* Nop requests aren't supposed to have a body. */ if (*pos < end && header->type != IPROTO_NOP) { @@ -243,8 +243,21 @@ xrow_header_encode(const struct xrow_header *header, uint64_t sync, d = mp_encode_double(d, header->tm); map_size++; } - if (header->txn_id != 0) { - if (header->txn_id != header->lsn || !header->is_commit) { + /* + * We do not encode tsn and is_commit flags for + * single-statement transactions to save space in the + * binary log. We also encode tsn as a diff from lsn + * to save space in every multi-statement transaction row. + * The rules when encoding are simple: + * - if tsn is *not* encoded, it's a single-statement + * transaction, tsn = lsn, is_commit = true + * - if tsn is present, it's a multi-statement + * transaction, tsn = tsn + lsn, check is_commit + * flag to find transaction boundary (last row in the + * transaction stream). + */ + if (header->tsn != 0) { + if (header->tsn != header->lsn || !header->is_commit) { /* * Encode a transaction identifier for multi row * transaction members. @@ -254,10 +267,10 @@ xrow_header_encode(const struct xrow_header *header, uint64_t sync, * Differential encoding: write a transaction serial * number (it is equal to lsn - transaction id) instead. */ - d = mp_encode_uint(d, header->lsn - header->txn_id); + d = mp_encode_uint(d, header->lsn - header->tsn); map_size++; } - if (header->is_commit && header->txn_id != header->lsn) { + if (header->is_commit && header->tsn != header->lsn) { /* Setup last row for multi row transaction. */ d = mp_encode_uint(d, IPROTO_FLAGS); d = mp_encode_uint(d, IPROTO_FLAG_COMMIT); diff --git a/src/box/xrow.h b/src/box/xrow.h index ccd57f8b2c45d146b61f6df50ff541f942fa4544..e37da1e0faca7536c5eaa3715a3040bc643fe39f 100644 --- a/src/box/xrow.h +++ b/src/box/xrow.h @@ -59,11 +59,32 @@ struct xrow_header { uint32_t type; uint32_t replica_id; + /** + * Replication group identifier. 0 - replicaset, + * 1 - replica-local. + */ uint32_t group_id; uint64_t sync; - int64_t lsn; /* LSN must be signed for correct comparison */ + /** Log sequence number. + * LSN must be signed for correct comparison + */ + int64_t lsn; + /** Timestamp. Used only when writing to the write ahead + * log. + */ double tm; - int64_t txn_id; + /* + * Transaction identifier. LSN of the first row in the + * transaction. + */ + int64_t tsn; + /** + * True for the last row in a multi-statement transaction, + * or single-statement transaction. Is only encoded in the + * write ahead log for multi-statement transactions. + * Single-statement transactions do not encode + * tsn and is_commit flag to save space. + */ bool is_commit; int bodycnt; diff --git a/test/unit/xrow.cc b/test/unit/xrow.cc index e0e7f1279b603808be68e2bb358dde95728dbb81..0a202cceaa3f925ec9fe829f948912cd6ec9fc1d 100644 --- a/test/unit/xrow.cc +++ b/test/unit/xrow.cc @@ -215,7 +215,7 @@ test_xrow_header_encode_decode() header.lsn = 400; header.tm = 123.456; header.bodycnt = 0; - header.txn_id = header.lsn; + header.tsn = header.lsn; header.is_commit = true; uint64_t sync = 100500; struct iovec vec[1]; diff --git a/test/xlog/transaction.result b/test/xlog/transaction.result index d2d9053ae610f152fb22c5d3feee6710fce8abd8..63adb0f25ea101648d9cb893064150d271c73751 100644 --- a/test/xlog/transaction.result +++ b/test/xlog/transaction.result @@ -69,37 +69,37 @@ data = read_xlog(fio.pathjoin(box.cfg.wal_dir, string.rep('0', 20 - #lsn_str) .. --- ... -- check nothing changed for single row transactions -data[1].HEADER.txn_id == nil and data[1].HEADER.commit == nil +data[1].HEADER.tsn == nil and data[1].HEADER.commit == nil --- - true ... -data[2].HEADER.txn_id == nil and data[2].HEADER.commit == nil +data[2].HEADER.tsn == nil and data[2].HEADER.commit == nil --- - true ... -- check two row transaction -data[3].HEADER.txn_id == data[3].HEADER.lsn and data[3].HEADER.commit == nil +data[3].HEADER.tsn == data[3].HEADER.lsn and data[3].HEADER.commit == nil --- - true ... -data[4].HEADER.txn_id == data[3].HEADER.txn_id and data[4].HEADER.commit == true +data[4].HEADER.tsn == data[3].HEADER.tsn and data[4].HEADER.commit == true --- - true ... -- check four row transaction -data[5].HEADER.txn_id == data[5].HEADER.lsn and data[5].HEADER.commit == nil +data[5].HEADER.tsn == data[5].HEADER.lsn and data[5].HEADER.commit == nil --- - true ... -data[6].HEADER.txn_id == data[5].HEADER.txn_id and data[6].HEADER.commit == nil +data[6].HEADER.tsn == data[5].HEADER.tsn and data[6].HEADER.commit == nil --- - true ... -data[7].HEADER.txn_id == data[5].HEADER.txn_id and data[7].HEADER.commit == nil +data[7].HEADER.tsn == data[5].HEADER.tsn and data[7].HEADER.commit == nil --- - true ... -data[8].HEADER.txn_id == data[5].HEADER.txn_id and data[8].HEADER.commit == true +data[8].HEADER.tsn == data[5].HEADER.tsn and data[8].HEADER.commit == true --- - true ... diff --git a/test/xlog/transaction.test.lua b/test/xlog/transaction.test.lua index 062635098d64b1f480839a49ca97f958cc8b1e3d..2d8090b4c59c9f107c37006af465e09e49c9c95b 100644 --- a/test/xlog/transaction.test.lua +++ b/test/xlog/transaction.test.lua @@ -33,14 +33,14 @@ box.snapshot() lsn_str = tostring(lsn) data = read_xlog(fio.pathjoin(box.cfg.wal_dir, string.rep('0', 20 - #lsn_str) .. tostring(lsn_str) .. '.xlog')) -- check nothing changed for single row transactions -data[1].HEADER.txn_id == nil and data[1].HEADER.commit == nil -data[2].HEADER.txn_id == nil and data[2].HEADER.commit == nil +data[1].HEADER.tsn == nil and data[1].HEADER.commit == nil +data[2].HEADER.tsn == nil and data[2].HEADER.commit == nil -- check two row transaction -data[3].HEADER.txn_id == data[3].HEADER.lsn and data[3].HEADER.commit == nil -data[4].HEADER.txn_id == data[3].HEADER.txn_id and data[4].HEADER.commit == true +data[3].HEADER.tsn == data[3].HEADER.lsn and data[3].HEADER.commit == nil +data[4].HEADER.tsn == data[3].HEADER.tsn and data[4].HEADER.commit == true -- check four row transaction -data[5].HEADER.txn_id == data[5].HEADER.lsn and data[5].HEADER.commit == nil -data[6].HEADER.txn_id == data[5].HEADER.txn_id and data[6].HEADER.commit == nil -data[7].HEADER.txn_id == data[5].HEADER.txn_id and data[7].HEADER.commit == nil -data[8].HEADER.txn_id == data[5].HEADER.txn_id and data[8].HEADER.commit == true +data[5].HEADER.tsn == data[5].HEADER.lsn and data[5].HEADER.commit == nil +data[6].HEADER.tsn == data[5].HEADER.tsn and data[6].HEADER.commit == nil +data[7].HEADER.tsn == data[5].HEADER.tsn and data[7].HEADER.commit == nil +data[8].HEADER.tsn == data[5].HEADER.tsn and data[8].HEADER.commit == true box.space.test:drop()