perf (backend): store antenna cache in memory

This commit is contained in:
naskya 2024-06-06 17:10:39 +09:00
parent 7e2493b257
commit 4390dfcbfb
No known key found for this signature in database
GPG key ID: 712D413B3A9FED5C
9 changed files with 54 additions and 22 deletions

View file

@ -1352,6 +1352,7 @@ export interface Webhook {
latestStatus: number | null
}
export function updateAntennasOnNewNote(note: Note, noteAuthor: Acct, noteMutedUsers: Array<string>): Promise<void>
export function updateAntennaCache(): Promise<void>
export function watchNote(watcherId: string, noteAuthorId: string, noteId: string): Promise<void>
export function unwatchNote(watcherId: string, noteId: string): Promise<void>
export enum PushNotificationKind {

View file

@ -310,7 +310,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, fetchMeta, updateMetaCache, metaToPugArgs, loadConfig, stringToAcct, acctToString, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, Protocol, Inbound, Outbound, greet, initializeRustLogger, showServerInfo, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isQuote, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrc, DriveFileUsageHint, MutedNoteReason, NoteVisibility, NotificationType, PageVisibility, PollNoteVisibility, RelayStatus, UserEmojiModPerm, UserProfileFfvisibility, UserProfileMutingNotificationTypes, updateAntennasOnNewNote, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, ChatEvent, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, fetchMeta, updateMetaCache, metaToPugArgs, loadConfig, stringToAcct, acctToString, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, Protocol, Inbound, Outbound, greet, initializeRustLogger, showServerInfo, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isQuote, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrc, DriveFileUsageHint, MutedNoteReason, NoteVisibility, NotificationType, PageVisibility, PollNoteVisibility, RelayStatus, UserEmojiModPerm, UserProfileFfvisibility, UserProfileMutingNotificationTypes, updateAntennasOnNewNote, updateAntennaCache, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, ChatEvent, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
module.exports.SECOND = SECOND
module.exports.MINUTE = MINUTE
@ -378,6 +378,7 @@ module.exports.UserEmojiModPerm = UserEmojiModPerm
module.exports.UserProfileFfvisibility = UserProfileFfvisibility
module.exports.UserProfileMutingNotificationTypes = UserProfileMutingNotificationTypes
module.exports.updateAntennasOnNewNote = updateAntennasOnNewNote
module.exports.updateAntennaCache = updateAntennaCache
module.exports.watchNote = watchNote
module.exports.unwatchNote = unwatchNote
module.exports.PushNotificationKind = PushNotificationKind

View file

@ -0,0 +1,27 @@
//! In-memory antennas cache handler
use crate::{database::db_conn, model::entity::antenna};
use sea_orm::prelude::*;
use std::sync::Mutex;
static CACHE: Mutex<Option<Vec<antenna::Model>>> = Mutex::new(None);
fn set(antennas: &[antenna::Model]) {
let _ = CACHE
.lock()
.map(|mut cache| *cache = Some(antennas.to_owned()));
}
pub(super) async fn update() -> Result<Vec<antenna::Model>, DbErr> {
tracing::debug!("updating cache");
let antennas = antenna::Entity::find().all(db_conn().await?).await?;
set(&antennas);
Ok(antennas)
}
pub(super) async fn get() -> Result<Vec<antenna::Model>, DbErr> {
if let Some(cache) = CACHE.lock().ok().and_then(|cache| cache.clone()) {
return Ok(cache);
}
update().await
}

View file

@ -1,2 +1,4 @@
mod cache;
mod check_hit;
pub mod process_new_note;
pub mod update;

View file

@ -1,10 +1,13 @@
use crate::{
database::{cache, db_conn, redis_conn, redis_key, RedisConnError},
database::{cache, redis_conn, redis_key, RedisConnError},
federation::acct::Acct,
misc::get_note_all_texts::{all_texts, PartialNoteToElaborate},
model::entity::{antenna, note},
service::antenna::check_hit::{check_hit_antenna, AntennaCheckError},
service::stream,
model::entity::note,
service::{
antenna,
antenna::check_hit::{check_hit_antenna, AntennaCheckError},
stream,
},
util::id::{get_timestamp, InvalidIdError},
};
use redis::{streams::StreamMaxlen, AsyncCommands, RedisError};
@ -32,20 +35,6 @@ pub enum Error {
// https://github.com/napi-rs/napi-rs/issues/2060
type Note = note::Model;
// TODO?: it might be better to store this directly in memory
// (like fetch_meta) instead of Redis as it's used so much
async fn antennas() -> Result<Vec<antenna::Model>, Error> {
const CACHE_KEY: &str = "antennas";
if let Some(antennas) = cache::get::<Vec<antenna::Model>>(CACHE_KEY).await? {
Ok(antennas)
} else {
let antennas = antenna::Entity::find().all(db_conn().await?).await?;
cache::set(CACHE_KEY, &antennas, 5 * 60).await?;
Ok(antennas)
}
}
#[crate::export]
pub async fn update_antennas_on_new_note(
note: Note,
@ -67,7 +56,7 @@ pub async fn update_antennas_on_new_note(
.await?;
// TODO: do this in parallel
for antenna in antennas().await?.iter() {
for antenna in antenna::cache::get().await?.iter() {
if note_muted_users.contains(&antenna.user_id) {
continue;
}

View file

@ -0,0 +1,7 @@
//! This module is (currently) used in the TypeScript backend only.
#[crate::ts_export]
pub async fn update_antenna_cache() -> Result<(), sea_orm::DbErr> {
super::cache::update().await?;
Ok(())
}

View file

@ -1,5 +1,5 @@
import define from "@/server/api/define.js";
import { fetchMeta, genIdAt } from "backend-rs";
import { fetchMeta, genIdAt, updateAntennaCache } from "backend-rs";
import { Antennas, UserLists, UserGroupJoinings } from "@/models/index.js";
import { ApiError } from "@/server/api/error.js";
import { publishInternalEvent } from "@/services/stream.js";
@ -171,8 +171,9 @@ export default define(meta, paramDef, async (ps, user) => {
withFile: ps.withFile,
notify: ps.notify,
}).then((x) => Antennas.findOneByOrFail(x.identifiers[0]));
publishInternalEvent("antennaCreated", antenna);
await updateAntennaCache();
return await Antennas.pack(antenna);
});

View file

@ -2,6 +2,7 @@ import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js";
import { Antennas } from "@/models/index.js";
import { publishInternalEvent } from "@/services/stream.js";
import { updateAntennaCache } from "backend-rs";
export const meta = {
tags: ["antennas"],
@ -40,4 +41,5 @@ export default define(meta, paramDef, async (ps, user) => {
await Antennas.delete(antenna.id);
publishInternalEvent("antennaDeleted", antenna);
await updateAntennaCache();
});

View file

@ -2,6 +2,7 @@ import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js";
import { Antennas, UserLists, UserGroupJoinings } from "@/models/index.js";
import { publishInternalEvent } from "@/services/stream.js";
import { updateAntennaCache } from "backend-rs";
export const meta = {
tags: ["antennas"],
@ -166,6 +167,7 @@ export default define(meta, paramDef, async (ps, user) => {
"antennaUpdated",
await Antennas.findOneByOrFail({ id: antenna.id }),
);
await updateAntennaCache();
return await Antennas.pack(antenna.id);
});