From 02d59a0529ef2812b3e1167427d180a88a73b496 Mon Sep 17 00:00:00 2001 From: Marcin Maliszkiewicz Date: Fri, 23 Jan 2026 16:33:31 +0100 Subject: [PATCH 1/4] audit: add batch bool to audit_info class In the following commit we'll use this field instead of costly dynamic_cast when emitting audit log. --- audit/audit.cc | 4 ++-- audit/audit.hh | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/audit/audit.cc b/audit/audit.cc index e455fee910..10d7f5d047 100644 --- a/audit/audit.cc +++ b/audit/audit.cc @@ -209,11 +209,11 @@ future<> audit::stop_audit() { }); } -audit_info_ptr audit::create_audit_info(statement_category cat, const sstring& keyspace, const sstring& table) { +audit_info_ptr audit::create_audit_info(statement_category cat, const sstring& keyspace, const sstring& table, bool batch) { if (!audit_instance().local_is_initialized()) { return nullptr; } - return std::make_unique(cat, keyspace, table); + return std::make_unique(cat, keyspace, table, batch); } audit_info_ptr audit::create_no_audit_info() { diff --git a/audit/audit.hh b/audit/audit.hh index 51c2c13344..29cfee46cd 100644 --- a/audit/audit.hh +++ b/audit/audit.hh @@ -75,11 +75,13 @@ class audit_info final { sstring _keyspace; sstring _table; sstring _query; + bool _batch; public: - audit_info(statement_category cat, sstring keyspace, sstring table) + audit_info(statement_category cat, sstring keyspace, sstring table, bool batch) : _category(cat) , _keyspace(std::move(keyspace)) , _table(std::move(table)) + , _batch(batch) { } void set_query_string(const std::string_view& query_string) { _query = sstring(query_string); @@ -89,6 +91,7 @@ public: const sstring& query() const { return _query; } sstring category_string() const; statement_category category() const { return _category; } + bool batch() const { return _batch; } }; using audit_info_ptr = std::unique_ptr; @@ -126,8 +129,8 @@ public: } static future<> start_audit(const db::config& cfg, sharded& stm, sharded& qp, sharded& mm); static future<> stop_audit(); - static audit_info_ptr create_audit_info(statement_category cat, const sstring& keyspace, const sstring& table); static audit_info_ptr create_no_audit_info(); + static audit_info_ptr create_audit_info(statement_category cat, const sstring& keyspace, const sstring& table, bool batch = false); audit(locator::shared_token_metadata& stm, cql3::query_processor& qp, service::migration_manager& mm, From a93ad3838f6e8cda7293d381f40673d01e01cc22 Mon Sep 17 00:00:00 2001 From: Marcin Maliszkiewicz Date: Fri, 23 Jan 2026 16:39:45 +0100 Subject: [PATCH 2/4] audit: cql: remove create_no_audit_info We don't need a special guard value, it's only being filled for batch statements for which we can simply ignore the value. Not having special value allows us to return fast when audit is not enabled. --- audit/audit.cc | 13 ++++++------- audit/audit.hh | 1 - cql3/statements/raw/batch_statement.hh | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/audit/audit.cc b/audit/audit.cc index 10d7f5d047..b49ef6cd59 100644 --- a/audit/audit.cc +++ b/audit/audit.cc @@ -216,10 +216,6 @@ audit_info_ptr audit::create_audit_info(statement_category cat, const sstring& k return std::make_unique(cat, keyspace, table, batch); } -audit_info_ptr audit::create_no_audit_info() { - return audit_info_ptr(); -} - future<> audit::start(const db::config& cfg) { return _storage_helper_ptr->start(cfg); } @@ -267,18 +263,21 @@ future<> audit::log_login(const sstring& username, socket_address client_ip, boo } future<> inspect(shared_ptr statement, service::query_state& query_state, const cql3::query_options& options, bool error) { + auto audit_info = statement->get_audit_info(); + if (!audit_info) { + return make_ready_future<>(); + } cql3::statements::batch_statement* batch = dynamic_cast(statement.get()); if (batch != nullptr) { return do_for_each(batch->statements().begin(), batch->statements().end(), [&query_state, &options, error] (auto&& m) { return inspect(m.statement, query_state, options, error); }); } else { - auto audit_info = statement->get_audit_info(); - if (bool(audit_info) && audit::local_audit_instance().should_log(audit_info)) { + if (audit::local_audit_instance().should_log(audit_info)) { return audit::local_audit_instance().log(audit_info, query_state, options, error); } + return make_ready_future<>(); } - return make_ready_future<>(); } future<> inspect_login(const sstring& username, socket_address client_ip, bool error) { diff --git a/audit/audit.hh b/audit/audit.hh index 29cfee46cd..5e24bccfaf 100644 --- a/audit/audit.hh +++ b/audit/audit.hh @@ -129,7 +129,6 @@ public: } static future<> start_audit(const db::config& cfg, sharded& stm, sharded& qp, sharded& mm); static future<> stop_audit(); - static audit_info_ptr create_no_audit_info(); static audit_info_ptr create_audit_info(statement_category cat, const sstring& keyspace, const sstring& table, bool batch = false); audit(locator::shared_token_metadata& stm, cql3::query_processor& qp, diff --git a/cql3/statements/raw/batch_statement.hh b/cql3/statements/raw/batch_statement.hh index cf2f0b83a0..1091d15ccd 100644 --- a/cql3/statements/raw/batch_statement.hh +++ b/cql3/statements/raw/batch_statement.hh @@ -50,8 +50,8 @@ public: protected: virtual audit::statement_category category() const override; virtual audit::audit_info_ptr audit_info() const override { - // We don't audit batch statements. Instead we audit statements that are inside the batch. - return audit::audit::create_no_audit_info(); + constexpr bool batch = true; + return audit::audit::create_audit_info(category(), sstring(), sstring(), batch); } }; From 6f32290756255da5e3f9cf7eb4e09122424edb43 Mon Sep 17 00:00:00 2001 From: Marcin Maliszkiewicz Date: Fri, 23 Jan 2026 17:03:22 +0100 Subject: [PATCH 3/4] audit: eliminate dynamic_cast to batch_statement in inspect This is costly and not needed we can use a simple bool flag for such check. It burns around 300 cpu instructions on a hot request's path. --- audit/audit.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audit/audit.cc b/audit/audit.cc index b49ef6cd59..7699939df5 100644 --- a/audit/audit.cc +++ b/audit/audit.cc @@ -267,8 +267,8 @@ future<> inspect(shared_ptr statement, service::query_state if (!audit_info) { return make_ready_future<>(); } - cql3::statements::batch_statement* batch = dynamic_cast(statement.get()); - if (batch != nullptr) { + if (audit_info->batch()) { + cql3::statements::batch_statement* batch = dynamic_cast(statement.get()); return do_for_each(batch->statements().begin(), batch->statements().end(), [&query_state, &options, error] (auto&& m) { return inspect(m.statement, query_state, options, error); }); From 19af46d83a582073c6620af5a58c02e4f691145a Mon Sep 17 00:00:00 2001 From: Marcin Maliszkiewicz Date: Mon, 26 Jan 2026 18:14:37 +0100 Subject: [PATCH 4/4] audit: replace batch dynamic_cast with static_cast Since we know already it's a batch we can use static cast now. --- audit/audit.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audit/audit.cc b/audit/audit.cc index 7699939df5..d7b1a3a26d 100644 --- a/audit/audit.cc +++ b/audit/audit.cc @@ -268,7 +268,7 @@ future<> inspect(shared_ptr statement, service::query_state return make_ready_future<>(); } if (audit_info->batch()) { - cql3::statements::batch_statement* batch = dynamic_cast(statement.get()); + cql3::statements::batch_statement* batch = static_cast(statement.get()); return do_for_each(batch->statements().begin(), batch->statements().end(), [&query_state, &options, error] (auto&& m) { return inspect(m.statement, query_state, options, error); });