mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
feat(dashboard): a simpler UGC card on Diagnostics
The card had the UGC page's counts per state for both kinds, totals since the server started and the last makes, which the UGC page's lists cover. It now says whether the UGC server is up (throttled, paused), how many items wait and failed (linking to the failed ones), its busy workers, CPU, memory and stored files. /api/diagnostics/ugc read the database on a worker thread; the counts are now read on the web thread and the worker only fetches the UGC server's status. Check: Diagnostics with the UGC server enabled: the card is short, reads right while it's up, throttled and stopped, and the failed link opens the failed ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -458,9 +458,10 @@ namespace UgcRoutes {
|
||||
online = ServerState::g_UgcStatus.online;
|
||||
}
|
||||
const auto base = InternalUrl();
|
||||
Workers::Reply(reply, context, false, [base, enabled, online](HTTPReply& out) {
|
||||
nlohmann::json body = { { "success", true }, { "enabled", enabled }, { "online", online },
|
||||
{ "counts", { { "model", Counts(Database::Get()->GetUgcProcessCounts()) }, { "modular", Counts(Database::Get()->GetModularBuildProcessCounts()) } } } };
|
||||
// The database only on this thread; the worker only fetches
|
||||
nlohmann::json body = { { "success", true }, { "enabled", enabled }, { "online", online },
|
||||
{ "counts", { { "model", Counts(Database::Get()->GetUgcProcessCounts()) }, { "modular", Counts(Database::Get()->GetModularBuildProcessCounts()) } } } };
|
||||
Workers::Reply(reply, context, false, [base, enabled, body = std::move(body)](HTTPReply& out) mutable {
|
||||
if (enabled) {
|
||||
const auto fetched = CachedGet(base + "/status", eCache::STATUS);
|
||||
if (fetched->status == 200) body["status"] = nlohmann::json::parse(fetched->body, nullptr, false);
|
||||
|
||||
@@ -83,37 +83,29 @@
|
||||
var direction = 'in';
|
||||
var data = null;
|
||||
|
||||
// The UGC server (when it's enabled): its work, what it uses, and what it has made
|
||||
// The UGC server (when it's enabled): whether it's up, what it's doing and uses, and what waits or failed. What it
|
||||
// made, and how long each took, is on the UGC page.
|
||||
function loadUgc() {
|
||||
api.get('/api/diagnostics/ugc').then(function (d) {
|
||||
var card = document.getElementById('ugcCard'), body = document.getElementById('ugcBody');
|
||||
card.classList.toggle('d-none', !d.enabled);
|
||||
if (!d.enabled) return;
|
||||
var counts = function (title, c) {
|
||||
return '<div>' + esc(title) + ': ' + fmt.badge(nf.format(c.done || 0) + ' made', 'success') + ' ' + fmt.badge(nf.format(c.pending || 0) + ' waiting', 'secondary') + ' ' +
|
||||
fmt.badge(nf.format(c.failed || 0) + ' failed', c.failed ? 'danger' : 'secondary') + (c.empty ? ' ' + fmt.badge(nf.format(c.empty) + ' empty', 'light') : '') + '</div>';
|
||||
};
|
||||
var html = counts('Models', d.counts.model) + counts('Cars and rockets', d.counts.modular);
|
||||
var waiting = (d.counts.model.pending || 0) + (d.counts.modular.pending || 0), failed = (d.counts.model.failed || 0) + (d.counts.modular.failed || 0);
|
||||
var row = function (k, v) { return '<tr><td class="text-body-secondary pe-2">' + esc(k) + '</td><td>' + v + '</td></tr>'; };
|
||||
var queue = row('Waiting', esc(nf.format(waiting)) + (failed ? ', ' + fmt.link('/ugc?view=list&state=failed', nf.format(failed) + ' failed') : ''));
|
||||
var s = d.status;
|
||||
if (!s) {
|
||||
body.innerHTML = fmt.badge('Unreachable', 'warning') + ' ' + esc(d.error || '') + '<hr class="my-2">' + html;
|
||||
body.innerHTML = '<div class="mb-1">' + fmt.badge('Unreachable', 'warning') + ' ' + esc(d.error || '') + '</div><table class="table table-sm table-borderless mb-0"><tbody>' + queue + '</tbody></table>';
|
||||
return;
|
||||
}
|
||||
var u = s.usage || {}, l = s.limits || {}, cores = u.cores || 1;
|
||||
var row = function (k, v) { return '<tr><td class="text-body-secondary pe-2">' + esc(k) + '</td><td>' + v + '</td></tr>'; };
|
||||
html = '<div class="mb-1">' + fmt.badge('Online', 'success') + (u.throttled ? ' ' + fmt.badge('Throttled', 'warning') : '') + (u.paused ? ' ' + fmt.badge('Paused', 'info') : '') + '</div>' +
|
||||
'<table class="table table-sm table-borderless mb-1"><tbody>' +
|
||||
row('Work', esc(s.active) + ' of ' + esc(s.workers) + ' workers busy, ' + esc(nf.format(s.queued)) + ' queued') +
|
||||
row('CPU', esc(((u.cpuPercent || 0) / cores).toFixed(0)) + '% of ' + esc(cores) + ' cores' + (l.maxCpus ? ' (limit ' + esc((l.maxCpus / cores * 100).toFixed(0)) + '%)' : '')) +
|
||||
row('Memory', esc(bytes(u.residentBytes || 0)) + ' in use; jobs ~' + esc(bytes(s.jobMemoryBytes || 0)) + (l.maxMemoryBytes ? ' of ' + esc(bytes(l.maxMemoryBytes)) : '') + (s.memoryWaits ? ', ' + esc(s.memoryWaits) + ' waits' : '')) +
|
||||
row('Files', esc(bytes(s.storedBytes || 0)) + (s.maxStorageBytes ? ' of ' + esc(bytes(s.maxStorageBytes)) : '') + (s.evicted ? ', ' + esc(s.evicted) + ' evicted' : '')) +
|
||||
row('Since start', esc(nf.format(s.made || 0)) + ' made, ' + esc(nf.format(s.failed || 0)) + ' failed attempts') +
|
||||
'</tbody></table>' + html;
|
||||
var recent = (s.recent || []).slice(0, 5);
|
||||
if (recent.length) html += '<div class="mt-1 text-body-secondary">Last: ' + recent.map(function (r) {
|
||||
return esc(r.kind) + ' ' + esc(r.id) + (r.ok ? ' ' + esc(ms(r.ms)) : ' failed');
|
||||
}).join(' · ') + '</div>';
|
||||
body.innerHTML = html;
|
||||
body.innerHTML = '<div class="mb-1">' + fmt.badge('Online', 'success') + (u.throttled ? ' ' + fmt.badge('Throttled', 'warning') : '') + (u.paused ? ' ' + fmt.badge('Paused', 'info') : '') + '</div>' +
|
||||
'<table class="table table-sm table-borderless mb-0"><tbody>' + queue +
|
||||
row('Workers', esc(s.active) + ' of ' + esc(s.workers) + ' busy') +
|
||||
row('CPU', esc(((u.cpuPercent || 0) / cores).toFixed(0)) + '%' + (l.maxCpus ? ' of ' + esc((l.maxCpus / cores * 100).toFixed(0)) + '% allowed' : '')) +
|
||||
row('Memory', esc(bytes(u.residentBytes || 0))) +
|
||||
row('Files', esc(bytes(s.storedBytes || 0)) + (s.maxStorageBytes ? ' of ' + esc(bytes(s.maxStorageBytes)) : '')) +
|
||||
'</tbody></table>';
|
||||
}).catch(function () {});
|
||||
}
|
||||
loadUgc();
|
||||
|
||||
@@ -698,7 +698,9 @@ second of each server, HTTP requests per second of the dashboard and the UGC ser
|
||||
their request counts, errors and latency. **Live** shows the last 5 minutes at one second and updates as reports arrive
|
||||
(the `traffic` WebSocket topic); **1 hour** is at 10 seconds; **24 hours** (5 minutes) and **7 days** (30 minutes)
|
||||
come from the database. The servers table also shows each server's RakNet connections, average ping and resent
|
||||
messages, and the worker threads of the dashboard and the UGC server.
|
||||
messages, and the worker threads of the dashboard and the UGC server. While the UGC server is enabled, its card says
|
||||
whether it is up (throttled, paused), how many items wait and failed (a link to the failed ones), its busy workers, CPU,
|
||||
memory and stored files; what it made and how long each took is on the UGC page.
|
||||
|
||||
How it is counted:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user