lpedro commited on
Commit
5bd83d8
·
1 Parent(s): 1ad39c4

main - Reduce workers to 8, increase cache to 300, redesign monitoring page

Browse files

Remove /health endpoint and per-request MEM log; monitoring is now
internal only via background reporter. Root page redesigned with card
grid, colour-coded RSS (green/amber/red), cache progress bars and
server-side rendering with meta-refresh.

Files changed (3) hide show
  1. Dockerfile +1 -1
  2. main.py +67 -28
  3. similarity.py +1 -5
Dockerfile CHANGED
@@ -16,4 +16,4 @@ RUN python -c "from tensorflow.keras.applications import MobileNetV2; MobileNetV
16
  COPY ./*.py /app/
17
 
18
 
19
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "10"]
 
16
  COPY ./*.py /app/
17
 
18
 
19
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "8"]
main.py CHANGED
@@ -1,4 +1,5 @@
1
  from contextlib import asynccontextmanager
 
2
  from typing import List
3
  from fastapi import FastAPI
4
  from fastapi.responses import HTMLResponse
@@ -16,42 +17,80 @@ async def lifespan(app: FastAPI):
16
 
17
  app = FastAPI(title="Prozis Similarity Service", version="1.0.0", lifespan=lifespan)
18
 
 
19
 
20
- @app.get("/health", include_in_schema=False)
21
- async def health():
22
- return get_all_memory_stats()
 
 
 
 
23
 
24
 
25
  @app.get("/", include_in_schema=False)
26
  async def root():
27
- return HTMLResponse(content=(
28
- "<html><head>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  "<link rel='preconnect' href='https://fonts.googleapis.com'>"
30
  "<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin>"
31
- "<link href='https://fonts.googleapis.com/css2?family=Barlow+Condensed:ital,wght@1,800&display=swap' rel='stylesheet'>"
32
- "</head><body style='margin:0;background:#000;padding:2rem'>"
33
- "<h1 style='color:#fff;font-family:\"Barlow Condensed\",sans-serif;font-weight:800;font-style:italic'>"
34
- "Prozis Similarity Service"
35
- "</h1>"
36
- "<table id='mem-stats' style='color:#8f8;font-family:monospace;font-size:0.9rem;border-collapse:collapse'></table>"
37
- "<script>"
38
- "async function refreshStats(){"
39
- "try{"
40
- "const r = await fetch('/health');"
41
- "const workers = await r.json();"
42
- "const rows = workers.map(w => "
43
- "`<tr><td style='padding:2px 12px 2px 0'>pid ${w.pid}</td>"
44
- "<td style='padding:2px 12px 2px 0'>${w.rss_mb} MB</td>"
45
- "<td style='padding:2px 0'>cache ${w.cache_size}</td></tr>`"
46
- ").join('');"
47
- "document.getElementById('mem-stats').innerHTML = rows;"
48
- "}catch(e){}"
49
- "}"
50
- "refreshStats();"
51
- "setInterval(refreshStats, 3000);"
52
- "</script>"
 
 
 
 
 
 
 
 
 
 
53
  "</body></html>"
54
- ))
 
 
55
 
56
 
57
  @app.post("/similarity", summary="Perform images similarity", response_model=List[ResponseModel], tags=["Similarities"])
 
1
  from contextlib import asynccontextmanager
2
+ from datetime import datetime
3
  from typing import List
4
  from fastapi import FastAPI
5
  from fastapi.responses import HTMLResponse
 
17
 
18
  app = FastAPI(title="Prozis Similarity Service", version="1.0.0", lifespan=lifespan)
19
 
20
+ _CACHE_MAX = 300
21
 
22
+
23
+ def _rss_color(rss_mb: float) -> str:
24
+ if rss_mb < 800:
25
+ return "#22c55e"
26
+ if rss_mb < 1000:
27
+ return "#f59e0b"
28
+ return "#ef4444"
29
 
30
 
31
  @app.get("/", include_in_schema=False)
32
  async def root():
33
+ workers = get_all_memory_stats()
34
+ total_rss_gb = sum(w["rss_mb"] for w in workers) / 1024
35
+ timestamp = datetime.now().strftime("%H:%M:%S")
36
+
37
+ cards = ""
38
+ for w in workers:
39
+ color = _rss_color(w["rss_mb"])
40
+ cache_pct = min(100, w["cache_size"] / _CACHE_MAX * 100)
41
+ cards += (
42
+ f"<div class='card' style='--c:{color}'>"
43
+ f"<div class='pid'>pid {w['pid']}</div>"
44
+ f"<div class='rss'>{w['rss_mb']}<span class='unit'> MB</span></div>"
45
+ f"<div class='bar-wrap'>"
46
+ f"<div class='bar-label'><span>cache</span><span>{w['cache_size']}/{_CACHE_MAX}</span></div>"
47
+ f"<div class='track'><div class='fill' style='width:{cache_pct:.0f}%'></div></div>"
48
+ f"</div></div>"
49
+ )
50
+
51
+ html = (
52
+ "<!DOCTYPE html><html><head>"
53
+ "<meta charset='UTF-8'>"
54
+ "<meta http-equiv='refresh' content='10'>"
55
+ "<title>Prozis Similarity Service</title>"
56
  "<link rel='preconnect' href='https://fonts.googleapis.com'>"
57
  "<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin>"
58
+ "<link href='https://fonts.googleapis.com/css2?family=Barlow+Condensed:ital,wght@1,800&family=Inter:wght@400;500&display=swap' rel='stylesheet'>"
59
+ "<style>"
60
+ "*{margin:0;padding:0;box-sizing:border-box}"
61
+ "body{background:#0d0d14;color:#e2e8f0;"
62
+ "font-family:'Inter',system-ui,sans-serif;min-height:100vh;padding:2rem 2.5rem}"
63
+ "h1{font-family:'Barlow Condensed',sans-serif;font-weight:800;font-style:italic;"
64
+ "font-size:2.4rem;color:#fff;letter-spacing:-.01em;line-height:1}"
65
+ ".meta{margin-top:.5rem;color:#4b5563;font-size:.78rem;font-family:'SF Mono','Fira Code',monospace;letter-spacing:.03em}"
66
+ ".meta b{color:#6b7280;font-weight:500}"
67
+ "header{margin-bottom:2rem;padding-bottom:1.5rem;border-bottom:1px solid #1a1a2e}"
68
+ ".grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:.875rem}"
69
+ ".card{background:#111827;border:1px solid #1e2435;border-radius:12px;padding:1.1rem 1.2rem;"
70
+ "position:relative;overflow:hidden;transition:border-color .15s,transform .15s}"
71
+ ".card:hover{border-color:#2d3748;transform:translateY(-1px)}"
72
+ ".card::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:var(--c)}"
73
+ ".pid{font-size:.62rem;color:#374151;font-family:'SF Mono','Fira Code',monospace;"
74
+ "text-transform:uppercase;letter-spacing:.1em;margin-bottom:.65rem}"
75
+ ".rss{font-size:1.55rem;font-weight:700;font-family:'SF Mono','Fira Code',monospace;"
76
+ "color:var(--c);line-height:1;white-space:nowrap}"
77
+ ".unit{font-size:.65rem;color:#6b7280;font-weight:400}"
78
+ ".bar-wrap{margin-top:.9rem}"
79
+ ".bar-label{font-size:.62rem;color:#374151;display:flex;justify-content:space-between;margin-bottom:.3rem}"
80
+ ".track{background:#1a2235;border-radius:99px;height:3px;overflow:hidden}"
81
+ ".fill{height:100%;border-radius:99px;background:#3b82f6;opacity:.75}"
82
+ "</style></head><body>"
83
+ f"<header>"
84
+ f"<h1>Prozis Similarity Service</h1>"
85
+ f"<div class='meta'>"
86
+ f"<b>{len(workers)}</b> workers &nbsp;&middot;&nbsp; "
87
+ f"<b>{total_rss_gb:.1f} GB</b> RSS &nbsp;&middot;&nbsp; {timestamp}"
88
+ f"</div></header>"
89
+ f"<div class='grid'>{cards}</div>"
90
  "</body></html>"
91
+ )
92
+
93
+ return HTMLResponse(content=html)
94
 
95
 
96
  @app.post("/similarity", summary="Perform images similarity", response_model=List[ResponseModel], tags=["Similarities"])
similarity.py CHANGED
@@ -35,7 +35,7 @@ _mobilenet_lock = threading.Lock()
35
 
36
  _image_cache = OrderedDict()
37
  _image_cache_lock = threading.Lock()
38
- _IMAGE_CACHE_MAX = 200
39
 
40
  _process = psutil.Process()
41
 
@@ -394,8 +394,4 @@ def check_similarity(images: List[RequestModel], quadrant: bool = True):
394
  for i in range(1, len(images))
395
  ]
396
 
397
- memory_stats = get_memory_stats()
398
- logging.info(f"[MEM] pid={memory_stats['pid']} rss={memory_stats['rss_mb']}MB "
399
- f"cache_size={memory_stats['cache_size']} images_in_request={len(images) - 1}")
400
-
401
  return results
 
35
 
36
  _image_cache = OrderedDict()
37
  _image_cache_lock = threading.Lock()
38
+ _IMAGE_CACHE_MAX = 300
39
 
40
  _process = psutil.Process()
41
 
 
394
  for i in range(1, len(images))
395
  ]
396
 
 
 
 
 
397
  return results