Skip to content

Commit fe3043d

Browse files
committed
wsgi can cache tracker instance enabled by feature flag.
Patch by Marcus Priesch caches a loaded tracker instance and reuse it for future client sessions. It is enabled by a feature flag in wsgi.py since it arrived during the 2.2.0 beta period. The provided wsgi.py is modified to enable it. Testing is run with flag enabled and disabled. Ralf Schlatterbeck and Marcus tested it on one of their larger more complex trackers and it sped up the response time to a client request by a factor of 3 (270ms down to about 80-85ms).
1 parent 6330101 commit fe3043d

File tree

5 files changed

+70
-8
lines changed

5 files changed

+70
-8
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ Features:
176176
i18n object is now also correctly set for the mail interface:
177177
previously the 'language' setting in the [mailgw] section seems to
178178
have been ignored. Thanks to Marcus Priesch for the patch.
179+
- issue2551212 - speed up wsgi interface by caching the tracker
180+
instance. Hidden behind a feature flag. See upgrading.txt for
181+
details. (Marcus Priesch with feature flag by John Rouillard)
179182

180183
2021-07-13 2.1.0
181184

doc/upgrading.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,25 @@ prompt). This prevents the new password from showing up in the output
209209
of ps or shell history. The new password will be encrypted using the
210210
default encryption method (usually pbkdf2).
211211

212+
Enable performance improvement for wsgi mode (optional)
213+
-------------------------------------------------------
214+
215+
There is an experimental wsgi performance improvement mode that caches
216+
the loaded roundup instance. This eliminates disk reads that are
217+
incurred on each connection. In one report it improves speed by a
218+
factor of 2 to 3 times. To enable this you should add a feature flag
219+
to your Roundup wsgi wrapper (see the file
220+
``.../share/frontends/wsgi.py``) so it looks like::
221+
222+
feature_flags = { "cache_tracker": "" }
223+
app = RequestDispatcher(tracker_home, feature_flags=feature_flags)
224+
225+
to enable this mode. Note that this is experimental and was added
226+
during the 2.2.0 beta period, so it is enabled using a feature flag.
227+
If you use this and it works for you please followup with an email to
228+
the roundup-users at lists.sourceforge.net mailing list so we can
229+
enable it by default in a future release.
230+
212231
Migrating from 2.0.0 to 2.1.0
213232
=============================
214233

frontends/wsgi.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,10 @@
1212
# Set the path to tracker home.
1313
tracker_home = '/path/to/tracker'
1414

15+
# Enable the feature flag to speed up wsgi response by caching the
16+
# Roundup tracker instance on startup. See upgrading.txt for
17+
# more info.
18+
feature_flags = { "cache_tracker": "" }
19+
1520
# Definition signature for app: app(environ, start_response):
16-
app = RequestDispatcher(tracker_home)
21+
app = RequestDispatcher(tracker_home= feature_flags=feature_flags)

roundup/cgi/wsgi_handler.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,24 @@ def get_wfile(self):
7474

7575

7676
class RequestDispatcher(object):
77-
def __init__(self, home, debug=False, timing=False, lang=None):
77+
def __init__(self, home, debug=False, timing=False, lang=None,
78+
feature_flags=None):
7879
assert os.path.isdir(home), '%r is not a directory' % (home,)
7980
self.home = home
8081
self.debug = debug
8182
self.timing = timing
83+
self.feature_flags= feature_flags or {}
84+
self.tracker = None
8285
if lang:
8386
self.translator = TranslationService.get_translation(lang,
8487
tracker_home=home)
8588
else:
8689
self.translator = None
87-
self.preload()
90+
91+
if "cache_tracker" in self.feature_flags:
92+
self.tracker = roundup.instance.open(self.home, not self.debug)
93+
else:
94+
self.preload()
8895

8996
def __call__(self, environ, start_response):
9097
"""Initialize with `apache.Request` object"""
@@ -116,15 +123,25 @@ def __call__(self, environ, start_response):
116123
else:
117124
form = BinaryFieldStorage(fp=environ['wsgi.input'], environ=environ)
118125

119-
with self.get_tracker() as tracker:
120-
client = tracker.Client(tracker, request, environ, form,
126+
if "cache_tracker" in self.feature_flags:
127+
client = self.tracker.Client(self.tracker, request, environ, form,
121128
self.translator)
122129
try:
123130
client.main()
124131
except roundup.cgi.client.NotFound:
125132
request.start_response([('Content-Type', 'text/html')], 404)
126133
request.wfile.write(s2b('Not found: %s' %
127134
html_escape(client.path)))
135+
else:
136+
with self.get_tracker() as tracker:
137+
client = tracker.Client(tracker, request, environ, form,
138+
self.translator)
139+
try:
140+
client.main()
141+
except roundup.cgi.client.NotFound:
142+
request.start_response([('Content-Type', 'text/html')], 404)
143+
request.wfile.write(s2b('Not found: %s' %
144+
html_escape(client.path)))
128145

129146
# all body data has been written using wfile
130147
return []

test/test_liveserver.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
_py3 = sys.version_info[0] > 2
3939

4040
@skip_requests
41-
class SimpleTest(LiveServerTestCase):
41+
class WsgiSetup(LiveServerTestCase):
4242
# have chicken and egg issue here. Need to encode the base_url
4343
# in the config file but we don't know it until after
4444
# the server is started and has read the config.ini.
@@ -103,7 +103,8 @@ def teardown_class(cls):
103103
i18n.DOMAIN = cls.backup_domain
104104

105105
def create_app(self):
106-
'''The wsgi app to start'''
106+
'''The wsgi app to start - no feature_flags set.'''
107+
107108
if _py3:
108109
return validator(RequestDispatcher(self.dirname))
109110
else:
@@ -112,6 +113,11 @@ def create_app(self):
112113
return RequestDispatcher(self.dirname)
113114

114115

116+
class BaseTestCases(WsgiSetup):
117+
"""Class with all tests to run against wsgi server. Is reused when
118+
wsgi server is started with various feature flags
119+
"""
120+
115121
def test_start_page(self):
116122
""" simple test that verifies that the server can serve a start page.
117123
"""
@@ -973,4 +979,16 @@ def test_new_file_via_rest(self):
973979
self.assertEqual(r.status_code, 201)
974980
print(r.status_code)
975981

976-
982+
class TestFeatureFlagCacheTrackerOn(BaseTestCases, WsgiSetup):
983+
"""Class to run all test in BaseTestCases with the cache_tracker
984+
feature flag enabled when starting the wsgi server
985+
"""
986+
def create_app(self):
987+
'''The wsgi app to start with feature flag enabled'''
988+
ff = { "cache_tracker": "" }
989+
if _py3:
990+
return validator(RequestDispatcher(self.dirname, feature_flags=ff))
991+
else:
992+
# wsgiref/validator.py InputWrapper::readline is broke and
993+
# doesn't support the max bytes to read argument.
994+
return RequestDispatcher(self.dirname, feature_flags=ff)

0 commit comments

Comments
 (0)