3
0
mirror of https://github.com/Qortal/qortal.git synced 2025-02-11 17:55:50 +00:00

Delete messages from cache after 1 hour to reduce memory usage.

This would allow a duplicate message to be send again after an hour, but this is okay as it is only really designed to prevent frequent spamming of the same content.
This commit is contained in:
CalDescent 2021-08-07 15:56:08 +01:00
parent b0f963cca7
commit 88711ae018
2 changed files with 130 additions and 12 deletions

View File

@ -1,19 +1,53 @@
package org.qortal.chat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.qortal.settings.Settings;
import org.qortal.utils.NTP;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class ChatDuplicateMessageFilter {
public class ChatDuplicateMessageFilter extends Thread {
public static class SimpleChatMessage {
private long timestamp;
private String message;
public SimpleChatMessage(long timestamp, String message) {
this.timestamp = timestamp;
this.message = message;
}
public long getTimestamp() {
return this.timestamp;
}
public String getMessage() {
return this.message;
}
@Override
public boolean equals(Object other) {
if (other == this)
return true;
if (!(other instanceof SimpleChatMessage))
return false;
SimpleChatMessage otherMessage = (SimpleChatMessage) other;
return Objects.equals(this.getMessage(), otherMessage.getMessage());
}
}
private static ChatDuplicateMessageFilter instance;
private volatile boolean isStopping = false;
private static final int numberOfUniqueMessagesToMonitor = 3;
private static final int numberOfUniqueMessagesToMonitor = 3; // Only hold the last 3 messages in memory
private static final long maxMessageAge = 60 * 60 * 1000L; // Forget messages after 1 hour
// Maintain a short list of recent chat messages for each address, to save having to query the database every time
private Map<String, List<String>> recentMessages = new ConcurrentHashMap<>();
private Map<String, List<SimpleChatMessage>> recentMessages = new ConcurrentHashMap<>();
public ChatDuplicateMessageFilter() {
@ -22,23 +56,48 @@ public class ChatDuplicateMessageFilter {
public static synchronized ChatDuplicateMessageFilter getInstance() {
if (instance == null) {
instance = new ChatDuplicateMessageFilter();
instance.start();
}
return instance;
}
public boolean isDuplicateMessage(String address, String message) {
@Override
public void run() {
Thread.currentThread().setName("Duplicate Chat Message Filter");
try {
while (!isStopping) {
Thread.sleep(60000);
this.cleanup();
}
} catch (InterruptedException e) {
// Fall-through to exit thread...
}
}
public void shutdown() {
isStopping = true;
this.interrupt();
}
public boolean isDuplicateMessage(String address, long timestamp, String message) {
boolean isDuplicateMessage;
boolean messagesUpdated = false;
// Add timestamp to array for address
List<String> messages = new ArrayList<>();
SimpleChatMessage thisMessage = new SimpleChatMessage(timestamp, message);
// Add message to array for address
List<SimpleChatMessage> messages = new ArrayList<>();
if (this.recentMessages.containsKey(address)) {
messages = this.recentMessages.get(address);
}
if (!messages.contains(message)) {
messages.add(message);
// Check for duplicate, and add if unique
if (!messages.contains(thisMessage)) {
messages.add(thisMessage);
this.recentMessages.put(address, messages);
messagesUpdated = true;
isDuplicateMessage = false;
@ -54,6 +113,18 @@ public class ChatDuplicateMessageFilter {
messagesUpdated = true;
}
// Ensure we're not holding on to messages for longer than a defined time period
Iterator iterator = messages.iterator();
long now = NTP.getTime();
while (iterator.hasNext()) {
SimpleChatMessage simpleChatMessage = (SimpleChatMessage) iterator.next();
if (simpleChatMessage.getTimestamp() < now - maxMessageAge) {
// Older than tracked interval
iterator.remove();
messagesUpdated = true;
}
}
if (messagesUpdated) {
if (messages.size() > 0) {
this.recentMessages.put(address, messages);
@ -66,4 +137,51 @@ public class ChatDuplicateMessageFilter {
return isDuplicateMessage;
}
private void cleanup() {
// Cleanup map of addresses and messages
this.deleteOldMessagesForAllAddresses();
}
private void deleteOldMessagesForAddress(String address, long now) {
if (address == null) {
return;
}
if (this.recentMessages.containsKey(address)) {
boolean messagesUpdated = false;
List<SimpleChatMessage> messages = recentMessages.get(address);
// Ensure we're not holding on to messages for longer than a defined time period
Iterator iterator = messages.iterator();
while (iterator.hasNext()) {
SimpleChatMessage simpleChatMessage = (SimpleChatMessage) iterator.next();
if (simpleChatMessage.getTimestamp() < now - maxMessageAge) {
// Older than tracked interval
iterator.remove();
messagesUpdated = true;
}
}
// Update messages for address
if (messagesUpdated) {
if (messages.size() > 0) {
this.recentMessages.put(address, messages);
}
else {
this.recentMessages.remove(address);
}
}
}
}
private void deleteOldMessagesForAllAddresses() {
long now = NTP.getTime();
for (Map.Entry<String, List<SimpleChatMessage>> entry : this.recentMessages.entrySet()) {
this.deleteOldMessagesForAddress(entry.getKey(), now);
}
}
}

View File

@ -172,7 +172,7 @@ public class ChatTransaction extends Transaction {
if (!chatTransactionData.getIsEncrypted() && chatTransactionData.getIsText()) {
ChatDuplicateMessageFilter duplicateFilter = ChatDuplicateMessageFilter.getInstance();
String message58 = Base58.encode(chatTransactionData.getData());
if (duplicateFilter.isDuplicateMessage(chatTransactionData.getSender(), message58))
if (duplicateFilter.isDuplicateMessage(chatTransactionData.getSender(), chatTransactionData.getTimestamp(), message58))
return ValidationResult.DUPLICATE_MESSAGE;
}