Bound RecipientStore address cache to prevent unbounded memory growth

The recipientAddressCache grows with every unique ServiceId resolved via
findByServiceId() and entries are never evicted. In a long-running daemon
handling group messages from many contacts, this map grows monotonically.

Replace the unbounded HashMap with an LRU-bounded LinkedHashMap (access
order, max 2000 entries). Evicted entries are reloaded from SQLite on
next access via an indexed lookup, so correctness is preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHzM2XLKQoX9iraEdhoh3h
This commit is contained in:
Tony Cebzanov 2026-07-11 21:57:48 -04:00
parent 98d9960e66
commit ffbbcabf8d

View File

@ -30,6 +30,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -51,7 +52,16 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
private final Map<Long, Long> recipientsMerged = new HashMap<>();
private final Map<ServiceId, RecipientWithAddress> recipientAddressCache = Collections.synchronizedMap(new HashMap<>());
private static final int MAX_RECIPIENT_CACHE_SIZE = 2000;
private final Map<ServiceId, RecipientWithAddress> recipientAddressCache = Collections.synchronizedMap(
new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<ServiceId, RecipientWithAddress> eldest) {
return size() > MAX_RECIPIENT_CACHE_SIZE;
}
});
public static void createSql(Connection connection) throws SQLException {
// When modifying the CREATE statement here, also add a migration in AccountDatabase.java