From 850c030642f9a27f3b4f522d53b7fe8c1a501d17 Mon Sep 17 00:00:00 2001
From: hsiegeln <37154749+hsiegeln@users.noreply.github.com>
Date: Wed, 22 Apr 2026 16:21:52 +0200
Subject: [PATCH] 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
'
as secondary key only when afterExecutionId is set preserves existing
behaviour for UI/stats callers.
---
.../server/app/search/ClickHouseSearchIndex.java | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/search/ClickHouseSearchIndex.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/search/ClickHouseSearchIndex.java
index ed3ff4a3..246557c1 100644
--- a/cameleer-server-app/src/main/java/com/cameleer/server/app/search/ClickHouseSearchIndex.java
+++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/search/ClickHouseSearchIndex.java
@@ -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