HA stage 2: DB-shared rate limiting + second server unit + monitor both-servers check (#52)

Co-authored-by: host <host@test.local>
This commit is contained in:
openqjandhost authored and GitHub committed 2026-10-27 00:15:16 +08:00
1 parent fc8e337d0f
commit 06b9fd12fd
4 files changed
+170 -19

No files matched your search

+74
View File
@@ -0,0 +1,74 @@
"""HA 阶段 2 单测:DB 共享限流。
DbRateLimiter 与内存版同接口:窗口内达到阈值即 False、窗口
滑过去后恢复、不同 key 互不干扰;关键一条是多实例共享计数
(两个 server 进程各建一个实例,计数仍合流——多 server 限流
的前提)。SQLite 跑同款 SQL,Postgres 方言差异由 db.py 适配层
覆盖(本文件不连真实 Postgres)。
"""
import os
import sys
import tempfile
import unittest
from pathlib import Path
try:
from cloud.server import DbRateLimiter, RateLimiter
except ImportError:
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from cloud.server import DbRateLimiter, RateLimiter
class DbRateLimiterTestCase(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.db_path = os.path.join(self.tmp.name, "control.db")
self.now = [1000.0]
def tearDown(self):
self.tmp.cleanup()
def _limiter(self, per_minute=3, window=60):
return DbRateLimiter(self.db_path, per_minute=per_minute,
window_seconds=window,
time_fn=lambda: self.now[0])
def test_allow_up_to_limit_then_false(self):
lim = self._limiter()
self.assertTrue(lim.allow("k1"))
self.assertTrue(lim.allow("k1"))
self.assertTrue(lim.allow("k1"))
self.assertFalse(lim.allow("k1"))
def test_window_slides_and_recovers(self):
lim = self._limiter()
for _ in range(3):
self.assertTrue(lim.allow("k1"))
self.assertFalse(lim.allow("k1"))
self.now[0] += 61
self.assertTrue(lim.allow("k1"))
def test_keys_are_independent(self):
lim = self._limiter()
for _ in range(3):
self.assertTrue(lim.allow("a"))
self.assertFalse(lim.allow("a"))
self.assertTrue(lim.allow("b"))
def test_two_instances_share_counts(self):
# 多 server 的核心性质:两个实例(两个进程)计数合流。
a = self._limiter()
b = self._limiter()
self.assertTrue(a.allow("shared"))
self.assertTrue(a.allow("shared"))
self.assertTrue(b.allow("shared"))
self.assertFalse(b.allow("shared"))
self.assertFalse(a.allow("shared"))
def test_memory_limiter_unchanged(self):
lim = RateLimiter(per_minute=2, window_seconds=60,
time_fn=lambda: self.now[0])
self.assertTrue(lim.allow("k"))
self.assertTrue(lim.allow("k"))
self.assertFalse(lim.allow("k"))