Wait for DB init for healthcheck + settings (#885)

* init check: (backend fix for #794)
- wait until db is inited before settings /api/settings to return 200
- also return 503 from healthcheck endpoint, until db is available
This commit is contained in:
Ilya Kreymer 2023-05-25 09:58:30 -07:00 committed by GitHub
parent 965aa7ff90
commit d7c19c7613
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 3 deletions

View File

@ -4,6 +4,8 @@ Browsertrix API Mongo DB initialization
import importlib.util import importlib.util
import os import os
import urllib import urllib
import asyncio
from typing import Optional from typing import Optional
import motor.motor_asyncio import motor.motor_asyncio
@ -49,6 +51,23 @@ def init_db():
return client, mdb return client, mdb
# ============================================================================
async def ping_db(mdb, db_inited):
"""run in loop until db is up, set db_inited['inited'] property to true"""
print("Waiting DB", flush=True)
while True:
try:
result = await mdb.command("ping")
assert result.get("ok")
db_inited["inited"] = True
print("DB Ready!")
break
# pylint: disable=broad-exception-caught
except Exception:
print("Retrying, waiting for DB to be ready")
await asyncio.sleep(3)
# ============================================================================ # ============================================================================
async def update_and_prepare_db( async def update_and_prepare_db(
# pylint: disable=R0913 # pylint: disable=R0913
@ -59,6 +78,7 @@ async def update_and_prepare_db(
crawl_config_ops, crawl_config_ops,
coll_ops, coll_ops,
invite_ops, invite_ops,
db_inited,
): ):
"""Prepare database for application. """Prepare database for application.
@ -68,6 +88,7 @@ async def update_and_prepare_db(
- Create/update default org - Create/update default org
""" """
await ping_db(mdb, db_inited)
print("Database setup started", flush=True) print("Database setup started", flush=True)
if await run_db_migrations(mdb, user_manager): if await run_db_migrations(mdb, user_manager):
await drop_indexes(mdb) await drop_indexes(mdb)

View File

@ -6,11 +6,11 @@ import os
import asyncio import asyncio
import sys import sys
from fastapi import FastAPI from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from fastapi.routing import APIRouter from fastapi.routing import APIRouter
from .db import init_db, update_and_prepare_db from .db import init_db, ping_db, update_and_prepare_db
from .emailsender import EmailSender from .emailsender import EmailSender
from .invites import init_invites from .invites import init_invites
@ -35,6 +35,8 @@ app_root = FastAPI(
openapi_url=API_PREFIX + "/openapi.json", openapi_url=API_PREFIX + "/openapi.json",
) )
db_inited = {"inited": False}
# ============================================================================ # ============================================================================
# pylint: disable=too-many-locals # pylint: disable=too-many-locals
@ -114,14 +116,25 @@ def main():
if run_once_lock("btrix-init-db"): if run_once_lock("btrix-init-db"):
asyncio.create_task( asyncio.create_task(
update_and_prepare_db( update_and_prepare_db(
mdb, user_manager, org_ops, crawls, crawl_config_ops, coll_ops, invites mdb,
user_manager,
org_ops,
crawls,
crawl_config_ops,
coll_ops,
invites,
db_inited,
) )
) )
else:
asyncio.create_task(ping_db(mdb, db_inited))
app.include_router(org_ops.router) app.include_router(org_ops.router)
@app.get("/settings") @app.get("/settings")
async def get_settings(): async def get_settings():
if not db_inited.get("inited"):
raise HTTPException(status_code=503, detail="not_ready_yet")
return settings return settings
# internal routes # internal routes
@ -132,6 +145,8 @@ def main():
@app_root.get("/healthz", include_in_schema=False) @app_root.get("/healthz", include_in_schema=False)
async def healthz(): async def healthz():
if not db_inited.get("inited"):
raise HTTPException(status_code=503, detail="not_ready_yet")
return {} return {}
app_root.include_router(app, prefix=API_PREFIX) app_root.include_router(app, prefix=API_PREFIX)