問題診斷: - 舊版 /api/resource/summary API 使用慢速 SQL (JOIN + ROW_NUMBER),導致 55s 逾時 - 壓力測試持續呼叫此 API,佔滿所有 worker threads - 每次 WIP API 請求都解析 14.8MB JSON,8 個並發請求造成 GIL 競爭 變更內容: - 移除舊版 /api/resource/summary 路由和 query_resource_status_summary 函數 - 刪除未使用的 status_summary.sql - 更新壓力測試和整合測試使用新版 /api/resource/status/summary - 加入 ProcessLevelCache 類別實作 process-level DataFrame 快取 (30s TTL) - 使用 double-check locking 確保只有一個 thread 解析 JSON 效能改善: - 新版 API 使用 Redis 三層快取,回應時間 < 100ms - Process-level 快取避免重複解析 14MB JSON,大幅改善並發效能 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
55 lines
1.7 KiB
Python
55 lines
1.7 KiB
Python
import unittest
|
|
|
|
from mes_dashboard.app import create_app
|
|
from mes_dashboard.core.cache import NoOpCache
|
|
import mes_dashboard.core.database as db
|
|
|
|
|
|
class AppFactoryTests(unittest.TestCase):
|
|
def setUp(self):
|
|
db._ENGINE = None
|
|
|
|
def test_create_app_default_config(self):
|
|
app = create_app()
|
|
self.assertTrue(app.config.get("DEBUG"))
|
|
self.assertEqual(app.config.get("ENV"), "development")
|
|
self.assertIsInstance(app.extensions.get("cache"), NoOpCache)
|
|
|
|
def test_create_app_production_config(self):
|
|
app = create_app("production")
|
|
self.assertFalse(app.config.get("DEBUG"))
|
|
self.assertEqual(app.config.get("ENV"), "production")
|
|
|
|
def test_create_app_independent_instances(self):
|
|
app1 = create_app()
|
|
db._ENGINE = None
|
|
app2 = create_app()
|
|
self.assertIsNot(app1, app2)
|
|
|
|
def test_routes_registered(self):
|
|
app = create_app()
|
|
rules = {rule.rule for rule in app.url_map.iter_rules()}
|
|
expected = {
|
|
"/",
|
|
"/tables",
|
|
"/resource",
|
|
"/wip-overview",
|
|
"/wip-detail",
|
|
"/excel-query",
|
|
"/api/wip/overview/summary",
|
|
"/api/wip/overview/matrix",
|
|
"/api/wip/overview/hold",
|
|
"/api/wip/detail/<workcenter>",
|
|
"/api/wip/meta/workcenters",
|
|
"/api/wip/meta/packages",
|
|
"/api/resource/status/summary",
|
|
"/api/dashboard/kpi",
|
|
"/api/excel-query/upload",
|
|
}
|
|
missing = expected - rules
|
|
self.assertFalse(missing, f"Missing routes: {sorted(missing)}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|