Skip to content

Commit 9826a0e

Browse files
author
gitlab
committed
Merge branch 'fix/ZSTAC-72079@@2' into '5.5.12'
<fix>[ai]: add eval task sort columns for ZQL and SDK fields See merge request zstackio/zstack!9266
2 parents 009b170 + 95b0ad5 commit 9826a0e

4 files changed

Lines changed: 87 additions & 14 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,33 @@
11
-- ZSTAC-75319: Add normalizedModelName column for GPU spec dedup
22
CALL ADD_COLUMN('GpuDeviceSpecVO', 'normalizedModelName', 'VARCHAR(255)', 1, NULL);
33
CALL CREATE_INDEX('GpuDeviceSpecVO', 'idx_gpu_spec_normalized_model', 'normalizedModelName');
4+
5+
-- Add totalScore and endTime columns to ModelEvaluationTaskVO for ZQL sorting support
6+
-- Previously these values were only stored inside the opaque JSON TEXT field,
7+
-- making them invisible to ZQL ORDER BY queries.
8+
CALL ADD_COLUMN('ModelEvaluationTaskVO', 'totalScore', 'DOUBLE', 1, NULL);
9+
CALL ADD_COLUMN('ModelEvaluationTaskVO', 'endTime', 'DATETIME', 1, NULL);
10+
11+
-- Add indexes to support efficient sorting
12+
CALL CREATE_INDEX('ModelEvaluationTaskVO', 'idx_ModelEvaluationTaskVO_totalScore', 'totalScore');
13+
CALL CREATE_INDEX('ModelEvaluationTaskVO', 'idx_ModelEvaluationTaskVO_endTime', 'endTime');
14+
15+
-- Backfill totalScore from opaque JSON for existing completed tasks
16+
-- Uses Json_getKeyValue defined in beforeMigrate.sql for MySQL 5.5+ compatibility
17+
UPDATE `zstack`.`ModelEvaluationTaskVO`
18+
SET `totalScore` = CAST(Json_getKeyValue(`opaque`, 'total_score') AS DECIMAL(20,6))
19+
WHERE `opaque` IS NOT NULL
20+
AND `totalScore` IS NULL
21+
AND Json_getKeyValue(`opaque`, 'total_score') IS NOT NULL;
22+
23+
-- Backfill endTime from opaque JSON for existing completed/failed tasks
24+
-- end_time format from Python agent: "MMM dd, yyyy hh:mm:ss a" (e.g. "Jan 01, 2025 10:30:00 AM")
25+
UPDATE `zstack`.`ModelEvaluationTaskVO`
26+
SET `endTime` = STR_TO_DATE(
27+
Json_getKeyValue(`opaque`, 'end_time'),
28+
'%b %d, %Y %h:%i:%s %p'
29+
)
30+
WHERE `opaque` IS NOT NULL
31+
AND `endTime` IS NULL
32+
AND Json_getKeyValue(`opaque`, 'end_time') IS NOT NULL
33+
AND Json_getKeyValue(`opaque`, 'end_time') != '';

core/src/main/java/org/zstack/core/Platform.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -938,10 +938,6 @@ public static boolean killProcess(int pid, Integer timeout) {
938938

939939
private static ErrorCodeElaboration elaborate(String fmt, Object...args) {
940940
try {
941-
if (String.format(fmt, args).length() > StringSimilarity.maxElaborationRegex) {
942-
return null;
943-
}
944-
945941
ErrorCodeElaboration elaboration = StringSimilarity.findSimilar(fmt, args);
946942
if (elaboration == null) {
947943
return null;

sdk/src/main/java/org/zstack/sdk/ModelEvaluationTaskInventory.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,20 @@ public java.lang.Integer getReadTimeout() {
228228
return this.readTimeout;
229229
}
230230

231+
public java.lang.Double totalScore;
232+
public void setTotalScore(java.lang.Double totalScore) {
233+
this.totalScore = totalScore;
234+
}
235+
public java.lang.Double getTotalScore() {
236+
return this.totalScore;
237+
}
238+
239+
public java.sql.Timestamp endTime;
240+
public void setEndTime(java.sql.Timestamp endTime) {
241+
this.endTime = endTime;
242+
}
243+
public java.sql.Timestamp getEndTime() {
244+
return this.endTime;
245+
}
246+
231247
}

utils/src/main/java/org/zstack/utils/string/StringSimilarity.java

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,15 @@ private static void logSearchSpend(String sub, long start, boolean found) {
287287
/**
288288
* find the most similar error code elaboration for the given error message.
289289
*
290+
* The method uses a two-phase strategy to avoid performance degradation
291+
* when format args produce very long strings (e.g., serialized error chains
292+
* or HTML response bodies):
293+
*
294+
* Phase 1: Try regex matching with the formatted string (length-guarded).
295+
* Phase 2: If Phase 1 misses (or formatted string too long), fallback
296+
* to the raw fmt template for regex matching.
297+
* Phase 3: Distance matching always uses the raw fmt template.
298+
*
290299
* @param sub error message or error message fmt
291300
* @param args arguments
292301
* @return the most similar error code elaboration
@@ -311,23 +320,41 @@ public static ErrorCodeElaboration findSimilar(String sub, Object...args) {
311320
errors.remove(sub);
312321
}
313322

314-
if (args != null && missed.get(String.format(sub, args)) != null) {
315-
logSearchSpend(sub, start, false);
316-
return null;
317-
} else if (missed.get(sub) != null) {
323+
// check missed cache for both fmt template and formatted string
324+
if (missed.get(sub) != null) {
318325
logSearchSpend(sub, start, false);
319326
return null;
320327
}
328+
if (args != null) {
329+
try {
330+
String formatted = String.format(sub, args);
331+
if (missed.get(formatted) != null) {
332+
logSearchSpend(sub, start, false);
333+
return null;
334+
}
335+
} catch (Exception e) {
336+
logger.trace(String.format("failed to format elaboration key: %s", e.getMessage()));
337+
}
338+
}
321339

322-
try {
323-
logger.trace(String.format("start to search elaboration for: %s", String.format(sub, args)));
324-
err = findMostSimilarRegex(String.format(sub, args));
325-
} catch (Exception e) {
326-
logger.trace(String.format("start search elaboration for: %s", sub));
340+
// Phase 1: try regex matching with formatted string (guarded by length limit)
341+
if (args != null && args.length > 0) {
342+
try {
343+
String formatted = String.format(sub, args);
344+
if (formatted.length() <= maxElaborationRegex) {
345+
err = findMostSimilarRegex(formatted);
346+
}
347+
} catch (Exception e) {
348+
logger.trace(String.format("failed to format for regex matching: %s", e.getMessage()));
349+
}
350+
}
351+
352+
// Phase 2: if formatted string missed or was too long, fallback to raw fmt template
353+
if (err == null) {
327354
err = findMostSimilarRegex(sub);
328355
}
329356

330-
// find by distance is not reliable disable it for now
357+
// Phase 3: distance matching uses the raw fmt template (always short)
331358
if (err == null) {
332359
err = findSimilarDistance(sub);
333360
}
@@ -355,6 +382,10 @@ private static boolean verifyElaboration(ErrorCodeElaboration elaboration, Strin
355382

356383
// better precision, worse performance
357384
private static ErrorCodeElaboration findMostSimilarRegex(String sub) {
385+
if (sub.length() > maxElaborationRegex) {
386+
return null;
387+
}
388+
358389
if (!isRegexMatchedByRetrees(sub)) {
359390
return null;
360391
}

0 commit comments

Comments
 (0)