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
1=== modified file 'src/provisioningserver/config.py'
2--- src/provisioningserver/config.py 2015-08-13 20:25:12 +0000
3+++ src/provisioningserver/config.py 2015-08-20 16:00:57 +0000
4@@ -125,7 +125,9 @@
5 contextmanager,
6 )
7 from copy import deepcopy
8+from itertools import islice
9 import json
10+import logging
11 import os
12 from os import environ
13 import os.path
14@@ -133,6 +135,8 @@
15 from shutil import copyfile
16 import sqlite3
17 from threading import RLock
18+from time import time
19+import traceback
20 import uuid
21
22 from formencode import (
23@@ -158,6 +162,9 @@
24 )
25 import yaml
26
27+
28+logger = logging.getLogger(__name__)
29+
30 # Default result for cluster UUID if not set
31 UUID_NOT_SET = '** UUID NOT SET **'
32
33@@ -536,20 +543,34 @@
34 **Note** that this returns a context manager which will save changes
35 to the configuration on a clean exit.
36 """
37- # Only one reader or writer at a time.
38- with RunLock(path).wait(timeout=5):
39- # Ensure `path` exists...
40- touch(path)
41- # before loading it in.
42- configfile = cls(path)
43- configfile.load()
44- try:
45- yield configfile
46- except:
47- raise
48- else:
49- if configfile.dirty:
50- configfile.save()
51+ time_opened = None
52+ try:
53+ # Only one reader or writer at a time.
54+ with RunLock(path).wait(timeout=5.0):
55+ time_opened = time()
56+ # Ensure `path` exists...
57+ touch(path)
58+ # before loading it in.
59+ configfile = cls(path)
60+ configfile.load()
61+ try:
62+ yield configfile
63+ except:
64+ raise
65+ else:
66+ if configfile.dirty:
67+ configfile.save()
68+ finally:
69+ if time_opened is not None:
70+ time_open = time() - time_opened
71+ if time_open >= 2.5:
72+ mini_stack = ", from ".join(
73+ "%s:%d" % (fn, lineno) for fn, lineno, _, _ in
74+ islice(reversed(traceback.extract_stack()), 2, 5))
75+ logger.warn(
76+ "Configuration file %s locked for %.1f seconds; this "
77+ "may starve other processes. Called from %s.", path,
78+ time_open, mini_stack)
79
80
81 class ConfigurationMeta(type):
82
83=== modified file 'src/provisioningserver/rpc/boot_images.py'
84--- src/provisioningserver/rpc/boot_images.py 2015-06-25 22:33:37 +0000
85+++ src/provisioningserver/rpc/boot_images.py 2015-08-20 16:00:57 +0000
86@@ -44,7 +44,8 @@
87 global CACHED_BOOT_IMAGES
88 if CACHED_BOOT_IMAGES is None:
89 with ClusterConfiguration.open() as config:
90- CACHED_BOOT_IMAGES = tftppath.list_boot_images(config.tftp_root)
91+ tftp_root = config.tftp_root
92+ CACHED_BOOT_IMAGES = tftppath.list_boot_images(tftp_root)
93 return CACHED_BOOT_IMAGES
94
95