Skip to content

Commit db1f185

Browse files
committed
Separate API and UI backends (closes #466)
1 parent f5c4982 commit db1f185

File tree

7 files changed

+74
-12
lines changed

7 files changed

+74
-12
lines changed

CHANGES.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
# Changelog
22

33

4-
## Version 2.14.0
4+
## Version 2.15.0
55

66
* UPGRADE NOTE: This version changes the docker-compose.yml used to deploy Backslash. Please update
77
the configuration when performing the upgrade
8+
* Switch to dual-backend mode - one for API calls and the other for UI-related traffic. This avoids
9+
starvation of the UI responsiveness when lots of sessions report to Backslash at a high rate
10+
11+
## Version 2.14.0
12+
13+
* UPGRADE NOTE: This version changes the docker-compose.yml used to deploy Backslash. Please update the configuration when performing the upgrade
814
* Added API server, written in Rust, to monitor API performance and abusing clients
915
* Many bug fixes and small enhancements
1016

docker/docker-compose-testing-override.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
version: '3'
22
services:
3-
webapp:
3+
python-backend-api:
4+
environment:
5+
- BACKSLASH_TESTING=1
6+
logging:
7+
driver: json-file
8+
python-backend-ui:
49
environment:
510
- BACKSLASH_TESTING=1
611
logging:

docker/docker-compose-unstable-overrides.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
version: '3'
22
services:
3-
webapp:
3+
python-backend-api:
4+
image: getslash/backslash:unstable
5+
python-backend-ui:
46
image: getslash/backslash:unstable
57
api-server:
68
image: getslash/backslash:unstable

docker/docker-compose.yml

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
version: '3'
22
services:
33

4-
webapp:
4+
python-backend-api:
55
image: getslash/backslash
66
command: [
77
"dockerize",
88
"-timeout", "3600s",
99
"-wait", "tcp://db:5432",
1010
"-wait", "tcp://rabbitmq:5672",
1111
"-wait", "tcp://api-server:8000",
12-
"pipenv", "run", "manage", "docker-start"
12+
"pipenv", "run", "manage", "docker-start",
13+
"-b", "python-backend-api",
1314
]
14-
volumes:
15+
volumes: &python-backend-volumes
1516
- "conf:/conf"
1617
- "uploads:/uploads"
17-
environment:
18+
environment: &python-backend-env
1819
- CONFIG_DIRECTORY=/conf
1920
- BACKSLASH_REDIS_SERVER=redis
2021
- BACKSLASH_DATABASE_URI=postgresql://backslash@db/backslash
@@ -28,6 +29,27 @@ services:
2829
logging:
2930
driver: journald
3031

32+
python-backend-ui:
33+
image: getslash/backslash
34+
command: [
35+
"dockerize",
36+
"-timeout", "3600s",
37+
"-wait", "tcp://db:5432",
38+
"-wait", "tcp://rabbitmq:5672",
39+
"-wait", "tcp://api-server:8000",
40+
# This is necessary to avoid a race in configuration directory creation
41+
"-wait", "tcp://python-backend-api:8000",
42+
"pipenv", "run", "manage", "docker-start",
43+
"-b", "python-backend-ui",
44+
]
45+
volumes: *python-backend-volumes
46+
environment: *python-backend-env
47+
depends_on:
48+
- python-backend-api
49+
logging:
50+
driver: journald
51+
52+
3153
worker:
3254
image: getslash/backslash
3355
command: dockerize -timeout 3600s -wait tcp://rabbitmq:5672 pipenv run celery -A flask_app.tasks.main worker -B --loglevel=info --max-tasks-per-child=500
@@ -92,12 +114,14 @@ services:
92114
- BACKSLASH_USE_SSL=
93115
command: ["dockerize",
94116
"-timeout", "3600s",
95-
"-wait", "http://webapp:8000",
117+
"-wait", "http://python-backend-api:8000",
118+
"-wait", "http://python-backend-ui:8000",
96119
"-wait", "http://api-server:8000/metrics",
97120
"pipenv", "run", "manage", "docker-nginx-start"]
98121

99122
depends_on:
100-
- webapp
123+
- python-backend-api
124+
- python-backend-ui
101125
- api-server
102126
ports:
103127
- "8000:80"

etc/nginx-site-conf.j2

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ log_format timed_combined '$remote_addr - $remote_user [$time_local] '
1717
'"$request" $status $body_bytes_sent '
1818
'$request_time $upstream_response_time $pipe';
1919

20+
upstream python-backend-api {
21+
server python-backend-api:8000;
22+
}
23+
24+
upstream python-backend-ui {
25+
server python-backend-ui:8000;
26+
}
27+
28+
map $http_user_agent $upstream {
29+
default python-backend-ui;
30+
"~*.*python.*" python-backend-api;
31+
}
2032

2133
server {
2234
{% if hostname %}
@@ -95,7 +107,7 @@ server {
95107
proxy_set_header X-Real-IP $remote_addr;
96108
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
97109
proxy_set_header X-Forwarded-Proto $scheme;
98-
proxy_pass http://webapp:8000;
110+
proxy_pass http://$upstream;
99111
client_max_body_size 10m;
100112
}
101113
}

flask_app/utils/profiling.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
_DB_HEADER_NAME = "X-Timing-DB"
1515
_API_ENDPOINT_HEADER_NAME = "X-API-Endpoint"
1616

17+
_backend_name = 'python-backend-generic'
18+
19+
def set_backend_name(new_name):
20+
global _backend_name
21+
if new_name is not None:
22+
_backend_name = new_name
23+
1724
_logger = logbook.Logger(__name__)
1825

1926

@@ -55,6 +62,7 @@ def profile_request_end(response):
5562
_send_metrics(db=db, active=active, total=total, endpoint=endpoint)
5663

5764
response.headers.extend(profile_data)
65+
response.headers['X-Backslash-Backend'] = _backend_name
5866
return response
5967

6068

manage.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,27 @@ def cli():
3939

4040
@cli.command(name='docker-start')
4141
@click.option('-p', '--port', default=8000, type=int)
42-
def docker_start(port):
42+
@click.option('-b', '--backend-name', default=None)
43+
def docker_start(port, backend_name):
4344
from flask_app.app import create_app
4445
from flask_app.models import db
46+
from flask_app.utils import profiling
4547
import flask_migrate
4648
import gunicorn.app.base
4749

4850
_ensure_conf()
4951

5052
app = create_app(config={'PROPAGATE_EXCEPTIONS': True})
53+
profiling.set_backend_name(backend_name)
5154

5255
flask_migrate.Migrate(app, db)
5356

5457
with app.app_context():
5558
flask_migrate.upgrade()
5659

57-
workers_count = (multiprocessing.cpu_count() * 2) + 1
60+
# We only allocate one worker per core, since we have two backends to account for
61+
# (both API and UI, not to mention the Rust backend in the future)
62+
workers_count = multiprocessing.cpu_count()
5863

5964
class StandaloneApplication(gunicorn.app.base.BaseApplication):
6065

0 commit comments

Comments
 (0)