This commit is contained in:
Alexander Aleksandrovič Klimov 2026-02-16 09:55:59 +01:00 committed by GitHub
commit 6fe989b390
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 113 additions and 41 deletions

View file

@ -22,6 +22,10 @@ String::String(const char *data)
: m_Data(data)
{ }
String::String(const std::string_view& data)
: m_Data(data)
{ }
String::String(std::string data)
: m_Data(std::move(data))
{ }
@ -140,6 +144,16 @@ String::operator boost::beast::string_view() const
return boost::beast::string_view(m_Data);
}
/**
* Conversion function to std::string_view.
*
* @return An std::string_view representing this string.
*/
String::operator std::string_view() const
{
return std::string_view(m_Data);
}
const char *String::CStr() const
{
return m_Data.c_str();

View file

@ -11,6 +11,7 @@
#include <boost/utility/string_view.hpp>
#include <functional>
#include <string>
#include <string_view>
#include <iosfwd>
namespace icinga {
@ -42,6 +43,7 @@ public:
String() = default;
String(const char *data);
String(const std::string_view& data);
String(std::string data);
String(String::SizeType n, char c);
String(const String& other);
@ -76,6 +78,7 @@ public:
operator const std::string&() const;
operator boost::beast::string_view() const;
operator std::string_view() const;
const char *CStr() const;

View file

@ -272,12 +272,12 @@ void IcingaDB::UpdateAllConfigObjects()
String configObject = m_PrefixConfigObject + lcType;
// Skimmed away attributes and checksums HMSETs' keys and values by Redis key.
std::map<String, std::vector<std::vector<String>>> ourContentRaw {{configCheckSum, {}}, {configObject, {}}};
std::map<String, RedisConnection::Queries> ourContentRaw {{configCheckSum, {}}, {configObject, {}}};
std::mutex ourContentMutex;
upqObjectType.ParallelFor(objectChunks, [&](decltype(objectChunks)::const_reference chunk) {
std::map<String, std::vector<String>> hMSets;
std::vector<String> hostZAdds = {"ZADD", "icinga:nextupdate:host"}, serviceZAdds = {"ZADD", "icinga:nextupdate:service"};
std::map<String, RedisConnection::Query> hMSets;
RedisConnection::Query hostZAdds = {"ZADD", "icinga:nextupdate:host"}, serviceZAdds = {"ZADD", "icinga:nextupdate:service"};
auto skimObjects ([&]() {
std::lock_guard<std::mutex> l (ourContentMutex);
@ -343,7 +343,7 @@ void IcingaDB::UpdateAllConfigObjects()
zAdds->emplace_back(GetObjectIdentifier(checkable));
if (zAdds->size() >= 102u) {
std::vector<String> header (zAdds->begin(), zAdds->begin() + 2u);
RedisConnection::Query header (zAdds->begin(), zAdds->begin() + 2u);
rcon->FireAndForgetQuery(std::move(*zAdds), Prio::CheckResult);
@ -384,7 +384,15 @@ void IcingaDB::UpdateAllConfigObjects()
upqObjectType.Enqueue([&]() {
for (auto& hMSet : source.second) {
for (decltype(hMSet.size()) i = 0, stop = hMSet.size() - 1u; i < stop; i += 2u) {
dest.emplace(std::move(hMSet[i]), std::move(hMSet[i + 1u]));
auto variantToString = [](RedisConnection::QueryArg v) -> String {
if (auto str (std::get_if<String>(&v.GetData())); str) {
return std::move(*str);
}
return std::get<std::string_view>(v.GetData());
};
dest.emplace(variantToString(std::move(hMSet[i])), variantToString(std::move(hMSet[i + 1u])));
}
hMSet.clear();
@ -399,7 +407,7 @@ void IcingaDB::UpdateAllConfigObjects()
auto& ourCheckSums (ourContent[configCheckSum]);
auto& ourObjects (ourContent[configObject]);
std::vector<String> setChecksum, setObject, delChecksum, delObject;
RedisConnection::Query setChecksum, setObject, delChecksum, delObject;
auto redisCurrent (redisCheckSums.begin());
auto redisEnd (redisCheckSums.end());
@ -412,12 +420,12 @@ void IcingaDB::UpdateAllConfigObjects()
setChecksum.insert(setChecksum.begin(), {"HMSET", configCheckSum});
setObject.insert(setObject.begin(), {"HMSET", configObject});
std::vector<std::vector<String>> transaction;
RedisConnection::Queries transaction;
transaction.emplace_back(std::vector<String>{"MULTI"});
transaction.emplace_back(RedisConnection::Query{"MULTI"});
transaction.emplace_back(std::move(setChecksum));
transaction.emplace_back(std::move(setObject));
transaction.emplace_back(std::vector<String>{"EXEC"});
transaction.emplace_back(RedisConnection::Query{"EXEC"});
setChecksum.clear();
setObject.clear();
@ -431,12 +439,12 @@ void IcingaDB::UpdateAllConfigObjects()
delChecksum.insert(delChecksum.begin(), {"HDEL", configCheckSum});
delObject.insert(delObject.begin(), {"HDEL", configObject});
std::vector<std::vector<String>> transaction;
RedisConnection::Queries transaction;
transaction.emplace_back(std::vector<String>{"MULTI"});
transaction.emplace_back(RedisConnection::Query{"MULTI"});
transaction.emplace_back(std::move(delChecksum));
transaction.emplace_back(std::move(delObject));
transaction.emplace_back(std::vector<String>{"EXEC"});
transaction.emplace_back(RedisConnection::Query{"EXEC"});
delChecksum.clear();
delObject.clear();
@ -564,7 +572,7 @@ std::vector<std::vector<intrusive_ptr<ConfigObject>>> IcingaDB::ChunkObjects(std
}
void IcingaDB::DeleteKeys(const RedisConnection::Ptr& conn, const std::vector<String>& keys, RedisConnection::QueryPriority priority) {
std::vector<String> query = {"DEL"};
RedisConnection::Query query = {"DEL"};
for (auto& key : keys) {
query.emplace_back(key);
}
@ -636,7 +644,7 @@ static ConfigObject::Ptr GetObjectByName(const String& name)
return ConfigObject::GetObject<ConfigType>(name);
}
void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const String typeName, std::map<String, std::vector<String>>& hMSets,
void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const String typeName, std::map<String, RedisConnection::Query>& hMSets,
std::vector<Dictionary::Ptr>& runtimeUpdates, bool runtimeUpdate)
{
String objectKey = GetObjectIdentifier(object);
@ -692,12 +700,12 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
auto id (HashValue(new Array({m_EnvironmentId, actionUrl})));
if (runtimeUpdate || m_DumpedGlobals.ActionUrl.IsNew(id)) {
actionUrls.emplace_back(std::move(id));
actionUrls.emplace_back(id);
Dictionary::Ptr data = new Dictionary({{"environment_id", m_EnvironmentId}, {"action_url", actionUrl}});
actionUrls.emplace_back(JsonEncode(data));
if (runtimeUpdate) {
AddObjectDataToRuntimeUpdates(runtimeUpdates, actionUrls.at(actionUrls.size() - 2u), m_PrefixConfigObject + "action:url", data);
AddObjectDataToRuntimeUpdates(runtimeUpdates, id, m_PrefixConfigObject + "action:url", data);
}
}
}
@ -707,12 +715,12 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
auto id (HashValue(new Array({m_EnvironmentId, notesUrl})));
if (runtimeUpdate || m_DumpedGlobals.NotesUrl.IsNew(id)) {
notesUrls.emplace_back(std::move(id));
notesUrls.emplace_back(id);
Dictionary::Ptr data = new Dictionary({{"environment_id", m_EnvironmentId}, {"notes_url", notesUrl}});
notesUrls.emplace_back(JsonEncode(data));
if (runtimeUpdate) {
AddObjectDataToRuntimeUpdates(runtimeUpdates, notesUrls.at(notesUrls.size() - 2u), m_PrefixConfigObject + "notes:url", data);
AddObjectDataToRuntimeUpdates(runtimeUpdates, id, m_PrefixConfigObject + "notes:url", data);
}
}
}
@ -722,12 +730,12 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
auto id (HashValue(new Array({m_EnvironmentId, iconImage})));
if (runtimeUpdate || m_DumpedGlobals.IconImage.IsNew(id)) {
iconImages.emplace_back(std::move(id));
iconImages.emplace_back(id);
Dictionary::Ptr data = new Dictionary({{"environment_id", m_EnvironmentId}, {"icon_image", iconImage}});
iconImages.emplace_back(JsonEncode(data));
if (runtimeUpdate) {
AddObjectDataToRuntimeUpdates(runtimeUpdates, iconImages.at(iconImages.size() - 2u), m_PrefixConfigObject + "icon:image", data);
AddObjectDataToRuntimeUpdates(runtimeUpdates, id, m_PrefixConfigObject + "icon:image", data);
}
}
}
@ -1334,7 +1342,7 @@ void IcingaDB::UpdateState(const Checkable::Ptr& checkable, StateUpdate mode)
if (mode & StateUpdate::RuntimeOnly) {
ObjectLock olock(stateAttrs);
std::vector<String> streamadd({
RedisConnection::Query streamadd({
"XADD", "icinga:runtime:state", "MAXLEN", "~", "1000000", "*",
"runtime_type", "upsert",
"redis_key", redisStateKey,
@ -1467,7 +1475,7 @@ void IcingaDB::SendConfigUpdate(const ConfigObject::Ptr& object, bool runtimeUpd
String typeName = GetLowerCaseTypeNameDB(object);
std::map<String, std::vector<String>> hMSets;
std::map<String, RedisConnection::Query> hMSets;
std::vector<Dictionary::Ptr> runtimeUpdates;
CreateConfigUpdate(object, typeName, hMSets, runtimeUpdates, runtimeUpdate);
@ -1807,7 +1815,7 @@ bool IcingaDB::PrepareObject(const ConfigObject::Ptr& object, Dictionary::Ptr& a
* icinga:config:object:downtime) need to be prepended. There is nothing to indicate success or failure.
*/
void
IcingaDB::CreateConfigUpdate(const ConfigObject::Ptr& object, const String typeName, std::map<String, std::vector<String>>& hMSets,
IcingaDB::CreateConfigUpdate(const ConfigObject::Ptr& object, const String typeName, std::map<String, RedisConnection::Query>& hMSets,
std::vector<Dictionary::Ptr>& runtimeUpdates, bool runtimeUpdate)
{
/* TODO: This isn't essentially correct as we don't keep track of config objects ourselves. This would avoid duplicated config updates at startup.
@ -1969,7 +1977,7 @@ void IcingaDB::SendStateChange(const ConfigObject::Ptr& object, const CheckResul
Array::Ptr rawId = new Array({m_EnvironmentId, object->GetName()});
rawId->Add(eventTs);
std::vector<String> xAdd ({
RedisConnection::Query xAdd ({
"XADD", "icinga:history:stream:state", "*",
"id", HashValue(rawId),
"environment_id", m_EnvironmentId,
@ -2054,7 +2062,7 @@ void IcingaDB::SendSentNotification(
auto notificationHistoryId (HashValue(rawId));
std::vector<String> xAdd ({
RedisConnection::Query xAdd ({
"XADD", "icinga:history:stream:notification", "*",
"id", notificationHistoryId,
"environment_id", m_EnvironmentId,
@ -2118,7 +2126,7 @@ void IcingaDB::SendStartedDowntime(const Downtime::Ptr& downtime)
/* Update checkable state as in_downtime may have changed. */
UpdateState(checkable, StateUpdate::Full);
std::vector<String> xAdd ({
RedisConnection::Query xAdd ({
"XADD", "icinga:history:stream:downtime", "*",
"downtime_id", GetObjectIdentifier(downtime),
"environment_id", m_EnvironmentId,
@ -2207,7 +2215,7 @@ void IcingaDB::SendRemovedDowntime(const Downtime::Ptr& downtime)
/* Update checkable state as in_downtime may have changed. */
UpdateState(checkable, StateUpdate::Full);
std::vector<String> xAdd ({
RedisConnection::Query xAdd ({
"XADD", "icinga:history:stream:downtime", "*",
"downtime_id", GetObjectIdentifier(downtime),
"environment_id", m_EnvironmentId,
@ -2293,7 +2301,7 @@ void IcingaDB::SendAddedComment(const Comment::Ptr& comment)
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
std::vector<String> xAdd ({
RedisConnection::Query xAdd ({
"XADD", "icinga:history:stream:comment", "*",
"comment_id", GetObjectIdentifier(comment),
"environment_id", m_EnvironmentId,
@ -2365,7 +2373,7 @@ void IcingaDB::SendRemovedComment(const Comment::Ptr& comment)
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
std::vector<String> xAdd ({
RedisConnection::Query xAdd ({
"XADD", "icinga:history:stream:comment", "*",
"comment_id", GetObjectIdentifier(comment),
"environment_id", m_EnvironmentId,
@ -2428,7 +2436,7 @@ void IcingaDB::SendFlappingChange(const Checkable::Ptr& checkable, double change
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
std::vector<String> xAdd ({
RedisConnection::Query xAdd ({
"XADD", "icinga:history:stream:flapping", "*",
"environment_id", m_EnvironmentId,
"host_id", GetObjectIdentifier(host),
@ -2525,7 +2533,7 @@ void IcingaDB::SendAcknowledgementSet(const Checkable::Ptr& checkable, const Str
/* Update checkable state as is_acknowledged may have changed. */
UpdateState(checkable, StateUpdate::Full);
std::vector<String> xAdd ({
RedisConnection::Query xAdd ({
"XADD", "icinga:history:stream:acknowledgement", "*",
"environment_id", m_EnvironmentId,
"host_id", GetObjectIdentifier(host),
@ -2583,7 +2591,7 @@ void IcingaDB::SendAcknowledgementCleared(const Checkable::Ptr& checkable, const
/* Update checkable state as is_acknowledged may have changed. */
UpdateState(checkable, StateUpdate::Full);
std::vector<String> xAdd ({
RedisConnection::Query xAdd ({
"XADD", "icinga:history:stream:acknowledgement", "*",
"environment_id", m_EnvironmentId,
"host_id", GetObjectIdentifier(host),
@ -3335,7 +3343,7 @@ void IcingaDB::DeleteRelationship(const String& id, const String& redisKeyWithou
String redisKey = m_PrefixConfigObject + redisKeyWithoutPrefix;
std::vector<std::vector<String>> queries;
RedisConnection::Queries queries;
if (hasChecksum) {
queries.push_back({"HDEL", m_PrefixConfigCheckSum + redisKeyWithoutPrefix, id});

View file

@ -176,7 +176,7 @@ void IcingaDB::PublishStats()
status->Set("timestamp", TimestampToMilliseconds(Utility::GetTime()));
status->Set("icingadb_environment", m_EnvironmentId);
std::vector<String> query {"XADD", "icinga:stats", "MAXLEN", "1", "*"};
RedisConnection::Query query {"XADD", "icinga:stats", "MAXLEN", "1", "*"};
{
ObjectLock statusLock (status);

View file

@ -113,13 +113,13 @@ private:
std::vector<String> GetTypeDumpSignalKeys(const Type::Ptr& type);
void InsertCheckableDependencies(const Checkable::Ptr& checkable, std::map<String, RedisConnection::Query>& hMSets,
std::vector<Dictionary::Ptr>* runtimeUpdates, const DependencyGroup::Ptr& onlyDependencyGroup = nullptr);
void InsertObjectDependencies(const ConfigObject::Ptr& object, const String typeName, std::map<String, std::vector<String>>& hMSets,
void InsertObjectDependencies(const ConfigObject::Ptr& object, const String typeName, std::map<String, RedisConnection::Query>& hMSets,
std::vector<Dictionary::Ptr>& runtimeUpdates, bool runtimeUpdate);
void UpdateDependenciesState(const Checkable::Ptr& checkable, const DependencyGroup::Ptr& onlyDependencyGroup = nullptr,
std::set<DependencyGroup*>* seenGroups = nullptr) const;
void UpdateState(const Checkable::Ptr& checkable, StateUpdate mode);
void SendConfigUpdate(const ConfigObject::Ptr& object, bool runtimeUpdate);
void CreateConfigUpdate(const ConfigObject::Ptr& object, const String type, std::map<String, std::vector<String>>& hMSets,
void CreateConfigUpdate(const ConfigObject::Ptr& object, const String type, std::map<String, RedisConnection::Query>& hMSets,
std::vector<Dictionary::Ptr>& runtimeUpdates, bool runtimeUpdate);
void SendConfigDelete(const ConfigObject::Ptr& object);
void SendStateChange(const ConfigObject::Ptr& object, const CheckResult::Ptr& cr, StateType type);

View file

@ -31,6 +31,15 @@ namespace asio = boost::asio;
boost::regex RedisConnection::m_ErrAuth ("\\AERR AUTH ");
RedisConnection::QueryArg::operator std::string_view() const
{
if (auto str (std::get_if<String>(&m_Data)); str) {
return *str;
}
return std::get<std::string_view>(m_Data);
}
RedisConnection::RedisConnection(const String& host, int port, const String& path, const String& username, const String& password, int db,
bool useTls, bool insecure, const String& certPath, const String& keyPath, const String& caPath, const String& crlPath,
const String& tlsProtocolmin, const String& cipherList, double connectTimeout, DebugInfo di, const RedisConnection::Ptr& parent)
@ -100,10 +109,12 @@ void LogQuery(RedisConnection::Query& query, Log& msg)
break;
}
if (arg.GetLength() > 64) {
msg << " '" << arg.SubStr(0, 61) << "...'";
std::string_view sv (arg);
if (sv.length() > 64) {
msg << " '" << sv.substr(0, 61) << "...'";
} else {
msg << " '" << arg << '\'';
msg << " '" << sv << '\'';
}
}
}

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,39 @@ namespace icinga
public:
DECLARE_PTR_TYPEDEFS(RedisConnection);
typedef std::vector<String> Query;
/**
* A Redis query argument. Either owned String, borrowed std::string_view or hardcoded const char[].
* Allows mixing these types in a single query transparently, not requiring any conversions.
*
* @ingroup icingadb
*/
class QueryArg
{
public:
explicit QueryArg(std::string_view data) : m_Data(data)
{
}
QueryArg(String data): m_Data(std::move(data))
{
}
QueryArg(const char* data) : m_Data(std::string_view(data))
{
}
explicit operator std::string_view() const;
std::variant<std::string_view, String>& GetData()
{
return m_Data;
}
private:
std::variant<std::string_view, String> m_Data;
};
typedef std::vector<QueryArg> Query;
typedef std::vector<Query> Queries;
typedef Value Reply;
typedef std::vector<Reply> Replies;
@ -665,7 +699,9 @@ 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";
std::string_view sv (arg);
msg << "$" << sv.length() << "\r\n" << sv << "\r\n";
}
asio::async_write(stream, writeBuffer, yc);