mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 16:49:40 +01:00
Create a write-through cache for PendingRetryReceiptDatabase.
This commit is contained in:
committed by
Alex Hart
parent
0921ebe5f1
commit
62040d06b4
@@ -172,6 +172,20 @@ private static final String[] GROUP_PROJECTION = {
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<GroupRecord> getGroupByDistributionId(@NonNull DistributionId distributionId) {
|
||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||
String query = DISTRIBUTION_ID + " = ?";
|
||||
String[] args = SqlUtil.buildArgs(distributionId);
|
||||
|
||||
try (Cursor cursor = db.query(TABLE_NAME, null, query, args, null, null, null)) {
|
||||
if (cursor.moveToFirst()) {
|
||||
return getGroup(cursor);
|
||||
} else {
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified members from the list of 'unmigrated V1 members' -- the list of members
|
||||
* that were either dropped or had to be invited when migrating the group from V1->V2.
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import org.thoughtcrime.securesms.database.model.PendingRetryReceiptModel
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
|
||||
/**
|
||||
* A write-through cache for [PendingRetryReceiptDatabase].
|
||||
*
|
||||
* We have to read from this cache every time we process an incoming message. As a result, it's a very performance-sensitive operation.
|
||||
*
|
||||
* This cache is very similar to our job storage cache or our key-value store, in the sense that the first access of it will fetch all data from disk so all
|
||||
* future reads can happen in memory.
|
||||
*/
|
||||
class PendingRetryReceiptCache @VisibleForTesting constructor(
|
||||
private val database: PendingRetryReceiptDatabase
|
||||
) {
|
||||
constructor(context: Context) : this(DatabaseFactory.getPendingRetryReceiptDatabase(context))
|
||||
|
||||
private val pendingRetries: MutableMap<RemoteMessageId, PendingRetryReceiptModel> = HashMap()
|
||||
private var populated: Boolean = false
|
||||
|
||||
fun insert(author: RecipientId, authorDevice: Int, sentTimestamp: Long, receivedTimestamp: Long, threadId: Long) {
|
||||
if (!FeatureFlags.senderKey()) return
|
||||
ensurePopulated()
|
||||
|
||||
synchronized(pendingRetries) {
|
||||
val model: PendingRetryReceiptModel = database.insert(author, authorDevice, sentTimestamp, receivedTimestamp, threadId)
|
||||
pendingRetries[RemoteMessageId(author, sentTimestamp)] = model
|
||||
}
|
||||
}
|
||||
|
||||
fun get(author: RecipientId, sentTimestamp: Long): PendingRetryReceiptModel? {
|
||||
if (!FeatureFlags.senderKey()) return null
|
||||
ensurePopulated()
|
||||
|
||||
synchronized(pendingRetries) {
|
||||
return pendingRetries[RemoteMessageId(author, sentTimestamp)]
|
||||
}
|
||||
}
|
||||
|
||||
fun getOldest(): PendingRetryReceiptModel? {
|
||||
if (!FeatureFlags.senderKey()) return null
|
||||
ensurePopulated()
|
||||
|
||||
synchronized(pendingRetries) {
|
||||
return pendingRetries.values.minByOrNull { it.receivedTimestamp }
|
||||
}
|
||||
}
|
||||
|
||||
fun delete(model: PendingRetryReceiptModel) {
|
||||
if (!FeatureFlags.senderKey()) return
|
||||
ensurePopulated()
|
||||
|
||||
synchronized(pendingRetries) {
|
||||
pendingRetries.remove(RemoteMessageId(model.author, model.sentTimestamp))
|
||||
database.delete(model)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensurePopulated() {
|
||||
if (!populated) {
|
||||
synchronized(pendingRetries) {
|
||||
if (!populated) {
|
||||
database.all.forEach { model ->
|
||||
pendingRetries[RemoteMessageId(model.author, model.sentTimestamp)] = model
|
||||
}
|
||||
|
||||
populated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class RemoteMessageId(val author: RecipientId, val sentTimestamp: Long)
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.database.model.PendingRetryReceiptModel;
|
||||
@@ -13,8 +12,13 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.CursorUtil;
|
||||
import org.thoughtcrime.securesms.util.SqlUtil;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Holds information about messages we've sent out retry receipts for.
|
||||
*
|
||||
* Do not use directly! The only class that should be accessing this is {@link PendingRetryReceiptCache}
|
||||
*/
|
||||
public final class PendingRetryReceiptDatabase extends Database {
|
||||
|
||||
@@ -39,7 +43,7 @@ public final class PendingRetryReceiptDatabase extends Database {
|
||||
super(context, databaseHelper);
|
||||
}
|
||||
|
||||
public void insert(@NonNull RecipientId author, int authorDevice, long sentTimestamp, long receivedTimestamp, long threadId) {
|
||||
@NonNull PendingRetryReceiptModel insert(@NonNull RecipientId author, int authorDevice, long sentTimestamp, long receivedTimestamp, long threadId) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(AUTHOR, author.serialize());
|
||||
values.put(DEVICE, authorDevice);
|
||||
@@ -47,37 +51,27 @@ public final class PendingRetryReceiptDatabase extends Database {
|
||||
values.put(RECEIVED_TIMESTAMP, receivedTimestamp);
|
||||
values.put(THREAD_ID, threadId);
|
||||
|
||||
databaseHelper.getWritableDatabase().insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
long id = databaseHelper.getWritableDatabase().insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
|
||||
return new PendingRetryReceiptModel(id, author, authorDevice, sentTimestamp, receivedTimestamp, threadId);
|
||||
}
|
||||
|
||||
public @Nullable PendingRetryReceiptModel get(@NonNull RecipientId author, long sentTimestamp) {
|
||||
String query = AUTHOR + " = ? AND " + SENT_TIMESTAMP + " = ?";
|
||||
String[] args = SqlUtil.buildArgs(author, sentTimestamp);
|
||||
@NonNull List<PendingRetryReceiptModel> getAll() {
|
||||
List<PendingRetryReceiptModel> models = new LinkedList<>();
|
||||
|
||||
try (Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, query, args, null, null, null)) {
|
||||
if (cursor.moveToFirst()) {
|
||||
return fromCursor(cursor);
|
||||
try (Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, null, null, null, null, null)) {
|
||||
while (cursor.moveToNext()) {
|
||||
models.add(fromCursor(cursor));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return models;
|
||||
}
|
||||
|
||||
public @Nullable PendingRetryReceiptModel getOldest() {
|
||||
try (Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, null, null, null, null, RECEIVED_TIMESTAMP + " ASC", "1")) {
|
||||
if (cursor.moveToFirst()) {
|
||||
return fromCursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
void delete(@NonNull PendingRetryReceiptModel model) {
|
||||
databaseHelper.getWritableDatabase().delete(TABLE_NAME, ID_WHERE, SqlUtil.buildArgs(model.getId()));
|
||||
}
|
||||
|
||||
public void delete(long id) {
|
||||
databaseHelper.getWritableDatabase().delete(TABLE_NAME, ID_WHERE, SqlUtil.buildArgs(id));
|
||||
}
|
||||
|
||||
|
||||
private static @NonNull PendingRetryReceiptModel fromCursor(@NonNull Cursor cursor) {
|
||||
return new PendingRetryReceiptModel(CursorUtil.requireLong(cursor, ID),
|
||||
RecipientId.from(CursorUtil.requireString(cursor, AUTHOR)),
|
||||
|
||||
Reference in New Issue
Block a user