Skip to content

Commit 50c95de

Browse files
committed
Convert master to flask server instead of using rabbitmq
1 parent 35ceffc commit 50c95de

9 files changed

+77
-241
lines changed

app.json

-4
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,12 @@
55
"DEBUG": "False"
66
},
77
"addons": [
8-
"cloudamqp",
98
"heroku-postgresql"
109
],
1110
"formation": {
1211
"web": {
1312
"quantity": 1
1413
},
15-
"worker": {
16-
"quantity": 1
17-
}
1814
},
1915
"stack": "container"
2016
}

backend/entrypoint.sh

+5-3
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ set -ex
33

44
#nginx
55

6-
#rabbitmq-server -detached
7-
86
# We probably don't want this to be automatic but it makes life a lot easier
97
# For setting up the cloud
108
python manage.py migrate
119
python manage.py init_db
1210

13-
#python -m main.worker &
11+
# Prevent outdated from making http requests
12+
echo '["0.2.0", "2099-01-01 00:00:00"]' > /tmp/outdated_cache_outdated
13+
echo '["0.8.3", "2099-01-01 00:00:00"]' > /tmp/outdated_cache_birdseye
14+
15+
gunicorn --bind 127.0.0.1:5000 main.workers.master:app --access-logfile - --error-log - --threads 10 --worker-class gthread &
1416

1517
gunicorn -c gunicorn_config.py book.wsgi:application --bind 0.0.0.0:${PORT:-3000}

backend/main/workers/communications.py

-41
This file was deleted.

backend/main/workers/master.py

+56-49
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import atexit
2+
import multiprocessing
23
import queue
34
from collections import defaultdict
45
from functools import lru_cache
56
from multiprocessing import Queue, Process
67
from threading import Thread
8+
from time import sleep
9+
10+
import flask
711

812
from main import simple_settings
9-
from main.workers.communications import AbstractCommunications, ThreadCommunications
1013
from main.workers.utils import internal_error_result, make_result
1114
from main.workers.worker import worker_loop_in_thread
1215

@@ -48,23 +51,23 @@ def handle_entry(self, entry):
4851

4952
self.task_queue.put(entry)
5053

51-
def await_result(self, callback):
52-
try:
53-
result = self._await_result()
54-
# if result["error"] and result["error"]["sentry_event"]:
55-
# event, hint = result["error"]["sentry_event"]
56-
# capture_event(event, hint)
57-
except Exception:
58-
result = internal_error_result()
54+
def await_result(self):
55+
result = self._await_result()
56+
# if result["error"] and result["error"]["sentry_event"]:
57+
# event, hint = result["error"]["sentry_event"]
58+
# capture_event(event, hint)
5959
self.awaiting_input = result["awaiting_input"]
60-
callback(result)
60+
return result
6161

6262
def _await_result(self):
6363
# TODO cancel if result was cancelled by a newer handle_entry
6464
result = None
65+
# TODO handle initial timeout better
66+
timeout = 10
6567
while result is None:
6668
try:
67-
result = self.result_queue.get(timeout=3)
69+
result = self.result_queue.get(timeout=timeout)
70+
timeout = 3
6871
except queue.Empty:
6972
alive = self.process.is_alive()
7073
print(f"Process {alive=}")
@@ -82,59 +85,63 @@ def _await_result(self):
8285
return result
8386

8487

85-
def master_consumer_loop(comms: AbstractCommunications):
86-
comms = comms.make_master_side_communications()
87-
user_processes = defaultdict(UserProcess)
88+
user_processes = defaultdict(UserProcess)
89+
90+
app = flask.Flask(__name__)
91+
92+
multiprocessing.set_start_method("spawn")
93+
94+
95+
@app.route("/run", methods=["POST"])
96+
def run():
97+
try:
98+
entry = flask.request.json
99+
user_process = user_processes[entry["user_id"]]
100+
user_process.handle_entry(entry)
101+
return user_process.await_result()
102+
except Exception:
103+
return internal_error_result()
104+
88105

89-
while True:
90-
entry = comms.recv_entry()
91-
user_id = str(entry["user_id"])
106+
@app.route("/health")
107+
def health():
108+
return "ok"
92109

93-
def callback(result):
94-
comms.send_result(user_id, result)
95110

96-
try:
97-
user_process = user_processes[user_id]
98-
user_process.handle_entry(entry)
99-
Thread(
100-
target=user_process.await_result,
101-
args=[callback],
102-
).start()
103-
except Exception:
104-
callback(internal_error_result())
111+
def run_server():
112+
app.run(host="0.0.0.0")
113+
114+
115+
master_url = "http://localhost:5000/"
105116

106117

107118
@lru_cache()
108-
def master_communications() -> AbstractCommunications:
109-
if simple_settings.CLOUDAMQP_URL:
110-
from .pika import PikaCommunications
111-
comms = PikaCommunications()
112-
else:
113-
comms = ThreadCommunications()
119+
def master_session():
120+
import requests
121+
session = requests.Session()
114122

115123
if not simple_settings.SEPARATE_WORKER_PROCESS:
116124
Thread(
117-
target=master_consumer_loop,
118-
args=[comms],
125+
target=run_server,
119126
daemon=True,
120-
name=master_consumer_loop.__name__,
127+
name=run_server.__name__,
121128
).start()
122129

123-
return comms
124-
130+
# Wait until alive
131+
while True:
132+
try:
133+
session.get(master_url + "health")
134+
break
135+
except requests.exceptions.ConnectionError:
136+
sleep(1)
125137

126-
def worker_result(entry):
127-
comms: AbstractCommunications = master_communications()
128-
comms.send_entry(entry)
129-
user_id = str(entry["user_id"])
130-
return comms.recv_result(user_id)
138+
return session
131139

132140

133-
def main():
134-
from main.workers.pika import PikaCommunications
135-
comms = PikaCommunications()
136-
master_consumer_loop(comms)
141+
def worker_result(entry):
142+
session = master_session()
143+
return session.post(master_url + "run", json=entry).json()
137144

138145

139146
if __name__ == '__main__':
140-
main()
147+
run_server()

backend/main/workers/pika.py

-98
This file was deleted.

docker-compose.yml

+9-20
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,18 @@ services:
1212
env_file: .env
1313
environment:
1414
SEPARATE_WORKER_PROCESS: 'True'
15-
CLOUDAMQP_URL: amqp://rabbitmq
1615
DATABASE_URL: postgres://postgres:pass@database/postgres
1716
stdin_open: true
1817
tty: true
19-
depends_on:
20-
- rabbitmq
21-
worker:
22-
image: python-init
23-
build: .
24-
env_file: .env
25-
stdin_open: true
26-
tty: true
27-
entrypoint: python -m main.workers.master
28-
environment:
29-
SEPARATE_WORKER_PROCESS: 'True'
30-
CLOUDAMQP_URL: amqp://rabbitmq
31-
depends_on:
32-
- rabbitmq
33-
rabbitmq:
34-
image: rabbitmq
35-
ports:
36-
- 5672:5672
37-
- 15672:15672
18+
# worker:
19+
# image: python-init
20+
# build: .
21+
# env_file: .env
22+
# stdin_open: true
23+
# tty: true
24+
# entrypoint: gunicorn --threads 10 --bind 0.0.0.0:5000 main.workers.master:app
25+
# ports:
26+
# - 5000:5000
3827
database:
3928
image: postgres:12
4029
volumes:

heroku.yml

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
build:
22
docker:
33
web: Dockerfile
4-
worker: Dockerfile
5-
run:
6-
worker: python -m main.workers.master

0 commit comments

Comments
 (0)