Skip to content

Commit d1a80a6

Browse files
committed
#28 - implement a safe shutdown endpoint
1 parent bcf7834 commit d1a80a6

2 files changed

Lines changed: 46 additions & 20 deletions

File tree

constfig.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ def __init__(self):
2020
self.load_config()
2121

2222
def set_constants(self):
23+
# `C` (the instance of `_C()` set below) is only created once even though it is imported in `ddb` and `pyxie`.
24+
# Probably not a good idea for _SHUTDOWN to depend on that behavior for the long term. Only used in `pyxie` currently.
25+
self._SHUTDOWN = False # Toggled to true to cleanly stop the service and ensure data is persisted to disk.
26+
2327
# Constant values
2428
self.APP_NAME = "pyxie"
2529
self.LOG = logging.getLogger(self.APP_NAME)

pyxie.py

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import os
12
from ddb import DDB
2-
from time import time
3+
from time import time, sleep
34
from constfig import C
4-
from flask import Flask, Response, request
5+
from flask import Flask, Response, request, abort
56

67

78
pyxie = Flask(C.APP_NAME)
@@ -13,39 +14,54 @@ def _validate_api_key():
1314
return api_key in C.API_KEYS
1415

1516

17+
@pyxie.before_request
18+
def check_if_shutting_down():
19+
# Since we're currently single threaded, we shouldn't really need this check. This is here
20+
# for any weird edge cases with flask.
21+
if C._SHUTDOWN:
22+
abort(503, description="Service is shutting down. Please try again later.")
23+
24+
25+
@pyxie.before_request
26+
def validate_api_key():
27+
if request.path == "/" or _validate_api_key():
28+
return
29+
return "Unauthorized", 401
30+
31+
32+
@pyxie.after_request
33+
def shutdown(response):
34+
if C._SHUTDOWN:
35+
_data.dump()
36+
os._exit(0)
37+
return response
38+
39+
1640
@pyxie.route("/register", methods=[C.HTTP_METHOD_POST])
1741
def register():
18-
if _validate_api_key():
19-
_data.register()
20-
return "Success", 201
21-
return "Unauthorized", 401
42+
_data.register()
43+
return "Success", 201
2244

2345

2446
@pyxie.route("/unregister", methods=[C.HTTP_METHOD_DELETE])
2547
def unregister():
26-
if _validate_api_key():
27-
_data.unregister()
28-
return "Success", 204
29-
return "Unauthorized", 401
48+
_data.unregister()
49+
return "Success", 204
3050

3151

3252
@pyxie.route("/stats", methods=[C.HTTP_METHOD_GET])
3353
def stats():
34-
if _validate_api_key():
35-
stats = {}
36-
for attr in dir(_data):
37-
if isinstance(getattr(type(_data), attr, None), property):
38-
stats[attr] = getattr(_data, attr)
39-
return stats, 200
40-
return "Unauthorized", 401
54+
stats = {}
55+
for attr in dir(_data):
56+
if isinstance(getattr(type(_data), attr, None), property):
57+
stats[attr] = getattr(_data, attr)
58+
return stats, 200
4159

4260

4361
@pyxie.route("/metrics", methods=[C.HTTP_METHOD_GET])
4462
def metrics():
4563
# [ TODO - Issue #7] - Export prometheus formatted metrics
46-
if _validate_api_key():
47-
return "Metrics", 501 # Not Implemented
48-
return "Unauthorized", 401
64+
return "Metrics", 501 # Not Implemented
4965

5066

5167
@pyxie.route("/", methods=[C.HTTP_METHOD_GET])
@@ -58,6 +74,12 @@ def root():
5874
return Response(C.ONE_BY_ONE, mimetype=C.HTTP_MIME_TYPE_PNG)
5975

6076

77+
@pyxie.route("/shutdown", methods=[C.HTTP_METHOD_POST])
78+
def shutdown():
79+
C._SHUTDOWN = True
80+
return "Service is now offline. Data will be saved.", 200
81+
82+
6183
def main():
6284
pyxie.run(host=C.LISTEN_IP, port=C.LISTEN_PORT)
6385

0 commit comments

Comments
 (0)