cordis/client/webapp/message-ram-cache.ts
2021-10-30 12:26:41 -05:00

135 lines
4.5 KiB
TypeScript

import * as electronRemote from '@electron/remote';
const electronConsole = electronRemote.getGlobal('console') as Console;
import Logger from '../../logger/logger';
const LOG = new Logger(__filename, electronConsole);
import Globals from './globals';
import { ShouldNeverHappenError } from './data-types';
// TODO: make this non-static
export default class RecentMessageRAMCache {
_data = new Map(); // (serverId, channelId) -> { messages, size, hasFirstMessage, lastUsed }
_size = 0;
clear(): void {
this._data.clear();
this._size = 0;
}
_cullIfNeeded(): void { // TODO: Test this
if (this._size > Globals.MAX_RAM_CACHED_MESSAGES_TOTAL_SIZE) {
let entries = Array.from(this._data.entries())
.map(([ key, value ]) => { return { id: key, value: value }})
.sort((a, b) => b.value.lastUsed - a.value.lastUsed);
while (this._size > Globals.MAX_RAM_CACHED_MESSAGES_TOTAL_SIZE) {
let entry = entries.pop();
if (entry === undefined) throw new ShouldNeverHappenError('No entry in the array but the message cache still has a size...');
this._data.delete(entry.id);
this._size -= entry.value.size;
}
}
}
_cullChannelIfNeeded(serverId: string, channelId: string): void { // TODO: test this
let id = `s#${serverId}/c#${channelId}`;
let value = this._data.get(id);
while (value.size > Globals.MAX_RAM_CACHED_MESSAGES_CHANNEL_SIZE) {
value.hasFirstMessage = false;
let message = value.messages.shift();
value.size -= message.text.length;
this._size -= message.text.length;
}
}
// @param messages may be modified in addNewMessage due to pass-by-reference fun
putRecentMessages(serverId: string, channelId: string, messages: any[]): void {
let size = 0;
for (let message of messages) {
size += message.text.length;
}
if (size > Globals.MAX_RAM_CACHED_MESSAGES_CHANNEL_SIZE) return;
let id = `s#${serverId}/c#${channelId}`;
this._data.set(id, {
messages: messages,
size: size,
hasFirstMessage: true, // will be false if this was ever channel-culled
lastUsed: new Date()
});
this._size += size;
this._cullIfNeeded();
}
addNewMessage(serverId: string, message: any): void {
let channelId = message.channel.id;
let id = `s#${serverId}/c#${channelId}`;
if (!this._data.has(id)) return;
let value = this._data.get(id);
value.messages.push(message);
value.size += message.text.length;
this._size += message.text.length;
this._cullChannelIfNeeded(serverId, channelId);
this._cullIfNeeded();
}
updateMessage(serverId: string, oldMessage: any, newMessage: any): void {
let channelId = oldMessage.channel.id;
let id = `s#${serverId}/c#${channelId}`;
if (!this._data.has(id)) return;
let value = this._data.get(id);
let taggedIndex = value.messages.findIndex(cachedMessage => cachedMessage.id == oldMessage.id);
let [ oldCachedMessage ] = value.messages.splice(taggedIndex, 1, newMessage);
value.size -= oldCachedMessage.text.length;
value.size += newMessage.text.length;
this._size -= oldCachedMessage.text.length;
this._size += newMessage.text.length;
this._cullChannelIfNeeded(serverId, channelId);
this._cullIfNeeded();
}
deleteMessage(serverId: string, message: any): void {
let channelId = message.channel.id;
let id = `s#${serverId}/c#${channelId}`;
if (!this._data.has(id)) return;
let value = this._data.get(id);
let taggedIndex = value.messages.findIndex(cachedMessage => cachedMessage.id == message.id);
let [ oldCachedMessage ] = value.messages.splice(taggedIndex, 1);
value.size -= oldCachedMessage.text.length;
this._size -= oldCachedMessage.text.length;
// No need to cull since we are always shrinking
}
dropChannel(serverId: string, channelId: string): void {
let id = `s#${serverId}/c#${channelId}`;
if (!this._data.has(id)) return;
let value = this._data.get(id);
this._size -= value.size;
this._data.delete(id);
}
getRecentMessages(serverId: string, channelId: string, number: number): any | null {
let id = `s#${serverId}/c#${channelId}`;
if (!this._data.has(id)) {
//LOG.silly(`recent message cache miss on ${id} requesting ${number}`);
return null; // not in the cache
}
let value = this._data.get(id);
if (!value.hasFirstMessage && value.messages.length < number) {
//LOG.silly(`recent message cache incomplete on ${id} requesting ${number}`, { value });
return null; // requesting older messages than we have
}
value.lastUsed = new Date();
return value.messages.slice(-number);
}
}