jettask 0.2.1__py3-none-any.whl → 0.2.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. jettask/constants.py +213 -0
  2. jettask/core/app.py +525 -205
  3. jettask/core/cli.py +193 -185
  4. jettask/core/consumer_manager.py +126 -34
  5. jettask/core/context.py +3 -0
  6. jettask/core/enums.py +137 -0
  7. jettask/core/event_pool.py +501 -168
  8. jettask/core/message.py +147 -0
  9. jettask/core/offline_worker_recovery.py +181 -114
  10. jettask/core/task.py +10 -174
  11. jettask/core/task_batch.py +153 -0
  12. jettask/core/unified_manager_base.py +243 -0
  13. jettask/core/worker_scanner.py +54 -54
  14. jettask/executors/asyncio.py +184 -64
  15. jettask/webui/backend/config.py +51 -0
  16. jettask/webui/backend/data_access.py +2083 -92
  17. jettask/webui/backend/data_api.py +3294 -0
  18. jettask/webui/backend/dependencies.py +261 -0
  19. jettask/webui/backend/init_meta_db.py +158 -0
  20. jettask/webui/backend/main.py +1358 -69
  21. jettask/webui/backend/main_unified.py +78 -0
  22. jettask/webui/backend/main_v2.py +394 -0
  23. jettask/webui/backend/namespace_api.py +295 -0
  24. jettask/webui/backend/namespace_api_old.py +294 -0
  25. jettask/webui/backend/namespace_data_access.py +611 -0
  26. jettask/webui/backend/queue_backlog_api.py +727 -0
  27. jettask/webui/backend/queue_stats_v2.py +521 -0
  28. jettask/webui/backend/redis_monitor_api.py +476 -0
  29. jettask/webui/backend/unified_api_router.py +1601 -0
  30. jettask/webui/db_init.py +204 -32
  31. jettask/webui/frontend/package-lock.json +492 -1
  32. jettask/webui/frontend/package.json +4 -1
  33. jettask/webui/frontend/src/App.css +105 -7
  34. jettask/webui/frontend/src/App.jsx +49 -20
  35. jettask/webui/frontend/src/components/NamespaceSelector.jsx +166 -0
  36. jettask/webui/frontend/src/components/QueueBacklogChart.jsx +298 -0
  37. jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +638 -0
  38. jettask/webui/frontend/src/components/QueueDetailsTable.css +65 -0
  39. jettask/webui/frontend/src/components/QueueDetailsTable.jsx +487 -0
  40. jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +465 -0
  41. jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +423 -0
  42. jettask/webui/frontend/src/components/TaskFilter.jsx +425 -0
  43. jettask/webui/frontend/src/components/TimeRangeSelector.css +21 -0
  44. jettask/webui/frontend/src/components/TimeRangeSelector.jsx +160 -0
  45. jettask/webui/frontend/src/components/layout/AppLayout.css +95 -0
  46. jettask/webui/frontend/src/components/layout/AppLayout.jsx +49 -0
  47. jettask/webui/frontend/src/components/layout/Header.css +34 -10
  48. jettask/webui/frontend/src/components/layout/Header.jsx +31 -23
  49. jettask/webui/frontend/src/components/layout/SideMenu.css +137 -0
  50. jettask/webui/frontend/src/components/layout/SideMenu.jsx +209 -0
  51. jettask/webui/frontend/src/components/layout/TabsNav.css +244 -0
  52. jettask/webui/frontend/src/components/layout/TabsNav.jsx +206 -0
  53. jettask/webui/frontend/src/components/layout/UserInfo.css +197 -0
  54. jettask/webui/frontend/src/components/layout/UserInfo.jsx +197 -0
  55. jettask/webui/frontend/src/contexts/NamespaceContext.jsx +72 -0
  56. jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +245 -0
  57. jettask/webui/frontend/src/main.jsx +1 -0
  58. jettask/webui/frontend/src/pages/Alerts.jsx +684 -0
  59. jettask/webui/frontend/src/pages/Dashboard.jsx +1330 -0
  60. jettask/webui/frontend/src/pages/QueueDetail.jsx +1109 -10
  61. jettask/webui/frontend/src/pages/QueueMonitor.jsx +236 -115
  62. jettask/webui/frontend/src/pages/Queues.jsx +5 -1
  63. jettask/webui/frontend/src/pages/ScheduledTasks.jsx +809 -0
  64. jettask/webui/frontend/src/pages/Settings.jsx +800 -0
  65. jettask/webui/frontend/src/services/api.js +7 -5
  66. jettask/webui/frontend/src/utils/suppressWarnings.js +22 -0
  67. jettask/webui/frontend/src/utils/userPreferences.js +154 -0
  68. jettask/webui/multi_namespace_consumer.py +543 -0
  69. jettask/webui/pg_consumer.py +983 -246
  70. jettask/webui/static/dist/assets/index-7129cfe1.css +1 -0
  71. jettask/webui/static/dist/assets/index-8d1935cc.js +774 -0
  72. jettask/webui/static/dist/index.html +2 -2
  73. jettask/webui/task_center.py +216 -0
  74. jettask/webui/task_center_client.py +150 -0
  75. jettask/webui/unified_consumer_manager.py +193 -0
  76. {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/METADATA +1 -1
  77. jettask-0.2.4.dist-info/RECORD +134 -0
  78. jettask/webui/pg_consumer_slow.py +0 -1099
  79. jettask/webui/pg_consumer_test.py +0 -678
  80. jettask/webui/static/dist/assets/index-823408e8.css +0 -1
  81. jettask/webui/static/dist/assets/index-9968b0b8.js +0 -543
  82. jettask/webui/test_pg_consumer_recovery.py +0 -547
  83. jettask/webui/test_recovery_simple.py +0 -492
  84. jettask/webui/test_self_recovery.py +0 -467
  85. jettask-0.2.1.dist-info/RECORD +0 -91
  86. {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/WHEEL +0 -0
  87. {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/entry_points.txt +0 -0
  88. {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/licenses/LICENSE +0 -0
  89. {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/top_level.txt +0 -0
jettask/core/cli.py CHANGED
@@ -29,19 +29,75 @@ def cli():
29
29
  @cli.command()
30
30
  @click.option('--host', default='0.0.0.0', help='服务器监听地址')
31
31
  @click.option('--port', default=8001, type=int, help='服务器监听端口')
32
- def webui(host, port):
33
- """启动 Web UI 监控界面"""
34
- from jettask.webui.backend.main import run_server
35
- click.echo(f"Starting JetTask Web UI on {host}:{port}")
32
+ @click.option('--reload', is_flag=True, default=False, help='启用自动重载')
33
+ @click.option('--log-level', default='info',
34
+ type=click.Choice(['debug', 'info', 'warning', 'error']),
35
+ help='日志级别')
36
+ @click.option('--unified', is_flag=True, default=False, help='使用统一的API路由器(实验性)')
37
+ def api(host, port, reload, log_level, unified):
38
+ """启动 API 服务和监控界面
36
39
 
37
- # 修改端口设置
40
+ 示例:
41
+ \b
42
+ # 使用默认配置启动
43
+ jettask api
44
+
45
+ # 指定端口和主机
46
+ jettask api --host 0.0.0.0 --port 8080
47
+
48
+ # 启用开发模式(自动重载)
49
+ jettask api --reload --log-level debug
50
+
51
+ # 使用旧版API(不推荐)
52
+ jettask api --no-unified
53
+ """
54
+ import os
38
55
  import uvicorn
39
- uvicorn.run(
40
- "jettask.webui.backend.main:app",
41
- host=host,
42
- port=port,
43
- log_level="info"
44
- )
56
+
57
+ # 设置环境变量(如果需要)
58
+ if not os.getenv('JETTASK_PG_URL'):
59
+ os.environ['JETTASK_PG_URL'] = 'postgresql://jettask:123456@localhost:5432/jettask'
60
+
61
+ if not os.getenv('JETTASK_CENTER_URL'):
62
+ os.environ['JETTASK_CENTER_URL'] = f'http://localhost:{port}/api/namespaces/default'
63
+
64
+ # 选择使用的应用模块
65
+ if unified:
66
+ app_module = "jettask.webui.backend.main_unified:app"
67
+ click.echo(f"Starting JetTask API Server (Unified Mode - Experimental) on {host}:{port}")
68
+ click.echo("All API endpoints are consolidated in unified_api_router.py")
69
+ click.echo("NOTE: This mode requires specific database schema. Use --no-unified for production.")
70
+ else:
71
+ app_module = "jettask.webui.backend.main:app"
72
+ click.echo(f"Starting JetTask API Server on {host}:{port}")
73
+
74
+ # 显示配置信息
75
+ click.echo("=" * 60)
76
+ click.echo("API Server Configuration")
77
+ click.echo("=" * 60)
78
+ click.echo(f"Host: {host}")
79
+ click.echo(f"Port: {port}")
80
+ click.echo(f"API Mode: {'Unified' if unified else 'Legacy'}")
81
+ click.echo(f"Auto-reload: {reload}")
82
+ click.echo(f"Log level: {log_level}")
83
+ click.echo(f"Database: {os.getenv('JETTASK_PG_URL', 'Not configured')}")
84
+ click.echo("=" * 60)
85
+
86
+ # 启动服务器
87
+ try:
88
+ uvicorn.run(
89
+ app_module,
90
+ host=host,
91
+ port=port,
92
+ log_level=log_level,
93
+ reload=reload
94
+ )
95
+ except KeyboardInterrupt:
96
+ click.echo("\nShutting down API Server...")
97
+ except Exception as e:
98
+ click.echo(f"Error starting API Server: {e}", err=True)
99
+ sys.exit(1)
100
+
45
101
 
46
102
  def load_module_from_path(module_path: str):
47
103
  """从文件路径加载 Python 模块"""
@@ -231,18 +287,51 @@ def worker(app_str, queues, executor, concurrency, prefetch, reload, config):
231
287
  sys.exit(1)
232
288
 
233
289
  @cli.command('webui-consumer')
234
- @click.option('--pg-url', help='PostgreSQL URL (optional, will use env var if not provided)')
235
- def webui_consumer(pg_url):
236
- """启动 Web UI 数据消费者(同步 Redis 数据到 PostgreSQL)"""
237
- click.echo("Starting WebUI Consumer...")
290
+ @click.option('--task-center', '-tc', envvar='JETTASK_CENTER_URL', required=True,
291
+ help='任务中心URL,如: http://localhost:8001 或 http://localhost:8001/api/namespaces/default')
292
+ @click.option('--check-interval', type=int, default=30,
293
+ help='命名空间检测间隔(秒),默认30秒')
294
+ @click.option('--debug', is_flag=True, help='启用调试模式')
295
+ def webui_consumer(task_center, check_interval, debug):
296
+ """启动数据消费者(自动识别单/多命名空间)
238
297
 
239
- import os
240
- if pg_url:
241
- os.environ['JETTASK_PG_URL'] = pg_url
298
+ 根据URL格式自动判断运行模式:
299
+ - 单命名空间: http://localhost:8001/api/namespaces/{name}
300
+ - 多命名空间: http://localhost:8001 或 http://localhost:8001/api
242
301
 
243
- # 启动 pg_consumer (它会从环境变量读取 Redis 配置)
244
- from jettask.webui.pg_consumer import main as consumer_main
245
- consumer_main()
302
+ 示例:
303
+ \b
304
+ # 为所有命名空间启动消费者(自动检测)
305
+ jettask webui-consumer --task-center http://localhost:8001
306
+ jettask webui-consumer --task-center http://localhost:8001/api
307
+
308
+ # 为单个命名空间启动消费者
309
+ jettask webui-consumer --task-center http://localhost:8001/api/namespaces/default
310
+
311
+ # 自定义检测间隔
312
+ jettask webui-consumer --task-center http://localhost:8001 --check-interval 60
313
+
314
+ # 使用环境变量
315
+ export JETTASK_CENTER_URL=http://localhost:8001
316
+ jettask webui-consumer
317
+ """
318
+ import asyncio
319
+ from jettask.webui.unified_consumer_manager import UnifiedConsumerManager
320
+
321
+ # 运行消费者管理器
322
+ async def run_manager():
323
+ """运行统一的消费者管理器"""
324
+ manager = UnifiedConsumerManager(
325
+ task_center_url=task_center,
326
+ check_interval=check_interval,
327
+ debug=debug
328
+ )
329
+ await manager.run()
330
+
331
+ try:
332
+ asyncio.run(run_manager())
333
+ except KeyboardInterrupt:
334
+ click.echo("\nShutdown complete")
246
335
 
247
336
  @cli.command()
248
337
  def monitor():
@@ -252,16 +341,57 @@ def monitor():
252
341
  monitor_main()
253
342
 
254
343
  @cli.command()
255
- def init():
256
- """初始化数据库和配置"""
344
+ @click.option('--task-center', '-tc', envvar='JETTASK_CENTER_URL',
345
+ help='任务中心URL,如: http://localhost:8001/api/namespaces/default')
346
+ def init(task_center):
347
+ """初始化数据库和配置
348
+
349
+ 示例:
350
+ \b
351
+ # 使用任务中心初始化
352
+ jettask init --task-center http://localhost:8001/api/namespaces/default
353
+ jettask init -tc http://localhost:8001/api/namespaces/production
354
+
355
+ # 使用环境变量
356
+ export JETTASK_CENTER_URL=http://localhost:8001/api/namespaces/default
357
+ jettask init
358
+
359
+ # 不使用任务中心(仅使用本地环境变量)
360
+ jettask init
361
+ """
257
362
  click.echo("Initializing JetTask...")
258
363
 
364
+ import os
365
+
366
+ # 如果提供了任务中心URL,尝试从任务中心获取配置
367
+ if task_center:
368
+ os.environ['JETTASK_CENTER_URL'] = task_center
369
+ click.echo(f"Using Task Center: {task_center}")
370
+
371
+ # 尝试从任务中心获取数据库配置
372
+ try:
373
+ from jettask.webui.task_center import TaskCenter
374
+ tc = TaskCenter(task_center)
375
+ if tc._connect_sync():
376
+ p_config = tc.pg_config
377
+ # 从任务中心获取的配置中提取数据库连接参数
378
+ os.environ['JETTASK_PG_HOST'] = p_config['host']
379
+ os.environ['JETTASK_PG_PORT'] = str(p_config['port'])
380
+ os.environ['JETTASK_PG_DATABASE'] = p_config['database']
381
+ os.environ['JETTASK_PG_USER'] = p_config['user']
382
+ os.environ['JETTASK_PG_PASSWORD'] = p_config['password']
383
+ else:
384
+ click.echo("⚠ Failed to connect to Task Center, using local configuration", err=True)
385
+ except Exception as e:
386
+ click.echo(f"⚠ Could not get configuration from Task Center: {e}", err=True)
387
+ click.echo(" Falling back to local environment variables")
388
+
259
389
  # 初始化数据库
260
390
  from jettask.webui.db_init import init_database
261
- click.echo("Initializing database...")
391
+ click.echo("\nInitializing database...")
262
392
  init_database()
263
393
 
264
- click.echo("JetTask initialized successfully!")
394
+ click.echo("\n✓ JetTask initialized successfully!")
265
395
 
266
396
  @cli.command()
267
397
  def status():
@@ -296,179 +426,57 @@ def status():
296
426
  click.echo("=" * 50)
297
427
 
298
428
  @cli.command()
299
- @click.argument('app_str', required=False, default=None)
300
- @click.option('--interval', '-i', type=float, default=None,
301
- help='检查间隔(秒),默认使用app配置')
302
- @click.option('--batch-size', '-b', type=int, default=None,
303
- help='每批处理的最大任务数,默认使用app配置')
304
- @click.option('--pg-url', '-p', envvar='JETTASK_PG_URL',
305
- help='PostgreSQL 连接 URL(或设置 JETTASK_PG_URL 环境变量)')
306
- @click.option('--redis-url', '-r', envvar='REDIS_URL',
307
- help='Redis 连接 URL(或设置 REDIS_URL 环境变量)')
429
+ @click.option('--task-center', '-tc', envvar='JETTASK_CENTER_URL', required=True,
430
+ help='任务中心URL,如: http://localhost:8001 http://localhost:8001/api/namespaces/default')
431
+ @click.option('--interval', '-i', type=float, default=0.1,
432
+ help='调度器扫描间隔(秒),默认0.1秒')
433
+ @click.option('--batch-size', '-b', type=int, default=100,
434
+ help='每批处理的最大任务数,默认100')
435
+ @click.option('--check-interval', type=int, default=30,
436
+ help='命名空间检测间隔(秒),仅多命名空间模式使用,默认30秒')
308
437
  @click.option('--debug', is_flag=True, help='启用调试模式')
309
- def scheduler(app_str, interval, batch_size, pg_url, redis_url, debug):
310
- """启动定时任务调度器
438
+ def scheduler(task_center, interval, batch_size, check_interval, debug):
439
+ """启动定时任务调度器(自动识别单/多命名空间)
440
+
441
+ 根据URL格式自动判断运行模式:
442
+ - 单命名空间: http://localhost:8001/api/namespaces/{name}
443
+ - 多命名空间: http://localhost:8001 或 http://localhost:8001/api
311
444
 
312
445
  示例:
313
446
  \b
314
- # 显式指定 app
315
- jettask scheduler main:app
316
- jettask scheduler tasks.py:app --interval 30
447
+ # 为所有命名空间启动调度器(自动检测)
448
+ jettask scheduler --task-center http://localhost:8001
449
+ jettask scheduler --task-center http://localhost:8001/api
317
450
 
318
- # 自动发现 app
319
- jettask scheduler
451
+ # 为单个命名空间启动调度器
452
+ jettask scheduler --task-center http://localhost:8001/api/namespaces/default
453
+
454
+ # 自定义配置
455
+ jettask scheduler --task-center http://localhost:8001 --check-interval 60 --interval 0.5
320
456
 
321
457
  # 使用环境变量
322
- export JETTASK_APP=myapp:app
323
- export JETTASK_PG_URL=postgresql://user:pass@localhost/db
458
+ export JETTASK_CENTER_URL=http://localhost:8001
324
459
  jettask scheduler
325
460
  """
326
461
  import asyncio
462
+ from jettask.scheduler.unified_scheduler_manager import UnifiedSchedulerManager
463
+
464
+ # 运行调度器管理器
465
+ async def run_manager():
466
+ """运行统一的调度器管理器"""
467
+ manager = UnifiedSchedulerManager(
468
+ task_center_url=task_center,
469
+ scan_interval=interval,
470
+ batch_size=batch_size,
471
+ check_interval=check_interval,
472
+ debug=debug
473
+ )
474
+ await manager.run()
327
475
 
328
- # 处理相对导入
329
- try:
330
- from ..scheduler.scheduler import TaskScheduler
331
- from ..scheduler.manager import ScheduledTaskManager
332
- except ImportError:
333
- # 直接运行时使用绝对导入
334
- from jettask.scheduler.scheduler import TaskScheduler
335
- from jettask.scheduler.manager import ScheduledTaskManager
336
-
337
- # 设置日志级别
338
- if debug:
339
- import logging
340
- logging.basicConfig(level=logging.DEBUG)
341
-
342
- # 加载应用
343
- try:
344
- if app_str:
345
- click.echo(f"Loading app from: {app_str}")
346
- app = import_app(app_str)
347
- else:
348
- click.echo("Auto-discovering Jettask app...")
349
- click.echo("Searching in: app.py, main.py, server.py, worker.py")
350
- app = import_app() # 自动发现
351
-
352
- # 显示应用信息
353
- app_info = AppImporter.get_app_info(app)
354
- click.echo(f"\nFound Jettask app:")
355
- click.echo(f" Tasks: {app_info['tasks']} registered")
356
- if app_info.get('task_names') and app_info['tasks'] > 0:
357
- task_preview = app_info['task_names'][:3]
358
- click.echo(f" Names: {', '.join(task_preview)}" +
359
- (f" (+{app_info['tasks'] - 3} more)" if app_info['tasks'] > 3 else ""))
360
- except ImportError as e:
361
- click.echo(f"Error: {e}", err=True)
362
- click.echo("\nTips:", err=True)
363
- click.echo(" - Specify app location: jettask scheduler myapp:app", err=True)
364
- click.echo(" - Or set environment variable: export JETTASK_APP=myapp:app", err=True)
365
- click.echo(" - Or ensure app.py or main.py exists in current directory", err=True)
366
- sys.exit(1)
367
- except Exception as e:
368
- import traceback
369
- click.echo(f"Error loading app: {e}", err=True)
370
- if debug:
371
- traceback.print_exc()
372
- sys.exit(1)
373
-
374
- # 获取配置
375
- # 优先级:命令行参数 > 环境变量 > app 配置
376
- redis_url = redis_url or getattr(app, 'redis_url', 'redis://localhost:6379/0')
377
- pg_url = pg_url or getattr(app, 'pg_url', None)
378
-
379
- # 从app的scheduler_config获取默认值
380
- scheduler_config = getattr(app, 'scheduler_config', {})
381
- if interval is None:
382
- interval = scheduler_config.get('scan_interval', 0.1)
383
- if batch_size is None:
384
- batch_size = scheduler_config.get('batch_size', 100)
385
-
386
- # 显示配置信息
387
- click.echo("\n" + "=" * 60)
388
- click.echo("JetTask Scheduler Configuration")
389
- click.echo("=" * 60)
390
- click.echo(f"App: {app_str or 'auto-discovered'}")
391
- click.echo(f"Redis URL: {redis_url}")
392
- click.echo(f"PostgreSQL: {pg_url or 'Not configured'}")
393
- if pg_url and not (pg_url.startswith('postgresql://') or pg_url.startswith('postgres://')):
394
- click.echo(f" Source: From app configuration")
395
- click.echo(f"Interval: {interval} seconds (from {'CLI' if '--interval' in sys.argv else 'app config'})")
396
- click.echo(f"Batch Size: {batch_size} (from {'CLI' if '--batch-size' in sys.argv else 'app config'})")
397
- click.echo(f"Debug: {'Enabled' if debug else 'Disabled'}")
398
- click.echo("=" * 60)
399
-
400
- # 检查 PostgreSQL 配置
401
- if not pg_url:
402
- click.echo("\n" + "=" * 60)
403
- click.echo("ERROR: PostgreSQL configuration is required for scheduler")
404
- click.echo("=" * 60)
405
- click.echo("\nThe scheduler requires PostgreSQL to:")
406
- click.echo(" • Store scheduled task definitions")
407
- click.echo(" • Track task execution history")
408
- click.echo(" • Manage cron schedules")
409
- click.echo("\nPlease configure PostgreSQL using one of these methods:")
410
- click.echo("\n1. Command line option:")
411
- click.echo(" jettask scheduler --pg-url postgresql://user:pass@localhost/db")
412
- click.echo("\n2. Environment variable:")
413
- click.echo(" export JETTASK_PG_URL=postgresql://user:pass@localhost/db")
414
- click.echo(" jettask scheduler")
415
- click.echo("\n3. Example with local PostgreSQL:")
416
- click.echo(" jettask scheduler --pg-url postgresql://jettask:123456@localhost/jettask")
417
- click.echo("=" * 60)
418
- sys.exit(1)
419
-
420
- # 创建调度器实例
421
- click.echo("\nUsing PostgreSQL for scheduled task management")
422
- manager = ScheduledTaskManager(pg_url)
423
- scheduler_instance = TaskScheduler(
424
- app=app,
425
- redis_url=redis_url,
426
- db_manager=manager,
427
- scan_interval=interval,
428
- batch_size=batch_size
429
- )
430
-
431
- # 运行调度器
432
- async def run_scheduler():
433
- """运行调度器的异步函数"""
434
- try:
435
- click.echo("\nStarting scheduler...")
436
- # 先连接
437
- await scheduler_instance.connect()
438
- # 然后运行
439
- await scheduler_instance.run()
440
- except KeyboardInterrupt:
441
- click.echo("\nReceived shutdown signal, stopping scheduler...")
442
- except Exception as e:
443
- click.echo(f"\nScheduler error: {e}", err=True)
444
- if debug:
445
- import traceback
446
- traceback.print_exc()
447
- finally:
448
- # 停止调度器
449
- scheduler_instance.stop()
450
-
451
- # 强制清理leader锁
452
- if scheduler_instance.is_leader and scheduler_instance.redis:
453
- try:
454
- leader_key = f"{scheduler_instance.redis_prefix}:leader"
455
- await scheduler_instance.redis.delete(leader_key)
456
- click.echo("Leader lock cleaned up")
457
- except Exception as e:
458
- click.echo(f"Failed to cleanup leader lock: {e}", err=True)
459
-
460
- # 确保断开连接
461
- await scheduler_instance.disconnect()
462
- click.echo("Scheduler stopped")
463
-
464
- # 启动事件循环
465
476
  try:
466
- asyncio.run(run_scheduler())
477
+ asyncio.run(run_manager())
467
478
  except KeyboardInterrupt:
468
- click.echo("\nScheduler shutdown complete")
469
- except Exception as e:
470
- click.echo(f"Fatal error: {e}", err=True)
471
- sys.exit(1)
479
+ click.echo("\nShutdown complete")
472
480
 
473
481
  def main():
474
482
  """主入口函数"""