This is exercised by the luxi QueryInstances call when the sinst_cnt and sint_list fields are used. This uses a lot of CPU and does a lot of short-lived heap allocation on clusters with many instances.
The reimplementation allocates fewer temporary values, and does fewer object lookups by UUID. The net effect is to reduce heap use from ~3.2GB to ~1.5GB, and CPU use from ~1200ms to ~770ms in a test harness using a config with 1000 DRBD instances on 80 nodes. Signed-off-by: Brian Foley <bpfo...@google.com> --- src/Ganeti/Config.hs | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/Ganeti/Config.hs b/src/Ganeti/Config.hs index cf2f885..5b40694 100644 --- a/src/Ganeti/Config.hs +++ b/src/Ganeti/Config.hs @@ -90,7 +90,7 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.UTF8 as UTF8 import qualified Data.Foldable as F import Data.List (foldl', nub) -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, mapMaybe) import Data.Monoid import qualified Data.Map as M import qualified Data.Set as S @@ -158,21 +158,36 @@ instNodes :: ConfigData -> Instance -> S.Set String instNodes cfg inst = maybe id S.insert (instPrimaryNode inst) $ instDiskNodes cfg inst --- | Computes the secondary nodes of an instance. Since this is valid --- only for DRBD, we call directly 'instDiskNodes', skipping over the --- extra primary insert. -instSecondaryNodes :: ConfigData -> Instance -> S.Set String -instSecondaryNodes cfg inst = - maybe id S.delete (instPrimaryNode inst) $ instDiskNodes cfg inst +-- | Computes the secondary node UUID for a DRBD disk +computeDiskSecondaryNode :: Disk -> Maybe String +computeDiskSecondaryNode dsk = + case diskLogicalId dsk of + Just (LIDDrbd8 _nodeA nodeB _ _ _ _) -> Just nodeB + _ -> Nothing -- | Get instances of a given node. -- The node is specified through its UUID. +-- The secondary calculation is expensive and frequently called, so optimise +-- this to allocate fewer temporary values getNodeInstances :: ConfigData -> String -> ([Instance], [Instance]) getNodeInstances cfg nname = - let all_inst = M.elems . fromContainer . configInstances $ cfg - pri_inst = filter ((== Just nname) . instPrimaryNode) all_inst - sec_inst = filter ((nname `S.member`) . instSecondaryNodes cfg) all_inst - in (pri_inst, sec_inst) + let all_insts = M.elems . fromContainer . configInstances $ cfg + all_disks = fromContainer . configDisks $ cfg + + pri_inst = filter ((== Just nname) . instPrimaryNode) $ all_insts + + find_disk :: String -> Maybe Disk + find_disk d_uuid = M.lookup (UTF8.fromString d_uuid) all_disks + + inst_disks :: [(Instance, [Disk])] + inst_disks = [(i, mapMaybe find_disk $ instDisks i) | i <- all_insts] + + sec_insts :: [Instance] + sec_insts = [inst | + (inst, disks) <- inst_disks, + s_uuid <- mapMaybe computeDiskSecondaryNode disks, + s_uuid == nname] + in (pri_inst, sec_insts) -- | Computes the role of a node. getNodeRole :: ConfigData -> Node -> NodeRole -- 2.8.0.rc3.226.g39d4020