jettask 0.2.5__py3-none-any.whl → 0.2.6__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 (93) hide show
  1. jettask/monitor/run_backlog_collector.py +96 -0
  2. jettask/monitor/stream_backlog_monitor.py +362 -0
  3. jettask/pg_consumer/pg_consumer_v2.py +403 -0
  4. jettask/pg_consumer/sql_utils.py +182 -0
  5. jettask/scheduler/__init__.py +17 -0
  6. jettask/scheduler/add_execution_count.sql +11 -0
  7. jettask/scheduler/add_priority_field.sql +26 -0
  8. jettask/scheduler/add_scheduler_id.sql +25 -0
  9. jettask/scheduler/add_scheduler_id_index.sql +10 -0
  10. jettask/scheduler/loader.py +249 -0
  11. jettask/scheduler/make_scheduler_id_required.sql +28 -0
  12. jettask/scheduler/manager.py +696 -0
  13. jettask/scheduler/migrate_interval_seconds.sql +9 -0
  14. jettask/scheduler/models.py +200 -0
  15. jettask/scheduler/multi_namespace_scheduler.py +294 -0
  16. jettask/scheduler/performance_optimization.sql +45 -0
  17. jettask/scheduler/run_scheduler.py +186 -0
  18. jettask/scheduler/scheduler.py +715 -0
  19. jettask/scheduler/schema.sql +84 -0
  20. jettask/scheduler/unified_manager.py +450 -0
  21. jettask/scheduler/unified_scheduler_manager.py +280 -0
  22. jettask/webui/backend/api/__init__.py +3 -0
  23. jettask/webui/backend/api/v1/__init__.py +17 -0
  24. jettask/webui/backend/api/v1/monitoring.py +431 -0
  25. jettask/webui/backend/api/v1/namespaces.py +504 -0
  26. jettask/webui/backend/api/v1/queues.py +342 -0
  27. jettask/webui/backend/api/v1/tasks.py +367 -0
  28. jettask/webui/backend/core/__init__.py +3 -0
  29. jettask/webui/backend/core/cache.py +221 -0
  30. jettask/webui/backend/core/database.py +200 -0
  31. jettask/webui/backend/core/exceptions.py +102 -0
  32. jettask/webui/backend/models/__init__.py +3 -0
  33. jettask/webui/backend/models/requests.py +236 -0
  34. jettask/webui/backend/models/responses.py +230 -0
  35. jettask/webui/backend/services/__init__.py +3 -0
  36. jettask/webui/frontend/index.html +13 -0
  37. jettask/webui/models/__init__.py +3 -0
  38. jettask/webui/models/namespace.py +63 -0
  39. jettask/webui/sql/batch_upsert_functions.sql +178 -0
  40. jettask/webui/sql/init_database.sql +640 -0
  41. {jettask-0.2.5.dist-info → jettask-0.2.6.dist-info}/METADATA +11 -9
  42. {jettask-0.2.5.dist-info → jettask-0.2.6.dist-info}/RECORD +46 -53
  43. jettask/webui/frontend/package-lock.json +0 -4833
  44. jettask/webui/frontend/package.json +0 -30
  45. jettask/webui/frontend/src/App.css +0 -109
  46. jettask/webui/frontend/src/App.jsx +0 -66
  47. jettask/webui/frontend/src/components/NamespaceSelector.jsx +0 -166
  48. jettask/webui/frontend/src/components/QueueBacklogChart.jsx +0 -298
  49. jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +0 -638
  50. jettask/webui/frontend/src/components/QueueDetailsTable.css +0 -65
  51. jettask/webui/frontend/src/components/QueueDetailsTable.jsx +0 -487
  52. jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +0 -465
  53. jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +0 -423
  54. jettask/webui/frontend/src/components/TaskFilter.jsx +0 -425
  55. jettask/webui/frontend/src/components/TimeRangeSelector.css +0 -21
  56. jettask/webui/frontend/src/components/TimeRangeSelector.jsx +0 -160
  57. jettask/webui/frontend/src/components/charts/QueueChart.jsx +0 -111
  58. jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +0 -115
  59. jettask/webui/frontend/src/components/charts/WorkerChart.jsx +0 -40
  60. jettask/webui/frontend/src/components/common/StatsCard.jsx +0 -18
  61. jettask/webui/frontend/src/components/layout/AppLayout.css +0 -95
  62. jettask/webui/frontend/src/components/layout/AppLayout.jsx +0 -49
  63. jettask/webui/frontend/src/components/layout/Header.css +0 -106
  64. jettask/webui/frontend/src/components/layout/Header.jsx +0 -106
  65. jettask/webui/frontend/src/components/layout/SideMenu.css +0 -137
  66. jettask/webui/frontend/src/components/layout/SideMenu.jsx +0 -209
  67. jettask/webui/frontend/src/components/layout/TabsNav.css +0 -244
  68. jettask/webui/frontend/src/components/layout/TabsNav.jsx +0 -206
  69. jettask/webui/frontend/src/components/layout/UserInfo.css +0 -197
  70. jettask/webui/frontend/src/components/layout/UserInfo.jsx +0 -197
  71. jettask/webui/frontend/src/contexts/LoadingContext.jsx +0 -27
  72. jettask/webui/frontend/src/contexts/NamespaceContext.jsx +0 -72
  73. jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +0 -245
  74. jettask/webui/frontend/src/index.css +0 -114
  75. jettask/webui/frontend/src/main.jsx +0 -20
  76. jettask/webui/frontend/src/pages/Alerts.jsx +0 -684
  77. jettask/webui/frontend/src/pages/Dashboard/index.css +0 -35
  78. jettask/webui/frontend/src/pages/Dashboard/index.jsx +0 -281
  79. jettask/webui/frontend/src/pages/Dashboard.jsx +0 -1330
  80. jettask/webui/frontend/src/pages/QueueDetail.jsx +0 -1117
  81. jettask/webui/frontend/src/pages/QueueMonitor.jsx +0 -527
  82. jettask/webui/frontend/src/pages/Queues.jsx +0 -12
  83. jettask/webui/frontend/src/pages/ScheduledTasks.jsx +0 -809
  84. jettask/webui/frontend/src/pages/Settings.jsx +0 -800
  85. jettask/webui/frontend/src/pages/Workers.jsx +0 -12
  86. jettask/webui/frontend/src/services/api.js +0 -114
  87. jettask/webui/frontend/src/services/queueTrend.js +0 -152
  88. jettask/webui/frontend/src/utils/suppressWarnings.js +0 -22
  89. jettask/webui/frontend/src/utils/userPreferences.js +0 -154
  90. {jettask-0.2.5.dist-info → jettask-0.2.6.dist-info}/WHEEL +0 -0
  91. {jettask-0.2.5.dist-info → jettask-0.2.6.dist-info}/entry_points.txt +0 -0
  92. {jettask-0.2.5.dist-info → jettask-0.2.6.dist-info}/licenses/LICENSE +0 -0
  93. {jettask-0.2.5.dist-info → jettask-0.2.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,640 @@
1
+ -- JetTask 数据库初始化脚本
2
+ -- 包含所有表结构、索引、函数和分区设置
3
+
4
+ -- ========================================
5
+ -- 1. 创建更新时间触发器函数
6
+ -- ========================================
7
+ CREATE OR REPLACE FUNCTION update_updated_at_column()
8
+ RETURNS TRIGGER AS $$
9
+ BEGIN
10
+ NEW.updated_at = CURRENT_TIMESTAMP;
11
+ RETURN NEW;
12
+ END;
13
+ $$ LANGUAGE plpgsql;
14
+
15
+ -- ========================================
16
+ -- 2. 创建 namespaces 表
17
+ -- ========================================
18
+ CREATE TABLE IF NOT EXISTS namespaces (
19
+ id SERIAL PRIMARY KEY,
20
+ name VARCHAR(255) UNIQUE NOT NULL,
21
+ description TEXT,
22
+ connection_url TEXT NOT NULL,
23
+ is_default BOOLEAN DEFAULT FALSE,
24
+ created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
25
+ updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
26
+ );
27
+
28
+ -- 插入默认命名空间
29
+ INSERT INTO namespaces (name, description, connection_url, is_default)
30
+ VALUES ('default', 'Default namespace', 'redis://localhost:6379/0', true)
31
+ ON CONFLICT (name) DO NOTHING;
32
+
33
+ -- ========================================
34
+ -- 3. 创建 scheduled_tasks 表
35
+ -- ========================================
36
+ CREATE TABLE IF NOT EXISTS scheduled_tasks (
37
+ id BIGSERIAL PRIMARY KEY,
38
+ scheduler_id VARCHAR(255) UNIQUE NOT NULL,
39
+ task_name VARCHAR(255) NOT NULL,
40
+ task_type VARCHAR(50) NOT NULL,
41
+ queue_name VARCHAR(100) NOT NULL,
42
+ task_args JSONB DEFAULT '[]'::jsonb,
43
+ task_kwargs JSONB DEFAULT '{}'::jsonb,
44
+ cron_expression VARCHAR(100),
45
+ interval_seconds NUMERIC(10,2),
46
+ next_run_time TIMESTAMPTZ,
47
+ last_run_time TIMESTAMPTZ,
48
+ enabled BOOLEAN DEFAULT true,
49
+ max_retries INTEGER DEFAULT 3,
50
+ retry_delay INTEGER DEFAULT 60,
51
+ timeout INTEGER DEFAULT 300,
52
+ description TEXT,
53
+ tags JSONB DEFAULT '[]'::jsonb,
54
+ metadata JSONB DEFAULT '{}'::jsonb,
55
+ created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
56
+ updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
57
+ execution_count INTEGER DEFAULT 0,
58
+ namespace VARCHAR(255) DEFAULT 'default',
59
+ priority INTEGER
60
+ );
61
+
62
+ -- 创建优化后的索引
63
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_next_run
64
+ ON scheduled_tasks (next_run_time)
65
+ INCLUDE (scheduler_id, task_name, queue_name, enabled)
66
+ WHERE enabled = true;
67
+
68
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_enabled_ns
69
+ ON scheduled_tasks (namespace, enabled)
70
+ WHERE enabled = true;
71
+
72
+ -- 创建触发器
73
+ CREATE TRIGGER update_scheduled_tasks_updated_at
74
+ BEFORE UPDATE ON scheduled_tasks
75
+ FOR EACH ROW
76
+ EXECUTE FUNCTION update_updated_at_column();
77
+
78
+ -- ========================================
79
+ -- 4. 创建 tasks 分区表
80
+ -- ========================================
81
+ CREATE TABLE IF NOT EXISTS tasks (
82
+ stream_id TEXT NOT NULL,
83
+ queue TEXT NOT NULL,
84
+ namespace TEXT NOT NULL DEFAULT 'default',
85
+ scheduled_task_id TEXT,
86
+ payload JSONB NOT NULL,
87
+ priority INTEGER DEFAULT 0,
88
+ created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
89
+ source TEXT NOT NULL DEFAULT 'redis_stream',
90
+ metadata JSONB DEFAULT '{}',
91
+ PRIMARY KEY (stream_id, created_at)
92
+ ) PARTITION BY RANGE (created_at);
93
+
94
+ -- 创建索引
95
+ CREATE INDEX IF NOT EXISTS idx_tasks_created
96
+ ON tasks (created_at DESC);
97
+
98
+ CREATE INDEX IF NOT EXISTS idx_tasks_ns_created
99
+ ON tasks (namespace, created_at DESC)
100
+ INCLUDE (queue, priority);
101
+
102
+ CREATE INDEX IF NOT EXISTS idx_tasks_queue_time
103
+ ON tasks (queue, created_at DESC)
104
+ WHERE namespace = 'default';
105
+
106
+ CREATE INDEX IF NOT EXISTS idx_tasks_scheduled_id
107
+ ON tasks (scheduled_task_id, created_at DESC)
108
+ WHERE scheduled_task_id IS NOT NULL;
109
+
110
+ -- ========================================
111
+ -- 5. 创建 task_runs 分区表
112
+ -- ========================================
113
+ CREATE TABLE IF NOT EXISTS task_runs (
114
+ id BIGSERIAL,
115
+ stream_id TEXT NOT NULL,
116
+ task_name TEXT NOT NULL,
117
+ consumer_group TEXT NOT NULL,
118
+ consumer_name TEXT,
119
+ worker_id TEXT,
120
+ status TEXT NOT NULL DEFAULT 'pending',
121
+ created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
122
+ start_time TIMESTAMPTZ,
123
+ end_time TIMESTAMPTZ,
124
+ retry_count INTEGER DEFAULT 0,
125
+ max_retries INTEGER DEFAULT 3,
126
+ error_message TEXT,
127
+ error_details JSONB,
128
+ result JSONB,
129
+ logs TEXT[],
130
+ updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
131
+ duration DOUBLE PRECISION,
132
+ execution_time DOUBLE PRECISION,
133
+ PRIMARY KEY (id, created_at),
134
+ UNIQUE (stream_id, consumer_group, created_at)
135
+ ) PARTITION BY RANGE (created_at);
136
+
137
+ -- 创建索引
138
+ CREATE INDEX IF NOT EXISTS idx_task_runs_stream_covering
139
+ ON task_runs (stream_id, created_at)
140
+ INCLUDE (status, execution_time, end_time, start_time);
141
+
142
+ CREATE INDEX IF NOT EXISTS idx_task_runs_status_created
143
+ ON task_runs (status, created_at);
144
+
145
+ CREATE INDEX IF NOT EXISTS idx_task_runs_success_time
146
+ ON task_runs (end_time DESC, created_at)
147
+ WHERE status = 'success';
148
+
149
+ CREATE INDEX IF NOT EXISTS idx_task_runs_consumer_created
150
+ ON task_runs (consumer_group, created_at DESC);
151
+
152
+ -- 创建触发器
153
+ CREATE TRIGGER update_task_runs_updated_at
154
+ BEFORE UPDATE ON task_runs
155
+ FOR EACH ROW
156
+ EXECUTE FUNCTION update_updated_at_column();
157
+
158
+ -- ========================================
159
+ -- 6. 创建 stream_backlog_monitor 分区表
160
+ -- ========================================
161
+ CREATE TABLE IF NOT EXISTS stream_backlog_monitor (
162
+ id BIGSERIAL,
163
+ namespace TEXT NOT NULL DEFAULT 'default',
164
+ stream_name TEXT NOT NULL,
165
+ consumer_group TEXT,
166
+ priority INTEGER,
167
+ last_published_offset TEXT,
168
+ last_consumed_offset TEXT,
169
+ last_delivered_offset BIGINT DEFAULT 0, -- 最后投递的偏移量
170
+ last_acked_offset BIGINT DEFAULT 0, -- 最后确认的偏移量
171
+ consumer_lag BIGINT,
172
+ pending_count BIGINT DEFAULT 0, -- 待处理任务数
173
+ backlog_undelivered BIGINT DEFAULT 0, -- 未投递的积压数
174
+ backlog_unprocessed BIGINT DEFAULT 0, -- 未处理的积压数
175
+ task_count BIGINT,
176
+ created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
177
+ collected_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
178
+ metadata JSONB DEFAULT '{}',
179
+ PRIMARY KEY (id, created_at)
180
+ ) PARTITION BY RANGE (created_at);
181
+
182
+ -- 创建索引
183
+ CREATE INDEX IF NOT EXISTS idx_stream_backlog_namespace_stream
184
+ ON stream_backlog_monitor (namespace, stream_name, created_at DESC);
185
+
186
+ CREATE INDEX IF NOT EXISTS idx_stream_backlog_created
187
+ ON stream_backlog_monitor (created_at DESC);
188
+
189
+ -- ========================================
190
+ -- 7. 创建 alert_rules 表
191
+ -- ========================================
192
+ CREATE TABLE IF NOT EXISTS alert_rules (
193
+ id BIGSERIAL PRIMARY KEY,
194
+ name VARCHAR(255) NOT NULL,
195
+ description TEXT,
196
+ rule_type VARCHAR(50) NOT NULL,
197
+ metric VARCHAR(100) NOT NULL,
198
+ condition VARCHAR(20) NOT NULL,
199
+ threshold DOUBLE PRECISION NOT NULL,
200
+ duration_seconds INTEGER DEFAULT 60,
201
+ enabled BOOLEAN DEFAULT true,
202
+ alert_channels JSONB DEFAULT '[]'::jsonb,
203
+ metadata JSONB DEFAULT '{}'::jsonb,
204
+ created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
205
+ updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
206
+ );
207
+
208
+ CREATE INDEX IF NOT EXISTS idx_alert_rules_enabled
209
+ ON alert_rules (enabled) WHERE enabled = true;
210
+
211
+ -- 创建触发器
212
+ CREATE TRIGGER update_alert_rules_updated_at
213
+ BEFORE UPDATE ON alert_rules
214
+ FOR EACH ROW
215
+ EXECUTE FUNCTION update_updated_at_column();
216
+
217
+ -- ========================================
218
+ -- 8. 创建 alert_history 表
219
+ -- ========================================
220
+ CREATE TABLE IF NOT EXISTS alert_history (
221
+ id BIGSERIAL PRIMARY KEY,
222
+ rule_id BIGINT REFERENCES alert_rules(id),
223
+ alert_time TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
224
+ metric_value DOUBLE PRECISION,
225
+ threshold_value DOUBLE PRECISION,
226
+ alert_message TEXT,
227
+ alert_level VARCHAR(20),
228
+ resolved BOOLEAN DEFAULT false,
229
+ resolved_time TIMESTAMPTZ,
230
+ metadata JSONB DEFAULT '{}'::jsonb
231
+ );
232
+
233
+ CREATE INDEX IF NOT EXISTS idx_alert_history_rule_id
234
+ ON alert_history (rule_id, alert_time DESC);
235
+
236
+ CREATE INDEX IF NOT EXISTS idx_alert_history_unresolved
237
+ ON alert_history (resolved, alert_time DESC) WHERE resolved = false;
238
+
239
+ -- ========================================
240
+ -- 9. 分区管理函数
241
+ -- ========================================
242
+
243
+ -- tasks 表分区管理
244
+ CREATE OR REPLACE FUNCTION create_tasks_partition()
245
+ RETURNS void AS $$
246
+ DECLARE
247
+ start_date date;
248
+ end_date date;
249
+ partition_name text;
250
+ BEGIN
251
+ -- 创建当前月分区
252
+ start_date := date_trunc('month', CURRENT_DATE);
253
+ end_date := start_date + interval '1 month';
254
+ partition_name := 'tasks_' || to_char(start_date, 'YYYY_MM');
255
+
256
+ IF NOT EXISTS (
257
+ SELECT 1 FROM pg_class WHERE relname = partition_name
258
+ ) THEN
259
+ EXECUTE format(
260
+ 'CREATE TABLE %I PARTITION OF tasks
261
+ FOR VALUES FROM (%L) TO (%L)',
262
+ partition_name, start_date, end_date
263
+ );
264
+ RAISE NOTICE 'Created partition: %', partition_name;
265
+ END IF;
266
+
267
+ -- 创建下月分区
268
+ start_date := date_trunc('month', CURRENT_DATE + interval '1 month');
269
+ end_date := start_date + interval '1 month';
270
+ partition_name := 'tasks_' || to_char(start_date, 'YYYY_MM');
271
+
272
+ IF NOT EXISTS (
273
+ SELECT 1 FROM pg_class WHERE relname = partition_name
274
+ ) THEN
275
+ EXECUTE format(
276
+ 'CREATE TABLE %I PARTITION OF tasks
277
+ FOR VALUES FROM (%L) TO (%L)',
278
+ partition_name, start_date, end_date
279
+ );
280
+ RAISE NOTICE 'Created partition: %', partition_name;
281
+ END IF;
282
+
283
+ -- 创建上月分区
284
+ start_date := date_trunc('month', CURRENT_DATE - interval '1 month');
285
+ end_date := start_date + interval '1 month';
286
+ partition_name := 'tasks_' || to_char(start_date, 'YYYY_MM');
287
+
288
+ IF NOT EXISTS (
289
+ SELECT 1 FROM pg_class WHERE relname = partition_name
290
+ ) THEN
291
+ EXECUTE format(
292
+ 'CREATE TABLE %I PARTITION OF tasks
293
+ FOR VALUES FROM (%L) TO (%L)',
294
+ partition_name, start_date, end_date
295
+ );
296
+ RAISE NOTICE 'Created partition: %', partition_name;
297
+ END IF;
298
+ END;
299
+ $$ LANGUAGE plpgsql;
300
+
301
+ -- task_runs 表分区管理
302
+ CREATE OR REPLACE FUNCTION create_task_runs_partition()
303
+ RETURNS void AS $$
304
+ DECLARE
305
+ start_date date;
306
+ end_date date;
307
+ partition_name text;
308
+ BEGIN
309
+ -- 创建当前月份分区
310
+ start_date := date_trunc('month', CURRENT_DATE);
311
+ end_date := start_date + interval '1 month';
312
+ partition_name := 'task_runs_' || to_char(start_date, 'YYYY_MM');
313
+
314
+ IF NOT EXISTS (
315
+ SELECT 1 FROM pg_class WHERE relname = partition_name
316
+ ) THEN
317
+ EXECUTE format(
318
+ 'CREATE TABLE %I PARTITION OF task_runs
319
+ FOR VALUES FROM (%L) TO (%L)',
320
+ partition_name, start_date, end_date
321
+ );
322
+ RAISE NOTICE 'Created partition: %', partition_name;
323
+ END IF;
324
+
325
+ -- 创建下个月分区
326
+ start_date := date_trunc('month', CURRENT_DATE + interval '1 month');
327
+ end_date := start_date + interval '1 month';
328
+ partition_name := 'task_runs_' || to_char(start_date, 'YYYY_MM');
329
+
330
+ IF NOT EXISTS (
331
+ SELECT 1 FROM pg_class WHERE relname = partition_name
332
+ ) THEN
333
+ EXECUTE format(
334
+ 'CREATE TABLE %I PARTITION OF task_runs
335
+ FOR VALUES FROM (%L) TO (%L)',
336
+ partition_name, start_date, end_date
337
+ );
338
+ RAISE NOTICE 'Created partition: %', partition_name;
339
+ END IF;
340
+
341
+ -- 创建前一个月分区
342
+ start_date := date_trunc('month', CURRENT_DATE - interval '1 month');
343
+ end_date := start_date + interval '1 month';
344
+ partition_name := 'task_runs_' || to_char(start_date, 'YYYY_MM');
345
+
346
+ IF NOT EXISTS (
347
+ SELECT 1 FROM pg_class WHERE relname = partition_name
348
+ ) THEN
349
+ EXECUTE format(
350
+ 'CREATE TABLE %I PARTITION OF task_runs
351
+ FOR VALUES FROM (%L) TO (%L)',
352
+ partition_name, start_date, end_date
353
+ );
354
+ RAISE NOTICE 'Created partition: %', partition_name;
355
+ END IF;
356
+ END;
357
+ $$ LANGUAGE plpgsql;
358
+
359
+ -- stream_backlog_monitor 表分区管理
360
+ CREATE OR REPLACE FUNCTION create_stream_backlog_partition()
361
+ RETURNS void AS $$
362
+ DECLARE
363
+ start_date date;
364
+ end_date date;
365
+ partition_name text;
366
+ BEGIN
367
+ -- 创建最近3个月的分区
368
+ FOR i IN -1..1 LOOP
369
+ start_date := date_trunc('month', CURRENT_DATE + (i || ' month')::interval);
370
+ end_date := start_date + interval '1 month';
371
+ partition_name := 'stream_backlog_monitor_' || to_char(start_date, 'YYYY_MM');
372
+
373
+ IF NOT EXISTS (
374
+ SELECT 1 FROM pg_class WHERE relname = partition_name
375
+ ) THEN
376
+ EXECUTE format(
377
+ 'CREATE TABLE %I PARTITION OF stream_backlog_monitor
378
+ FOR VALUES FROM (%L) TO (%L)',
379
+ partition_name, start_date, end_date
380
+ );
381
+ RAISE NOTICE 'Created partition: %', partition_name;
382
+ END IF;
383
+ END LOOP;
384
+ END;
385
+ $$ LANGUAGE plpgsql;
386
+
387
+ -- ========================================
388
+ -- 10. 分区维护函数
389
+ -- ========================================
390
+
391
+ -- tasks 表维护
392
+ CREATE OR REPLACE FUNCTION maintain_tasks_partitions()
393
+ RETURNS void AS $$
394
+ DECLARE
395
+ partition_date date;
396
+ partition_name text;
397
+ old_partition_date date;
398
+ old_partition_name text;
399
+ BEGIN
400
+ -- 创建未来分区
401
+ PERFORM create_tasks_partition();
402
+
403
+ -- 删除超过2个月的旧分区
404
+ old_partition_date := date_trunc('month', CURRENT_DATE - interval '2 months');
405
+ old_partition_name := 'tasks_' || to_char(old_partition_date, 'YYYY_MM');
406
+
407
+ IF EXISTS (
408
+ SELECT 1 FROM pg_class WHERE relname = old_partition_name
409
+ ) THEN
410
+ EXECUTE format('DROP TABLE IF EXISTS %I CASCADE', old_partition_name);
411
+ RAISE NOTICE 'Dropped old partition: %', old_partition_name;
412
+ END IF;
413
+
414
+ -- 对活跃分区执行 VACUUM ANALYZE
415
+ FOR partition_date IN
416
+ SELECT generate_series(
417
+ date_trunc('month', CURRENT_DATE - interval '1 month'),
418
+ date_trunc('month', CURRENT_DATE),
419
+ interval '1 month'
420
+ )::date
421
+ LOOP
422
+ partition_name := 'tasks_' || to_char(partition_date, 'YYYY_MM');
423
+ IF EXISTS (
424
+ SELECT 1 FROM pg_class WHERE relname = partition_name
425
+ ) THEN
426
+ EXECUTE format('VACUUM ANALYZE %I', partition_name);
427
+ END IF;
428
+ END LOOP;
429
+ END;
430
+ $$ LANGUAGE plpgsql;
431
+
432
+ -- task_runs 表维护
433
+ CREATE OR REPLACE FUNCTION maintain_task_runs_partitions()
434
+ RETURNS void AS $$
435
+ DECLARE
436
+ partition_date date;
437
+ partition_name text;
438
+ old_partition_date date;
439
+ old_partition_name text;
440
+ BEGIN
441
+ -- 创建未来分区
442
+ PERFORM create_task_runs_partition();
443
+
444
+ -- 删除超过3个月的旧分区
445
+ old_partition_date := date_trunc('month', CURRENT_DATE - interval '3 months');
446
+ old_partition_name := 'task_runs_' || to_char(old_partition_date, 'YYYY_MM');
447
+
448
+ IF EXISTS (
449
+ SELECT 1 FROM pg_class WHERE relname = old_partition_name
450
+ ) THEN
451
+ EXECUTE format('DROP TABLE IF EXISTS %I', old_partition_name);
452
+ RAISE NOTICE 'Dropped old partition: %', old_partition_name;
453
+ END IF;
454
+
455
+ -- 对活跃分区执行 VACUUM ANALYZE
456
+ FOR partition_date IN
457
+ SELECT generate_series(
458
+ date_trunc('month', CURRENT_DATE - interval '1 month'),
459
+ date_trunc('month', CURRENT_DATE + interval '1 month'),
460
+ interval '1 month'
461
+ )::date
462
+ LOOP
463
+ partition_name := 'task_runs_' || to_char(partition_date, 'YYYY_MM');
464
+ IF EXISTS (
465
+ SELECT 1 FROM pg_class WHERE relname = partition_name
466
+ ) THEN
467
+ EXECUTE format('VACUUM ANALYZE %I', partition_name);
468
+ RAISE NOTICE 'Vacuumed partition: %', partition_name;
469
+ END IF;
470
+ END LOOP;
471
+ END;
472
+ $$ LANGUAGE plpgsql;
473
+
474
+ -- scheduled_tasks 表维护
475
+ CREATE OR REPLACE FUNCTION maintain_scheduled_tasks()
476
+ RETURNS void AS $$
477
+ BEGIN
478
+ -- 重置长时间未执行的任务
479
+ UPDATE scheduled_tasks
480
+ SET next_run_time = CURRENT_TIMESTAMP
481
+ WHERE enabled = true
482
+ AND next_run_time < CURRENT_TIMESTAMP - interval '1 hour';
483
+
484
+ -- 记录维护日志
485
+ RAISE NOTICE 'Scheduled tasks maintenance completed at %', CURRENT_TIMESTAMP;
486
+
487
+ -- 更新统计信息
488
+ ANALYZE scheduled_tasks;
489
+ END;
490
+ $$ LANGUAGE plpgsql;
491
+
492
+ -- ========================================
493
+ -- 11. 创建初始分区
494
+ -- ========================================
495
+ SELECT create_tasks_partition();
496
+ SELECT create_task_runs_partition();
497
+ SELECT create_stream_backlog_partition();
498
+
499
+ -- ========================================
500
+ -- 12. 批量操作优化函数
501
+ -- ========================================
502
+
503
+ -- task_runs批量UPSERT函数
504
+ CREATE OR REPLACE FUNCTION batch_upsert_task_runs(
505
+ p_records jsonb
506
+ ) RETURNS INTEGER AS $$
507
+ DECLARE
508
+ v_count INTEGER := 0;
509
+ v_record jsonb;
510
+ BEGIN
511
+ -- 遍历每条记录
512
+ FOR v_record IN SELECT * FROM jsonb_array_elements(p_records)
513
+ LOOP
514
+ -- 先尝试UPDATE
515
+ UPDATE task_runs SET
516
+ consumer_name = COALESCE((v_record->>'consumer_name')::TEXT, consumer_name),
517
+ status = CASE
518
+ WHEN (v_record->>'status')::TEXT IS NULL THEN status
519
+ WHEN status = 'pending' THEN COALESCE((v_record->>'status')::TEXT, status)
520
+ WHEN status = 'running' AND (v_record->>'status')::TEXT IN ('success', 'failed', 'timeout', 'skipped') THEN (v_record->>'status')::TEXT
521
+ WHEN status IN ('success', 'failed', 'timeout', 'skipped') THEN status
522
+ ELSE COALESCE((v_record->>'status')::TEXT, status)
523
+ END,
524
+ result = CASE
525
+ WHEN status IN ('success', 'failed', 'timeout', 'skipped') AND (v_record->>'status')::TEXT NOT IN ('success', 'failed', 'timeout', 'skipped') THEN result
526
+ ELSE COALESCE((v_record->>'result')::jsonb, result)
527
+ END,
528
+ error_message = CASE
529
+ WHEN status IN ('success', 'failed', 'timeout', 'skipped') AND (v_record->>'status')::TEXT NOT IN ('success', 'failed', 'timeout', 'skipped') THEN error_message
530
+ ELSE COALESCE((v_record->>'error_message')::TEXT, error_message)
531
+ END,
532
+ start_time = COALESCE((v_record->>'started_at')::TIMESTAMPTZ, start_time),
533
+ end_time = CASE
534
+ WHEN status IN ('success', 'failed', 'timeout', 'skipped') AND (v_record->>'status')::TEXT NOT IN ('success', 'failed', 'timeout', 'skipped') THEN end_time
535
+ ELSE COALESCE((v_record->>'completed_at')::TIMESTAMPTZ, end_time)
536
+ END,
537
+ worker_id = COALESCE((v_record->>'worker_id')::TEXT, worker_id),
538
+ duration = COALESCE((v_record->>'duration')::DOUBLE PRECISION, duration),
539
+ execution_time = COALESCE((v_record->>'execution_time')::DOUBLE PRECISION, execution_time),
540
+ updated_at = CURRENT_TIMESTAMP
541
+ WHERE stream_id = (v_record->>'stream_id')::TEXT
542
+ AND consumer_group = (v_record->>'consumer_group')::TEXT;
543
+
544
+ -- 如果没有更新到任何行,则INSERT
545
+ IF NOT FOUND THEN
546
+ INSERT INTO task_runs (
547
+ stream_id, task_name, consumer_group, consumer_name, status, result, error_message,
548
+ start_time, end_time, worker_id, duration, execution_time,
549
+ created_at, updated_at
550
+ ) VALUES (
551
+ (v_record->>'stream_id')::TEXT,
552
+ (v_record->>'task_name')::TEXT,
553
+ (v_record->>'consumer_group')::TEXT,
554
+ (v_record->>'consumer_name')::TEXT,
555
+ COALESCE((v_record->>'status')::TEXT, 'pending'),
556
+ (v_record->>'result')::jsonb,
557
+ (v_record->>'error_message')::TEXT,
558
+ (v_record->>'started_at')::TIMESTAMPTZ,
559
+ (v_record->>'completed_at')::TIMESTAMPTZ,
560
+ (v_record->>'worker_id')::TEXT,
561
+ (v_record->>'duration')::DOUBLE PRECISION,
562
+ (v_record->>'execution_time')::DOUBLE PRECISION,
563
+ CURRENT_TIMESTAMP,
564
+ CURRENT_TIMESTAMP
565
+ );
566
+ END IF;
567
+
568
+ v_count := v_count + 1;
569
+ END LOOP;
570
+
571
+ RETURN v_count;
572
+ END;
573
+ $$ LANGUAGE plpgsql;
574
+
575
+ -- 批量插入tasks的优化函数
576
+ CREATE OR REPLACE FUNCTION batch_insert_tasks(
577
+ p_records jsonb
578
+ ) RETURNS INTEGER AS $$
579
+ DECLARE
580
+ v_inserted INTEGER;
581
+ BEGIN
582
+ -- 使用单个INSERT语句批量插入,忽略冲突
583
+ WITH data AS (
584
+ SELECT
585
+ (value->>'stream_id')::TEXT as stream_id,
586
+ (value->>'queue')::TEXT as queue,
587
+ (value->>'namespace')::TEXT as namespace,
588
+ (value->>'scheduled_task_id')::TEXT as scheduled_task_id,
589
+ (value->>'payload')::jsonb as payload,
590
+ (value->>'priority')::INTEGER as priority,
591
+ (value->>'created_at')::TIMESTAMPTZ as created_at,
592
+ (value->>'source')::TEXT as source,
593
+ (value->>'metadata')::jsonb as metadata
594
+ FROM jsonb_array_elements(p_records)
595
+ )
596
+ INSERT INTO tasks (stream_id, queue, namespace, scheduled_task_id, payload, priority, created_at, source, metadata)
597
+ SELECT * FROM data
598
+ ON CONFLICT DO NOTHING;
599
+
600
+ GET DIAGNOSTICS v_inserted = ROW_COUNT;
601
+ RETURN v_inserted;
602
+ END;
603
+ $$ LANGUAGE plpgsql;
604
+
605
+ -- 批量清理已完成任务的函数
606
+ CREATE OR REPLACE FUNCTION cleanup_completed_tasks(
607
+ p_stream_ids TEXT[]
608
+ ) RETURNS INTEGER AS $$
609
+ DECLARE
610
+ v_deleted INTEGER;
611
+ BEGIN
612
+ -- 批量删除已完成的任务
613
+ DELETE FROM task_runs
614
+ WHERE stream_id = ANY(p_stream_ids)
615
+ AND status IN ('success', 'failed', 'timeout', 'skipped', 'cancelled');
616
+
617
+ GET DIAGNOSTICS v_deleted = ROW_COUNT;
618
+ RETURN v_deleted;
619
+ END;
620
+ $$ LANGUAGE plpgsql;
621
+
622
+ -- ========================================
623
+ -- 13. 创建额外的优化索引
624
+ -- ========================================
625
+
626
+ -- 为task_runs的UPSERT操作创建覆盖索引(如果不存在)
627
+ CREATE INDEX IF NOT EXISTS idx_task_runs_upsert
628
+ ON task_runs (stream_id, consumer_group)
629
+ INCLUDE (status, updated_at);
630
+
631
+ -- ========================================
632
+ -- 14. 创建默认数据
633
+ -- ========================================
634
+ -- 这里可以插入一些默认的配置或测试数据
635
+
636
+ -- ========================================
637
+ -- 完成
638
+ -- ========================================
639
+ -- 注意:最后的 RAISE NOTICE 可能在某些环境中不支持,可以注释掉
640
+ -- RAISE NOTICE 'Database initialization completed successfully!';
@@ -1,9 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jettask
3
- Version: 0.2.5
3
+ Version: 0.2.6
4
4
  Summary: A high-performance distributed task queue system with web monitoring
5
- Home-page: https://github.com/yourusername/jettask
6
- Author: JetTask Team
7
5
  Author-email: JetTask Team <support@jettask.io>
8
6
  Project-URL: Homepage, https://github.com/yourusername/jettask
9
7
  Project-URL: Bug Tracker, https://github.com/yourusername/jettask/issues
@@ -23,17 +21,24 @@ Requires-Python: >=3.8
23
21
  Description-Content-Type: text/markdown
24
22
  License-File: LICENSE
25
23
  Requires-Dist: redis>=4.5.0
24
+ Requires-Dist: aioredis>=2.0.0
25
+ Requires-Dist: msgpack>=1.0.0
26
26
  Requires-Dist: watchdog>=3.0.0
27
27
  Requires-Dist: uvloop>=0.17.0
28
28
  Requires-Dist: ujson>=5.6.0
29
29
  Requires-Dist: fastapi>=0.100.0
30
- Requires-Dist: uvicorn>=0.23.0
30
+ Requires-Dist: uvicorn[standard]>=0.23.0
31
31
  Requires-Dist: websockets>=11.0
32
- Requires-Dist: sqlalchemy[asyncio]>=2.0.0
33
- Requires-Dist: psycopg[binary,pool]>=3.1.0
34
32
  Requires-Dist: pydantic>=2.0.0
35
33
  Requires-Dist: python-multipart>=0.0.6
34
+ Requires-Dist: aiohttp>=3.8.0
35
+ Requires-Dist: sqlalchemy[asyncio]>=2.0.0
36
+ Requires-Dist: psycopg[binary,pool]>=3.1.0
37
+ Requires-Dist: asyncpg>=0.28.0
38
+ Requires-Dist: croniter>=1.4.0
36
39
  Requires-Dist: click>=8.1.0
40
+ Requires-Dist: python-dotenv>=1.0.0
41
+ Requires-Dist: psutil>=5.9.0
37
42
  Provides-Extra: dev
38
43
  Requires-Dist: pytest>=7.0; extra == "dev"
39
44
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -41,7 +46,4 @@ Requires-Dist: black>=23.0; extra == "dev"
41
46
  Requires-Dist: flake8>=6.0; extra == "dev"
42
47
  Requires-Dist: mypy>=1.4.0; extra == "dev"
43
48
  Requires-Dist: coverage>=7.0; extra == "dev"
44
- Dynamic: author
45
- Dynamic: home-page
46
49
  Dynamic: license-file
47
- Dynamic: requires-python