funboost 47.9__py3-none-any.whl → 48.1__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.

Potentially problematic release.


This version of funboost might be problematic. Click here for more details.

Files changed (39) hide show
  1. funboost/__init__.py +3 -3
  2. funboost/assist/celery_helper.py +1 -1
  3. funboost/constant.py +7 -0
  4. funboost/consumers/base_consumer.py +132 -75
  5. funboost/consumers/redis_consumer_ack_able.py +1 -1
  6. funboost/core/active_cousumer_info_getter.py +60 -0
  7. funboost/core/current_task.py +37 -0
  8. funboost/core/func_params_model.py +2 -2
  9. funboost/core/function_result_status_saver.py +1 -1
  10. funboost/function_result_web/__pycache__/functions.cpython-37.pyc +0 -0
  11. funboost/function_result_web/__pycache__/functions.cpython-39.pyc +0 -0
  12. funboost/function_result_web/app.py +104 -7
  13. funboost/function_result_web/functions.py +17 -4
  14. funboost/function_result_web/static/css/content_page_style.css +39 -0
  15. funboost/function_result_web/static/images/favicon.ico +0 -0
  16. funboost/function_result_web/static/js/bootstrap-datetimepicker.min.js +2 -0
  17. funboost/function_result_web/static/js/echarts.min.js +32478 -0
  18. funboost/function_result_web/static/js/moment-with-locales.min.js +1 -0
  19. funboost/function_result_web/static/js/select2.min.js +2 -0
  20. funboost/function_result_web/templates/about.html +67 -0
  21. funboost/function_result_web/templates/conusme_speed.html +217 -0
  22. funboost/function_result_web/templates/fun_result_table.html +433 -0
  23. funboost/function_result_web/templates/index.html +194 -423
  24. funboost/function_result_web/templates/index_backup.html +475 -0
  25. funboost/function_result_web/templates/index_/321/204/342/225/225/320/235/321/205/320/237/320/277/321/206/320/232/320/250/321/205/320/237/320/260.html +153 -0
  26. funboost/function_result_web/templates/queue_op.html +490 -0
  27. funboost/function_result_web/templates/running_consumer_by_ip.html +220 -0
  28. funboost/function_result_web/templates/running_consumer_by_queue_name.html +216 -0
  29. funboost/timing_job/__init__.py +3 -218
  30. funboost/timing_job/apscheduler_use_redis_store.py +6 -1
  31. funboost/timing_job/timing_job_base.py +213 -0
  32. funboost/timing_job/timing_push.py +136 -0
  33. funboost/utils/ctrl_c_end.py +1 -1
  34. {funboost-47.9.dist-info → funboost-48.1.dist-info}/METADATA +78 -76
  35. {funboost-47.9.dist-info → funboost-48.1.dist-info}/RECORD +39 -22
  36. {funboost-47.9.dist-info → funboost-48.1.dist-info}/WHEEL +1 -1
  37. {funboost-47.9.dist-info → funboost-48.1.dist-info}/LICENSE +0 -0
  38. {funboost-47.9.dist-info → funboost-48.1.dist-info}/entry_points.txt +0 -0
  39. {funboost-47.9.dist-info → funboost-48.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,490 @@
1
+
2
+
3
+
4
+ <!DOCTYPE html>
5
+ <html lang="zh">
6
+ <head>
7
+ <meta charset="UTF-8">
8
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
9
+ <title>队列操作</title>
10
+ <!-- Bootstrap CSS -->
11
+ <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
12
+ <!-- Tabulator CSS -->
13
+ <link href="https://cdn.bootcdn.net/ajax/libs/tabulator/5.5.0/css/tabulator.min.css" rel="stylesheet">
14
+ <link href="https://cdn.bootcdn.net/ajax/libs/tabulator/5.5.0/css/tabulator_bootstrap3.min.css" rel="stylesheet">
15
+
16
+ <!-- jQuery -->
17
+ <script src="{{ url_for('static',filename='js/jquery-1.11.0.min.js') }}" type="text/javascript"></script>
18
+ <!-- <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script> -->
19
+ <!-- Bootstrap JS -->
20
+ <script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
21
+ <!-- <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script> -->
22
+ <!-- Tabulator JS -->
23
+ <script type="text/javascript" src="https://unpkg.com/tabulator-tables@5.5.0/dist/js/tabulator.min.js"></script>
24
+ <!-- <script src="https://cdn.bootcdn.net/ajax/libs/tabulator/5.5.0/js/tabulator.min.js"></script> -->
25
+
26
+ <style>
27
+ .action-btn {
28
+ margin: 2px;
29
+ }
30
+ .search-container {
31
+ margin-bottom: 15px;
32
+ }
33
+ #searchInput {
34
+ width: 300px;
35
+ display: inline-block;
36
+ }
37
+ </style>
38
+ </head>
39
+ <body>
40
+ <div class="container-fluid" style="margin-top: 5px;">
41
+ <div class="search-container">
42
+ <div class="input-group">
43
+ <input type="text" id="searchInput" class="form-control" placeholder="输入队列名称进行过滤...">
44
+ <span class="input-group-btn">
45
+ <button class="btn btn-default" type="button" onclick="clearSearch()">
46
+ <i class="glyphicon glyphicon-remove"></i>
47
+ </button>
48
+ </span>
49
+ </div>
50
+ </div>
51
+ <div id="queue-table"></div>
52
+ </div>
53
+
54
+
55
+ <script>
56
+ // 创建表格实例
57
+ var table = new Tabulator("#queue-table", {
58
+ theme: "bootstrap3",
59
+ ajaxURL: "/queue/params_and_active_consumers",
60
+ layout: "fitColumns",
61
+ responsiveLayout: "collapse",
62
+ pagination: true,
63
+ paginationSize: 1000,
64
+ locale: true,
65
+ langs: {
66
+ "zh-cn": {
67
+ "pagination": {
68
+ "first": "首页",
69
+ "first_title": "首页",
70
+ "last": "末页",
71
+ "last_title": "末页",
72
+ "prev": "上一页",
73
+ "prev_title": "上一页",
74
+ "next": "下一页",
75
+ "next_title": "下一页",
76
+ }
77
+ }
78
+ },
79
+ columns: [
80
+ { title: "<br>队列名字", field: "queue_name", sorter: "string", width: 250 },
81
+ { title: "broker<br>类型", field: "broker_kind", sorter: "string", width: 100 },
82
+ { title: "近10秒<br>完成", field: "all_consumers_last_x_s_execute_count", sorter: "number", width: 100 },
83
+ { title: "近10秒<br>失败", field: "all_consumers_last_x_s_execute_count_fail", sorter: "int", width: 100 },
84
+ { title: "<br>消息数量", field: "msg_count", sorter: "number", width: 170,
85
+ formatter: function(cell) {
86
+ const row = cell.getRow().getData();
87
+ return `
88
+ <div style="display: flex; align-items: center; justify-content: space-between; width: 100%; padding-right: 30px;">
89
+ <span id="msg-count-${row.queue_name}" style="min-width: 70px; text-align: right; padding-right: 25px;">${cell.getValue() === null ? '' : cell.getValue()}</span>
90
+ <button class="btn btn-primary btn-sm" onclick="getMessageCount('${row.queue_name}')">获取</button>
91
+ </div>
92
+ `;
93
+ }
94
+ },
95
+ { title: "<br>consumer数量", field: "consumer_count", sorter: "number", width: 200,
96
+ formatter: function(cell) {
97
+ const row = cell.getRow().getData();
98
+ var consumers = row.active_consumers;
99
+ return `
100
+ <div style="display: flex; align-items: center; justify-content: space-between; width: 100%; padding-right: 10px;">
101
+ <span style="min-width: 50px; text-align: right; padding-right: 15px;">${cell.getValue() || ''}</span>
102
+ <button class="btn btn-primary btn-sm" onclick='showConsumerDetails(${JSON.stringify(consumers)}, "${row.queue_name}")'>
103
+ 查看消费者详情
104
+ </button>
105
+ </div>
106
+ `;
107
+ }
108
+ },
109
+ {
110
+ title: "暂停消<br>费状态",
111
+ field: "pause_flag",
112
+ width: 100,
113
+ formatter: function(cell) {
114
+ return cell.getValue()===1 ? '<span style="color: red;">已暂停</span>' : "";
115
+ }
116
+ },
117
+ {
118
+ title: "<br>操作",
119
+ width: 400,
120
+ formatter: function(cell) {
121
+ const row = cell.getRow().getData();
122
+ return `
123
+ <button class="btn btn-info btn-sm action-btn" onclick='showParams(${JSON.stringify(row.queue_params)})'>查看消费者配置</button>
124
+ <button class="btn btn-danger btn-sm action-btn" onclick="clearQueue('${row.queue_name}')">清空队列消息</button>
125
+ <button class="btn btn-warning btn-sm action-btn" onclick="pauseConsume('${row.queue_name}')">暂停消费</button>
126
+ <button class="btn btn-success btn-sm action-btn" onclick="resumeConsume('${row.queue_name}')">恢复消费</button>
127
+ `;
128
+ }
129
+ },
130
+ ],
131
+ ajaxResponse: function(url, params, response) {
132
+ // 转换API响应为表格数据
133
+ return Object.entries(response).map(([queue_name, data]) => ({
134
+ queue_name: queue_name,
135
+ broker_kind: data.queue_params.broker_kind,
136
+ all_consumers_last_x_s_execute_count: data.all_consumers_last_x_s_execute_count,
137
+ all_consumers_last_x_s_execute_count_fail: data.all_consumers_last_x_s_execute_count_fail,
138
+ msg_count: data.msg_num_in_broker, // 初始化为空字符串
139
+ consumer_count: data.active_consumers.length,
140
+ active_consumers: data.active_consumers,
141
+ queue_params: data.queue_params,
142
+ pause_flag: data.pause_flag
143
+ }));
144
+ },
145
+ });
146
+
147
+ // 搜索功能实现
148
+ document.getElementById("searchInput").addEventListener("keyup", function() {
149
+ table.setFilter("queue_name", "like", this.value);
150
+ });
151
+
152
+ // 清除搜索
153
+ function clearSearch() {
154
+ document.getElementById("searchInput").value = "";
155
+ table.clearFilter();
156
+ }
157
+
158
+ // 显示参数的模态框
159
+ function showParams(params) {
160
+ // 如果已存在模态框,先移除
161
+ if ($("#paramsModal").length) {
162
+ $("#paramsModal").remove();
163
+ }
164
+
165
+ const modalHtml = `
166
+ <div class="modal" id="paramsModal" tabindex="-1" role="dialog">
167
+ <div class="modal-dialog" role="document">
168
+ <div class="modal-content">
169
+ <div class="modal-header">
170
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
171
+ <h4 class="modal-title">消费者配置详情</h4>
172
+ </div>
173
+ <div class="modal-body" style="max-height: 80vh; overflow-y: auto;">
174
+ <pre style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; border: 1px solid #dee2e6; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;">${JSON.stringify(params, null, 2)}</pre>
175
+ </div>
176
+ <div class="modal-footer">
177
+ <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
178
+ </div>
179
+ </div>
180
+ </div>
181
+ </div>
182
+ `;
183
+
184
+ // 添加模态框到body
185
+ $("body").append(modalHtml);
186
+
187
+ // 初始化并显示模态框
188
+ $("#paramsModal").modal({
189
+ backdrop: "static",
190
+ keyboard: false
191
+ });
192
+ }
193
+ // 操作函数
194
+ function getMessageCount(queueName) {
195
+ const row = table.getRows().find(row => row.getData().queue_name === queueName);
196
+ if (!row) {
197
+ alert('找不到对应的队列数据');
198
+ return;
199
+ }
200
+ const broker_kind = row.getData().broker_kind;
201
+ // 获取消息数量的API调用
202
+ $.get(`/queue/message_count/${broker_kind}/${queueName}`, function(response) {
203
+ if (response.success) {
204
+ // 更新表格中的消息数量
205
+ row.update({msg_count: response.count});
206
+ // 直接更新显示的数字
207
+ alert(`获取 ${queueName} 队列 消息数量成功`);
208
+ document.getElementById(`msg-count-${queueName}`).textContent = response.count;
209
+
210
+ }
211
+ });
212
+ }
213
+
214
+ function clearQueue(queueName) {
215
+ const row = table.getRows().find(row => row.getData().queue_name === queueName);
216
+ if (!row) {
217
+ alert('找不到对应的队列数据');
218
+ return;
219
+ }
220
+ const broker_kind = row.getData().broker_kind;
221
+ if (confirm(`确定要清空队列 ${queueName} 的所有消息吗?`)) {
222
+ $.post(`/queue/clear/${broker_kind}/${queueName}`, function(response) {
223
+ alert(`清空 ${queueName} 队列成功`);
224
+ // table.replaceData();
225
+ getMessageCount(queueName); // 自动获取最新的消息数量
226
+ });
227
+ }
228
+ }
229
+
230
+ function pauseConsume(queueName) {
231
+ $.post(`/queue/pause/${queueName}`, function(response) {
232
+ if (response.success) {
233
+ alert("暂停消费成功");
234
+ const row = table.getRows().find(row => row.getData().queue_name === queueName);
235
+ if (row) {
236
+ row.update({pause_flag: 1});
237
+ }
238
+ }
239
+ });
240
+ }
241
+
242
+ function resumeConsume(queueName) {
243
+ $.post(`/queue/resume/${queueName}`, function(response) {
244
+ if (response.success) {
245
+ alert("恢复消费成功");
246
+ const row = table.getRows().find(row => row.getData().queue_name === queueName);
247
+ if (row) {
248
+ row.update({pause_flag: 0});
249
+ }
250
+ }
251
+ });
252
+ }
253
+
254
+ // 显示消费者详情的模态框
255
+ function showConsumerDetails(consumers, queueName) {
256
+ $.ajax({
257
+ url: '/running_consumer/hearbeat_info_by_queue_name',
258
+ data: { queue_name: queueName },
259
+ success: function(consumers) {
260
+ let consumerRows = '';
261
+ consumers.forEach(consumer => {
262
+ consumerRows += `
263
+ <tr>
264
+ <td>${consumer.computer_ip}</td>
265
+ <td>${consumer.computer_name}</td>
266
+ <td>${consumer.hearbeat_datetime_str}</td>
267
+ <td>${consumer.process_id}</td>
268
+ <td>${consumer.start_datetime_str}</td>
269
+ <td>${consumer.code_filename}</td>,
270
+ <td>${consumer.last_x_s_execute_count}</td>
271
+ <td>${consumer.last_x_s_execute_count_fail}</td>
272
+ <td>${consumer.total_consume_count_from_start}</td>
273
+ <td>${consumer.total_consume_count_from_start_fail}</td>
274
+ </tr>
275
+ `;
276
+ });
277
+
278
+ const modalHtml = `
279
+ <div class="modal" id="consumerDetailsModal" tabindex="-1" role="dialog">
280
+ <div class="modal-dialog" style="width: 80%;" role="document">
281
+ <div class="modal-content">
282
+ <div class="modal-header">
283
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
284
+ <h4 class="modal-title">${queueName}队列的消费者详情信息</h4>
285
+ </div>
286
+ <div class="modal-body">
287
+ <div class="table-responsive">
288
+ <table class="table table-striped">
289
+ <thead>
290
+ <tr>
291
+ <th>计算机IP</th>
292
+ <th>计算机名称</th>
293
+ <th>最后心跳时间</th>
294
+ <th>进程ID</th>
295
+ <th>启动时间</th>
296
+ <th>代码文件名</th>
297
+ <th>近10秒<br>运行完成<br>消息个数</th>
298
+ <th>近10秒<br>运行失败<br>消息个数</th>
299
+ <th>累计<br>运行完成<br>消息个数</th>
300
+ <th>累计<br>运行失败<br>消息个数</th>
301
+ </tr>
302
+ </thead>
303
+ <tbody>
304
+ ${consumerRows}
305
+ </tbody>
306
+ </table>
307
+ </div>
308
+ </div>
309
+ <div class="modal-footer">
310
+ <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
311
+ </div>
312
+ </div>
313
+ </div>
314
+ </div>
315
+ `;
316
+
317
+ // 移除已存在的模态框
318
+ $('#consumerDetailsModal').remove();
319
+ // 添加新的模态框到body
320
+ $('body').append(modalHtml);
321
+ // 显示模态框
322
+ $('#consumerDetailsModal').modal('show');
323
+ },
324
+ error: function(xhr, status, error) {
325
+ console.error('获取消费者详情失败:', error);
326
+ alert('获取消费者详情失败');
327
+ }
328
+ });
329
+ }
330
+
331
+ // 定时刷新表格数据
332
+ // setInterval(function() {
333
+ // table.refreshData();
334
+ // }, 5000);
335
+ </script>
336
+ </body>
337
+ </html>
338
+
339
+
340
+ <!--
341
+ 队列名字 broker_kind 消息数量 consumer数量 消费者参数 是否暂停消费状态 操作(这一列都是按钮)
342
+ 获取消息数量、清空队列消息、 暂停消费 、恢复消费
343
+
344
+
345
+
346
+ 接口 /queue/params_and_active_consumers 返回的是如下字典,字典中的key是队列名字,
347
+ value是一个字典,字典中有两个key,一个是active_consumers,一个是 queue_params ,
348
+ queue_params的broker_kind 是队列的类型,active_consumers 数组长度是 consumer数量
349
+
350
+
351
+ 显示到表格中
352
+
353
+ {
354
+ "queue_test_g01t": {
355
+ "active_consumers": [
356
+ {
357
+ "code_filename": "d:/codes/funboost/test_frame/test_function_status_result_persist/test_persist.py",
358
+ "computer_ip": "10.0.133.57",
359
+ "computer_name": "LAPTOP-7V78BBO2",
360
+ "consumer_id": 2642746547464,
361
+ "consumer_uuid": "5ba1aa04-1067-4173-8ee6-0c1e29f8b015",
362
+ "consuming_function": "f",
363
+ "hearbeat_datetime_str": "2025-02-26 20:29:40",
364
+ "hearbeat_timestamp": 1740572980.216993,
365
+ "process_id": 51852,
366
+ "queue_name": "queue_test_g01t",
367
+ "start_datetime_str": "2025-02-26 20:03:06",
368
+ "start_timestamp": 1740571386.7500842,
369
+ "execute_task_times_every_unit_time_temp": 2
370
+ }
371
+ ],
372
+ "queue_params": {
373
+ "auto_generate_info": {
374
+ "where_to_instantiate": "d:/codes/funboost/test_frame/test_function_status_result_persist/test_persist.py:10"
375
+ },
376
+ "broker_exclusive_config": {
377
+ "pull_msg_batch_size": 100,
378
+ "redis_bulk_push": 1
379
+ },
380
+ "broker_kind": "REDIS",
381
+ "concurrent_mode": "threading",
382
+ "concurrent_num": 50,
383
+ "consuming_function": "<function f at 0x000002674C8A1708>",
384
+ "consuming_function_kind": "COMMON_FUNCTION",
385
+ "consuming_function_raw": "<function f at 0x000002674C8A1708>",
386
+ "create_logger_file": true,
387
+ "delay_task_apscheduler_jobstores_kind": "redis",
388
+ "do_not_run_by_specify_time": [
389
+ "10:00:00",
390
+ "22:00:00"
391
+ ],
392
+ "do_task_filtering": false,
393
+ "function_result_status_persistance_conf": {
394
+ "expire_seconds": 604800,
395
+ "is_save_result": true,
396
+ "is_save_status": true,
397
+ "is_use_bulk_insert": false
398
+ },
399
+ "is_auto_start_consuming_message": false,
400
+ "is_do_not_run_by_specify_time_effect": false,
401
+ "is_print_detail_exception": true,
402
+ "is_push_to_dlx_queue_when_retry_max_times": false,
403
+ "is_send_consumer_hearbeat_to_redis": true,
404
+ "is_show_message_get_from_broker": false,
405
+ "is_support_remote_kill_task": false,
406
+ "is_using_distributed_frequency_control": false,
407
+ "is_using_rpc_mode": false,
408
+ "log_level": 10,
409
+ "logger_name": "",
410
+ "logger_prefix": "",
411
+ "max_retry_times": 3,
412
+ "publish_msg_log_use_full_msg": false,
413
+ "queue_name": "queue_test_g01t",
414
+ "retry_interval": 0,
415
+ "rpc_result_expire_seconds": 600,
416
+ "schedule_tasks_on_main_thread": false,
417
+ "should_check_publish_func_params": true,
418
+ "task_filtering_expire_seconds": 0
419
+ }
420
+ },
421
+ "queue_test_g02t": {
422
+ "active_consumers": [
423
+ {
424
+ "code_filename": "d:/codes/funboost/test_frame/test_function_status_result_persist/test_persist.py",
425
+ "computer_ip": "10.0.133.57",
426
+ "computer_name": "LAPTOP-7V78BBO2",
427
+ "consumer_id": 2642746605384,
428
+ "consumer_uuid": "a5528e66-2949-47ca-9aea-bbf920165c53",
429
+ "consuming_function": "f2",
430
+ "hearbeat_datetime_str": "2025-02-26 20:29:40",
431
+ "hearbeat_timestamp": 1740572980.13895,
432
+ "process_id": 51852,
433
+ "queue_name": "queue_test_g02t",
434
+ "start_datetime_str": "2025-02-26 20:03:06",
435
+ "start_timestamp": 1740571386.7650468,
436
+ "execute_task_times_every_unit_time_temp": 2
437
+ }
438
+ ],
439
+ "queue_params": {
440
+ "auto_generate_info": {
441
+ "where_to_instantiate": "d:/codes/funboost/test_frame/test_function_status_result_persist/test_persist.py:18"
442
+ },
443
+ "broker_exclusive_config": {
444
+ "pull_msg_batch_size": 100,
445
+ "redis_bulk_push": 1
446
+ },
447
+ "broker_kind": "REDIS",
448
+ "concurrent_mode": "threading",
449
+ "concurrent_num": 50,
450
+ "consuming_function": "<function f2 at 0x000002674FF5DE58>",
451
+ "consuming_function_kind": "COMMON_FUNCTION",
452
+ "consuming_function_raw": "<function f2 at 0x000002674FF5DE58>",
453
+ "create_logger_file": true,
454
+ "delay_task_apscheduler_jobstores_kind": "redis",
455
+ "do_not_run_by_specify_time": [
456
+ "10:00:00",
457
+ "22:00:00"
458
+ ],
459
+ "do_task_filtering": false,
460
+ "function_result_status_persistance_conf": {
461
+ "expire_seconds": 604800,
462
+ "is_save_result": true,
463
+ "is_save_status": true,
464
+ "is_use_bulk_insert": false
465
+ },
466
+ "is_auto_start_consuming_message": false,
467
+ "is_do_not_run_by_specify_time_effect": false,
468
+ "is_print_detail_exception": true,
469
+ "is_push_to_dlx_queue_when_retry_max_times": false,
470
+ "is_send_consumer_hearbeat_to_redis": true,
471
+ "is_show_message_get_from_broker": false,
472
+ "is_support_remote_kill_task": false,
473
+ "is_using_distributed_frequency_control": false,
474
+ "is_using_rpc_mode": false,
475
+ "log_level": 10,
476
+ "logger_name": "",
477
+ "logger_prefix": "",
478
+ "max_retry_times": 3,
479
+ "publish_msg_log_use_full_msg": false,
480
+ "queue_name": "queue_test_g02t",
481
+ "retry_interval": 0,
482
+ "rpc_result_expire_seconds": 600,
483
+ "schedule_tasks_on_main_thread": false,
484
+ "should_check_publish_func_params": true,
485
+ "task_filtering_expire_seconds": 0
486
+ }
487
+ }
488
+ }
489
+
490
+ -->