Skip to content

Commit 218e9f1

Browse files
committed
FIX: Fix PHP memory exhaustion on maps with many hosts
The #437 memory fix made host-/servicegroup members lazy, but the follow-up "Restore service list in host hover menus" (CMK-35933) kept plain host objects loading eagerly, because hosts had no num_members fallback to drive the frontend lazy fetch. As a result, viewing a map with many hosts and services still queued GET_SINGLE_MEMBER_STATES for every host, so fetchHostMemberDetails() instantiated a NagVisService object for every service of every host in a single request, exhausting the 128 MB PHP memory limit. Extend the lazy loading to hosts: 1. NagVisHost::getNumMembers() derives the service count from the cheap aggregate hostMemberState counts (excluding the host's own state), so the frontend can trigger the lazy getObjectMembers fetch on hover. 2. NagVisMapObj::queueState() queues hosts lazily like groups, but only when recognize_services is enabled, since that is the only case in which the aggregate service state counts are fetched. Without it, hosts keep loading their services eagerly to avoid altering the summary state. CMK-36603
1 parent daf34e5 commit 218e9f1

3 files changed

Lines changed: 57 additions & 10 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
FIX: Fix PHP memory exhaustion on maps with many hosts by lazily loading host services on demand (#437)

share/server/core/classes/objects/NagVisHost.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,44 @@ public function fetchSummariesFromCounts()
8989
}
9090
}
9191

92+
/**
93+
* Returns the number of service members.
94+
* When the service details have not been loaded yet (lazy loading), the
95+
* total is derived from the service state counts fetched via hostMemberState.
96+
* The host's own state, which is merged into the counts for the summary
97+
* calculation, is excluded. The count is only reported when the host is
98+
* configured to show its services in the hover menu, matching the condition
99+
* under which the service details are loaded on demand.
100+
*
101+
* @return int
102+
*/
103+
public function getNumMembers()
104+
{
105+
if (!empty($this->members)) {
106+
return count($this->members);
107+
}
108+
if (
109+
!$this->recognize_services
110+
|| $this->hover_menu != 1
111+
|| $this->hover_childs_show != 1
112+
|| $this->aStateCounts === null
113+
) {
114+
return 0;
115+
}
116+
$total = 0;
117+
foreach ($this->aStateCounts as $sState => $aSubstates) {
118+
// The host state is added to the counts for summary purposes; only
119+
// the service states represent actual members.
120+
if (is_host_state($sState)) {
121+
continue;
122+
}
123+
foreach ($aSubstates as $iCount) {
124+
$total += $iCount;
125+
}
126+
}
127+
return $total;
128+
}
129+
92130
/**
93131
* Queues the state fetching to the backend.
94132
*

share/server/core/classes/objects/NagVisMapObj.php

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -357,24 +357,32 @@ public function queueState($_unused_flag = true, $_unused_flag2 = true)
357357
foreach ($this->getStateRelevantMembers() as $OBJ) {
358358
$sType = $OBJ->getType();
359359

360-
// Host- and servicegroups can contain thousands of members. To avoid
361-
// exhausting the PHP memory limit their member details are not loaded
362-
// on map load, but lazily on demand via the getObjectMembers endpoint
363-
// when a hover menu is opened (see #437). Only the cheap aggregate
364-
// state counts are queried here, which is enough for icon colouring
365-
// and for reporting the correct num_members to the frontend.
366-
$bLazyMembers = $sType === 'hostgroup' || $sType === 'servicegroup';
360+
// Hosts (with their services) and host-/servicegroups can contain
361+
// thousands of members. To avoid exhausting the PHP memory limit their
362+
// member details are not loaded on map load, but lazily on demand via
363+
// the getObjectMembers endpoint when a hover menu is opened (see #437).
364+
// Only the cheap aggregate state counts are queried here, which is
365+
// enough for icon colouring and for reporting the correct num_members
366+
// to the frontend so it can trigger the lazy fetch.
367+
//
368+
// Hosts only expose a num_members fallback (and therefore only support
369+
// lazy loading) when recognize_services is enabled, because that is the
370+
// only case in which the aggregate service state counts are fetched.
371+
// Without it, hosts keep loading their services eagerly.
372+
$bLazyMembers = $sType === 'hostgroup'
373+
|| $sType === 'servicegroup'
374+
|| ($sType === 'host' && $OBJ->getRecognizeServices());
367375

368376
// Gadgets render synchronously and read conf.members directly at
369377
// render time, so their member details must be loaded eagerly even
370-
// for the otherwise lazily loaded group types.
378+
// for the otherwise lazily loaded object types.
371379
$bGadget = $OBJ->get('view_type') === 'gadget';
372380

373381
if ($bLazyMembers && !$bGadget) {
374382
$OBJ->queueState(GET_STATE, DONT_GET_SINGLE_MEMBER_STATES);
375383
} elseif ($this->isView === true || $bGadget) {
376-
// On a viewed map the hover menus of hosts, dyngroups, aggregates
377-
// and submaps need their single member states right away. When the
384+
// On a viewed map the hover menus of dyngroups, aggregates and
385+
// submaps need their single member states right away. When the
378386
// map object is only rendered as a summary icon (e.g. overview or
379387
// multisite snapin) no hover menu is shown, so the details are not
380388
// fetched.

0 commit comments

Comments
 (0)