-
Notifications
You must be signed in to change notification settings - Fork 594
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tx/group compaction fixes #24637
base: dev
Are you sure you want to change the base?
tx/group compaction fixes #24637
Changes from 1 commit
03c425e
5e8358a
00df369
b80c4d9
e247e1f
1db8d69
4f292ff
15a0d0a
796ac17
c1c904d
e5b9b46
10ea2db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -92,6 +92,46 @@ | |
] | ||
} | ||
] | ||
}, | ||
{ | ||
"path": "/v1/transaction/{group_id}/unsafe_abort_group_transaction", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: this looks a bit inconsistent, because group_id can be easily confused with transactional_id (when comparing with other endpoints) |
||
"operations": [ | ||
{ | ||
"method": "POST", | ||
"summary": "Unsafely abort a transaction from a group, only for debugging", | ||
"type": "void", | ||
"nickname": "unsafe_abort_group_transaction", | ||
"produces": [ | ||
"application/json" | ||
], | ||
"parameters": [ | ||
{ | ||
"name": "group_id", | ||
"in": "path", | ||
"required": true, | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "producer_id", | ||
"in": "query", | ||
"required": true, | ||
"type": "integer" | ||
}, | ||
{ | ||
"name": "producer_epoch", | ||
"in": "query", | ||
"required": true, | ||
"type": "integer" | ||
}, | ||
{ | ||
"name": "sequence", | ||
"in": "int", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo? should be |
||
"required": true, | ||
"type": "integer" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
], | ||
"models": { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,8 @@ | |
#include "cluster/partition_manager.h" | ||
#include "cluster/tx_gateway_frontend.h" | ||
#include "container/lw_shared_container.h" | ||
#include "kafka/server/coordinator_ntp_mapper.h" | ||
#include "kafka/server/server.h" | ||
#include "redpanda/admin/api-doc/transaction.json.hh" | ||
#include "redpanda/admin/server.h" | ||
#include "redpanda/admin/util.h" | ||
|
@@ -36,6 +38,12 @@ void admin_server::register_transaction_routes() { | |
[this](std::unique_ptr<ss::http::request> req) { | ||
return find_tx_coordinator_handler(std::move(req)); | ||
}); | ||
|
||
register_route<user>( | ||
ss::httpd::transaction_json::unsafe_abort_group_transaction, | ||
[this](std::unique_ptr<ss::http::request> req) { | ||
return unsafe_abort_group_transaction(std::move(req)); | ||
}); | ||
} | ||
|
||
ss::future<ss::json::json_return_type> | ||
|
@@ -213,3 +221,73 @@ admin_server::delete_partition_handler(std::unique_ptr<ss::http::request> req) { | |
co_await throw_on_error(*req, res, ntp); | ||
co_return ss::json::json_return_type(ss::json::json_void()); | ||
} | ||
|
||
ss::future<ss::json::json_return_type> | ||
admin_server::unsafe_abort_group_transaction( | ||
std::unique_ptr<ss::http::request> request) { | ||
if (!_tx_gateway_frontend.local_is_initialized()) { | ||
throw ss::httpd::bad_request_exception("Transaction are disabled"); | ||
} | ||
|
||
auto group_id = request->get_path_param("group_id"); | ||
auto pid_str = request->get_query_param("producer_id"); | ||
auto epoch_str = request->get_query_param("producer_epoch"); | ||
auto sequence_str = request->get_query_param("sequence"); | ||
|
||
if (group_id.empty()) { | ||
throw ss::httpd::bad_param_exception("group_id cannot be empty"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this actually happen or it is guaranteed by the router that this is not empty? nit: Anyway, the right error here would be 404. |
||
} | ||
|
||
if (pid_str.empty() || epoch_str.empty() || sequence_str.empty()) { | ||
throw ss::httpd::bad_param_exception( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should happen out of the box btw. This seems to be called implicitly when handling requests https://github.com/redpanda-data/seastar/blob/09a59a23ff2740a2fa591b0e65d978ca83d2b9e3/include/seastar/http/handlers.hh#L76 |
||
"invalid producer_id/epoch, should be >= 0"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: the error message is misaligned |
||
} | ||
|
||
std::optional<model::producer_id> pid; | ||
try { | ||
auto parsed_pid = boost::lexical_cast<model::producer_id::type>( | ||
pid_str); | ||
pid = model::producer_id{parsed_pid}; | ||
} catch (const boost::bad_lexical_cast& e) { | ||
throw ss::httpd::bad_param_exception( | ||
fmt::format("invalid producer_id, should be >= 0: {}", e)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I guess printing values should be more useful than printing the exception? |
||
} | ||
|
||
std::optional<model::producer_epoch> epoch; | ||
try { | ||
auto parsed_epoch = boost::lexical_cast<model::producer_epoch::type>( | ||
epoch_str); | ||
epoch = model::producer_epoch{parsed_epoch}; | ||
} catch (const boost::bad_lexical_cast& e) { | ||
throw ss::httpd::bad_param_exception( | ||
fmt::format("invalid producer_epoch, should be >= 0: {}", e)); | ||
} | ||
|
||
std::optional<model::tx_seq> seq; | ||
try { | ||
auto parsed_seq = boost::lexical_cast<model::tx_seq::type>( | ||
sequence_str); | ||
seq = model::tx_seq{parsed_seq}; | ||
} catch (const boost::bad_lexical_cast& e) { | ||
throw ss::httpd::bad_param_exception( | ||
fmt::format("invalid transaction sequence, should be >= 0: {}", e)); | ||
} | ||
|
||
auto& mapper = _kafka_server.local().coordinator_mapper(); | ||
auto kafka_gid = kafka::group_id{group_id}; | ||
auto group_ntp = mapper.ntp_for(kafka::group_id{group_id}); | ||
if (!group_ntp) { | ||
throw ss::httpd::server_error_exception( | ||
"consumer_offsets topic now found"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo now -> not |
||
} | ||
|
||
auto result | ||
= co_await _tx_gateway_frontend.local().unsafe_abort_group_transaction( | ||
std::move(kafka_gid), | ||
model::producer_identity{pid.value(), epoch.value()}, | ||
seq.value(), | ||
5s); | ||
|
||
co_await throw_on_error(*request, result, group_ntp.value()); | ||
co_return ss::json::json_return_type(ss::json::json_void()); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1849,3 +1849,15 @@ def get_debug_bundle_file(self, filename: str, node: MaybeNode = None): | |
def delete_debug_bundle_file(self, filename: str, node: MaybeNode = None): | ||
path = f"debug/bundle/file/{filename}" | ||
return self._request("DELETE", path, node=node) | ||
|
||
def unsafe_abort_group_transaction(self, group_id: str, pid: int, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: |
||
epoch: int, sequence: int): | ||
params = { | ||
"producer_id": pid, | ||
"producer_epoch": epoch, | ||
"sequence": sequence, | ||
} | ||
params = "&".join([f"{k}={v}" for k, v in params.items()]) | ||
return self._request( | ||
'POST', | ||
f"transaction/{group_id}/unsafe_abort_group_transaction?{params}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add a comment describing what is "unsafe" about this operation? what invariants will break? what semantic behavior breaks?