Fix removal of local only unregistered accounts in storage sync

This commit is contained in:
AsamK 2026-05-23 22:38:33 +02:00
parent 2a827f1285
commit 40b1928844
3 changed files with 39 additions and 20 deletions

View File

@ -2,6 +2,7 @@ package org.asamk.signal.manager.helper;
import org.asamk.signal.manager.api.GroupIdV1; import org.asamk.signal.manager.api.GroupIdV1;
import org.asamk.signal.manager.api.GroupIdV2; import org.asamk.signal.manager.api.GroupIdV2;
import org.asamk.signal.manager.api.Pair;
import org.asamk.signal.manager.api.Profile; import org.asamk.signal.manager.api.Profile;
import org.asamk.signal.manager.internal.SignalDependencies; import org.asamk.signal.manager.internal.SignalDependencies;
import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.SignalAccount;
@ -38,6 +39,7 @@ import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -211,20 +213,23 @@ public class StorageHelper {
remoteOnlyRecords.size()); remoteOnlyRecords.size());
} }
// This logic is wrong, records should only be deleted if they're deleted remotely, not if the remote record is updated final var listListPair = processKnownRecords(connection, remoteOnlyRecords);
// if (!idDifference.localOnlyIds().isEmpty()) { final var unknownInserts = listListPair.first();
// final var updated = account.getRecipientStore() final var updatedStorageIds = listListPair.second();
// .removeStorageIdsFromLocalOnlyUnregisteredRecipients(connection, final var oldUnregisteredLocalOnlyIds = new HashSet<>(idDifference.localOnlyIds());
// idDifference.localOnlyIds()); updatedStorageIds.forEach(oldUnregisteredLocalOnlyIds::remove);
// if (!idDifference.localOnlyIds().isEmpty()) {
// if (updated > 0) { final var updated = account.getRecipientStore()
// logger.warn( .removeStorageIdsFromLocalOnlyUnregisteredRecipients(connection,
// "Found {} records that were deleted remotely but only marked unregistered locally. Removed those from local store.", oldUnregisteredLocalOnlyIds);
// updated);
// } if (updated > 0) {
// } logger.warn(
// "Found {} records that were deleted remotely but only marked unregistered locally. Removed those from local store.",
final var unknownInserts = processKnownRecords(connection, remoteOnlyRecords); updated);
}
}
final var unknownDeletes = idDifference.localOnlyIds() final var unknownDeletes = idDifference.localOnlyIds()
.stream() .stream()
.filter(id -> !KNOWN_TYPES.contains(id.getType())) .filter(id -> !KNOWN_TYPES.contains(id.getType()))
@ -480,13 +485,13 @@ public class StorageHelper {
private Map<GroupIdV1, StorageId> generateGroupV1StorageIds(List<GroupIdV1> groupIds) { private Map<GroupIdV1, StorageId> generateGroupV1StorageIds(List<GroupIdV1> groupIds) {
return groupIds.stream() return groupIds.stream()
.collect(Collectors.toMap(recipientId -> recipientId, .collect(Collectors.toMap(recipientId -> recipientId,
recipientId -> StorageId.forGroupV1(KeyUtils.createRawStorageId()))); _ -> StorageId.forGroupV1(KeyUtils.createRawStorageId())));
} }
private Map<GroupIdV2, StorageId> generateGroupV2StorageIds(List<GroupIdV2> groupIds) { private Map<GroupIdV2, StorageId> generateGroupV2StorageIds(List<GroupIdV2> groupIds) {
return groupIds.stream() return groupIds.stream()
.collect(Collectors.toMap(recipientId -> recipientId, .collect(Collectors.toMap(recipientId -> recipientId,
recipientId -> StorageId.forGroupV2(KeyUtils.createRawStorageId()))); _ -> StorageId.forGroupV2(KeyUtils.createRawStorageId())));
} }
private void storeManifestLocally( private void storeManifestLocally(
@ -630,16 +635,17 @@ public class StorageHelper {
return new IdDifferenceResult(remoteOnlyKeys, localOnlyKeys, hasTypeMismatch); return new IdDifferenceResult(remoteOnlyKeys, localOnlyKeys, hasTypeMismatch);
} }
private List<StorageId> processKnownRecords( private Pair<List<StorageId>, List<StorageId>> processKnownRecords(
final Connection connection, final Connection connection,
List<SignalStorageRecord> records List<SignalStorageRecord> records
) throws SQLException { ) throws SQLException {
final var unknownRecords = new ArrayList<StorageId>(); final var unknownRecords = new ArrayList<StorageId>();
final var processedRecords = new ArrayList<StorageId>();
final var accountRecordProcessor = new AccountRecordProcessor(account, connection, context.getJobExecutor()); final var accountRecordProcessor = new AccountRecordProcessor(account, connection, context.getJobExecutor());
final var contactRecordProcessor = new ContactRecordProcessor(account, connection, context.getJobExecutor());
final var groupV1RecordProcessor = new GroupV1RecordProcessor(account, connection); final var groupV1RecordProcessor = new GroupV1RecordProcessor(account, connection);
final var groupV2RecordProcessor = new GroupV2RecordProcessor(account, connection); final var groupV2RecordProcessor = new GroupV2RecordProcessor(account, connection);
final var contactRecordProcessor = new ContactRecordProcessor(account, connection, context.getJobExecutor());
for (final var record : records) { for (final var record : records) {
if (record.getProto().account != null) { if (record.getProto().account != null) {
@ -662,8 +668,12 @@ public class StorageHelper {
unknownRecords.add(record.getId()); unknownRecords.add(record.getId());
} }
} }
processedRecords.addAll(accountRecordProcessor.getUpdatedStorageIds());
processedRecords.addAll(groupV1RecordProcessor.getUpdatedStorageIds());
processedRecords.addAll(groupV2RecordProcessor.getUpdatedStorageIds());
processedRecords.addAll(contactRecordProcessor.getUpdatedStorageIds());
return unknownRecords; return new Pair<>(unknownRecords, processedRecords);
} }
/** /**

View File

@ -878,7 +878,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
public int removeStorageIdsFromLocalOnlyUnregisteredRecipients( public int removeStorageIdsFromLocalOnlyUnregisteredRecipients(
final Connection connection, final Connection connection,
final List<StorageId> storageIds final Collection<StorageId> storageIds
) throws SQLException { ) throws SQLException {
final var sql = ( final var sql = (
""" """

View File

@ -6,7 +6,9 @@ import org.whispersystems.signalservice.api.storage.SignalRecord;
import org.whispersystems.signalservice.api.storage.StorageId; import org.whispersystems.signalservice.api.storage.StorageId;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
@ -24,6 +26,7 @@ abstract class DefaultStorageRecordProcessor<E extends SignalRecord<?>> implemen
private static final Logger logger = LoggerFactory.getLogger(DefaultStorageRecordProcessor.class); private static final Logger logger = LoggerFactory.getLogger(DefaultStorageRecordProcessor.class);
private final Set<E> matchedRecords = new TreeSet<>(this); private final Set<E> matchedRecords = new TreeSet<>(this);
private final Set<StorageId> updatedStorageIds = new HashSet<>();
/** /**
* One type of invalid remote data this handles is two records mapping to the same local data. We * One type of invalid remote data this handles is two records mapping to the same local data. We
@ -50,6 +53,7 @@ abstract class DefaultStorageRecordProcessor<E extends SignalRecord<?>> implemen
if (local.isEmpty()) { if (local.isEmpty()) {
debug(remote.getId(), remote, "[Local Insert] No matching local record. Inserting."); debug(remote.getId(), remote, "[Local Insert] No matching local record. Inserting.");
updatedStorageIds.add(remote.getId());
insertLocal(remote); insertLocal(remote);
return; return;
} }
@ -64,6 +68,7 @@ abstract class DefaultStorageRecordProcessor<E extends SignalRecord<?>> implemen
matchedRecords.add(local.get()); matchedRecords.add(local.get());
final var merged = merge(remote, local.get()); final var merged = merge(remote, local.get());
updatedStorageIds.add(merged.getId());
if (!merged.equals(remote)) { if (!merged.equals(remote)) {
debug(remote.getId(), remote, "[Remote Update] " + merged.describeDiff(remote)); debug(remote.getId(), remote, "[Remote Update] " + merged.describeDiff(remote));
} }
@ -75,6 +80,10 @@ abstract class DefaultStorageRecordProcessor<E extends SignalRecord<?>> implemen
} }
} }
public Set<StorageId> getUpdatedStorageIds() {
return Collections.unmodifiableSet(updatedStorageIds);
}
private void debug(StorageId i, E record, String message) { private void debug(StorageId i, E record, String message) {
logger.debug("[{}][{}] {}", i, record.getClass().getSimpleName(), message); logger.debug("[{}][{}] {}", i, record.getClass().getSimpleName(), message);
} }