[4.1] add flag to protect batch update
This commit is contained in:
@ -1019,6 +1019,9 @@ int ObDMLService::init_dml_param(const ObDASDMLBaseCtDef &base_ctdef,
|
|||||||
dml_param.is_batch_stmt_ = base_ctdef.is_batch_stmt_;
|
dml_param.is_batch_stmt_ = base_ctdef.is_batch_stmt_;
|
||||||
dml_param.dml_allocator_ = &das_alloc;
|
dml_param.dml_allocator_ = &das_alloc;
|
||||||
dml_param.snapshot_ = snapshot;
|
dml_param.snapshot_ = snapshot;
|
||||||
|
if (base_ctdef.is_batch_stmt_) {
|
||||||
|
dml_param.write_flag_.set_is_dml_batch_opt();
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -53,74 +53,92 @@ int check_sequence_set_violation(const concurrent_control::ObWriteFlag write_fla
|
|||||||
// example, add the lob flag for the case 2.1 to prevent the other scenes from
|
// example, add the lob flag for the case 2.1 to prevent the other scenes from
|
||||||
// happening.
|
// happening.
|
||||||
|
|
||||||
if (writer_tx_id == locker_tx_id
|
if (writer_tx_id == locker_tx_id) {
|
||||||
// For statements during sql and threads during PDML, the following rules is
|
// We need guarantee the right sequence of the same txn operations
|
||||||
// guaranteed:
|
if (writer_seq_no < locker_seq_no) {
|
||||||
// 1. reader seq no is bigger or equal than the seq no of the last statements
|
|
||||||
&& reader_seq_no < locker_seq_no) {
|
|
||||||
// Case 1: It may happens that two pdml unique index tasks insert the same
|
|
||||||
// row concurrently, so we report duplicate key under the case to prevent
|
|
||||||
// the insertion.
|
|
||||||
if (blocksstable::ObDmlFlag::DF_INSERT == writer_dml_flag
|
|
||||||
&& blocksstable::ObDmlFlag::DF_INSERT == locker_dml_flag) {
|
|
||||||
ret = OB_ERR_PRIMARY_KEY_DUPLICATE;
|
|
||||||
TRANS_LOG(WARN, "pdml duplicate primary key found", K(ret),
|
|
||||||
K(writer_tx_id), K(writer_dml_flag), K(writer_seq_no),
|
|
||||||
K(locker_tx_id), K(locker_dml_flag), K(locker_seq_no));
|
|
||||||
// Case 2.1: For the case of the update in the storage layer, it may be
|
|
||||||
// split into lock and update in a single statement and fail the check, so
|
|
||||||
// we need bypass this case(Currently only the update of the lob will cause
|
|
||||||
// it). We use the common idea that all operations split in the storage
|
|
||||||
// layer will use same sequence number, so we bypass the check if the writer
|
|
||||||
// sequence number is equal to the locker sequence number.
|
|
||||||
// } else if (writer_seq_no == locker_seq_no &&
|
|
||||||
// (blocksstable::ObDmlFlag::DF_UPDATE == writer_dml_flag
|
|
||||||
// && blocksstable::ObDmlFlag::DF_LOCK == locker_dml_flag)) {
|
|
||||||
//
|
|
||||||
// Case 2.2: For the case of the self reference of the foreign key, it may
|
|
||||||
// be split into lock and insert/update in a single statement, so we need
|
|
||||||
// bypass this case(TODO(handora.qc): remove the requirement after yichang's
|
|
||||||
// modification).
|
|
||||||
} else if (blocksstable::ObDmlFlag::DF_LOCK == locker_dml_flag) {
|
|
||||||
// bypass the case
|
|
||||||
// Case 3: For the case of the update of the primary key in the sql layer,
|
|
||||||
// it will be split into a delete of the old rowkey and an insert for the
|
|
||||||
// new one which will fail to pass the check. And the two operations may
|
|
||||||
// even be split into two sequentially ordered steps to follow the sql-layer
|
|
||||||
// semantics, so we need bypass this case.
|
|
||||||
} else if (blocksstable::ObDmlFlag::DF_INSERT == writer_dml_flag
|
|
||||||
&& blocksstable::ObDmlFlag::DF_DELETE == locker_dml_flag) {
|
|
||||||
// bypass the case
|
|
||||||
// Case 4: For the case of the insert of two same rowkeys with insert onto
|
|
||||||
// duplicate, it will be split into a insert of the rowkey and an update or
|
|
||||||
// delete of the same one which will fail to pass the check. And the two
|
|
||||||
// operations is split into two sequentially ordered steps so we need bypass
|
|
||||||
// this case.
|
|
||||||
} else if ((blocksstable::ObDmlFlag::DF_UPDATE == writer_dml_flag
|
|
||||||
|| blocksstable::ObDmlFlag::DF_DELETE == writer_dml_flag)
|
|
||||||
&& blocksstable::ObDmlFlag::DF_INSERT == locker_dml_flag) {
|
|
||||||
// bypass the case
|
|
||||||
// Case 5: For the case of table api, it inserts rows under the same stmt,
|
|
||||||
// and so fail to pass the check. We must bypass the case.
|
|
||||||
} else if (write_flag.is_table_api()) {
|
|
||||||
// bypass the case
|
|
||||||
// Case 6: For the case of deleting rows during building the unique index
|
|
||||||
// concurrently, it may exist that two rows of the main table point to one
|
|
||||||
// row of the newly created index, which means the unique index will abort
|
|
||||||
// itself during consistency check. While because of the feature of the
|
|
||||||
// online ddl, the concurrent delete will start to operate on the newly
|
|
||||||
// created index, which causes these two delete operations and fail to pass
|
|
||||||
// the check. So we need bypass this case.
|
|
||||||
} else if (blocksstable::ObDmlFlag::DF_DELETE == writer_dml_flag
|
|
||||||
&& blocksstable::ObDmlFlag::DF_DELETE == locker_dml_flag) {
|
|
||||||
// bypass the case
|
|
||||||
} else {
|
|
||||||
// Case 7: It will never happen that two operaions on the same row for the
|
|
||||||
// same txn except the above cases. So we should report unexpected error.
|
|
||||||
ret = OB_ERR_UNEXPECTED;
|
ret = OB_ERR_UNEXPECTED;
|
||||||
TRANS_LOG(ERROR, "multiple modification on one row found", K(reader_seq_no),
|
TRANS_LOG(ERROR, "wrong row of sequence on one row found", K(reader_seq_no),
|
||||||
K(writer_tx_id), K(writer_dml_flag), K(writer_seq_no),
|
K(writer_tx_id), K(writer_dml_flag), K(writer_seq_no),
|
||||||
K(locker_tx_id), K(locker_dml_flag), K(locker_seq_no));
|
K(locker_tx_id), K(locker_dml_flag), K(locker_seq_no));
|
||||||
|
// For statements during sql and threads during PDML, the following rules is
|
||||||
|
// guaranteed:
|
||||||
|
// 1. reader seq no is bigger or equal than the seq no of the last statements
|
||||||
|
} else if (reader_seq_no < locker_seq_no) {
|
||||||
|
// Case 1: It may happens that two pdml unique index tasks insert the same
|
||||||
|
// row concurrently, so we report duplicate key under the case to prevent
|
||||||
|
// the insertion.
|
||||||
|
if (blocksstable::ObDmlFlag::DF_INSERT == writer_dml_flag
|
||||||
|
&& blocksstable::ObDmlFlag::DF_INSERT == locker_dml_flag) {
|
||||||
|
ret = OB_ERR_PRIMARY_KEY_DUPLICATE;
|
||||||
|
TRANS_LOG(WARN, "pdml duplicate primary key found", K(ret),
|
||||||
|
K(writer_tx_id), K(writer_dml_flag), K(writer_seq_no),
|
||||||
|
K(locker_tx_id), K(locker_dml_flag), K(locker_seq_no));
|
||||||
|
// Case 2.1: For the case of the update in the storage layer, it may be
|
||||||
|
// split into lock and update in a single statement and fail the check, so
|
||||||
|
// we need bypass this case(Currently only the update of the lob will cause
|
||||||
|
// it). We use the common idea that all operations split in the storage
|
||||||
|
// layer will use same sequence number, so we bypass the check if the writer
|
||||||
|
// sequence number is equal to the locker sequence number.
|
||||||
|
// } else if (writer_seq_no == locker_seq_no &&
|
||||||
|
// (blocksstable::ObDmlFlag::DF_UPDATE == writer_dml_flag
|
||||||
|
// && blocksstable::ObDmlFlag::DF_LOCK == locker_dml_flag)) {
|
||||||
|
//
|
||||||
|
// Case 2.2: For the case of the self reference of the foreign key, it may
|
||||||
|
// be split into lock and insert/update in a single statement, so we need
|
||||||
|
// bypass this case(TODO(handora.qc): remove the requirement after yichang's
|
||||||
|
// modification).
|
||||||
|
} else if (blocksstable::ObDmlFlag::DF_LOCK == locker_dml_flag) {
|
||||||
|
// bypass the case
|
||||||
|
// Case 3: For the case of the update of the primary key in the sql layer,
|
||||||
|
// it will be split into a delete of the old rowkey and an insert for the
|
||||||
|
// new one which will fail to pass the check. And the two operations may
|
||||||
|
// even be split into two sequentially ordered steps to follow the sql-layer
|
||||||
|
// semantics, so we need bypass this case.
|
||||||
|
} else if (blocksstable::ObDmlFlag::DF_INSERT == writer_dml_flag
|
||||||
|
&& blocksstable::ObDmlFlag::DF_DELETE == locker_dml_flag) {
|
||||||
|
// bypass the case
|
||||||
|
// Case 4: For the case of the insert of two same rowkeys with insert onto
|
||||||
|
// duplicate, it will be split into a insert of the rowkey and an update or
|
||||||
|
// delete of the same one which will fail to pass the check. And the two
|
||||||
|
// operations is split into two sequentially ordered steps so we need bypass
|
||||||
|
// this case.
|
||||||
|
} else if ((blocksstable::ObDmlFlag::DF_UPDATE == writer_dml_flag
|
||||||
|
|| blocksstable::ObDmlFlag::DF_DELETE == writer_dml_flag)
|
||||||
|
&& blocksstable::ObDmlFlag::DF_INSERT == locker_dml_flag) {
|
||||||
|
// bypass the case
|
||||||
|
// Case 5: For the case of table api, it inserts rows under the same stmt,
|
||||||
|
// and so fail to pass the check. We must bypass the case.
|
||||||
|
} else if (write_flag.is_table_api()) {
|
||||||
|
// bypass the case
|
||||||
|
// Case 6: For the case of deleting rows during building the unique index
|
||||||
|
// concurrently, it may exist that two rows of the main table point to one
|
||||||
|
// row of the newly created index, which means the unique index will abort
|
||||||
|
// itself during consistency check. While because of the feature of the
|
||||||
|
// online ddl, the concurrent delete will start to operate on the newly
|
||||||
|
// created index, which causes these two delete operations and fail to pass
|
||||||
|
// the check. So we need bypass this case.
|
||||||
|
} else if (blocksstable::ObDmlFlag::DF_DELETE == writer_dml_flag
|
||||||
|
&& blocksstable::ObDmlFlag::DF_DELETE == locker_dml_flag) {
|
||||||
|
// bypass the case
|
||||||
|
// Case 7: For the case of batch dml operation, it may operate the same row
|
||||||
|
// concurrently if the first operation has no effects.(SQL layer will check
|
||||||
|
// the modification of the row before the second operation, and report the
|
||||||
|
// error if the row has been modified while the first row may have no effect
|
||||||
|
// and the parallel insert may happen). So we need report the batched stmt
|
||||||
|
// warning according to this case.
|
||||||
|
} else if (write_flag.is_dml_batch_opt()) {
|
||||||
|
ret = OB_BATCHED_MULTI_STMT_ROLLBACK;
|
||||||
|
TRANS_LOG(WARN, "batch multi stmt rollback found", K(ret),
|
||||||
|
K(writer_tx_id), K(writer_dml_flag), K(writer_seq_no),
|
||||||
|
K(locker_tx_id), K(locker_dml_flag), K(locker_seq_no));
|
||||||
|
} else {
|
||||||
|
// Others: It will never happen that two operaions on the same row for the
|
||||||
|
// same txn except the above cases. So we should report unexpected error.
|
||||||
|
ret = OB_ERR_UNEXPECTED;
|
||||||
|
TRANS_LOG(ERROR, "multiple modification on one row found", K(reader_seq_no),
|
||||||
|
K(writer_tx_id), K(writer_dml_flag), K(writer_seq_no),
|
||||||
|
K(locker_tx_id), K(locker_dml_flag), K(locker_seq_no));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,11 +27,13 @@ struct ObWriteFlag
|
|||||||
#define OBWF_BIT_TABLE_API 1
|
#define OBWF_BIT_TABLE_API 1
|
||||||
#define OBWF_BIT_TABLE_LOCK 1
|
#define OBWF_BIT_TABLE_LOCK 1
|
||||||
#define OBWF_BIT_MDS 1
|
#define OBWF_BIT_MDS 1
|
||||||
|
#define OBWF_BIT_DML_BATCH_OPT 1
|
||||||
#define OBWF_BIT_RESERVED 61
|
#define OBWF_BIT_RESERVED 61
|
||||||
|
|
||||||
static const uint64_t OBWF_MASK_TABLE_API = (0x1UL << OBWF_BIT_TABLE_API) - 1;
|
static const uint64_t OBWF_MASK_TABLE_API = (0x1UL << OBWF_BIT_TABLE_API) - 1;
|
||||||
static const uint64_t OBWF_MASK_TABLE_LOCK = (0x1UL << OBWF_BIT_TABLE_LOCK) - 1;
|
static const uint64_t OBWF_MASK_TABLE_LOCK = (0x1UL << OBWF_BIT_TABLE_LOCK) - 1;
|
||||||
static const uint64_t OBWF_MASK_MDS = (0x1UL << OBWF_BIT_MDS) - 1;
|
static const uint64_t OBWF_MASK_MDS = (0x1UL << OBWF_BIT_MDS) - 1;
|
||||||
|
static const uint64_t OBWF_MASK_DML_BATCH_OPT = (0x1UL << OBWF_BIT_DML_BATCH_OPT) - 1;
|
||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
@ -41,6 +43,7 @@ struct ObWriteFlag
|
|||||||
uint64_t is_table_api_ : OBWF_BIT_TABLE_API; // 0: false(default), 1: true
|
uint64_t is_table_api_ : OBWF_BIT_TABLE_API; // 0: false(default), 1: true
|
||||||
uint64_t is_table_lock_ : OBWF_BIT_TABLE_LOCK; // 0: false(default), 1: true
|
uint64_t is_table_lock_ : OBWF_BIT_TABLE_LOCK; // 0: false(default), 1: true
|
||||||
uint64_t is_mds_ : OBWF_BIT_MDS; // 0: false(default), 1: true
|
uint64_t is_mds_ : OBWF_BIT_MDS; // 0: false(default), 1: true
|
||||||
|
uint64_t is_dml_batch_opt_ : OBWF_BIT_DML_BATCH_OPT; // 0: false(default), 1: true
|
||||||
uint64_t reserved_ : OBWF_BIT_RESERVED;
|
uint64_t reserved_ : OBWF_BIT_RESERVED;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -53,10 +56,13 @@ struct ObWriteFlag
|
|||||||
inline void set_is_table_lock() { is_table_lock_ = true; }
|
inline void set_is_table_lock() { is_table_lock_ = true; }
|
||||||
inline bool is_mds() const { return is_mds_; }
|
inline bool is_mds() const { return is_mds_; }
|
||||||
inline void set_is_mds() { is_mds_ = true; }
|
inline void set_is_mds() { is_mds_ = true; }
|
||||||
|
inline bool is_dml_batch_opt() const { return is_dml_batch_opt_; }
|
||||||
|
inline void set_is_dml_batch_opt() { is_dml_batch_opt_ = true; }
|
||||||
|
|
||||||
TO_STRING_KV("is_table_api", is_table_api_,
|
TO_STRING_KV("is_table_api", is_table_api_,
|
||||||
"is_table_lock", is_table_lock_,
|
"is_table_lock", is_table_lock_,
|
||||||
"is_mds", is_mds_);
|
"is_mds", is_mds_,
|
||||||
|
"is_dml_batch_opt", is_dml_batch_opt_);
|
||||||
|
|
||||||
OB_UNIS_VERSION(1);
|
OB_UNIS_VERSION(1);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user