Release failed member snapshot requests and validate deletion frames
This commit is contained in:
1 parent
36af8d077f
commit
9383a68f17
2 files changed
+31
-2
No files matched your search
@@ -8,7 +8,9 @@ import java.nio.ByteOrder
|
||||
data class MemberKey(val group: String, val owner: Long)
|
||||
|
||||
/** Полные снимки CHAT-групп. Все методы вызываются последовательно на main, события не replay-ятся.
|
||||
* loaded хранится отдельно: отсутствие участника в полученном списке не требует нового запроса. */
|
||||
* loaded хранится отдельно: отсутствие участника в полученном списке не требует нового запроса.
|
||||
* Pending подавляет повтор 10 секунд; отказ/исключение enqueue разрешает повтор сразу, не очищая последний снимок.
|
||||
* Полный кадр применяется атомарно, отдельные update/remove меняют одного участника; stop очищает всё и увеличивает generation. */
|
||||
class MemberRepository(
|
||||
private val enqueue: (String) -> Boolean = NativeLib::requestMemberList,
|
||||
private val ready: () -> Boolean = NativeLib::isReady,
|
||||
@@ -37,7 +39,10 @@ class MemberRepository(
|
||||
if (started != null && now - started < 10_000) return
|
||||
if (started != null) log("WARN", "Snapshot response timeout channel=$group elapsedMs=${now - started}")
|
||||
pending[group] = now
|
||||
if (!enqueue(group)) {
|
||||
val accepted = try { enqueue(group) } catch (e: Exception) {
|
||||
pending.remove(group); log("ERROR", "Snapshot enqueue threw channel=$group: $e"); return
|
||||
}
|
||||
if (!accepted) {
|
||||
pending.remove(group); log("ERROR", "Snapshot enqueue failed channel=$group"); return
|
||||
}
|
||||
log("DEBUG", "Snapshot requested channel=$group loaded=${group in loaded}")
|
||||
@@ -61,10 +66,12 @@ class MemberRepository(
|
||||
val previous = _members.value
|
||||
val updated = previous.toMutableMap()
|
||||
if (type == 31) {
|
||||
require(data.size == 1 + length) { "invalid channel deletion size" }
|
||||
updated.keys.removeAll { it.group == group }; loaded.remove(group); pending.remove(group)
|
||||
} else if (type == 25) {
|
||||
require(data.size == 1 + length + 8) { "invalid removal size" }
|
||||
val node = ByteBuffer.wrap(data, 1 + length, 8).order(ByteOrder.LITTLE_ENDIAN).long
|
||||
require(node != 0L) { "zero removal node" }
|
||||
updated.remove(MemberKey(group, node))
|
||||
} else {
|
||||
val countSize = if (type == 23) 2 else 1
|
||||
|
||||
@@ -88,4 +88,26 @@ class MemberRepositoryTest {
|
||||
repo.onEvent(23, event("123", row(1)))
|
||||
repo.refresh("123"); assertTrue(repo.member("123", 1)!!.phoneStatusKnown)
|
||||
}
|
||||
|
||||
@Test fun enqueueExceptionReleasesPendingAndKeepsLastSnapshot() {
|
||||
var requests = 0
|
||||
val diagnostics = mutableListOf<String>()
|
||||
val repo = MemberRepository({ if (++requests == 1) error("JNI request failed") else true }, { true }, { 0 },
|
||||
{ _, message -> diagnostics.add(message) })
|
||||
repo.onEvent(12, null); repo.onEvent(23, event("123", row(1)))
|
||||
val before = repo.members.value
|
||||
repo.refresh("123"); repo.refresh("123")
|
||||
assertEquals(2, requests); assertEquals(before, repo.members.value)
|
||||
assertTrue(diagnostics.any { "enqueue threw" in it })
|
||||
}
|
||||
|
||||
@Test fun malformedDeletionCannotClearAnOtherwiseValidSnapshot() {
|
||||
val repo = repository(); repo.onEvent(23, event("123", row(1)))
|
||||
val before = repo.members.value
|
||||
val header = byteArrayOf(3) + "123".toByteArray()
|
||||
repo.onEvent(31, header + byteArrayOf(0))
|
||||
assertEquals(before, repo.members.value)
|
||||
repo.onEvent(31, header)
|
||||
assertTrue(repo.members.value.isEmpty())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user