Recipient Id cache.

This commit is contained in:
Alan Evans
2020-05-11 12:33:45 -03:00
committed by Alex Hart
parent a9ecdbdfec
commit a510bc74e6
6 changed files with 493 additions and 25 deletions

View File

@@ -128,11 +128,11 @@ public final class LiveRecipient {
Log.w(TAG, "[Resolve][MAIN] " + getId(), new Throwable());
}
Recipient updated = fetchRecipientFromDisk(getId());
Recipient updated = fetchAndCacheRecipientFromDisk(getId());
List<Recipient> participants = Stream.of(updated.getParticipants())
.filter(Recipient::isResolving)
.map(Recipient::getId)
.map(this::fetchRecipientFromDisk)
.map(this::fetchAndCacheRecipientFromDisk)
.toList();
for (Recipient participant : participants) {
@@ -155,10 +155,10 @@ public final class LiveRecipient {
Log.w(TAG, "[Refresh][MAIN] " + getId(), new Throwable());
}
Recipient recipient = fetchRecipientFromDisk(getId());
Recipient recipient = fetchAndCacheRecipientFromDisk(getId());
List<Recipient> participants = Stream.of(recipient.getParticipants())
.map(Recipient::getId)
.map(this::fetchRecipientFromDisk)
.map(this::fetchAndCacheRecipientFromDisk)
.toList();
for (Recipient participant : participants) {
@@ -172,12 +172,14 @@ public final class LiveRecipient {
return liveData;
}
private @NonNull Recipient fetchRecipientFromDisk(RecipientId id) {
private @NonNull Recipient fetchAndCacheRecipientFromDisk(@NonNull RecipientId id) {
RecipientSettings settings = recipientDatabase.getRecipientSettings(id);
RecipientDetails details = settings.getGroupId() != null ? getGroupRecipientDetails(settings)
: getIndividualRecipientDetails(settings);
return new Recipient(id, details);
Recipient recipient = new Recipient(id, details);
RecipientIdCache.INSTANCE.put(recipient);
return recipient;
}
private @NonNull RecipientDetails getIndividualRecipientDetails(RecipientSettings settings) {
@@ -194,7 +196,7 @@ public final class LiveRecipient {
if (groupRecord.isPresent()) {
String title = groupRecord.get().getTitle();
List<Recipient> members = Stream.of(groupRecord.get().getMembers()).filterNot(RecipientId::isUnknown).map(this::fetchRecipientFromDisk).toList();
List<Recipient> members = Stream.of(groupRecord.get().getMembers()).filterNot(RecipientId::isUnknown).map(this::fetchAndCacheRecipientFromDisk).toList();
Optional<Long> avatarId = Optional.absent();
if (settings.getGroupId() != null && settings.getGroupId().isPush() && title == null) {

View File

@@ -1,17 +1,24 @@
package org.thoughtcrime.securesms.recipients;
import android.annotation.SuppressLint;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.Util;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.regex.Pattern;
public class RecipientId implements Parcelable, Comparable<RecipientId> {
@@ -39,6 +46,21 @@ public class RecipientId implements Parcelable, Comparable<RecipientId> {
}
}
/**
* Always supply both {@param uuid} and {@param e164} if you have both.
*/
@AnyThread
@SuppressLint("WrongThread")
public static @NonNull RecipientId from(@Nullable UUID uuid, @Nullable String e164) {
RecipientId recipientId = RecipientIdCache.INSTANCE.get(uuid, e164);
if (recipientId == null) {
recipientId = Recipient.externalPush(ApplicationDependencies.getApplication(), uuid, e164).getId();
}
return recipientId;
}
private RecipientId(long id) {
this.id = id;
}

View File

@@ -0,0 +1,73 @@
package org.thoughtcrime.securesms.recipients;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.logging.Log;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
/**
* Thread safe cache that allows faster looking up of {@link RecipientId}s without hitting the database.
*/
final class RecipientIdCache {
private static final int INSTANCE_CACHE_LIMIT = 1000;
static final RecipientIdCache INSTANCE = new RecipientIdCache(INSTANCE_CACHE_LIMIT);
private static final String TAG = Log.tag(RecipientIdCache.class);
private final Map<Object, RecipientId> ids;
RecipientIdCache(int limit) {
ids = new LinkedHashMap<Object, RecipientId>(128, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Entry<Object, RecipientId> eldest) {
return size() > limit;
}
};
}
synchronized void put(@NonNull Recipient recipient) {
RecipientId recipientId = recipient.getId();
Optional<String> e164 = recipient.getE164();
Optional<UUID> uuid = recipient.getUuid();
if (e164.isPresent()) {
ids.put(e164.get(), recipientId);
}
if (uuid.isPresent()) {
ids.put(uuid.get(), recipientId);
}
}
synchronized @Nullable RecipientId get(@Nullable UUID uuid, @Nullable String e164) {
if (uuid != null && e164 != null) {
RecipientId recipientIdByUuid = ids.get(uuid);
if (recipientIdByUuid == null) return null;
RecipientId recipientIdByE164 = ids.get(e164);
if (recipientIdByE164 == null) return null;
if (recipientIdByUuid.equals(recipientIdByE164)) {
return recipientIdByUuid;
} else {
ids.remove(uuid);
ids.remove(e164);
Log.w(TAG, "Seen invalid RecipientIdCacheState");
return null;
}
} else if (uuid != null) {
return ids.get(uuid);
} else if (e164 != null) {
return ids.get(e164);
}
return null;
}
}

View File

@@ -17,12 +17,12 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException;
@@ -129,11 +129,11 @@ public final class GroupUtil {
public static class GroupDescription {
@NonNull private final Context context;
@Nullable private final GroupContext groupContext;
@Nullable private final List<Recipient> members;
@NonNull private final Context context;
@Nullable private final GroupContext groupContext;
@Nullable private final List<RecipientId> members;
public GroupDescription(@NonNull Context context, @Nullable GroupContext groupContext) {
GroupDescription(@NonNull Context context, @Nullable GroupContext groupContext) {
this.context = context.getApplicationContext();
this.groupContext = groupContext;
@@ -143,9 +143,9 @@ public final class GroupUtil {
this.members = new LinkedList<>();
for (GroupContext.Member member : groupContext.getMembersList()) {
Recipient recipient = Recipient.externalPush(context, new SignalServiceAddress(UuidUtil.parseOrNull(member.getUuid()), member.getE164()));
if (!recipient.isLocalNumber()) {
this.members.add(recipient);
RecipientId recipientId = RecipientId.from(UuidUtil.parseOrNull(member.getUuid()), member.getE164());
if (!recipientId.equals(Recipient.self().getId())) {
this.members.add(recipientId);
}
}
}
@@ -178,31 +178,31 @@ public final class GroupUtil {
public void addObserver(RecipientForeverObserver listener) {
if (this.members != null) {
for (Recipient member : this.members) {
member.live().observeForever(listener);
for (RecipientId member : this.members) {
Recipient.live(member).observeForever(listener);
}
}
}
public void removeObserver(RecipientForeverObserver listener) {
if (this.members != null) {
for (Recipient member : this.members) {
member.live().removeForeverObserver(listener);
for (RecipientId member : this.members) {
Recipient.live(member).removeForeverObserver(listener);
}
}
}
private String toString(List<Recipient> recipients) {
String result = "";
private String toString(List<RecipientId> recipients) {
StringBuilder result = new StringBuilder();
for (int i=0;i<recipients.size();i++) {
result += recipients.get(i).toShortString(context);
for (int i = 0; i < recipients.size(); i++) {
result.append(Recipient.live(recipients.get(i)).get().toShortString(context));
if (i != recipients.size() -1 )
result += ", ";
result.append(", ");
}
return result;
return result.toString();
}
}
}