Show More
@@ -1,112 +1,118 b'' | |||
|
1 | 1 | .. _performance: |
|
2 | 2 | |
|
3 | 3 | ================================ |
|
4 | 4 | Optimizing Kallithea performance |
|
5 | 5 | ================================ |
|
6 | 6 | |
|
7 | 7 | When serving a large amount of big repositories, Kallithea can start performing |
|
8 | 8 | slower than expected. Because of the demanding nature of handling large amounts |
|
9 | 9 | of data from version control systems, here are some tips on how to get the best |
|
10 | 10 | performance. |
|
11 | 11 | |
|
12 | 12 | |
|
13 | 13 | Fast storage |
|
14 | 14 | ------------ |
|
15 | 15 | |
|
16 | 16 | Kallithea is often I/O bound, and hence a fast disk (SSD/SAN) and plenty of RAM |
|
17 | 17 | is usually more important than a fast CPU. |
|
18 | 18 | |
|
19 | 19 | |
|
20 | 20 | Caching |
|
21 | 21 | ------- |
|
22 | 22 | |
|
23 | 23 | Tweak beaker cache settings in the ini file. The actual effect of that is |
|
24 | 24 | questionable. |
|
25 | 25 | |
|
26 | 26 | |
|
27 | 27 | Database |
|
28 | 28 | -------- |
|
29 | 29 | |
|
30 | 30 | SQLite is a good option when having a small load on the system. But due to |
|
31 | 31 | locking issues with SQLite, it is not recommended to use it for larger |
|
32 | 32 | deployments. |
|
33 | 33 | |
|
34 | 34 | Switching to MySQL or PostgreSQL will result in an immediate performance |
|
35 | 35 | increase. A tool like SQLAlchemyGrate_ can be used for migrating to another |
|
36 | 36 | database platform. |
|
37 | 37 | |
|
38 | 38 | |
|
39 | 39 | Horizontal scaling |
|
40 | 40 | ------------------ |
|
41 | 41 | |
|
42 | 42 | Scaling horizontally means running several Kallithea instances and let them |
|
43 | 43 | share the load. That can give huge performance benefits when dealing with large |
|
44 | 44 | amounts of traffic (many users, CI servers, etc.). Kallithea can be scaled |
|
45 | 45 | horizontally on one (recommended) or multiple machines. |
|
46 | 46 | |
|
47 | 47 | It is generally possible to run WSGI applications multithreaded, so that |
|
48 | 48 | several HTTP requests are served from the same Python process at once. That can |
|
49 | 49 | in principle give better utilization of internal caches and less process |
|
50 | 50 | overhead. |
|
51 | 51 | |
|
52 | 52 | One danger of running multithreaded is that program execution becomes much more |
|
53 | 53 | complex; programs must be written to consider all combinations of events and |
|
54 | 54 | problems might depend on timing and be impossible to reproduce. |
|
55 | 55 | |
|
56 | 56 | Kallithea can't promise to be thread-safe, just like the embedded Mercurial |
|
57 | 57 | backend doesn't make any strong promises when used as Kallithea uses it. |
|
58 | 58 | Instead, we recommend scaling by using multiple server processes. |
|
59 | 59 | |
|
60 | 60 | Web servers with multiple worker processes (such as ``mod_wsgi`` with the |
|
61 | 61 | ``WSGIDaemonProcess`` ``processes`` parameter) will work out of the box. |
|
62 | 62 | |
|
63 | 63 | In order to scale horizontally on multiple machines, you need to do the |
|
64 | 64 | following: |
|
65 | 65 | |
|
66 | 66 | - Each instance's ``data`` storage needs to be configured to be stored on a |
|
67 | 67 | shared disk storage, preferably together with repositories. This ``data`` |
|
68 | 68 | dir contains template caches, sessions, whoosh index and is used for |
|
69 | 69 | task locking (so it is safe across multiple instances). Set the |
|
70 | 70 | ``cache_dir``, ``index_dir``, ``beaker.cache.data_dir``, ``beaker.cache.lock_dir`` |
|
71 | 71 | variables in each .ini file to a shared location across Kallithea instances |
|
72 | 72 | - If using several Celery instances, |
|
73 | 73 | the message broker should be common to all of them (e.g., one |
|
74 | 74 | shared RabbitMQ server) |
|
75 | 75 | - Load balance using round robin or IP hash, recommended is writing LB rules |
|
76 | 76 | that will separate regular user traffic from automated processes like CI |
|
77 | 77 | servers or build bots. |
|
78 | 78 | |
|
79 | 79 | |
|
80 | 80 | Serve static files directly from the web server |
|
81 | 81 | ----------------------------------------------- |
|
82 | 82 | |
|
83 | 83 | With the default ``static_files`` ini setting, the Kallithea WSGI application |
|
84 |
will take care of serving the static files f |
|
|
85 | the root of the application URL. While doing that, it will currently also | |
|
86 | apply buffering and compression of all the responses it is serving. | |
|
84 | will take care of serving the static files from ``kallithea/public/`` at the | |
|
85 | root of the application URL. | |
|
87 | 86 | |
|
88 |
The actual serving of the static files is unlikely to be a |
|
|
89 | Kallithea setup. The buffering of responses is more likely to be a problem; | |
|
90 | large responses (clones or pulls) will have to be fully processed and spooled | |
|
91 | to disk or memory before the client will see any response. | |
|
87 | The actual serving of the static files is very fast and unlikely to be a | |
|
88 | problem in a Kallithea setup - the responses generated by Kallithea from | |
|
89 | database and repository content will take significantly more time and | |
|
90 | resources. | |
|
92 | 91 | |
|
93 | 92 | To serve static files from the web server, use something like this Apache config |
|
94 | 93 | snippet:: |
|
95 | 94 | |
|
96 | 95 | Alias /images/ /srv/kallithea/kallithea/kallithea/public/images/ |
|
97 | 96 | Alias /css/ /srv/kallithea/kallithea/kallithea/public/css/ |
|
98 | 97 | Alias /js/ /srv/kallithea/kallithea/kallithea/public/js/ |
|
99 | 98 | Alias /codemirror/ /srv/kallithea/kallithea/kallithea/public/codemirror/ |
|
100 | 99 | Alias /fontello/ /srv/kallithea/kallithea/kallithea/public/fontello/ |
|
101 | 100 | |
|
102 | 101 | Then disable serving of static files in the ``.ini`` ``app:main`` section:: |
|
103 | 102 | |
|
104 | 103 | static_files = false |
|
105 | 104 | |
|
106 | 105 | If using Kallithea installed as a package, you should be able to find the files |
|
107 | under site-packages/kallithea, either in your Python installation or in your | |
|
106 | under ``site-packages/kallithea``, either in your Python installation or in your | |
|
108 | 107 | virtualenv. When upgrading, make sure to update the web server configuration |
|
109 | 108 | too if necessary. |
|
110 | 109 | |
|
110 | It might also be possible to improve performance by configuring the web server | |
|
111 | to compress responses (served from static files or generated by Kallithea) when | |
|
112 | serving them. That might also imply buffering of responses - that is more | |
|
113 | likely to be a problem; large responses (clones or pulls) will have to be fully | |
|
114 | processed and spooled to disk or memory before the client will see any | |
|
115 | response. See the documentation for your web server. | |
|
116 | ||
|
111 | 117 | |
|
112 | 118 | .. _SQLAlchemyGrate: https://github.com/shazow/sqlalchemygrate |
@@ -1,112 +1,110 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | # This program is free software: you can redistribute it and/or modify |
|
3 | 3 | # it under the terms of the GNU General Public License as published by |
|
4 | 4 | # the Free Software Foundation, either version 3 of the License, or |
|
5 | 5 | # (at your option) any later version. |
|
6 | 6 | # |
|
7 | 7 | # This program is distributed in the hope that it will be useful, |
|
8 | 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
9 | 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
10 | 10 | # GNU General Public License for more details. |
|
11 | 11 | # |
|
12 | 12 | # You should have received a copy of the GNU General Public License |
|
13 | 13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
14 | 14 | """ |
|
15 | 15 | Pylons middleware initialization |
|
16 | 16 | """ |
|
17 | 17 | |
|
18 | 18 | from routes.middleware import RoutesMiddleware |
|
19 | 19 | from paste.cascade import Cascade |
|
20 | 20 | from paste.registry import RegistryManager |
|
21 | 21 | from paste.urlparser import StaticURLParser |
|
22 | 22 | from paste.deploy.converters import asbool |
|
23 | from paste.gzipper import make_gzip_middleware | |
|
24 | 23 | |
|
25 | 24 | from pylons.middleware import ErrorHandler, StatusCodeRedirect |
|
26 | 25 | from pylons.wsgiapp import PylonsApp |
|
27 | 26 | |
|
28 | 27 | from kallithea.lib.middleware.simplehg import SimpleHg |
|
29 | 28 | from kallithea.lib.middleware.simplegit import SimpleGit |
|
30 | 29 | from kallithea.lib.middleware.https_fixup import HttpsFixup |
|
31 | 30 | from kallithea.lib.middleware.sessionmiddleware import SecureSessionMiddleware |
|
32 | 31 | from kallithea.config.environment import load_environment |
|
33 | 32 | from kallithea.lib.middleware.wrapper import RequestWrapper |
|
34 | 33 | |
|
35 | 34 | |
|
36 | 35 | def make_app(global_conf, full_stack=True, static_files=True, **app_conf): |
|
37 | 36 | """Create a Pylons WSGI application and return it |
|
38 | 37 | |
|
39 | 38 | ``global_conf`` |
|
40 | 39 | The inherited configuration for this application. Normally from |
|
41 | 40 | the [DEFAULT] section of the Paste ini file. |
|
42 | 41 | |
|
43 | 42 | ``full_stack`` |
|
44 | 43 | Whether or not this application provides a full WSGI stack (by |
|
45 | 44 | default, meaning it handles its own exceptions and errors). |
|
46 | 45 | Disable full_stack when this application is "managed" by |
|
47 | 46 | another WSGI middleware. |
|
48 | 47 | |
|
49 | 48 | ``app_conf`` |
|
50 | 49 | The application's local configuration. Normally specified in |
|
51 | 50 | the [app:<name>] section of the Paste ini file (where <name> |
|
52 | 51 | defaults to main). |
|
53 | 52 | |
|
54 | 53 | """ |
|
55 | 54 | # Configure the Pylons environment |
|
56 | 55 | config = load_environment(global_conf, app_conf) |
|
57 | 56 | |
|
58 | 57 | # The Pylons WSGI app |
|
59 | 58 | app = PylonsApp(config=config) |
|
60 | 59 | |
|
61 | 60 | # Routing/Session/Cache Middleware |
|
62 | 61 | app = RoutesMiddleware(app, config['routes.map'], use_method_override=False) |
|
63 | 62 | app = SecureSessionMiddleware(app, config) |
|
64 | 63 | |
|
65 | 64 | # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares) |
|
66 | 65 | if asbool(config['pdebug']): |
|
67 | 66 | from kallithea.lib.profiler import ProfilingMiddleware |
|
68 | 67 | app = ProfilingMiddleware(app) |
|
69 | 68 | |
|
70 | 69 | if asbool(full_stack): |
|
71 | 70 | |
|
72 | 71 | from kallithea.lib.middleware.sentry import Sentry |
|
73 | 72 | from kallithea.lib.middleware.errormator import Errormator |
|
74 | 73 | if Errormator and asbool(config['app_conf'].get('errormator')): |
|
75 | 74 | app = Errormator(app, config) |
|
76 | 75 | elif Sentry: |
|
77 | 76 | app = Sentry(app, config) |
|
78 | 77 | |
|
79 | 78 | # Handle Python exceptions |
|
80 | 79 | app = ErrorHandler(app, global_conf, **config['pylons.errorware']) |
|
81 | 80 | |
|
82 | 81 | # Display error documents for 401, 403, 404 status codes (and |
|
83 | 82 | # 500 when debug is disabled) |
|
84 | 83 | # Note: will buffer the output in memory! |
|
85 | 84 | if asbool(config['debug']): |
|
86 | 85 | app = StatusCodeRedirect(app) |
|
87 | 86 | else: |
|
88 | 87 | app = StatusCodeRedirect(app, [400, 401, 403, 404, 500]) |
|
89 | 88 | |
|
90 | 89 | # we want our low level middleware to get to the request ASAP. We don't |
|
91 | 90 | # need any pylons stack middleware in them - especially no StatusCodeRedirect buffering |
|
92 | 91 | app = SimpleHg(app, config) |
|
93 | 92 | app = SimpleGit(app, config) |
|
94 | 93 | |
|
95 | 94 | # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy |
|
96 | 95 | if any(asbool(config.get(x)) for x in ['https_fixup', 'force_https', 'use_htsts']): |
|
97 | 96 | app = HttpsFixup(app, config) |
|
98 | 97 | |
|
99 | 98 | app = RequestWrapper(app, config) # logging |
|
100 | 99 | |
|
101 | 100 | # Establish the Registry for this application |
|
102 | 101 | app = RegistryManager(app) # thread / request-local module globals / variables |
|
103 | 102 | |
|
104 | 103 | if asbool(static_files): |
|
105 | 104 | # Serve static files |
|
106 | 105 | static_app = StaticURLParser(config['pylons.paths']['static_files']) |
|
107 | 106 | app = Cascade([static_app, app]) |
|
108 | app = make_gzip_middleware(app, global_conf, compress_level=1) | |
|
109 | 107 | |
|
110 | 108 | app.config = config |
|
111 | 109 | |
|
112 | 110 | return app |
General Comments 0
You need to be logged in to leave comments.
Login now