jettask 0.2.5__py3-none-any.whl → 0.2.7__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.
- jettask/monitor/run_backlog_collector.py +96 -0
- jettask/monitor/stream_backlog_monitor.py +362 -0
- jettask/pg_consumer/pg_consumer_v2.py +403 -0
- jettask/pg_consumer/sql_utils.py +182 -0
- jettask/scheduler/__init__.py +17 -0
- jettask/scheduler/add_execution_count.sql +11 -0
- jettask/scheduler/add_priority_field.sql +26 -0
- jettask/scheduler/add_scheduler_id.sql +25 -0
- jettask/scheduler/add_scheduler_id_index.sql +10 -0
- jettask/scheduler/loader.py +249 -0
- jettask/scheduler/make_scheduler_id_required.sql +28 -0
- jettask/scheduler/manager.py +696 -0
- jettask/scheduler/migrate_interval_seconds.sql +9 -0
- jettask/scheduler/models.py +200 -0
- jettask/scheduler/multi_namespace_scheduler.py +294 -0
- jettask/scheduler/performance_optimization.sql +45 -0
- jettask/scheduler/run_scheduler.py +186 -0
- jettask/scheduler/scheduler.py +715 -0
- jettask/scheduler/schema.sql +84 -0
- jettask/scheduler/unified_manager.py +450 -0
- jettask/scheduler/unified_scheduler_manager.py +280 -0
- jettask/webui/backend/api/__init__.py +3 -0
- jettask/webui/backend/api/v1/__init__.py +17 -0
- jettask/webui/backend/api/v1/monitoring.py +431 -0
- jettask/webui/backend/api/v1/namespaces.py +504 -0
- jettask/webui/backend/api/v1/queues.py +342 -0
- jettask/webui/backend/api/v1/tasks.py +367 -0
- jettask/webui/backend/core/__init__.py +3 -0
- jettask/webui/backend/core/cache.py +221 -0
- jettask/webui/backend/core/database.py +200 -0
- jettask/webui/backend/core/exceptions.py +102 -0
- jettask/webui/backend/models/__init__.py +3 -0
- jettask/webui/backend/models/requests.py +236 -0
- jettask/webui/backend/models/responses.py +230 -0
- jettask/webui/backend/services/__init__.py +3 -0
- jettask/webui/frontend/index.html +13 -0
- jettask/webui/models/__init__.py +3 -0
- jettask/webui/models/namespace.py +63 -0
- jettask/webui/sql/batch_upsert_functions.sql +178 -0
- jettask/webui/sql/init_database.sql +640 -0
- {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/METADATA +80 -10
- {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/RECORD +46 -53
- jettask/webui/frontend/package-lock.json +0 -4833
- jettask/webui/frontend/package.json +0 -30
- jettask/webui/frontend/src/App.css +0 -109
- jettask/webui/frontend/src/App.jsx +0 -66
- jettask/webui/frontend/src/components/NamespaceSelector.jsx +0 -166
- jettask/webui/frontend/src/components/QueueBacklogChart.jsx +0 -298
- jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +0 -638
- jettask/webui/frontend/src/components/QueueDetailsTable.css +0 -65
- jettask/webui/frontend/src/components/QueueDetailsTable.jsx +0 -487
- jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +0 -465
- jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +0 -423
- jettask/webui/frontend/src/components/TaskFilter.jsx +0 -425
- jettask/webui/frontend/src/components/TimeRangeSelector.css +0 -21
- jettask/webui/frontend/src/components/TimeRangeSelector.jsx +0 -160
- jettask/webui/frontend/src/components/charts/QueueChart.jsx +0 -111
- jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +0 -115
- jettask/webui/frontend/src/components/charts/WorkerChart.jsx +0 -40
- jettask/webui/frontend/src/components/common/StatsCard.jsx +0 -18
- jettask/webui/frontend/src/components/layout/AppLayout.css +0 -95
- jettask/webui/frontend/src/components/layout/AppLayout.jsx +0 -49
- jettask/webui/frontend/src/components/layout/Header.css +0 -106
- jettask/webui/frontend/src/components/layout/Header.jsx +0 -106
- jettask/webui/frontend/src/components/layout/SideMenu.css +0 -137
- jettask/webui/frontend/src/components/layout/SideMenu.jsx +0 -209
- jettask/webui/frontend/src/components/layout/TabsNav.css +0 -244
- jettask/webui/frontend/src/components/layout/TabsNav.jsx +0 -206
- jettask/webui/frontend/src/components/layout/UserInfo.css +0 -197
- jettask/webui/frontend/src/components/layout/UserInfo.jsx +0 -197
- jettask/webui/frontend/src/contexts/LoadingContext.jsx +0 -27
- jettask/webui/frontend/src/contexts/NamespaceContext.jsx +0 -72
- jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +0 -245
- jettask/webui/frontend/src/index.css +0 -114
- jettask/webui/frontend/src/main.jsx +0 -20
- jettask/webui/frontend/src/pages/Alerts.jsx +0 -684
- jettask/webui/frontend/src/pages/Dashboard/index.css +0 -35
- jettask/webui/frontend/src/pages/Dashboard/index.jsx +0 -281
- jettask/webui/frontend/src/pages/Dashboard.jsx +0 -1330
- jettask/webui/frontend/src/pages/QueueDetail.jsx +0 -1117
- jettask/webui/frontend/src/pages/QueueMonitor.jsx +0 -527
- jettask/webui/frontend/src/pages/Queues.jsx +0 -12
- jettask/webui/frontend/src/pages/ScheduledTasks.jsx +0 -809
- jettask/webui/frontend/src/pages/Settings.jsx +0 -800
- jettask/webui/frontend/src/pages/Workers.jsx +0 -12
- jettask/webui/frontend/src/services/api.js +0 -114
- jettask/webui/frontend/src/services/queueTrend.js +0 -152
- jettask/webui/frontend/src/utils/suppressWarnings.js +0 -22
- jettask/webui/frontend/src/utils/userPreferences.js +0 -154
- {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/WHEEL +0 -0
- {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/entry_points.txt +0 -0
- {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/licenses/LICENSE +0 -0
- {jettask-0.2.5.dist-info → jettask-0.2.7.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!';
|