Merge lp:~allenap/maas/avoid-long-held-config-locks into lp:~maas-committers/maas/trunk

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 4211
Proposed branch: lp:~allenap/maas/avoid-long-held-config-locks
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 93 lines (+37/-15)
2 files modified
src/provisioningserver/config.py (+35/-14)
src/provisioningserver/rpc/boot_images.py (+2/-1)
To merge this branch: bzr merge lp:~allenap/maas/avoid-long-held-config-locks
Reviewer Review Type Date Requested Status
Andres Rodriguez (community) Approve
Review via email: mp+268606@code.launchpad.net

Commit message

Don't hold a lock on the cluster configuration while finding boot images.

Log a warning when a lock has been held for too long.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

Just saying, but none of this would have been a problem if we had stayed with the SQLite-based configuration class.

Revision history for this message
Andres Rodriguez (andreserl) wrote :

lgtm! The big question, however, is why would the SQLite-based configuration would have avoided this problem, when the problem that we were actually seeing was related to the MAAS import process? (at least that was in the Lab)

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

> lgtm! The big question, however, is why would the SQLite-based
> configuration would have avoided this problem, when the problem that
> we were actually seeing was related to the MAAS import process? (at
> least that was in the Lab)

It would have prevented the problem of exclusive locking; everyone could
have shared the configuration safely.

Revision history for this message
Gavin Panella (allenap) wrote :

Thanks for the review!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/provisioningserver/config.py'
--- src/provisioningserver/config.py 2015-08-13 20:25:12 +0000
+++ src/provisioningserver/config.py 2015-08-20 16:00:57 +0000
@@ -125,7 +125,9 @@
125 contextmanager,125 contextmanager,
126)126)
127from copy import deepcopy127from copy import deepcopy
128from itertools import islice
128import json129import json
130import logging
129import os131import os
130from os import environ132from os import environ
131import os.path133import os.path
@@ -133,6 +135,8 @@
133from shutil import copyfile135from shutil import copyfile
134import sqlite3136import sqlite3
135from threading import RLock137from threading import RLock
138from time import time
139import traceback
136import uuid140import uuid
137141
138from formencode import (142from formencode import (
@@ -158,6 +162,9 @@
158)162)
159import yaml163import yaml
160164
165
166logger = logging.getLogger(__name__)
167
161# Default result for cluster UUID if not set168# Default result for cluster UUID if not set
162UUID_NOT_SET = '** UUID NOT SET **'169UUID_NOT_SET = '** UUID NOT SET **'
163170
@@ -536,20 +543,34 @@
536 **Note** that this returns a context manager which will save changes543 **Note** that this returns a context manager which will save changes
537 to the configuration on a clean exit.544 to the configuration on a clean exit.
538 """545 """
539 # Only one reader or writer at a time.546 time_opened = None
540 with RunLock(path).wait(timeout=5):547 try:
541 # Ensure `path` exists...548 # Only one reader or writer at a time.
542 touch(path)549 with RunLock(path).wait(timeout=5.0):
543 # before loading it in.550 time_opened = time()
544 configfile = cls(path)551 # Ensure `path` exists...
545 configfile.load()552 touch(path)
546 try:553 # before loading it in.
547 yield configfile554 configfile = cls(path)
548 except:555 configfile.load()
549 raise556 try:
550 else:557 yield configfile
551 if configfile.dirty:558 except:
552 configfile.save()559 raise
560 else:
561 if configfile.dirty:
562 configfile.save()
563 finally:
564 if time_opened is not None:
565 time_open = time() - time_opened
566 if time_open >= 2.5:
567 mini_stack = ", from ".join(
568 "%s:%d" % (fn, lineno) for fn, lineno, _, _ in
569 islice(reversed(traceback.extract_stack()), 2, 5))
570 logger.warn(
571 "Configuration file %s locked for %.1f seconds; this "
572 "may starve other processes. Called from %s.", path,
573 time_open, mini_stack)
553574
554575
555class ConfigurationMeta(type):576class ConfigurationMeta(type):
556577
=== modified file 'src/provisioningserver/rpc/boot_images.py'
--- src/provisioningserver/rpc/boot_images.py 2015-06-25 22:33:37 +0000
+++ src/provisioningserver/rpc/boot_images.py 2015-08-20 16:00:57 +0000
@@ -44,7 +44,8 @@
44 global CACHED_BOOT_IMAGES44 global CACHED_BOOT_IMAGES
45 if CACHED_BOOT_IMAGES is None:45 if CACHED_BOOT_IMAGES is None:
46 with ClusterConfiguration.open() as config:46 with ClusterConfiguration.open() as config:
47 CACHED_BOOT_IMAGES = tftppath.list_boot_images(config.tftp_root)47 tftp_root = config.tftp_root
48 CACHED_BOOT_IMAGES = tftppath.list_boot_images(tftp_root)
48 return CACHED_BOOT_IMAGES49 return CACHED_BOOT_IMAGES
4950
5051