From cfbbdcb6e874d73ef9429293d3afff58e1d6c216 Mon Sep 17 00:00:00 2001 From: pepper <3782201+rohvani@users.noreply.github.com> Date: Tue, 1 Sep 2026 16:13:34 -0400 Subject: Restore canonical ChatService tail ordering Apply the durable archive boundary before reconciling dated legacy overlaps so post-boundary messages stay in canonical service order and survive consumer caps. Name the offset-less SLT conversion at the seam and cover capped archive summaries and UTC-7/UTC-8 boundary cases. Validated with the RelWithDebInfo secondlife-bin build, the LLChatServiceHistoryCore suite (14 tests), the LLConversationLog suite (3 tests), and all 19 newview CTest targets. --- indra/newview/llchatservicehistory.cpp | 19 +++++++------------ indra/newview/llchatservicehistorycore.cpp | 5 +++++ indra/newview/llchatservicehistorycore.h | 4 ++++ .../newview/tests/llchatservicehistorycore_test.cpp | 21 +++++++++++++++++++++ 4 files changed, 37 insertions(+), 12 deletions(-) (limited to 'indra') diff --git a/indra/newview/llchatservicehistory.cpp b/indra/newview/llchatservicehistory.cpp index b140259de1..b73937b546 100644 --- a/indra/newview/llchatservicehistory.cpp +++ b/indra/newview/llchatservicehistory.cpp @@ -2848,9 +2848,8 @@ LLChatServiceHistory::HistoryResult readStitched( std::vector> dated; std::list undated; - // Exact canonical overlaps keep their legacy position. For unmatched rows, the - // durable archive's oldest key controls the seam; retain undated rows and dated - // rows whose earliest possible SLT interpretation predates that boundary. + // Admit only the legacy prefix that may predate the durable service boundary. + // Offset-less SLT timestamps use their earliest UTC interpretation (UTC-7). for (const LLSD& message : legacy) { F64 wall = 0.0; @@ -2858,14 +2857,13 @@ LLChatServiceHistory::HistoryResult readStitched( { undated.push_back(message); } - else + else if (!archive.has_oldest || legacyWallMayPrecedeService(wall, service_epoch)) { LLSD stitched = message; stitched[LEGACY_WALL_TIME] = wall; - // Replace an exact legacy occurrence in place with its canonical row. - // This keeps local ordering around same-minute system messages while - // consuming only one occurrence from each source. + // Reconcile exact overlaps only inside the admitted legacy prefix, + // consuming one occurrence from each source. size_t match = service.size(); for (const F64 offset : { 7.0 * 3600.0, 8.0 * 3600.0 }) { @@ -2889,11 +2887,7 @@ LLChatServiceHistory::HistoryResult readStitched( stitched = serviceMessage(service[match]); service_placed[match] = true; } - if (match != service.size() || !archive.has_oldest || - wall + 7.0 * 3600.0 < service_epoch) - { - dated.emplace_back(wall, stitched); - } + dated.emplace_back(wall, stitched); } } std::stable_sort(dated.begin(), dated.end(), @@ -2921,6 +2915,7 @@ LLChatServiceHistory::HistoryResult readStitched( } } + // Apply the consumer limit to the complete seam order, retaining the service tail. while (limit && result.messages.size() > limit) { result.messages.pop_front(); diff --git a/indra/newview/llchatservicehistorycore.cpp b/indra/newview/llchatservicehistorycore.cpp index 3478bb2f11..06fdd8ecf6 100644 --- a/indra/newview/llchatservicehistorycore.cpp +++ b/indra/newview/llchatservicehistorycore.cpp @@ -297,6 +297,11 @@ bool sameDirectSenderName(const std::string& left, const std::string& right) return !left_resident.empty() && left_resident == right_resident; } +bool legacyWallMayPrecedeService(F64 wall_epoch, F64 service_epoch) +{ + return wall_epoch + 7.0 * 3600.0 < service_epoch; +} + bool parseCreatedAt(const std::string& text, std::string& normalized) { // Validate the complete wire shape before LLDate performs calendar conversion. diff --git a/indra/newview/llchatservicehistorycore.h b/indra/newview/llchatservicehistorycore.h index 86c6b0131e..9b4254eca6 100644 --- a/indra/newview/llchatservicehistorycore.h +++ b/indra/newview/llchatservicehistorycore.h @@ -110,6 +110,10 @@ namespace LLChatServiceHistoryCore bool parseCreatedAt(const std::string& text, std::string& normalized); bool sameDirectSenderName(const std::string& left, const std::string& right); + // Legacy SLT wall times carry no UTC offset; UTC-7 is their earliest possible + // interpretation at the durable service boundary. + bool legacyWallMayPrecedeService(F64 wall_epoch, F64 service_epoch); + // Both wire validators are all-or-nothing and clear their output before parsing. bool validateConversationList(const LLSD& value, const LLUUID& agent_id, std::vector& entries); diff --git a/indra/newview/tests/llchatservicehistorycore_test.cpp b/indra/newview/tests/llchatservicehistorycore_test.cpp index e5f542d3f7..52bc16edba 100644 --- a/indra/newview/tests/llchatservicehistorycore_test.cpp +++ b/indra/newview/tests/llchatservicehistorycore_test.cpp @@ -233,9 +233,12 @@ template<> template<> void object_t::test<9>() { NamedTempFile file("chatservice", validArchive()); ArchiveScan scan; + TimeUuidKey oldest; + parseTimeUuid(FIRST, oldest); ensure("valid archive", scanArchive(file.getPath().string(), AGENT, RESIDENT, 0, 1, scan)); ensure_equals("two summarized", scan.row_count, U32(2)); + ensure("display cap preserves durable oldest", scan.oldest == oldest); ensure_equals("newest display cap", scan.display_rows.size(), size_t(1)); ensure_equals("newest retained", scan.display_rows.front().msg_id, std::string(SECOND)); } @@ -288,4 +291,22 @@ template<> template<> void object_t::test<13>() ensure("different residents stay distinct", !sameDirectSenderName("bridie.linden", "beanie.tester")); } + +template<> template<> void object_t::test<14>() +{ + const F64 wall = 1000.0; + const F64 daylight_utc = wall + 7.0 * 3600.0; + + // The earlier UTC-7 interpretation controls conservative seam admission. + ensure("possible pre-boundary row retained", + legacyWallMayPrecedeService(wall, daylight_utc + 1.0)); + ensure("row at boundary excluded", + !legacyWallMayPrecedeService(wall, daylight_utc)); + ensure("post-boundary row excluded", + !legacyWallMayPrecedeService(wall, daylight_utc - 1.0)); + + // A boundary between the UTC-7 and UTC-8 interpretations remains ambiguous. + ensure("ambiguous DST row retained", + legacyWallMayPrecedeService(wall, daylight_utc + 30.0 * 60.0)); +} } -- cgit v1.3