summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpepper <3782201+rohvani@users.noreply.github.com>2026-09-21 17:31:12 -0400
committerpepper <3782201+rohvani@users.noreply.github.com>2026-09-23 15:24:07 -0400
commit40a7b0866c55196cd1e0dced29c83a3cb0301c1d (patch)
tree7d78fe7e770a81293928dbf7df4b719f50967239
parent6c8205c204c64edb4ab83fe1d70506cebe1236f6 (diff)
Derive conversation identity from validated participants
Derive direct conversation IDs from the account and resident at request and validation time. Remove their duplicate storage and the validator's redundant conversation-ID argument. Remove the first-list flag because empty coverage already schedules first discovery. Retain strict list/page identity checks and add an assertion rejecting another conversation's response.
-rw-r--r--indra/newview/llchatservicehistory.cpp20
-rw-r--r--indra/newview/llchatservicehistorycore.cpp4
-rw-r--r--indra/newview/llchatservicehistorycore.h2
-rw-r--r--indra/newview/tests/llchatservicehistorycore_test.cpp20
4 files changed, 20 insertions, 26 deletions
diff --git a/indra/newview/llchatservicehistory.cpp b/indra/newview/llchatservicehistory.cpp
index d9055fe372..ec1d64d970 100644
--- a/indra/newview/llchatservicehistory.cpp
+++ b/indra/newview/llchatservicehistory.cpp
@@ -138,7 +138,6 @@ struct Metadata
struct Resident
{
// Discovery identity and the durable service range already covered.
- std::string conversation_id;
std::string advertised_token;
std::string covered_token;
U32 archive_serial = 0;
@@ -185,7 +184,6 @@ struct Runtime
// Discovery and archive-maintenance state.
bool initialized_archives = false;
- bool list_valid = false;
bool list_needed = true;
bool list_retry_used = false;
@@ -1161,14 +1159,12 @@ bool processList(const LLSD& body, bool& confirm_activity_discovery)
setWorkActive(pair.first, false);
}
}
- const bool first_list = !sRuntime.list_valid;
for (const ListEntry& entry : entries)
{
Resident& resident = sRuntime.residents[entry.resident_id];
const bool placeholder = std::find(sRuntime.queue.begin(), sRuntime.queue.end(),
entry.resident_id) != sRuntime.queue.end();
const bool changed = resident.advertised_token != entry.last_msg_id;
- resident.conversation_id = entry.conversation_id;
resident.advertised_token = entry.last_msg_id;
resident.listed = true;
resident.activity_discovery = DISCOVERY_NONE;
@@ -1177,16 +1173,14 @@ bool processList(const LLSD& body, bool& confirm_activity_discovery)
resident.metadata.state = META_UNREQUESTED;
}
- // Queue only when discovery or archive coverage says the head may contain
- // unseen rows; token equality is not used as a TimeUUID ordering claim.
- if (!placeholder && (first_list || changed ||
- resident.covered_token != resident.advertised_token))
+ // Empty coverage queues first discovery. Later lists queue changed or
+ // uncovered heads; token equality is not a TimeUUID ordering claim.
+ if (!placeholder && (changed || resident.covered_token != resident.advertised_token))
{
resident.retry_used = false;
queueResident(entry.resident_id, false);
}
}
- sRuntime.list_valid = true;
return true;
}
@@ -1293,7 +1287,7 @@ void syncResident(const LLUUID& id, const CapabilityContext& context, U32 epoch)
// Resolve prerequisites before starting a pass. Only the manager erases
// residents; its checked waits terminate before a reference can become stale.
- if (!resident.listed || resident.conversation_id.empty())
+ if (!resident.listed)
{
resident.activity_discovery = DISCOVERY_NONE;
resident.snapshot.head_preview.clear();
@@ -1345,6 +1339,7 @@ void syncResident(const LLUUID& id, const CapabilityContext& context, U32 epoch)
bool had_boundary = !needs_prepare && resident.summary.state == ARCHIVE_VALID &&
resident.summary.has_oldest;
TimeUuidKey stored_newest = resident.summary.newest;
+ const std::string conversation_id = directConversationId(sRuntime.agent_id, id);
std::vector<Row> staged;
size_t staged_bytes = 0;
U32 returned_rows = 0;
@@ -1388,7 +1383,7 @@ void syncResident(const LLUUID& id, const CapabilityContext& context, U32 epoch)
}
resident.first_request_started = true;
LLSD post;
- post["conversation_id"] = resident.conversation_id;
+ post["conversation_id"] = conversation_id;
post["limit"] = 100;
if (!cursor.empty())
{
@@ -1410,8 +1405,7 @@ void syncResident(const LLUUID& id, const CapabilityContext& context, U32 epoch)
return;
}
Page page;
- if (!validateHistoryPage(response.body, sRuntime.agent_id, id,
- resident.conversation_id, cursor,
+ if (!validateHistoryPage(response.body, sRuntime.agent_id, id, cursor,
sRuntime.deleted_before_ticks, page))
{
return;
diff --git a/indra/newview/llchatservicehistorycore.cpp b/indra/newview/llchatservicehistorycore.cpp
index 4223f57155..88b890e7a1 100644
--- a/indra/newview/llchatservicehistorycore.cpp
+++ b/indra/newview/llchatservicehistorycore.cpp
@@ -760,7 +760,7 @@ bool validateConversationList(const LLSD& value, const LLUUID& agent_id,
// resident conversations can have a local history archive.
if (resident.notNull())
{
- entries.push_back({resident, conversation, token});
+ entries.push_back({resident, token});
}
}
@@ -769,11 +769,11 @@ bool validateConversationList(const LLSD& value, const LLUUID& agent_id,
bool validateHistoryPage(const LLSD& value, const LLUUID& agent_id,
const LLUUID& resident_id,
- const std::string& conversation_id,
const std::string& requested_cursor,
U64 deleted_before_ticks, Page& page)
{
page = Page();
+ const std::string conversation_id = directConversationId(agent_id, resident_id);
// The service echoes the exact conversation and fixed page size. Coercible LLSD
// types are rejected so paging state cannot drift across malformed responses.
diff --git a/indra/newview/llchatservicehistorycore.h b/indra/newview/llchatservicehistorycore.h
index 957243cfba..60ecbf59a8 100644
--- a/indra/newview/llchatservicehistorycore.h
+++ b/indra/newview/llchatservicehistorycore.h
@@ -57,7 +57,6 @@ namespace LLChatServiceHistoryCore
struct ListEntry
{
LLUUID resident_id;
- std::string conversation_id;
std::string last_msg_id;
};
@@ -150,7 +149,6 @@ namespace LLChatServiceHistoryCore
std::vector<ListEntry>& entries);
bool validateHistoryPage(const LLSD& value, const LLUUID& agent_id,
const LLUUID& resident_id,
- const std::string& conversation_id,
const std::string& requested_cursor,
U64 deleted_before_ticks, Page& page);
diff --git a/indra/newview/tests/llchatservicehistorycore_test.cpp b/indra/newview/tests/llchatservicehistorycore_test.cpp
index 4f28f85317..38f77de4af 100644
--- a/indra/newview/tests/llchatservicehistorycore_test.cpp
+++ b/indra/newview/tests/llchatservicehistorycore_test.cpp
@@ -118,21 +118,21 @@ template<> template<> void object_t::test<4>()
Page page;
ensure("strict descending page", validateHistoryPage(
- wirePage(rows), AGENT, RESIDENT, directConversationId(AGENT, RESIDENT),
+ wirePage(rows), AGENT, RESIDENT,
"", 0, page));
ensure_equals("all rows retained", page.rows.size(), size_t(2));
ensure_equals("oldest cursor", page.next_cursor, std::string(FIRST));
// The deletion boundary is inclusive and ends paging, but not validation.
ensure("cutoff page accepted", validateHistoryPage(
- wirePage(rows), AGENT, RESIDENT, directConversationId(AGENT, RESIDENT), "", 1, page));
+ wirePage(rows), AGENT, RESIDENT, "", 1, page));
ensure_equals("only newer rows retained", page.rows.size(), size_t(1));
ensure_equals("newer row retained", page.rows.front().msg_id, std::string(SECOND));
ensure("cutoff ends paging", page.terminal && page.next_cursor.empty());
rows.append(wireRow("00000000-0000-1000-8000-000000000000"));
rows[2]["dialog"] = 99;
ensure("invalid row below cutoff rejected", !validateHistoryPage(
- wirePage(rows), AGENT, RESIDENT, directConversationId(AGENT, RESIDENT), "", 1, page));
+ wirePage(rows), AGENT, RESIDENT, "", 1, page));
}
template<> template<> void object_t::test<5>()
@@ -144,21 +144,25 @@ template<> template<> void object_t::test<5>()
Page page;
ensure("ascending page rejected", !validateHistoryPage(
- wirePage(rows), AGENT, RESIDENT, directConversationId(AGENT, RESIDENT),
+ wirePage(rows), AGENT, RESIDENT,
"", 0, page));
// Ordering alone rejects duplicates, including replay of the preceding page's cursor.
rows[1] = rows[0];
ensure("duplicate row rejected", !validateHistoryPage(
- wirePage(rows), AGENT, RESIDENT, directConversationId(AGENT, RESIDENT), "", 0, page));
+ wirePage(rows), AGENT, RESIDENT, "", 0, page));
rows.erase(1);
ensure("cursor row cannot repeat on the next page", !validateHistoryPage(
- wirePage(rows, FIRST), AGENT, RESIDENT, directConversationId(AGENT, RESIDENT), FIRST, 0, page));
+ wirePage(rows, FIRST), AGENT, RESIDENT, FIRST, 0, page));
LLSD wrong = wirePage(LLSD::emptyArray());
+ wrong["conversation_id"] = directConversationId(AGENT, LLUUID::null);
+ ensure("another conversation rejected", !validateHistoryPage(
+ wrong, AGENT, RESIDENT, "", 0, page));
+ wrong = wirePage(LLSD::emptyArray());
wrong["limit"] = "100";
ensure("coercible type rejected", !validateHistoryPage(
- wrong, AGENT, RESIDENT, directConversationId(AGENT, RESIDENT), "", 0, page));
+ wrong, AGENT, RESIDENT, "", 0, page));
}
template<> template<> void object_t::test<6>()
@@ -1047,8 +1051,6 @@ template<> template<> void object_t::test<36>()
validateConversationList(list, AGENT, entries));
ensure_equals("only the resident is scheduled", entries.size(), size_t(1));
ensure_equals("resident identity preserved", entries.front().resident_id, RESIDENT);
- ensure_equals("conversation identity preserved", entries.front().conversation_id,
- peer["conversation_id"].asString());
ensure_equals("history token preserved", entries.front().last_msg_id, std::string(SECOND));
}
}