Change RedisConnection::Query::value_type to a string/view variant

Especially our history messages contain lots of hardcoded C string literals
`"like this one"`. At runtime, they get translated to pointers to constant
global memory, `const char*`. String `malloc(3)`s and copies these data every
time. In contrast, the new type just stores the address if any. (Actually,
`const char*` is wrapped by `std::string_view` to not compute its length every
time.)
This commit is contained in:
Alexander A. Klimov 2026-03-03 11:29:07 +01:00
parent c351f6a88c
commit 1702eebd41
3 changed files with 58 additions and 9 deletions

View file

@ -697,7 +697,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
actionUrls.emplace_back(JsonEncode(data));
if (runtimeUpdate) {
AddObjectDataToRuntimeUpdates(runtimeUpdates, actionUrls.at(actionUrls.size() - 2u), m_PrefixConfigObject + "action:url", data);
AddObjectDataToRuntimeUpdates(runtimeUpdates, static_cast<String>(actionUrls.at(actionUrls.size() - 2u)), m_PrefixConfigObject + "action:url", data);
}
}
}
@ -712,7 +712,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
notesUrls.emplace_back(JsonEncode(data));
if (runtimeUpdate) {
AddObjectDataToRuntimeUpdates(runtimeUpdates, notesUrls.at(notesUrls.size() - 2u), m_PrefixConfigObject + "notes:url", data);
AddObjectDataToRuntimeUpdates(runtimeUpdates, static_cast<String>(notesUrls.at(notesUrls.size() - 2u)), m_PrefixConfigObject + "notes:url", data);
}
}
}
@ -727,7 +727,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
iconImages.emplace_back(JsonEncode(data));
if (runtimeUpdate) {
AddObjectDataToRuntimeUpdates(runtimeUpdates, iconImages.at(iconImages.size() - 2u), m_PrefixConfigObject + "icon:image", data);
AddObjectDataToRuntimeUpdates(runtimeUpdates, static_cast<String>(iconImages.at(iconImages.size() - 2u)), m_PrefixConfigObject + "icon:image", data);
}
}
}

View file

@ -94,14 +94,14 @@ void LogQuery(RedisConnection::Query& query, Log& msg)
{
int i = 0;
for (auto& arg : query) {
for (std::string_view arg : query) {
if (++i == 8) {
msg << " ...";
break;
}
if (arg.GetLength() > 64) {
msg << " '" << arg.SubStr(0, 61) << "...'";
if (arg.length() > 64) {
msg << " '" << arg.substr(0, 61) << "...'";
} else {
msg << " '" << arg << '\'';
}

View file

@ -39,7 +39,9 @@
#include <queue>
#include <set>
#include <stdexcept>
#include <string_view>
#include <utility>
#include <variant>
#include <vector>
namespace icinga
@ -54,7 +56,54 @@ namespace icinga
public:
DECLARE_PTR_TYPEDEFS(RedisConnection);
typedef String QueryArg;
/**
* A Redis query argument. Either owned String or hardcoded const char[].
* Allows mixing these types in a single query transparently, not requiring any conversions.
*
* @ingroup icingadb
*/
class QueryArg
{
public:
QueryArg(const char data[]) noexcept : m_Data(std::in_place_type<std::string_view>, data)
{
}
QueryArg(String data) noexcept : m_Data(std::move(data))
{
}
bool operator<(const QueryArg& rhs) const noexcept // For std::map keys
{
return static_cast<std::string_view>(*this) < static_cast<std::string_view>(rhs);
}
operator std::string_view() const noexcept
{
return std::visit([](auto& data) { return ViewOf(data); }, m_Data);
}
explicit operator String() const
{
std::string_view sv (*this);
return String(sv.begin(), sv.end());
}
private:
std::variant<std::string_view, String> m_Data;
static std::string_view ViewOf(const std::string_view& data) noexcept
{
return data;
}
static std::string_view ViewOf(const String& data) noexcept
{
return {data.CStr(), data.GetLength()};
}
};
typedef std::vector<QueryArg> Query;
typedef std::vector<Query> Queries;
typedef Value Reply;
@ -665,8 +714,8 @@ void RedisConnection::WriteRESP(AsyncWriteStream& stream, const Query& query, bo
msg << "*" << query.size() << "\r\n";
for (auto& arg : query) {
msg << "$" << arg.GetLength() << "\r\n" << arg << "\r\n";
for (std::string_view arg : query) {
msg << "$" << arg.length() << "\r\n" << arg << "\r\n";
}
asio::async_write(stream, writeBuffer, yc);