From 10d5a30b3964ada16924064f3d1029548f2c1d82 Mon Sep 17 00:00:00 2001 From: Kian Maniago Date: Fri, 24 Oct 2025 14:12:07 +0800 Subject: [PATCH] Display of available tasks and max concurrency for better monitoring of tasks --- flower/static/js/flower.js | 31 ++++++++++++++++++++----------- flower/templates/workers.html | 6 ++++++ flower/views/workers.py | 17 +++++++++++++++++ 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/flower/static/js/flower.js b/flower/static/js/flower.js index 6edde42bf..3bb679838 100644 --- a/flower/static/js/flower.js +++ b/flower/static/js/flower.js @@ -443,15 +443,14 @@ var flower = (function () { order: [ [1, "des"] ], - footerCallback: function( tfoot, data, start, end, display ) { + footerCallback: function(tfoot, data, start, end, display) { var api = this.api(); - var columns = {2:"STARTED", 3:"", 4:"FAILURE", 5:"SUCCESS", 6:"RETRY"}; + var columns = {2:"", 3:"", 4:"STARTED", 5:"", 6:"FAILURE", 7:"SUCCESS", 8:"RETRY"}; for (const [column, state] of Object.entries(columns)) { var total = api.column(column).data().reduce(sum, 0); var footer = total; - if (total !== 0) { - let queryParams = (state !== '' ? `?state=${state}` : ''); - footer = '' + total + ''; + if (total !== 0 && state !== '') { + footer = '' + total + ''; } $(api.column(column).footer()).html(footer); } @@ -476,37 +475,47 @@ var flower = (function () { } } }, { - targets: 2, + targets: 2, + data: 'max_concurrency', + className: "text-center", + width: "10%", + }, { + targets: 3, + data: 'available_slots', + className: "text-center", + width: "10%", + }, { + targets: 4, data: 'active', className: "text-center", width: "10%", defaultContent: 0 }, { - targets: 3, + targets: 5, data: 'task-received', className: "text-center", width: "10%", defaultContent: 0 }, { - targets: 4, + targets: 6, data: 'task-failed', className: "text-center", width: "10%", defaultContent: 0 }, { - targets: 5, + targets: 7, data: 'task-succeeded', className: "text-center", width: "10%", defaultContent: 0 }, { - targets: 6, + targets: 8, data: 'task-retried', className: "text-center", width: "10%", defaultContent: 0 }, { - targets: 7, + targets: 9, data: 'loadavg', width: "10%", className: "text-center text-nowrap", diff --git a/flower/templates/workers.html b/flower/templates/workers.html index 2e9d849db..9380129ad 100644 --- a/flower/templates/workers.html +++ b/flower/templates/workers.html @@ -12,6 +12,8 @@ Worker Status + Max Concurrencies + Available Active Processed Failed @@ -24,6 +26,8 @@ {% for name, info in workers.items() %} {{ name }} + {{ info.get('stats', {}).get('pool', {}).get('max_concurrency', 0) }} + {{ info.get('stats', {}).get('pool', {}).get('available_slots', 0) }} {{ info.get('status', None) }} {{ info.get('active', 0) or 0 }} {{ info.get('task-received', 0) }} @@ -44,6 +48,8 @@ + + diff --git a/flower/views/workers.py b/flower/views/workers.py index defd0469a..123e41b54 100644 --- a/flower/views/workers.py +++ b/flower/views/workers.py @@ -49,8 +49,25 @@ async def get(self): info = dict(values) info.update(self._as_dict(worker)) info.update(status=worker.alive) + + try: + worker_data = self.application.workers.get(name, {}) + worker_stats = worker_data.get('stats', {}) + max_concurrency = worker_stats.get('pool', {}).get('max-concurrency', 0) + active = info.get('active', 0) or 0 + + available_slots = max(0, max_concurrency - active) + + info['max_concurrency'] = max_concurrency + info['available_slots'] = available_slots + + logger.info(f"[Flower] {name} max concurrency = {max_concurrency}") + except Exception as e: + logger.warning(f"Failed to get max concurrency for {name}: {e}") + workers[name] = info + if options.purge_offline_workers is not None: timestamp = int(time.time()) offline_workers = []