mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-09 11:46:49 +00:00
fix(computes): instance sync asks libvirt outside the DB transaction
refresh_instance_database opened the transaction first and then asked libvirt for the domain list. A host that goes silent on an established connection keeps that call waiting until keepalive gives up (30 s). On SQLite, the default database, the open transaction holds a read lock, so every other write of the app failed meanwhile with "database is locked", logins (session writes) included. Reproduced in the test setup: with a relay to a TCP compute frozen after connecting, three session writes during the 30 s refresh each failed after 5 s. - The connection check and listAllDomains run before the transaction, still under the compute lock, so create, delete and migration of the compute stay excluded as before. - The transaction only applies the changes. It checks the compute row first: a compute deleted while libvirt was asked gets no instances (this ended in a foreign key error before). A failed listing still leaves the database untouched. With the change, the same reproduction writes the sessions at once and keeps the instances. Both new tests fail on the previous code.
This commit is contained in:
parent
7e08224330
commit
fbd780af02
2 changed files with 71 additions and 23 deletions
|
|
@ -220,37 +220,42 @@ def refresh_instance_database(compute):
|
|||
|
||||
try:
|
||||
with libvirt_compute_lock(compute, timeout=15.0):
|
||||
with transaction.atomic():
|
||||
comp_locked = Compute.objects.select_for_update().filter(pk=compute_pk).first()
|
||||
if not comp_locked:
|
||||
return
|
||||
# libvirt is asked before the transaction: a silent host can keep a
|
||||
# call waiting for 30 s, and on SQLite an open transaction makes every
|
||||
# other write of the app fail meanwhile. The compute lock still keeps
|
||||
# create/delete/migration of this compute out until the end.
|
||||
if hasattr(compute, "status") and compute.status is False:
|
||||
return
|
||||
|
||||
if hasattr(compute, "status") and compute.status is False:
|
||||
return
|
||||
try:
|
||||
domains = compute.proxy.wvm.listAllDomains()
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"Failed to retrieve domains from compute %s (%s): %s",
|
||||
getattr(compute, "name", compute),
|
||||
getattr(compute, "hostname", ""),
|
||||
e,
|
||||
)
|
||||
return
|
||||
|
||||
# Map libvirt UUIDs to current domain names
|
||||
host_domains = {}
|
||||
for d in domains:
|
||||
try:
|
||||
domains = compute.proxy.wvm.listAllDomains()
|
||||
host_domains[d.UUIDString()] = d.name()
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"Failed to retrieve domains from compute %s (%s): %s",
|
||||
"Error reading domain metadata from compute %s: %s",
|
||||
getattr(compute, "name", compute),
|
||||
getattr(compute, "hostname", ""),
|
||||
e,
|
||||
)
|
||||
return
|
||||
|
||||
# Map libvirt UUIDs to current domain names
|
||||
host_domains = {}
|
||||
for d in domains:
|
||||
try:
|
||||
host_domains[d.UUIDString()] = d.name()
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"Error reading domain metadata from compute %s: %s",
|
||||
getattr(compute, "name", compute),
|
||||
e,
|
||||
)
|
||||
return
|
||||
with transaction.atomic():
|
||||
# the compute may have been deleted while libvirt was asked
|
||||
comp_locked = Compute.objects.select_for_update().filter(pk=compute_pk).first()
|
||||
if not comp_locked:
|
||||
return
|
||||
|
||||
db_instances = list(Instance.objects.filter(compute=compute).select_for_update())
|
||||
db_uuids = set(inst.uuid for inst in db_instances)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue