feat(ugc): each make's CPU time and memory; durations in readable units

The UGC server records, with each successful make's time, the worker
thread's CPU time for it and the memory it estimated the job needs (the
figure its memory budget counts; not a measurement)
(process_cpu_ms and process_memory_kb on ugc and ugc_modular_build;
migrations sqlite 74, mysql 91). The UGC page shows them with the time
and duration ("took 12.4 s, CPU 11.9 s, ~96 MB RAM (est.)"), and
durations use the largest units that fit, up to days.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-27 20:33:09 -05:00
parent 432b8e231e
commit cd81cf4c74
20 changed files with 89 additions and 31 deletions

View File

@@ -95,8 +95,13 @@ public:
// What the UGC server counted when it made a model: its bricks and the most detailed mesh's triangles
virtual void SetUgcModelStats(const LWOOBJID id, const uint32_t bricks, const uint32_t triangles) = 0;
// How long the last successful make of a model took (process_ms), for the dashboard
virtual void SetUgcModelProcessMs(const LWOOBJID id, const uint32_t milliseconds) = 0;
// The last successful make of a model: how long it took, the worker's CPU time and the estimated memory
struct ProcessStats {
uint32_t milliseconds{};
uint32_t cpuMilliseconds{};
uint32_t memoryKb{};
};
virtual void SetUgcModelProcessStats(const LWOOBJID id, const ProcessStats& stats) = 0;
virtual std::optional<ProcessInfo> GetUgcProcessInfo(const LWOOBJID id) = 0;

View File

@@ -37,6 +37,8 @@ public:
uint32_t bricks{}; // models: counted by the UGC server when it made them (0: not yet)
uint32_t triangles{}; // models: of the made mesh's most detailed level (0: not yet)
uint32_t processMs{}; // how long the last successful make took (0: not made, or made before it was timed)
uint32_t processCpuMs{}; // the worker thread's CPU time for it
uint32_t processMemoryKb{}; // the memory the UGC server estimated for it (not measured)
};
// What SearchUgc matches. A number (when set) is matched against ids; text against names

View File

@@ -32,8 +32,8 @@ public:
// The combination of modules a build was made as (UgcModularKey::StorageId of its modules), whose files it shares
virtual void SetModularBuildCombination(const LWOOBJID id, const LWOOBJID combinationId) = 0;
// How long the last successful make of a build's icon took (process_ms), for the dashboard
virtual void SetModularBuildProcessMs(const LWOOBJID id, const uint32_t milliseconds) = 0;
// The last successful make of a build's icon (see IUgc::ProcessStats)
virtual void SetModularBuildProcessStats(const LWOOBJID id, const IUgc::ProcessStats& stats) = 0;
// Up to `limit` builds whose combination isn't recorded yet (combination_id 0): their ids and modules
virtual std::vector<PendingBuild> GetModularBuildsWithoutCombination(const uint32_t limit) = 0;

View File

@@ -31,10 +31,10 @@ namespace UgcLookupSql {
return (modular
? "SELECT b.ugc_id AS id, b.character_id, c.name AS character_name, COALESCE(c.account_id, 0) AS account_id, a.name AS account_name, "
"b.is_optimized, b.process_error, b.ldf_config AS detail, b.process_attempts, b.processed_at, 0 AS process_after, 0 AS bake_ao, "
"0 AS brick_count, 0 AS triangle_count, b.process_ms "
"0 AS brick_count, 0 AS triangle_count, b.process_ms, b.process_cpu_ms, b.process_memory_kb "
: "SELECT u.id, u.character_id, c.name AS character_name, u.account_id, a.name AS account_name, "
"u.is_optimized, u.process_error, u.filename AS detail, u.process_attempts, u.processed_at, u.process_after, u.bake_ao, "
"u.brick_count, u.triangle_count, u.process_ms ") + From(modular);
"u.brick_count, u.triangle_count, u.process_ms, u.process_cpu_ms, u.process_memory_kb ") + From(modular);
}
// Whether a search has anything to match (else a list is of everything)

View File

@@ -432,8 +432,8 @@ public:
void SetUgcFileChecksum(const eFileOwner owner, const LWOOBJID storageId, const std::string_view file, const std::string_view md5, const uint32_t size) override;
std::optional<IUgc::FileChecksum> GetUgcFileChecksum(const LWOOBJID blueprintId, const std::string_view file) override;
void SetModularBuildCombination(const LWOOBJID id, const LWOOBJID combinationId) override;
void SetUgcModelProcessMs(const LWOOBJID id, const uint32_t milliseconds) override;
void SetModularBuildProcessMs(const LWOOBJID id, const uint32_t milliseconds) override;
void SetUgcModelProcessStats(const LWOOBJID id, const ProcessStats& stats) override;
void SetModularBuildProcessStats(const LWOOBJID id, const IUgc::ProcessStats& stats) override;
std::vector<IUgcModularBuild::PendingBuild> GetModularBuildsWithoutCombination(const uint32_t limit) override;
std::optional<IUgc::ProcessInfo> GetUgcProcessInfo(const LWOOBJID id) override;
uint64_t ResetUgcModelProcessing(const std::optional<LWOOBJID> id, const bool failedOnly) override;

View File

@@ -195,6 +195,6 @@ std::optional<IUgc::FileChecksum> MySQLDatabase::GetUgcFileChecksum(const LWOOBJ
return IUgc::FileChecksum{ std::string(result->getString("md5").c_str()), static_cast<uint32_t>(result->getUInt("size")) };
}
void MySQLDatabase::SetUgcModelProcessMs(const LWOOBJID id, const uint32_t milliseconds) {
ExecuteUpdate("UPDATE ugc SET process_ms = ? WHERE id = ?;", milliseconds, id);
void MySQLDatabase::SetUgcModelProcessStats(const LWOOBJID id, const ProcessStats& stats) {
ExecuteUpdate("UPDATE ugc SET process_ms = ?, process_cpu_ms = ?, process_memory_kb = ? WHERE id = ?;", stats.milliseconds, stats.cpuMilliseconds, stats.memoryKb, id);
}

View File

@@ -21,6 +21,8 @@ namespace {
entry.bricks = static_cast<uint32_t>(result->getInt64("brick_count"));
entry.triangles = static_cast<uint32_t>(result->getInt64("triangle_count"));
entry.processMs = static_cast<uint32_t>(result->getInt64("process_ms"));
entry.processCpuMs = static_cast<uint32_t>(result->getInt64("process_cpu_ms"));
entry.processMemoryKb = static_cast<uint32_t>(result->getInt64("process_memory_kb"));
return entry;
}
}

View File

@@ -120,6 +120,6 @@ std::vector<IUgcModularBuild::PendingBuild> MySQLDatabase::GetModularBuildsWitho
return builds;
}
void MySQLDatabase::SetModularBuildProcessMs(const LWOOBJID id, const uint32_t milliseconds) {
ExecuteUpdate("UPDATE ugc_modular_build SET process_ms = ? WHERE ugc_id = ?;", milliseconds, id);
void MySQLDatabase::SetModularBuildProcessStats(const LWOOBJID id, const IUgc::ProcessStats& stats) {
ExecuteUpdate("UPDATE ugc_modular_build SET process_ms = ?, process_cpu_ms = ?, process_memory_kb = ? WHERE ugc_id = ?;", stats.milliseconds, stats.cpuMilliseconds, stats.memoryKb, id);
}

View File

@@ -416,8 +416,8 @@ public:
void SetUgcFileChecksum(const eFileOwner owner, const LWOOBJID storageId, const std::string_view file, const std::string_view md5, const uint32_t size) override;
std::optional<IUgc::FileChecksum> GetUgcFileChecksum(const LWOOBJID blueprintId, const std::string_view file) override;
void SetModularBuildCombination(const LWOOBJID id, const LWOOBJID combinationId) override;
void SetUgcModelProcessMs(const LWOOBJID id, const uint32_t milliseconds) override;
void SetModularBuildProcessMs(const LWOOBJID id, const uint32_t milliseconds) override;
void SetUgcModelProcessStats(const LWOOBJID id, const ProcessStats& stats) override;
void SetModularBuildProcessStats(const LWOOBJID id, const IUgc::ProcessStats& stats) override;
std::vector<IUgcModularBuild::PendingBuild> GetModularBuildsWithoutCombination(const uint32_t limit) override;
std::optional<IUgc::ProcessInfo> GetUgcProcessInfo(const LWOOBJID id) override;
uint64_t ResetUgcModelProcessing(const std::optional<LWOOBJID> id, const bool failedOnly) override;

View File

@@ -198,6 +198,6 @@ std::optional<IUgc::FileChecksum> SQLiteDatabase::GetUgcFileChecksum(const LWOOB
return IUgc::FileChecksum{ std::string(result.getStringField("md5", "")), static_cast<uint32_t>(result.getInt64Field("size")) };
}
void SQLiteDatabase::SetUgcModelProcessMs(const LWOOBJID id, const uint32_t milliseconds) {
ExecuteUpdate("UPDATE ugc SET process_ms = ? WHERE id = ?;", milliseconds, id);
void SQLiteDatabase::SetUgcModelProcessStats(const LWOOBJID id, const ProcessStats& stats) {
ExecuteUpdate("UPDATE ugc SET process_ms = ?, process_cpu_ms = ?, process_memory_kb = ? WHERE id = ?;", stats.milliseconds, stats.cpuMilliseconds, stats.memoryKb, id);
}

View File

@@ -21,6 +21,8 @@ namespace {
entry.bricks = static_cast<uint32_t>(result.getInt64Field("brick_count"));
entry.triangles = static_cast<uint32_t>(result.getInt64Field("triangle_count"));
entry.processMs = static_cast<uint32_t>(result.getInt64Field("process_ms"));
entry.processCpuMs = static_cast<uint32_t>(result.getInt64Field("process_cpu_ms"));
entry.processMemoryKb = static_cast<uint32_t>(result.getInt64Field("process_memory_kb"));
return entry;
}
}

View File

@@ -127,6 +127,6 @@ std::vector<IUgcModularBuild::PendingBuild> SQLiteDatabase::GetModularBuildsWith
return builds;
}
void SQLiteDatabase::SetModularBuildProcessMs(const LWOOBJID id, const uint32_t milliseconds) {
ExecuteUpdate("UPDATE ugc_modular_build SET process_ms = ? WHERE ugc_id = ?;", milliseconds, id);
void SQLiteDatabase::SetModularBuildProcessStats(const LWOOBJID id, const IUgc::ProcessStats& stats) {
ExecuteUpdate("UPDATE ugc_modular_build SET process_ms = ?, process_cpu_ms = ?, process_memory_kb = ? WHERE ugc_id = ?;", stats.milliseconds, stats.cpuMilliseconds, stats.memoryKb, id);
}

View File

@@ -414,8 +414,8 @@ class TestSQLDatabase : public GameDatabase {
void SetUgcFileChecksum(const eFileOwner owner, const LWOOBJID storageId, const std::string_view file, const std::string_view md5, const uint32_t size) override {}
std::optional<IUgc::FileChecksum> GetUgcFileChecksum(const LWOOBJID blueprintId, const std::string_view file) override { return std::nullopt; }
void SetModularBuildCombination(const LWOOBJID id, const LWOOBJID combinationId) override {}
void SetUgcModelProcessMs(const LWOOBJID id, const uint32_t milliseconds) override {}
void SetModularBuildProcessMs(const LWOOBJID id, const uint32_t milliseconds) override {}
void SetUgcModelProcessStats(const LWOOBJID id, const ProcessStats& stats) override {}
void SetModularBuildProcessStats(const LWOOBJID id, const IUgc::ProcessStats& stats) override {}
std::vector<IUgcModularBuild::PendingBuild> GetModularBuildsWithoutCombination(const uint32_t limit) override { return {}; }
std::optional<IUgc::ProcessInfo> GetUgcProcessInfo(const LWOOBJID id) override { return {}; }
uint64_t ResetUgcModelProcessing(const std::optional<LWOOBJID> id, const bool failedOnly) override { return 0; }