mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 01:40:07 +01:00
Introduce extra caching for group message processing.
This commit is contained in:
@@ -12,6 +12,7 @@ import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.groups.GroupIdentifier;
|
||||
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
|
||||
import org.signal.libsignal.zkgroup.groups.GroupSecretParams;
|
||||
import org.thoughtcrime.securesms.util.LRUCache;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -29,6 +30,8 @@ public abstract class GroupId implements DatabaseId {
|
||||
|
||||
private final String encodedId;
|
||||
|
||||
private static final LRUCache<GroupMasterKey, GroupIdentifier> groupIdentifierCache = new LRUCache<>(1000);
|
||||
|
||||
private GroupId(@NonNull String prefix, @NonNull byte[] bytes) {
|
||||
this.encodedId = prefix + Hex.toStringCondensed(bytes);
|
||||
}
|
||||
@@ -80,9 +83,23 @@ public abstract class GroupId implements DatabaseId {
|
||||
}
|
||||
|
||||
public static GroupId.V2 v2(@NonNull GroupMasterKey masterKey) {
|
||||
return v2(GroupSecretParams.deriveFromMasterKey(masterKey)
|
||||
.getPublicParams()
|
||||
.getGroupIdentifier());
|
||||
return v2(getIdentifierForMasterKey(masterKey));
|
||||
}
|
||||
|
||||
public static GroupIdentifier getIdentifierForMasterKey(@NonNull GroupMasterKey masterKey) {
|
||||
GroupIdentifier cachedIdentifier;
|
||||
synchronized (groupIdentifierCache) {
|
||||
cachedIdentifier = groupIdentifierCache.get(masterKey);
|
||||
}
|
||||
if (cachedIdentifier == null) {
|
||||
cachedIdentifier = GroupSecretParams.deriveFromMasterKey(masterKey)
|
||||
.getPublicParams()
|
||||
.getGroupIdentifier();
|
||||
synchronized (groupIdentifierCache) {
|
||||
groupIdentifierCache.put(masterKey, cachedIdentifier);
|
||||
}
|
||||
}
|
||||
return cachedIdentifier;
|
||||
}
|
||||
|
||||
public static GroupId.Push push(ByteString bytes) throws BadGroupIdException {
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -187,12 +188,15 @@ public final class GroupManager {
|
||||
@Nullable byte[] signedGroupChange)
|
||||
throws GroupChangeBusyException, IOException, GroupNotAMemberException
|
||||
{
|
||||
return updateGroupFromServer(context, groupMasterKey, null, revision, timestamp, signedGroupChange);
|
||||
try (GroupManagerV2.GroupUpdater updater = new GroupManagerV2(context).updater(groupMasterKey)) {
|
||||
return updater.updateLocalToServerRevision(revision, timestamp, null, signedGroupChange);
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static GroupsV2StateProcessor.GroupUpdateResult updateGroupFromServer(@NonNull Context context,
|
||||
@NonNull GroupMasterKey groupMasterKey,
|
||||
@NonNull Optional<GroupRecord> groupRecord,
|
||||
@Nullable GroupSecretParams groupSecretParams,
|
||||
int revision,
|
||||
long timestamp,
|
||||
@@ -200,7 +204,7 @@ public final class GroupManager {
|
||||
throws GroupChangeBusyException, IOException, GroupNotAMemberException
|
||||
{
|
||||
try (GroupManagerV2.GroupUpdater updater = new GroupManagerV2(context).updater(groupMasterKey)) {
|
||||
return updater.updateLocalToServerRevision(revision, timestamp, groupSecretParams, signedGroupChange);
|
||||
return updater.updateLocalToServerRevision(revision, timestamp, groupRecord, groupSecretParams, signedGroupChange);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -804,6 +804,14 @@ final class GroupManagerV2 {
|
||||
.updateLocalGroupToRevision(revision, timestamp, getDecryptedGroupChange(signedGroupChange));
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
GroupsV2StateProcessor.GroupUpdateResult updateLocalToServerRevision(int revision, long timestamp, @NonNull Optional<GroupRecord> localRecord, @Nullable GroupSecretParams groupSecretParams, @Nullable byte[] signedGroupChange)
|
||||
throws IOException, GroupNotAMemberException
|
||||
{
|
||||
return new GroupsV2StateProcessor(context).forGroup(serviceIds, groupMasterKey, groupSecretParams)
|
||||
.updateLocalGroupToRevision(revision, timestamp, localRecord, getDecryptedGroupChange(signedGroupChange));
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
void forceSanityUpdateFromServer(long timestamp)
|
||||
throws IOException, GroupNotAMemberException
|
||||
@@ -929,11 +937,11 @@ final class GroupManagerV2 {
|
||||
alreadyAMember = true;
|
||||
}
|
||||
|
||||
Optional<GroupRecord> unmigratedV1Group = groupDatabase.getGroupV1ByExpectedV2(groupId);
|
||||
GroupRecord unmigratedV1Group = GroupsV1MigratedCache.getV1GroupByV2Id(groupId);
|
||||
|
||||
if (unmigratedV1Group.isPresent()) {
|
||||
if (unmigratedV1Group != null) {
|
||||
Log.i(TAG, "Group link was for a migrated V1 group we know about! Migrating it and using that as the base.");
|
||||
GroupsV1MigrationUtil.performLocalMigration(context, unmigratedV1Group.get().getId().requireV1());
|
||||
GroupsV1MigrationUtil.performLocalMigration(context, unmigratedV1Group.getId().requireV1());
|
||||
}
|
||||
|
||||
DecryptedGroup decryptedGroup = createPlaceholderGroup(joinInfo, requestToJoin);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.groups
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.signal.core.util.orNull
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord
|
||||
import org.thoughtcrime.securesms.util.LRUCache
|
||||
|
||||
/**
|
||||
* Cache to keep track of groups we know do not need a migration run on. This is to save time looking for a gv1 group
|
||||
* with the expected v2 id.
|
||||
*/
|
||||
object GroupsV1MigratedCache {
|
||||
private const val MAX_CACHE = 1000
|
||||
|
||||
private val noV1GroupCache = LRUCache<GroupId.V2, Boolean>(MAX_CACHE)
|
||||
|
||||
@JvmStatic
|
||||
@WorkerThread
|
||||
fun hasV1Group(groupId: GroupId.V2): Boolean {
|
||||
return getV1GroupByV2Id(groupId) != null
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@WorkerThread
|
||||
fun getV1GroupByV2Id(groupId: GroupId.V2): GroupRecord? {
|
||||
synchronized(noV1GroupCache) {
|
||||
if (noV1GroupCache.containsKey(groupId)) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
val v1Group = SignalDatabase.groups.getGroupV1ByExpectedV2(groupId)
|
||||
if (!v1Group.isPresent) {
|
||||
synchronized(noV1GroupCache) {
|
||||
noV1GroupCache.put(groupId, true)
|
||||
}
|
||||
}
|
||||
return v1Group.orNull()
|
||||
}
|
||||
}
|
||||
@@ -266,8 +266,21 @@ public class GroupsV2StateProcessor {
|
||||
@Nullable DecryptedGroupChange signedGroupChange)
|
||||
throws IOException, GroupNotAMemberException
|
||||
{
|
||||
Optional<GroupRecord> localRecord = groupDatabase.getGroup(groupId);
|
||||
return updateLocalGroupToRevision(revision, timestamp, groupDatabase.getGroup(groupId), signedGroupChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Using network where required, will attempt to bring the local copy of the group up to the revision specified.
|
||||
*
|
||||
* @param revision use {@link #LATEST} to get latest.
|
||||
*/
|
||||
@WorkerThread
|
||||
public GroupUpdateResult updateLocalGroupToRevision(final int revision,
|
||||
final long timestamp,
|
||||
@NonNull Optional<GroupRecord> localRecord,
|
||||
@Nullable DecryptedGroupChange signedGroupChange)
|
||||
throws IOException, GroupNotAMemberException
|
||||
{
|
||||
if (localIsAtLeast(localRecord, revision)) {
|
||||
return new GroupUpdateResult(GroupState.GROUP_CONSISTENT_OR_AHEAD, null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user