File size: 2,265 Bytes
f382dd4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import hashlib
import json
import threading
from typing import Any

from fastapi import APIRouter
from fastapi.responses import JSONResponse

from . import runtime, search_api
from .search_logic import SearchRequest
from .storage_lock import serving_lock


router = APIRouter()
bootstrap_cache: tuple[tuple[Any, ...], dict[str, Any]] | None = None
bootstrap_lock = threading.Lock()


def generation_id(generation: tuple[Any, ...]) -> str:
    return hashlib.sha256(repr(generation).encode()).hexdigest()[:16]


@router.get("/api/bootstrap")
def bootstrap():
    global bootstrap_cache
    with bootstrap_lock:
        generation = runtime.serving_generation_token()
        if runtime.index_status() == "ready" and bootstrap_cache and bootstrap_cache[0] == generation:
            return JSONResponse(bootstrap_cache[1], headers={"Cache-Control": "no-store"})
        if not runtime.serving_generation_ready():
            return JSONResponse({"error": "serving generation is not ready"}, status_code=503)

        for _attempt in range(2):
            generation = runtime.serving_generation_token()
            search_response = search_api.search(SearchRequest())
            if not isinstance(search_response, JSONResponse) or search_response.status_code != 200:
                return JSONResponse({"error": "default search is unavailable"}, status_code=503)
            search_payload = json.loads(search_response.body)
            with serving_lock():
                sources = runtime.cached_sources_payload()
                facets = {
                    kind: runtime.cached_facet_payload(kind, 1, 200, "")
                    for kind in ("source", "author", "tag")
                }
            if runtime.index_status() != "ready" or runtime.serving_generation_token() != generation:
                continue
            payload = {
                "generation": generation_id(generation),
                "search": search_payload,
                "sources": sources,
                "facets": facets,
            }
            bootstrap_cache = (generation, payload)
            return JSONResponse(payload, headers={"Cache-Control": "no-store"})

        return JSONResponse({"error": "serving generation changed during bootstrap"}, status_code=503)