-
Notifications
You must be signed in to change notification settings - Fork 365
Description
Суть изменений: добавить ретраер над транзакцией в userver, создание транзакции без ретраера - закопать, чтобы даже не было возможности выстрелить в ногу.
Было
/// settings.hpp:
enum class TransactionMode { kSerializableRW, kOnlineRO, kStaleRO };
/// table.hpp:
class TableClient final {
public:
Transaction Begin(utils::StringLiteral transaction_name, OperationSettings settings = {});
Transaction Begin(DynamicTransactionName transaction_name, OperationSettings settings = {});
Transaction Begin(utils::StringLiteral transaction_name, TransactionMode tx_mode);
};Стало
/// settings.hpp:
enum class TransactionMode { kSerializableRW, kOnlineRO, kStaleRO, kImplicitTx };
struct RequestSettings final {
std::chrono::milliseconds timeout_ms{0};
std::string trace_id{};
};
using ExecuteSettings = RequestSettings;
using CommitSettings = RequestSettings;
using RollbackSettings = RequestSettings;
struct RetryTxSettings final {
TransactionMode tx_mode{TransactionMode::kImplicitTx};
std::chrono::milliseconds timeout_ms{0};
std::uint32_t retries{10};
bool is_idempotent{false};
CommitSettings commit_settings;
RollbackSettings rollback_settings;
};
/// transaction.hpp
class TxActor {
public:
/// Execute a single data query as a part of the transaction. Query parameters
/// are passed in `Args` as "string key - value" pairs:
///
/// @code
/// client.Execute(query, "name1", value1, "name2", value2, ...);
/// @endcode
///
/// Use ydb::PreparedArgsBuilder for storing a generic buffer of query params
/// if needed.
///
/// @{
template <typename... Args>
ExecuteResponse Execute(const Query& query, Args&&... args);
template <typename... Args>
ExecuteResponse Execute(ExecuteSettings settings, const Query& query, Args&&... args);
ExecuteResponse Execute(ExecuteSettings settings, const Query& query, PreparedArgsBuilder&& builder);
/// @}
};
enum class TxAction {
kCommit,
kRollback,
};
using RetryTxFunction = std::function<TxAction(TxActor&)>;
/// table.hpp:
class TableClient final {
public:
/// объявить deprecated, правильно создавать транзакции только через ретраер
/// @{
Transaction Begin(utils::StringLiteral transaction_name, OperationSettings settings = {});
Transaction Begin(DynamicTransactionName transaction_name, OperationSettings settings = {});
Transaction Begin(utils::StringLiteral transaction_name, TransactionMode tx_mode);
/// @}
/// @{
void RetryTx(utils::StringLiteral transaction_name, RetryTxSettings retry_settings, RetryTxFunction fn)
void RetryTx(DynamicTransactionName transaction_name, RetryTxSettings retry_settings, RetryTxFunction fn)
/// @}
};Пример:
client.RetryTx("transaction_name", {
.retries = 3,
.timeout_ms = std::chrono::seconds(10),
.tx_mode = TransactionMode::kSerializableRW,
.is_idempotent = true,
},
[](TxActor& tx) {
tx.Execute("SELECT * FROM users WHERE id = $id", "$id", 1);
return TxAction::kCommit;
}
);Теперь интерактивные транзакции будут предоставляться через RetryTx ручку. Пользователь передает всю лямбду со своей транзакцией в ретраер, и при ошибках она будет ретраится.
timeout_ms - таймаут на все ретраи вместе. В userver он будет передан в RetryQuery, который сам гарантирует его соблюдение.
TransactionMode::kImplicitTx будет добавлен после поддержки QueryService в userver, если не успеем, дефолт временно будет kSerializableRW, потом поменяем на kImplicitTx.
Метод RetryTx будет вызывать метод RetryQuery из SDK. На полученной сессии будет создана транзакция, обернута в userver'ный класс и передан пользователю. Идейно это будет выглядеть так:
void RetryTx(utils::StringLiteral transaction_name, RetryTxSettings retry_settings, RetryFunction fn) {
client.RetryQuery([&](NYdb::NQuery::TSession session) -> NYdb::TStatus {
try {
auto tx = session.BeginTransaction(retry_settings);
Transaction tx(table_client_, std::move(tx), transaction_name);
auto tx_action = fn(tx);
...
} catch (const YdbResponseError& e) {
return e.GetStatus();
}
}, PrepareRetrySettings(retry_settings));
}Пользователь в своей лямбде должен вернуть, хочет ли он сделать коммит или роллбек. После этого работа ретраера завершится. Отдельные ручки Commit и Rollback мы специально убираем.
Ошибки будут обрабатываться через исключения. Если это YdbResponseError - мы делаем (или не делаем) ретрай на основе статуса, просто передаем его в SDK'шный ретраер. Если это неизвестное исключение - завершаем работу ретраера, это ошибка в пользовательской логике.