JHyeok5 commited on
Commit
e0fee1a
ยท
verified ยท
1 Parent(s): 168acc6

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. keep_alive.py +76 -39
keep_alive.py CHANGED
@@ -2,12 +2,12 @@
2
  HuggingFace Spaces Keep-Alive Pinger
3
 
4
  HuggingFace Spaces๋Š” ์ผ์ • ์‹œ๊ฐ„ ์š”์ฒญ์ด ์—†์œผ๋ฉด Sleep ๋ชจ๋“œ๋กœ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค.
5
- ์ด ๋ชจ๋“ˆ์€ 5๋ถ„๋งˆ๋‹ค ์ž์‹ ์˜ /health + OSRM Space๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ Cold Start๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
6
 
7
  @module keep_alive
8
  @description
9
- - 5๋ถ„(300์ดˆ) ์ฃผ๊ธฐ๋กœ ์ž๊ธฐ ์ž์‹ ์˜ /health ์—”๋“œํฌ์ธํŠธ ํ˜ธ์ถœ
10
- - OSRM Space๋„ ํ•จ๊ป˜ pingํ•˜์—ฌ sleep ๋ฐฉ์ง€
11
  - asyncio ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํƒœ์Šคํฌ๋กœ ์‹คํ–‰
12
  - ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ์—๋„ ๊ณ„์† ์‹คํ–‰ (๋ฌดํ•œ ๋ฃจํ”„)
13
  - ๋กœ๊น…์„ ํ†ตํ•œ ์ƒํƒœ ๋ชจ๋‹ˆํ„ฐ๋ง
@@ -43,12 +43,15 @@ _OSRM_SELF_HOSTED = "router.project-osrm.org" not in _OSRM_BASE_URL
43
  # ๊ฒฝ๋Ÿ‰ OSRM ping: ์ œ์ฃผ ์• ์›” ๊ทผ์ฒ˜ ์งง์€ ๊ฒฝ๋กœ ์š”์ฒญ
44
  _OSRM_PING_URL = f"{_OSRM_BASE_URL}/route/v1/foot/126.3397,33.4627;126.3450,33.4630" if _OSRM_SELF_HOSTED else None
45
 
46
- # Keep-alive ์„ค์ •
47
- PING_INTERVAL_SECONDS = 300 # 5๋ถ„
48
  PING_TIMEOUT_SECONDS = 10 # ์š”์ฒญ ํƒ€์ž„์•„์›ƒ
49
- OSRM_PING_TIMEOUT_SECONDS = 30 # OSRM์€ cold start ์‹œ ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆผ
50
  MAX_CONSECUTIVE_FAILURES = 10 # ์ตœ๋Œ€ ์—ฐ์† ์‹คํŒจ ํ—ˆ์šฉ (๋กœ๊น…์šฉ)
51
 
 
 
 
 
52
  # ์ƒํƒœ ์ถ”์  - Backend
53
  _ping_task: Optional[asyncio.Task] = None
54
  _consecutive_failures: int = 0
@@ -177,18 +180,13 @@ async def ping_osrm() -> bool:
177
 
178
  async def keep_alive_loop():
179
  """
180
- 5๋ถ„๋งˆ๋‹ค ์ž๊ธฐ ์ž์‹  + OSRM Space๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ HuggingFace Spaces sleep ๋ฐฉ์ง€.
181
-
182
- - ๋ฌดํ•œ ๋ฃจํ”„๋กœ ์‹คํ–‰
183
- - ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ๊ณ„์† ์‹คํ–‰
184
- - ์—ฐ์† ์‹คํŒจ ์‹œ ๊ฒฝ๊ณ  ๋กœ๊ทธ
185
  """
186
  global _consecutive_failures
187
 
188
- osrm_status = f", osrm={_OSRM_PING_URL}" if _OSRM_PING_URL else ""
189
  logger.info(
190
- f"[keep-alive] Starting keep-alive pinger. "
191
- f"url={HF_SPACE_URL}, interval={PING_INTERVAL_SECONDS}s{osrm_status}"
192
  )
193
 
194
  # ์ฒซ ping์€ ์„œ๋ฒ„ ์‹œ์ž‘ ํ›„ 1๋ถ„ ๋’ค์— ์‹คํ–‰ (์™„์ „ํžˆ ์ค€๋น„๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ)
@@ -196,18 +194,47 @@ async def keep_alive_loop():
196
 
197
  while True:
198
  try:
199
- # Backend self-ping๊ณผ OSRM ping ๋™์‹œ ์‹คํ–‰
200
- tasks = [ping_self()]
201
- if _OSRM_PING_URL:
202
- tasks.append(ping_osrm())
203
- await asyncio.gather(*tasks)
204
 
205
- # ์—ฐ์† ์‹คํŒจ ๊ฒฝ๊ณ 
206
  if _consecutive_failures >= MAX_CONSECUTIVE_FAILURES:
207
  logger.error(
208
  f"[keep-alive] {_consecutive_failures} consecutive failures. "
209
  f"Service may be unhealthy."
210
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  if _osrm_consecutive_failures >= MAX_CONSECUTIVE_FAILURES:
212
  logger.error(
213
  f"[keep-alive:osrm] {_osrm_consecutive_failures} consecutive failures. "
@@ -215,53 +242,62 @@ async def keep_alive_loop():
215
  )
216
 
217
  except asyncio.CancelledError:
218
- logger.info("[keep-alive] Keep-alive task cancelled")
219
  raise
220
 
221
  except Exception as e:
222
- # ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์—๋Ÿฌ๋„ ๋ฌด์‹œํ•˜๊ณ  ๊ณ„์† ์‹คํ–‰
223
  logger.error(
224
- f"[keep-alive] Loop error (will continue): "
225
  f"{type(e).__name__}: {str(e)[:100]}"
226
  )
227
 
228
- # ๋‹ค์Œ ping๊นŒ์ง€ ๋Œ€๊ธฐ
229
- await asyncio.sleep(PING_INTERVAL_SECONDS)
 
 
230
 
231
 
232
  def start_keep_alive_task() -> asyncio.Task:
233
  """
234
  Keep-alive ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํƒœ์Šคํฌ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
 
235
 
236
  Returns:
237
- asyncio.Task: ์ƒ์„ฑ๋œ ํƒœ์Šคํฌ ๊ฐ์ฒด
238
  """
239
- global _ping_task
240
 
241
  if _ping_task is not None and not _ping_task.done():
242
  logger.warning("[keep-alive] Task already running")
243
  return _ping_task
244
 
245
  _ping_task = asyncio.create_task(keep_alive_loop())
246
- logger.info("[keep-alive] Background task started")
 
 
 
 
 
247
  return _ping_task
248
 
249
 
250
  async def stop_keep_alive_task():
251
  """
252
- Keep-alive ํƒœ์Šคํฌ๋ฅผ ์ค‘์ง€ํ•ฉ๋‹ˆ๋‹ค.
253
  """
254
- global _ping_task
255
 
256
- if _ping_task is not None and not _ping_task.done():
257
- _ping_task.cancel()
258
- try:
259
- await _ping_task
260
- except asyncio.CancelledError:
261
- pass
262
- logger.info("[keep-alive] Background task stopped")
 
263
 
264
  _ping_task = None
 
265
 
266
 
267
  def get_keep_alive_stats() -> dict:
@@ -274,7 +310,7 @@ def get_keep_alive_stats() -> dict:
274
  stats = {
275
  "enabled": _ping_task is not None and not _ping_task.done(),
276
  "target_url": HF_SPACE_URL,
277
- "interval_seconds": PING_INTERVAL_SECONDS,
278
  "total_pings": _total_pings,
279
  "successful_pings": _successful_pings,
280
  "success_rate": round(_successful_pings / _total_pings * 100, 2) if _total_pings > 0 else 0,
@@ -283,8 +319,9 @@ def get_keep_alive_stats() -> dict:
283
 
284
  if _OSRM_SELF_HOSTED:
285
  stats["osrm"] = {
286
- "enabled": True,
287
  "target_url": _OSRM_BASE_URL,
 
288
  "total_pings": _osrm_total_pings,
289
  "successful_pings": _osrm_successful_pings,
290
  "success_rate": round(_osrm_successful_pings / _osrm_total_pings * 100, 2) if _osrm_total_pings > 0 else 0,
 
2
  HuggingFace Spaces Keep-Alive Pinger
3
 
4
  HuggingFace Spaces๋Š” ์ผ์ • ์‹œ๊ฐ„ ์š”์ฒญ์ด ์—†์œผ๋ฉด Sleep ๋ชจ๋“œ๋กœ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค.
5
+ ์ด ๋ชจ๋“ˆ์€ Backend(5๋ถ„) + OSRM Space(30๋ถ„) ์ฃผ๊ธฐ๋กœ pingํ•˜์—ฌ Cold Start๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
6
 
7
  @module keep_alive
8
  @description
9
+ - Backend: 5๋ถ„(300์ดˆ) ์ฃผ๊ธฐ๋กœ ์ž๊ธฐ ์ž์‹ ์˜ /health ์—”๋“œํฌ์ธํŠธ ํ˜ธ์ถœ
10
+ - OSRM: 30๋ถ„(1800์ดˆ) ์ฃผ๊ธฐ๋กœ ๊ฒฝ๋Ÿ‰ ๊ฒฝ๋กœ ์š”์ฒญ (HF sleep ํƒ€์ž„์•„์›ƒ 48์‹œ๊ฐ„ ๋Œ€๋น„ ์ถฉ๋ถ„)
11
  - asyncio ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํƒœ์Šคํฌ๋กœ ์‹คํ–‰
12
  - ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ์—๋„ ๊ณ„์† ์‹คํ–‰ (๋ฌดํ•œ ๋ฃจํ”„)
13
  - ๋กœ๊น…์„ ํ†ตํ•œ ์ƒํƒœ ๋ชจ๋‹ˆํ„ฐ๋ง
 
43
  # ๊ฒฝ๋Ÿ‰ OSRM ping: ์ œ์ฃผ ์• ์›” ๊ทผ์ฒ˜ ์งง์€ ๊ฒฝ๋กœ ์š”์ฒญ
44
  _OSRM_PING_URL = f"{_OSRM_BASE_URL}/route/v1/foot/126.3397,33.4627;126.3450,33.4630" if _OSRM_SELF_HOSTED else None
45
 
46
+ # Keep-alive ์„ค์ • - Backend
47
+ PING_INTERVAL_SECONDS = 300 # 5๋ถ„ (์‚ฌ์šฉ์ž ์‘๋‹ต ์†๋„ ์œ ์ง€)
48
  PING_TIMEOUT_SECONDS = 10 # ์š”์ฒญ ํƒ€์ž„์•„์›ƒ
 
49
  MAX_CONSECUTIVE_FAILURES = 10 # ์ตœ๋Œ€ ์—ฐ์† ์‹คํŒจ ํ—ˆ์šฉ (๋กœ๊น…์šฉ)
50
 
51
+ # Keep-alive ์„ค์ • - OSRM (HF sleep ํƒ€์ž„์•„์›ƒ 48์‹œ๊ฐ„์ด๋ฏ€๋กœ 30๋ถ„์ด๋ฉด ์ถฉ๋ถ„)
52
+ OSRM_PING_INTERVAL_SECONDS = 1800 # 30๋ถ„
53
+ OSRM_PING_TIMEOUT_SECONDS = 30 # OSRM์€ cold start ์‹œ ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆผ
54
+
55
  # ์ƒํƒœ ์ถ”์  - Backend
56
  _ping_task: Optional[asyncio.Task] = None
57
  _consecutive_failures: int = 0
 
180
 
181
  async def keep_alive_loop():
182
  """
183
+ 5๋ถ„๋งˆ๋‹ค Backend self-ping. HuggingFace Spaces sleep ๋ฐฉ์ง€.
 
 
 
 
184
  """
185
  global _consecutive_failures
186
 
 
187
  logger.info(
188
+ f"[keep-alive] Starting backend pinger. "
189
+ f"url={HF_SPACE_URL}, interval={PING_INTERVAL_SECONDS}s"
190
  )
191
 
192
  # ์ฒซ ping์€ ์„œ๋ฒ„ ์‹œ์ž‘ ํ›„ 1๋ถ„ ๋’ค์— ์‹คํ–‰ (์™„์ „ํžˆ ์ค€๋น„๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ)
 
194
 
195
  while True:
196
  try:
197
+ await ping_self()
 
 
 
 
198
 
 
199
  if _consecutive_failures >= MAX_CONSECUTIVE_FAILURES:
200
  logger.error(
201
  f"[keep-alive] {_consecutive_failures} consecutive failures. "
202
  f"Service may be unhealthy."
203
  )
204
+
205
+ except asyncio.CancelledError:
206
+ logger.info("[keep-alive] Backend keep-alive task cancelled")
207
+ raise
208
+
209
+ except Exception as e:
210
+ logger.error(
211
+ f"[keep-alive] Loop error (will continue): "
212
+ f"{type(e).__name__}: {str(e)[:100]}"
213
+ )
214
+
215
+ await asyncio.sleep(PING_INTERVAL_SECONDS)
216
+
217
+
218
+ async def osrm_keep_alive_loop():
219
+ """
220
+ 30๋ถ„๋งˆ๋‹ค OSRM Space ping. HF sleep ํƒ€์ž„์•„์›ƒ(48์‹œ๊ฐ„) ๋Œ€๋น„ ์ถฉ๋ถ„ํ•œ ๊ฐ„๊ฒฉ.
221
+ ์ž์ฒด ํ˜ธ์ŠคํŒ… OSRM์ด ์•„๋‹ˆ๋ฉด ์ฆ‰์‹œ ์ข…๋ฃŒ.
222
+ """
223
+ if not _OSRM_PING_URL:
224
+ return
225
+
226
+ logger.info(
227
+ f"[keep-alive:osrm] Starting OSRM pinger. "
228
+ f"url={_OSRM_BASE_URL}, interval={OSRM_PING_INTERVAL_SECONDS}s (30min)"
229
+ )
230
+
231
+ # OSRM warmup์€ server.py์—์„œ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ์—ฌ๊ธฐ์„  30๋ถ„ ๋’ค ์ฒซ ping
232
+ await asyncio.sleep(OSRM_PING_INTERVAL_SECONDS)
233
+
234
+ while True:
235
+ try:
236
+ await ping_osrm()
237
+
238
  if _osrm_consecutive_failures >= MAX_CONSECUTIVE_FAILURES:
239
  logger.error(
240
  f"[keep-alive:osrm] {_osrm_consecutive_failures} consecutive failures. "
 
242
  )
243
 
244
  except asyncio.CancelledError:
245
+ logger.info("[keep-alive:osrm] OSRM keep-alive task cancelled")
246
  raise
247
 
248
  except Exception as e:
 
249
  logger.error(
250
+ f"[keep-alive:osrm] Loop error (will continue): "
251
  f"{type(e).__name__}: {str(e)[:100]}"
252
  )
253
 
254
+ await asyncio.sleep(OSRM_PING_INTERVAL_SECONDS)
255
+
256
+
257
+ _osrm_ping_task: Optional[asyncio.Task] = None
258
 
259
 
260
  def start_keep_alive_task() -> asyncio.Task:
261
  """
262
  Keep-alive ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํƒœ์Šคํฌ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
263
+ Backend(5๋ถ„) + OSRM(30๋ถ„) ๋ณ„๋„ ๋ฃจํ”„๋กœ ์‹คํ–‰.
264
 
265
  Returns:
266
+ asyncio.Task: Backend ํƒœ์Šคํฌ ๊ฐ์ฒด
267
  """
268
+ global _ping_task, _osrm_ping_task
269
 
270
  if _ping_task is not None and not _ping_task.done():
271
  logger.warning("[keep-alive] Task already running")
272
  return _ping_task
273
 
274
  _ping_task = asyncio.create_task(keep_alive_loop())
275
+ logger.info("[keep-alive] Backend background task started (5min interval)")
276
+
277
+ if _OSRM_PING_URL and (_osrm_ping_task is None or _osrm_ping_task.done()):
278
+ _osrm_ping_task = asyncio.create_task(osrm_keep_alive_loop())
279
+ logger.info("[keep-alive:osrm] OSRM background task started (30min interval)")
280
+
281
  return _ping_task
282
 
283
 
284
  async def stop_keep_alive_task():
285
  """
286
+ Keep-alive ํƒœ์Šคํฌ๋ฅผ ๋ชจ๋‘ ์ค‘์ง€ํ•ฉ๋‹ˆ๋‹ค.
287
  """
288
+ global _ping_task, _osrm_ping_task
289
 
290
+ for name, task in [("backend", _ping_task), ("osrm", _osrm_ping_task)]:
291
+ if task is not None and not task.done():
292
+ task.cancel()
293
+ try:
294
+ await task
295
+ except asyncio.CancelledError:
296
+ pass
297
+ logger.info(f"[keep-alive] {name} task stopped")
298
 
299
  _ping_task = None
300
+ _osrm_ping_task = None
301
 
302
 
303
  def get_keep_alive_stats() -> dict:
 
310
  stats = {
311
  "enabled": _ping_task is not None and not _ping_task.done(),
312
  "target_url": HF_SPACE_URL,
313
+ "interval_seconds": PING_INTERVAL_SECONDS, # 5๋ถ„
314
  "total_pings": _total_pings,
315
  "successful_pings": _successful_pings,
316
  "success_rate": round(_successful_pings / _total_pings * 100, 2) if _total_pings > 0 else 0,
 
319
 
320
  if _OSRM_SELF_HOSTED:
321
  stats["osrm"] = {
322
+ "enabled": _osrm_ping_task is not None and not _osrm_ping_task.done(),
323
  "target_url": _OSRM_BASE_URL,
324
+ "interval_seconds": OSRM_PING_INTERVAL_SECONDS, # 30๋ถ„
325
  "total_pings": _osrm_total_pings,
326
  "successful_pings": _osrm_successful_pings,
327
  "success_rate": round(_osrm_successful_pings / _osrm_total_pings * 100, 2) if _osrm_total_pings > 0 else 0,