mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
perf(computes): the host's info is read once per request
The overview read the host's info four times, now once (12 libvirt calls). A connection object keeps it next to the capabilities, and the VM page and VM stats take it from there; the VM page's cached_reads block holds only the VM's own data. Free memory is still read each time.
This commit is contained in:
parent
9adc808d26
commit
57f592df70
6 changed files with 57 additions and 32 deletions
|
|
@ -403,6 +403,11 @@ class wvmConnect(object):
|
|||
"""The host's capabilities, read once per object (a request): most getters below parse them"""
|
||||
return self.wvm.getCapabilities()
|
||||
|
||||
@cached_property
|
||||
def host_info(self):
|
||||
"""The host's info (architecture, memory in MiB, CPUs, ...), read once per object"""
|
||||
return self.wvm.getInfo()
|
||||
|
||||
@cached_property
|
||||
def dom_cap_xmls(self):
|
||||
"""The domain capabilities read so far, by emulator, arch, machine and domain type"""
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class wvmHostDetails(wvmConnect):
|
|||
"""
|
||||
Function return memory usage on node.
|
||||
"""
|
||||
all_mem = self.wvm.getInfo()[1] * 1048576
|
||||
all_mem = self.host_info[1] * 1048576
|
||||
freemem = self.wvm.getMemoryStats(-1, 0)
|
||||
if isinstance(freemem, dict):
|
||||
free = (freemem["buffers"] + freemem["free"] + freemem["cached"]) * 1024
|
||||
|
|
@ -78,9 +78,9 @@ class wvmHostDetails(wvmConnect):
|
|||
Function return host server information: hostname, cpu, memory, ...
|
||||
"""
|
||||
info = [self.wvm.getHostname()] # hostname
|
||||
info.append(self.wvm.getInfo()[0]) # architecture
|
||||
info.append(self.wvm.getInfo()[1] * 1048576) # memory
|
||||
info.append(self.wvm.getInfo()[2]) # cpu core count
|
||||
info.append(self.host_info[0]) # architecture
|
||||
info.append(self.host_info[1] * 1048576) # memory
|
||||
info.append(self.host_info[2]) # cpu core count
|
||||
info.append(
|
||||
get_xml_path(self.wvm.getSysinfo(0), func=cpu_version)
|
||||
) # cpu version
|
||||
|
|
|
|||
|
|
@ -490,13 +490,13 @@ class wvmInstance(wvmConnect):
|
|||
@contextlib.contextmanager
|
||||
def cached_reads(self):
|
||||
"""
|
||||
Within the block the domain XML (per flags value), the VM's state,
|
||||
the host's info and each disk's volume are read from libvirt once
|
||||
(the host's capabilities are read once per object anyway), for pages
|
||||
that only read: a VM page reads the XML in some 30 getters and each
|
||||
disk in two lists. Code that changes the VM must not run inside: most
|
||||
changes (devices, QoS, memory) do not go through _defineXML, the only
|
||||
call that drops the cache.
|
||||
Within the block the domain XML (per flags value), the VM's state and
|
||||
each disk's volume are read from libvirt once (the host's info and
|
||||
capabilities are read once per object anyway), for pages that only
|
||||
read: a VM page reads the XML in some 30 getters and each disk in two
|
||||
lists. Code that changes the VM must not run inside: most changes
|
||||
(devices, QoS, memory) do not go through _defineXML, the only call
|
||||
that drops the cache.
|
||||
"""
|
||||
if self._read_cache is not None:
|
||||
raise RuntimeError("cached_reads blocks do not nest")
|
||||
|
|
@ -522,9 +522,6 @@ class wvmInstance(wvmConnect):
|
|||
self._read_cache.clear()
|
||||
return self.wvm.defineXML(xml)
|
||||
|
||||
def _host_info(self):
|
||||
return self._cached("host info", self.wvm.getInfo)
|
||||
|
||||
def get_status(self):
|
||||
"""
|
||||
VIR_DOMAIN_NOSTATE = 0
|
||||
|
|
@ -617,11 +614,11 @@ class wvmInstance(wvmConnect):
|
|||
return description or ""
|
||||
|
||||
def get_max_memory(self):
|
||||
return self._host_info()[1] * 1048576
|
||||
return self.host_info[1] * 1048576
|
||||
|
||||
def get_max_cpus(self):
|
||||
"""Get number of physical CPUs."""
|
||||
hostinfo = self._host_info()
|
||||
hostinfo = self.host_info
|
||||
pcpus = hostinfo[4] * hostinfo[5] * hostinfo[6] * hostinfo[7]
|
||||
return range(1, int(pcpus + 1))
|
||||
|
||||
|
|
@ -1308,7 +1305,7 @@ class wvmInstance(wvmConnect):
|
|||
"nics": [{"dev": i, "rx": 0, "tx": 0} for i in range(len(nics))],
|
||||
}
|
||||
|
||||
key = [self.instance.ID(), self._host_info()[2], disks, nics]
|
||||
key = [self.instance.ID(), self.host_info[2], disks, nics]
|
||||
unread = (-1,) * 8
|
||||
|
||||
def read():
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"""A wvmConnect reads the host's capabilities once, and its domain
|
||||
"""A wvmConnect reads the host's info and capabilities once, and its domain
|
||||
capabilities once per emulator, arch, machine and domain type: the create
|
||||
page's getters parse them some 60 times. An object lives for one request."""
|
||||
page's getters parse the capabilities some 60 times, the overview reads the
|
||||
info four times. An object lives for one request."""
|
||||
|
||||
import unittest
|
||||
|
||||
|
|
@ -10,6 +11,7 @@ if not settings.configured:
|
|||
settings.configure(MAC_OUI="52:54:10")
|
||||
|
||||
from vrtManager.connection import wvmConnect
|
||||
from vrtManager.hostdetails import wvmHostDetails
|
||||
|
||||
CAPABILITIES = """<capabilities><host><cpu><arch>x86_64</arch></cpu></host>
|
||||
<guest><os_type>hvm</os_type><arch name='x86_64'><emulator>/usr/bin/qemu-kvm</emulator>
|
||||
|
|
@ -25,13 +27,30 @@ class FakeConn:
|
|||
self.reads.append("capabilities")
|
||||
return CAPABILITIES
|
||||
|
||||
def getInfo(self):
|
||||
self.reads.append("info")
|
||||
return ["x86_64", 2048, 4, 0, 1, 2, 2, 1]
|
||||
|
||||
def getMemoryStats(self, cell, flags):
|
||||
self.reads.append("memory")
|
||||
return {"buffers": 0, "free": 1048576, "cached": 0}
|
||||
|
||||
def getHostname(self):
|
||||
return "host"
|
||||
|
||||
def getSysinfo(self, flags):
|
||||
return "<sysinfo/>"
|
||||
|
||||
def getURI(self):
|
||||
return "qemu:///system"
|
||||
|
||||
def getDomainCapabilities(self, emulator, arch, machine, virttype):
|
||||
self.reads.append((emulator, arch, machine, virttype))
|
||||
return "<domainCapabilities/>"
|
||||
|
||||
|
||||
def connection():
|
||||
conn = wvmConnect.__new__(wvmConnect) # no libvirt connection: only these calls
|
||||
def connection(cls=wvmConnect):
|
||||
conn = cls.__new__(cls) # no libvirt connection: only these calls
|
||||
conn.wvm = FakeConn()
|
||||
return conn
|
||||
|
||||
|
|
@ -57,6 +76,13 @@ class CapabilitiesOnceTestCase(unittest.TestCase):
|
|||
["capabilities", ("/usr/bin/qemu-kvm", "x86_64", "q35", "kvm"), ("/usr/bin/qemu-kvm", "x86_64", "pc", "kvm")],
|
||||
)
|
||||
|
||||
def test_the_overview_reads_the_info_once_and_the_free_memory_each_time(self):
|
||||
conn = connection(wvmHostDetails)
|
||||
conn.get_node_info()
|
||||
conn.get_memory_usage()
|
||||
self.assertEqual(conn.get_memory_usage()["total"], 2048 * 1048576)
|
||||
self.assertEqual(conn.wvm.reads, ["info", "memory", "memory"])
|
||||
|
||||
def test_two_objects_do_not_share_their_reads(self):
|
||||
a, b = connection(), connection()
|
||||
a.get_cap_xml()
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
"""wvmInstance.cached_reads(): inside the block the domain XML (per flags),
|
||||
the VM's state, the host info and each disk's volume are read from libvirt
|
||||
once; outside it every read goes to libvirt, as code that changes the VM
|
||||
needs. The host capabilities are read once per object."""
|
||||
the VM's state and each disk's volume are read from libvirt once; outside it
|
||||
every read goes to libvirt, as code that changes the VM needs. The host's
|
||||
info and capabilities are read once per object."""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock
|
||||
|
|
@ -60,20 +60,17 @@ class CachedReadsTestCase(unittest.TestCase):
|
|||
p = proxy()
|
||||
p._XMLDesc(0)
|
||||
p._XMLDesc(0)
|
||||
p._host_info()
|
||||
p._host_info()
|
||||
self.assertEqual((p.instance.reads, p.wvm.caps), ([0, 0], 2))
|
||||
p.get_status()
|
||||
p.get_status()
|
||||
self.assertEqual(p.instance.reads, [0, 0, "info", "info"])
|
||||
|
||||
def test_inside_the_block_each_flags_value_is_read_once(self):
|
||||
p = proxy()
|
||||
with p.cached_reads():
|
||||
first = [p._XMLDesc(0), p._XMLDesc(1), p._XMLDesc(3)]
|
||||
again = [p._XMLDesc(0), p._XMLDesc(1), p._XMLDesc(3)]
|
||||
p._host_info()
|
||||
p._host_info()
|
||||
self.assertEqual(first, again)
|
||||
self.assertEqual(p.instance.reads, [0, 1, 3])
|
||||
self.assertEqual(p.wvm.caps, 1)
|
||||
|
||||
def test_the_block_ends_with_its_cache(self):
|
||||
p = proxy()
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ def instance(state, clock):
|
|||
proxy._read_cache = None
|
||||
proxy.instance = FakeDomain(state, clock)
|
||||
proxy._XMLDesc = lambda flags: XML
|
||||
proxy._host_info = lambda: ["x86_64", 0, 4] # 4 host CPUs
|
||||
proxy.host_info = ["x86_64", 0, 4] # 4 host CPUs
|
||||
proxy.get_status = lambda: state
|
||||
return proxy
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ class UsageTestCase(unittest.TestCase):
|
|||
proxy.instance.domain_id = 8
|
||||
|
||||
def other_host_cpus(proxy):
|
||||
proxy._host_info = lambda: ["x86_64", 0, 8]
|
||||
proxy.host_info = ["x86_64", 0, 8]
|
||||
|
||||
def nic_renamed(proxy):
|
||||
proxy._XMLDesc = lambda flags: XML.replace("vnet0", "vnet1")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue