search: compose ORDER BY with execution_id when afterExecutionId set

Follow-up to Task 1.2 flagged by Task 1.5 review (I-1). Single-column
ORDER BY could drop tail rows in a same-millisecond group >50 when
paginating via the composite cursor. Appending ', execution_id <dir>'
as secondary key only when afterExecutionId is set preserves existing
behaviour for UI/stats callers.
This commit is contained in:
hsiegeln
2026-04-22 16:21:52 +02:00
parent 4acf0aeeff
commit 850c030642

View File

@@ -81,13 +81,24 @@ public class ClickHouseSearchIndex implements SearchIndex {
String sortColumn = SORT_FIELD_MAP.getOrDefault(request.sortField(), "start_time");
String sortDir = "asc".equalsIgnoreCase(request.sortDir()) ? "ASC" : "DESC";
// Composite-cursor callers (afterExecutionId set) need a deterministic tiebreak inside
// same-millisecond groups so the client-side last-row pick matches ClickHouse's row order.
// Without this, a same-start_time tail >LIMIT can silently drop rows: the page ends mid-ms,
// the cursor advances past the returned lastRowId, and the skipped rows with smaller
// execution_id values never reappear. Other callers (UI/stats) keep the unchanged
// single-column ORDER BY — they don't use the composite cursor.
String orderBy = sortColumn + " " + sortDir;
if (request.afterExecutionId() != null) {
orderBy += ", execution_id " + sortDir;
}
String dataSql = "SELECT execution_id, route_id, instance_id, application_id, "
+ "status, start_time, end_time, duration_ms, correlation_id, "
+ "error_message, error_stacktrace, diagram_content_hash, attributes, "
+ "has_trace_data, is_replay, "
+ "input_body, output_body, input_headers, output_headers, root_cause_message "
+ "FROM executions FINAL WHERE " + whereClause
+ " ORDER BY " + sortColumn + " " + sortDir
+ " ORDER BY " + orderBy
+ " LIMIT ? OFFSET ?";
List<Object> dataParams = new ArrayList<>(params);