Skip to content

Commit 6695294

Browse files
julianbrostyhabteab
authored andcommitted
IcingaDB: use polymorphism for queue entries
1 parent 7b92314 commit 6695294

2 files changed

Lines changed: 129 additions & 143 deletions

File tree

lib/icingadb/icingadb-worker.cpp

Lines changed: 85 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,23 @@
66

77
using namespace icinga;
88

9-
PendingQueueItem::PendingQueueItem(PendingItemKey&& id, uint32_t dirtyBits)
10-
: DirtyBits{dirtyBits & DirtyBitsAll}, ID{std::move(id)}, EnqueueTime{std::chrono::steady_clock::now()}
11-
{
12-
}
13-
149
PendingConfigItem::PendingConfigItem(const ConfigObject::Ptr& obj, uint32_t bits)
15-
: PendingQueueItem{std::make_pair(obj, nullptr), bits}, Object{obj}
10+
: Object{obj}, DirtyBits{bits & DirtyBitsAll}
1611
{
1712
}
13+
1814
PendingDependencyGroupStateItem::PendingDependencyGroupStateItem(const DependencyGroup::Ptr& depGroup)
19-
: PendingQueueItem{std::make_pair(nullptr, depGroup), 0}, DepGroup{depGroup}
15+
: DepGroup{depGroup}
2016
{
2117
}
2218

2319
PendingDependencyEdgeItem::PendingDependencyEdgeItem(const DependencyGroup::Ptr& depGroup, const Checkable::Ptr& child)
24-
: PendingQueueItem{std::make_pair(child, depGroup), 0}, DepGroup{depGroup}, Child{child}
20+
: DepGroup{depGroup}, Child{child}
2521
{
2622
}
2723

28-
RelationsDeletionItem::RelationsDeletionItem(const String& id, RelationsKeyMap relations)
29-
: PendingQueueItem{id, 0}, Relations{std::move(relations)}
24+
RelationsDeletionItem::RelationsDeletionItem(const String& id, const RelationsKeyMap& relations)
25+
: ID{id}, Relations{relations}
3026
{
3127
}
3228

@@ -103,42 +99,39 @@ std::chrono::duration<double> IcingaDB::DequeueAndProcessOne(std::unique_lock<st
10399
auto& seqView = m_PendingItems.get<1>();
104100
for (auto it(seqView.begin()); it != seqView.end(); ++it) {
105101
if (it != seqView.begin()) {
106-
if (std::holds_alternative<RelationsDeletionItem>(*it)) {
102+
if (dynamic_cast<const RelationsDeletionItem*>(it->get())) {
107103
// We don't know whether the previous items are related to this deletion item or not,
108104
// thus we can't just process this right now when there are older items in the queue.
109105
// Otherwise, we might delete something that is going to be updated/created.
110106
break;
111107
}
112108
}
113109

114-
auto age = now - std::visit([](const auto& item) { return item.EnqueueTime; }, *it);
110+
auto age = now - (*it)->EnqueueTime;
115111
if (GetActive() && 1000ms > age) {
116112
if (it == seqView.begin()) {
117113
retryAfter = 1000ms - age;
118114
}
119115
break;
120116
}
121117

122-
ConfigObject::Ptr cobj;
123-
if (auto* citem = std::get_if<PendingConfigItem>(&*it); citem) {
124-
cobj = citem->Object;
125-
}
126-
118+
ConfigObject::Ptr cobj = (*it)->GetObjectToLock();
127119
ObjectLock olock(cobj, std::defer_lock);
128120
if (cobj && !olock.TryLock()) {
129121
continue; // Can't lock the object right now, try the next one.
130122
}
131123

132-
PendingItemVariant itemToProcess = *it;
124+
auto itemToProcess = *it;
133125
seqView.erase(it);
134126
madeProgress = true;
135127

136128
lock.unlock();
137129
try {
138-
std::visit([this](const auto& item) { ProcessPendingItem(item); }, itemToProcess);
130+
itemToProcess->Execute(*this);
139131
} catch (const std::exception& ex) {
132+
PendingQueueItem& itemRef = *itemToProcess; // For typeid(operand of typeid must not have any side effects).
140133
Log(LogCritical, "IcingaDB")
141-
<< "Exception while processing pending item of type index '" << itemToProcess.index() << "': "
134+
<< "Exception while processing pending item of type '" << typeid(itemRef).name() << "': "
142135
<< DiagnosticInformation(ex, GetActive());
143136
}
144137
lock.lock();
@@ -152,24 +145,27 @@ std::chrono::duration<double> IcingaDB::DequeueAndProcessOne(std::unique_lock<st
152145
return retryAfter;
153146
}
154147

148+
ConfigObject::Ptr PendingConfigItem::GetObjectToLock() const
149+
{
150+
return Object;
151+
}
152+
155153
/**
156-
* Process a single pending object.
154+
* Execute the pending configuration item.
157155
*
158-
* This function processes a single pending object based on its dirty bits. It checks if the object is a
159-
* @c ConfigObject and performs the appropriate actions such as sending configuration updates, state updates,
160-
* or deletions to the Redis connection. The function handles different types of objects, including @c Checkable
161-
* objects, and ensures that the correct updates are sent based on the dirty bits set for the object.
156+
* This function processes the pending configuration item by performing the necessary Redis operations based
157+
* on the dirty bits set for the associated configuration object. It handles configuration deletions, updates,
158+
* and state updates for checkable objects.
162159
*
163-
* @param item The pending item containing the object and its dirty bits.
160+
* @param icingadb The IcingaDB instance to use for executing Redis queries.
164161
*/
165-
void IcingaDB::ProcessPendingItem(const PendingConfigItem& item)
166-
{
167-
if (item.DirtyBits & ConfigDelete) {
168-
String typeName = GetLowerCaseTypeNameDB(item.Object);
169-
m_RconWorker->FireAndForgetQueries(
162+
void PendingConfigItem::Execute(IcingaDB& icingadb) const {
163+
if (DirtyBits & ConfigDelete) {
164+
String typeName = icingadb.GetLowerCaseTypeNameDB(Object);
165+
icingadb.m_RconWorker->FireAndForgetQueries(
170166
{
171-
{"HDEL", m_PrefixConfigObject + typeName, GetObjectIdentifier(item.Object)},
172-
{"HDEL", m_PrefixConfigCheckSum + typeName, GetObjectIdentifier(item.Object)},
167+
{"HDEL", icingadb.m_PrefixConfigObject + typeName, icingadb.GetObjectIdentifier(Object)},
168+
{"HDEL", icingadb.m_PrefixConfigCheckSum + typeName, icingadb.GetObjectIdentifier(Object)},
173169
{
174170
"XADD",
175171
"icinga:runtime",
@@ -178,88 +174,84 @@ void IcingaDB::ProcessPendingItem(const PendingConfigItem& item)
178174
"1000000",
179175
"*",
180176
"redis_key",
181-
m_PrefixConfigObject + typeName,
177+
icingadb.m_PrefixConfigObject + typeName,
182178
"id",
183-
GetObjectIdentifier(item.Object),
179+
icingadb.GetObjectIdentifier(Object),
184180
"runtime_type",
185181
"delete"
186182
}
187183
}
188184
);
189185
}
190186

191-
if (item.DirtyBits & ConfigUpdate) {
187+
if (DirtyBits & ConfigUpdate) {
192188
std::map<String, std::vector<String>> hMSets;
193189
std::vector<Dictionary::Ptr> runtimeUpdates;
194-
CreateConfigUpdate(item.Object, GetLowerCaseTypeNameDB(item.Object), hMSets, runtimeUpdates, true);
195-
ExecuteRedisTransaction(m_RconWorker, hMSets, runtimeUpdates);
190+
icingadb.CreateConfigUpdate(Object, icingadb.GetLowerCaseTypeNameDB(Object), hMSets, runtimeUpdates, true);
191+
icingadb.ExecuteRedisTransaction(icingadb.m_RconWorker, hMSets, runtimeUpdates);
196192
}
197193

198-
if (auto checkable = dynamic_pointer_cast<Checkable>(item.Object); checkable) {
199-
if (item.DirtyBits & FullState) {
200-
UpdateState(checkable, item.DirtyBits);
194+
if (auto checkable = dynamic_pointer_cast<Checkable>(Object); checkable) {
195+
if (DirtyBits & FullState) {
196+
icingadb.UpdateState(checkable, DirtyBits);
201197
}
202-
if (item.DirtyBits & NextUpdate) {
203-
SendNextUpdate(checkable);
198+
if (DirtyBits & NextUpdate) {
199+
icingadb.SendNextUpdate(checkable);
204200
}
205201
}
206202
}
207203

208204
/**
209-
* Process a single pending dependency group state item.
205+
* Execute the pending dependency group state item.
210206
*
211-
* This function processes a single pending dependency group state item by updating the dependencies
212-
* state for the associated dependency group. It selects any child checkable from the dependency group
213-
* to initiate the state update process.
207+
* This function processes the pending dependency group state item by updating the state of the
208+
* dependency group in Redis. It selects any child checkable from the dependency group to initiate
209+
* the state update, as all children share the same dependency group state.
214210
*
215-
* @param item The pending dependency group state item containing the dependency group.
211+
* @param icingadb The IcingaDB instance to use for executing Redis queries.
216212
*/
217-
void IcingaDB::ProcessPendingItem(const PendingDependencyGroupStateItem& item) const
213+
void PendingDependencyGroupStateItem::Execute(IcingaDB& icingadb) const
218214
{
219215
// For dependency group state updates, we don't actually care which child triggered the update,
220216
// since all children share the same dependency group state. Thus, we can just pick any child to
221217
// start the update from.
222-
if (auto child = item.DepGroup->GetAnyChild(); child) {
223-
UpdateDependenciesState(child, item.DepGroup);
218+
if (auto child = DepGroup->GetAnyChild(); child) {
219+
icingadb.UpdateDependenciesState(child, DepGroup);
224220
}
225221
}
226222

227223
/**
228-
* Process a single pending dependency edge item.
224+
* Execute the pending dependency edge item.
229225
*
230-
* This function fully serializes a single pending dependency edge item (child registration)
231-
* and sends all the resulting Redis queries in a single transaction. The dependencies (edges)
232-
* to serialize are determined by the dependency group and child checkable the provided item represents.
226+
* This function processes the pending dependency edge item and ensures that the necessary Redis
227+
* operations are performed to register the child checkable as part of the dependency group.
233228
*
234-
* @param item The pending dependency edge item containing the dependency group and child checkable.
229+
* @param icingadb The IcingaDB instance to use for executing Redis queries.
235230
*/
236-
void IcingaDB::ProcessPendingItem(const PendingDependencyEdgeItem& item)
231+
void PendingDependencyEdgeItem::Execute(IcingaDB& icingadb) const
237232
{
238233
std::vector<Dictionary::Ptr> runtimeUpdates;
239234
std::map<String, RedisConnection::Query> hMSets;
240-
InsertCheckableDependencies(item.Child, hMSets, &runtimeUpdates, item.DepGroup);
241-
ExecuteRedisTransaction(m_RconWorker, hMSets, runtimeUpdates);
235+
icingadb.InsertCheckableDependencies(Child, hMSets, &runtimeUpdates, DepGroup);
236+
icingadb.ExecuteRedisTransaction(icingadb.m_RconWorker, hMSets, runtimeUpdates);
242237
}
243238

244239
/**
245-
* Process a single pending deletion item.
240+
* Execute the pending relations deletion item.
246241
*
247-
* This function processes a single pending deletion item by deleting the specified sub-keys
248-
* from Redis based on the provided deletion keys map. It ensures that the object's ID is
249-
* removed from the specified Redis keys and their corresponding checksum keys if indicated.
242+
* This function processes the pending relations deletion item by deleting the specified relations
243+
* from Redis. It iterates over the map of Redis keys and deletes the relations associated with
244+
* the given ID.
250245
*
251-
* @param item The pending deletion item containing the ID and deletion keys map.
246+
* @param icingadb The IcingaDB instance to use for executing Redis queries.
252247
*/
253-
void IcingaDB::ProcessPendingItem(const RelationsDeletionItem& item)
248+
void RelationsDeletionItem::Execute(IcingaDB& icingadb) const
254249
{
255-
ASSERT(std::holds_alternative<std::string>(item.ID)); // Relation deletion items must have real IDs.
256-
257-
auto id = std::get<std::string>(item.ID);
258-
for (auto [redisKey, hasChecksum] : item.Relations) {
259-
if (IsStateKey(redisKey)) {
260-
DeleteState(id, redisKey, hasChecksum);
250+
for (auto [redisKey, hasChecksum] : Relations) {
251+
if (icingadb.IsStateKey(redisKey)) {
252+
icingadb.DeleteState(ID, redisKey, hasChecksum);
261253
} else {
262-
DeleteRelationship(id, redisKey, hasChecksum);
254+
icingadb.DeleteRelationship(ID, redisKey, hasChecksum);
263255
}
264256
}
265257
}
@@ -278,24 +270,20 @@ void IcingaDB::EnqueueConfigObject(const ConfigObject::Ptr& object, uint32_t bit
278270

279271
{
280272
std::lock_guard lock(m_PendingItemsMutex);
281-
if (auto [it, inserted] = m_PendingItems.insert(PendingConfigItem{object, bits}); !inserted) {
282-
m_PendingItems.modify(it, [bits](PendingItemVariant& itemToProcess) mutable {
283-
std::visit(
284-
[&bits](auto& item) {
285-
if (bits & ConfigDelete) {
286-
// A config delete and config update cancel each other out, and we don't need
287-
// to keep any state updates either, as the object is being deleted.
288-
item.DirtyBits &= ~(ConfigUpdate | FullState);
289-
bits &= ~(ConfigUpdate | FullState); // Must not add these bits either.
290-
} else if (bits & ConfigUpdate) {
291-
// A new config update cancels any pending config deletion for the same object.
292-
item.DirtyBits &= ~ConfigDelete;
293-
bits &= ~ConfigDelete;
294-
}
295-
item.DirtyBits |= bits & DirtyBitsAll;
296-
},
297-
itemToProcess
298-
);
273+
if (auto [it, inserted] = m_PendingItems.insert(std::make_shared<PendingConfigItem>(object, bits)); !inserted) {
274+
m_PendingItems.modify(it, [bits](const std::shared_ptr<PendingQueueItem>& item) mutable {
275+
auto configItem = dynamic_cast<PendingConfigItem*>(item.get());
276+
if (bits & ConfigDelete) {
277+
// A config delete and config update cancel each other out, and we don't need
278+
// to keep any state updates either, as the object is being deleted.
279+
configItem->DirtyBits &= ~(ConfigUpdate | FullState);
280+
bits &= ~(ConfigUpdate | FullState); // Must not add these bits either.
281+
} else if (bits & ConfigUpdate) {
282+
// A new config update cancels any pending config deletion for the same object.
283+
configItem->DirtyBits &= ~ConfigDelete;
284+
bits &= ~ConfigDelete;
285+
}
286+
configItem->DirtyBits |= bits & DirtyBitsAll;
299287
});
300288
}
301289
}
@@ -307,7 +295,7 @@ void IcingaDB::EnqueueDependencyGroupStateUpdate(const DependencyGroup::Ptr& dep
307295
if (GetActive() && m_RconWorker && m_RconWorker->IsConnected()) {
308296
{
309297
std::lock_guard lock(m_PendingItemsMutex);
310-
m_PendingItems.insert(PendingDependencyGroupStateItem{depGroup});
298+
m_PendingItems.insert(std::make_shared<PendingDependencyGroupStateItem>(depGroup));
311299
}
312300
m_PendingItemsCV.notify_one();
313301
}
@@ -327,7 +315,7 @@ void IcingaDB::EnqueueDependencyChildRegistered(const DependencyGroup::Ptr& depG
327315
if (GetActive() && m_RconWorker && m_RconWorker->IsConnected()) {
328316
{
329317
std::lock_guard lock(m_PendingItemsMutex);
330-
m_PendingItems.insert(PendingDependencyEdgeItem{depGroup, child});
318+
m_PendingItems.insert(std::make_shared<PendingDependencyEdgeItem>(depGroup, child));
331319
}
332320
m_PendingItemsCV.notify_one();
333321
}
@@ -469,18 +457,18 @@ void IcingaDB::EnqueueDependencyChildRemoved(
469457
* @param id The ID of the relation to be deleted.
470458
* @param relations A map of Redis keys from which to delete the relation.
471459
*/
472-
void IcingaDB::EnqueueRelationsDeletion(const String& id, const RelationsKeyMap& relations)
460+
void IcingaDB::EnqueueRelationsDeletion(const String& id, const RelationsDeletionItem::RelationsKeyMap& relations)
473461
{
474462
if (!GetActive() || !m_RconWorker || !m_RconWorker->IsConnected()) {
475463
return; // No need to enqueue anything if we're not connected.
476464
}
477465

478466
{
479467
std::lock_guard lock(m_PendingItemsMutex);
480-
if (auto [it, inserted] = m_PendingItems.insert(RelationsDeletionItem{id, relations}); !inserted) {
481-
m_PendingItems.modify(it, [&relations](PendingItemVariant& val) {
482-
auto& item = std::get<RelationsDeletionItem>(val);
483-
item.Relations.insert(relations.begin(), relations.end());
468+
if (auto [it, inserted] = m_PendingItems.insert(std::make_shared<RelationsDeletionItem>(id, relations)); !inserted) {
469+
m_PendingItems.modify(it, [&relations](std::shared_ptr<PendingQueueItem>& val) {
470+
auto item = dynamic_cast<RelationsDeletionItem*>(val.get());
471+
item->Relations.insert(relations.begin(), relations.end());
484472
});
485473
}
486474
}

0 commit comments

Comments
 (0)