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