66
77using 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-
149PendingConfigItem::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+
1814PendingDependencyGroupStateItem::PendingDependencyGroupStateItem (const DependencyGroup::Ptr& depGroup)
19- : PendingQueueItem{ std::make_pair ( nullptr , depGroup), 0 }, DepGroup{depGroup}
15+ : DepGroup{depGroup}
2016{
2117}
2218
2319PendingDependencyEdgeItem::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