forked from FoundKeyGang/FoundKey
server: refactor Cache to hold fetcher as attribute
Instead of having to pass the fetcher every time you want to fetch something, the fetcher is stored in an attribute of the Cache.
This commit is contained in:
parent
131c12a30b
commit
d1ec058d5c
11 changed files with 137 additions and 132 deletions
|
@ -1,10 +1,12 @@
|
||||||
export class Cache<T> {
|
export class Cache<T> {
|
||||||
public cache: Map<string | null, { date: number; value: T; }>;
|
public cache: Map<string | null, { date: number; value: T; }>;
|
||||||
private lifetime: number;
|
private lifetime: number;
|
||||||
|
public fetcher: (key: string | null) => Promise<T | undefined>;
|
||||||
|
|
||||||
constructor(lifetime: Cache<never>['lifetime']) {
|
constructor(lifetime: number, fetcher: Cache<T>['fetcher']) {
|
||||||
this.cache = new Map();
|
this.cache = new Map();
|
||||||
this.lifetime = lifetime;
|
this.lifetime = lifetime;
|
||||||
|
this.fetcher = fetcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set(key: string | null, value: T): void {
|
public set(key: string | null, value: T): void {
|
||||||
|
@ -17,10 +19,13 @@ export class Cache<T> {
|
||||||
public get(key: string | null): T | undefined {
|
public get(key: string | null): T | undefined {
|
||||||
const cached = this.cache.get(key);
|
const cached = this.cache.get(key);
|
||||||
if (cached == null) return undefined;
|
if (cached == null) return undefined;
|
||||||
|
|
||||||
|
// discard if past the cache lifetime
|
||||||
if ((Date.now() - cached.date) > this.lifetime) {
|
if ((Date.now() - cached.date) > this.lifetime) {
|
||||||
this.cache.delete(key);
|
this.cache.delete(key);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cached.value;
|
return cached.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,52 +34,22 @@ export class Cache<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
|
* If the value is cached, it is returned. Otherwise the fetcher is
|
||||||
* optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
|
* run to get the value. If the fetcher returns undefined, it is
|
||||||
|
* returned but not cached.
|
||||||
*/
|
*/
|
||||||
public async fetch(key: string | null, fetcher: () => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> {
|
public async fetch(key: string | null): Promise<T | undefined> {
|
||||||
const cachedValue = this.get(key);
|
const cached = this.get(key);
|
||||||
if (cachedValue !== undefined) {
|
if (cached !== undefined) {
|
||||||
if (validator) {
|
return cached;
|
||||||
if (validator(cachedValue)) {
|
|
||||||
// Cache HIT
|
|
||||||
return cachedValue;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Cache HIT
|
const value = await this.fetcher();
|
||||||
return cachedValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache MISS
|
// don't cache undefined
|
||||||
const value = await fetcher();
|
if (value !== undefined)
|
||||||
this.set(key, value);
|
this.set(key, value);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
|
|
||||||
* optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
|
|
||||||
*/
|
|
||||||
public async fetchMaybe(key: string | null, fetcher: () => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> {
|
|
||||||
const cachedValue = this.get(key);
|
|
||||||
if (cachedValue !== undefined) {
|
|
||||||
if (validator) {
|
|
||||||
if (validator(cachedValue)) {
|
|
||||||
// Cache HIT
|
|
||||||
return cachedValue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Cache HIT
|
|
||||||
return cachedValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache MISS
|
|
||||||
const value = await fetcher();
|
|
||||||
if (value !== undefined) {
|
|
||||||
this.set(key, value);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,22 +3,26 @@ import { Note } from '@/models/entities/note.js';
|
||||||
import { User } from '@/models/entities/user.js';
|
import { User } from '@/models/entities/user.js';
|
||||||
import { UserListJoinings, UserGroupJoinings, Blockings } from '@/models/index.js';
|
import { UserListJoinings, UserGroupJoinings, Blockings } from '@/models/index.js';
|
||||||
import * as Acct from '@/misc/acct.js';
|
import * as Acct from '@/misc/acct.js';
|
||||||
|
import { MINUTE } from '@/const.js';
|
||||||
import { getFullApAccount } from './convert-host.js';
|
import { getFullApAccount } from './convert-host.js';
|
||||||
import { Packed } from './schema.js';
|
import { Packed } from './schema.js';
|
||||||
import { Cache } from './cache.js';
|
import { Cache } from './cache.js';
|
||||||
|
|
||||||
const blockingCache = new Cache<User['id'][]>(1000 * 60 * 5);
|
const blockingCache = new Cache<User['id'][]>(
|
||||||
|
5 * MINUTE,
|
||||||
|
(blockerId) => Blockings.findBy({ blockerId }).then(res => res.map(x => x.blockeeId)),
|
||||||
|
);
|
||||||
|
|
||||||
// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている
|
// designation for users you follow, list users and groups is disabled for performance reasons
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* noteUserFollowers / antennaUserFollowing はどちらか一方が指定されていればよい
|
* either noteUserFollowers or antennaUserFollowing must be specified
|
||||||
*/
|
*/
|
||||||
export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise<boolean> {
|
export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise<boolean> {
|
||||||
if (note.visibility === 'specified') return false;
|
if (note.visibility === 'specified') return false;
|
||||||
|
|
||||||
// アンテナ作成者がノート作成者にブロックされていたらスキップ
|
// skip if the antenna creator is blocked by the note author
|
||||||
const blockings = await blockingCache.fetch(noteUser.id, () => Blockings.findBy({ blockerId: noteUser.id }).then(res => res.map(x => x.blockeeId)));
|
const blockings = await blockingCache.fetch(noteUser.id);
|
||||||
if (blockings.some(blocking => blocking === antenna.userId)) return false;
|
if (blockings.some(blocking => blocking === antenna.userId)) return false;
|
||||||
|
|
||||||
if (note.visibility === 'followers') {
|
if (note.visibility === 'followers') {
|
||||||
|
|
|
@ -3,8 +3,11 @@ import { User } from '@/models/entities/user.js';
|
||||||
import { UserKeypair } from '@/models/entities/user-keypair.js';
|
import { UserKeypair } from '@/models/entities/user-keypair.js';
|
||||||
import { Cache } from './cache.js';
|
import { Cache } from './cache.js';
|
||||||
|
|
||||||
const cache = new Cache<UserKeypair>(Infinity);
|
const cache = new Cache<UserKeypair>(
|
||||||
|
Infinity,
|
||||||
|
(userId) => UserKeypairs.findOneByOrFail({ userId }),
|
||||||
|
);
|
||||||
|
|
||||||
export async function getUserKeypair(userId: User['id']): Promise<UserKeypair> {
|
export async function getUserKeypair(userId: User['id']): Promise<UserKeypair> {
|
||||||
return await cache.fetch(userId, () => UserKeypairs.findOneByOrFail({ userId }));
|
return await cache.fetch(userId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,24 @@ import { Emojis } from '@/models/index.js';
|
||||||
import { Emoji } from '@/models/entities/emoji.js';
|
import { Emoji } from '@/models/entities/emoji.js';
|
||||||
import { Note } from '@/models/entities/note.js';
|
import { Note } from '@/models/entities/note.js';
|
||||||
import { query } from '@/prelude/url.js';
|
import { query } from '@/prelude/url.js';
|
||||||
|
import { HOUR } from '@/const.js';
|
||||||
import { Cache } from './cache.js';
|
import { Cache } from './cache.js';
|
||||||
import { isSelfHost, toPunyNullable } from './convert-host.js';
|
import { isSelfHost, toPunyNullable } from './convert-host.js';
|
||||||
import { decodeReaction } from './reaction-lib.js';
|
import { decodeReaction } from './reaction-lib.js';
|
||||||
|
|
||||||
const cache = new Cache<Emoji | null>(1000 * 60 * 60 * 12);
|
/**
|
||||||
|
* composite cache key: `${host ?? ''}:${name}`
|
||||||
|
*/
|
||||||
|
const cache = new Cache<Emoji | null>(
|
||||||
|
12 * HOUR,
|
||||||
|
async (key) => {
|
||||||
|
const [host, name] = key.split(':');
|
||||||
|
return (await Emojis.findOneBy({
|
||||||
|
name,
|
||||||
|
host: host || IsNull(),
|
||||||
|
})) || null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information needed to attach in ActivityPub
|
* Information needed to attach in ActivityPub
|
||||||
|
@ -51,12 +64,7 @@ export async function populateEmoji(emojiName: string, noteUserHost: string | nu
|
||||||
const { name, host } = parseEmojiStr(emojiName, noteUserHost);
|
const { name, host } = parseEmojiStr(emojiName, noteUserHost);
|
||||||
if (name == null) return null;
|
if (name == null) return null;
|
||||||
|
|
||||||
const queryOrNull = async () => (await Emojis.findOneBy({
|
const emoji = await cache.fetch(`${host ?? ''}:${name}`);
|
||||||
name,
|
|
||||||
host: host ?? IsNull(),
|
|
||||||
})) || null;
|
|
||||||
|
|
||||||
const emoji = await cache.fetch(`${name} ${host}`, queryOrNull);
|
|
||||||
|
|
||||||
if (emoji == null) return null;
|
if (emoji == null) return null;
|
||||||
|
|
||||||
|
@ -105,7 +113,10 @@ export function aggregateNoteEmojis(notes: Note[]) {
|
||||||
* Query list of emojis in bulk and add them to the cache.
|
* Query list of emojis in bulk and add them to the cache.
|
||||||
*/
|
*/
|
||||||
export async function prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise<void> {
|
export async function prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise<void> {
|
||||||
const notCachedEmojis = emojis.filter(emoji => cache.get(`${emoji.name} ${emoji.host}`) == null);
|
const notCachedEmojis = emojis.filter(emoji => {
|
||||||
|
// check if the cache has this emoji
|
||||||
|
return cache.get(`${emoji.host ?? ''}:${emoji.name}`) == null;
|
||||||
|
});
|
||||||
|
|
||||||
// check if there even are any uncached emoji to handle
|
// check if there even are any uncached emoji to handle
|
||||||
if (notCachedEmojis.length === 0) return;
|
if (notCachedEmojis.length === 0) return;
|
||||||
|
@ -127,7 +138,7 @@ export async function prefetchEmojis(emojis: { name: string; host: string | null
|
||||||
}).then(emojis => {
|
}).then(emojis => {
|
||||||
// store all emojis into the cache
|
// store all emojis into the cache
|
||||||
emojis.forEach(emoji => {
|
emojis.forEach(emoji => {
|
||||||
cache.set(`${emoji.name} ${emoji.host}`, emoji);
|
cache.set(`${emoji.host ?? ''}:${emoji.name}`, emoji);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,16 @@ import { Packed } from '@/misc/schema.js';
|
||||||
import { awaitAll, Promiseable } from '@/prelude/await-all.js';
|
import { awaitAll, Promiseable } from '@/prelude/await-all.js';
|
||||||
import { populateEmojis } from '@/misc/populate-emojis.js';
|
import { populateEmojis } from '@/misc/populate-emojis.js';
|
||||||
import { getAntennas } from '@/misc/antenna-cache.js';
|
import { getAntennas } from '@/misc/antenna-cache.js';
|
||||||
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
|
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD, HOUR } from '@/const.js';
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
import { db } from '@/db/postgre.js';
|
import { db } from '@/db/postgre.js';
|
||||||
import { Instance } from '../entities/instance.js';
|
import { Instance } from '../entities/instance.js';
|
||||||
import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, RenoteMutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, AntennaNotes, ChannelFollowings, Instances, DriveFiles } from '../index.js';
|
import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, RenoteMutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, AntennaNotes, ChannelFollowings, Instances, DriveFiles } from '../index.js';
|
||||||
|
|
||||||
const userInstanceCache = new Cache<Instance | null>(1000 * 60 * 60 * 3);
|
const userInstanceCache = new Cache<Instance | null>(
|
||||||
|
3 * HOUR,
|
||||||
|
(host) => Instances.findOneBy({ host }).then(x => x ?? undefined),
|
||||||
|
);
|
||||||
|
|
||||||
type IsUserDetailed<Detailed extends boolean> = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>;
|
type IsUserDetailed<Detailed extends boolean> = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>;
|
||||||
type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends boolean> =
|
type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends boolean> =
|
||||||
|
@ -309,17 +312,15 @@ export const UserRepository = db.getRepository(User).extend({
|
||||||
isModerator: user.isModerator || falsy,
|
isModerator: user.isModerator || falsy,
|
||||||
isBot: user.isBot || falsy,
|
isBot: user.isBot || falsy,
|
||||||
isCat: user.isCat || falsy,
|
isCat: user.isCat || falsy,
|
||||||
instance: user.host ? userInstanceCache.fetch(user.host,
|
instance: !user.host ? undefined : userInstanceCache.fetch(user.host)
|
||||||
() => Instances.findOneBy({ host: user.host! }),
|
.then(instance => !instance ? undefined : {
|
||||||
v => v != null,
|
|
||||||
).then(instance => instance ? {
|
|
||||||
name: instance.name,
|
name: instance.name,
|
||||||
softwareName: instance.softwareName,
|
softwareName: instance.softwareName,
|
||||||
softwareVersion: instance.softwareVersion,
|
softwareVersion: instance.softwareVersion,
|
||||||
iconUrl: instance.iconUrl,
|
iconUrl: instance.iconUrl,
|
||||||
faviconUrl: instance.faviconUrl,
|
faviconUrl: instance.faviconUrl,
|
||||||
themeColor: instance.themeColor,
|
themeColor: instance.themeColor,
|
||||||
} : undefined) : undefined,
|
}),
|
||||||
emojis: populateEmojis(user.emojis, user.host),
|
emojis: populateEmojis(user.emojis, user.host),
|
||||||
onlineStatus: this.getOnlineStatus(user),
|
onlineStatus: this.getOnlineStatus(user),
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,14 @@ import { uriPersonCache, userByIdCache } from '@/services/user-cache.js';
|
||||||
import { IObject, getApId } from './type.js';
|
import { IObject, getApId } from './type.js';
|
||||||
import { resolvePerson } from './models/person.js';
|
import { resolvePerson } from './models/person.js';
|
||||||
|
|
||||||
const publicKeyCache = new Cache<UserPublickey | null>(Infinity);
|
const publicKeyCache = new Cache<UserPublickey>(
|
||||||
const publicKeyByUserIdCache = new Cache<UserPublickey | null>(Infinity);
|
Infinity,
|
||||||
|
(keyId) => UserPublickeys.findOneBy({ keyId }).then(x => x ?? undefined),
|
||||||
|
);
|
||||||
|
const publicKeyByUserIdCache = new Cache<UserPublickey>(
|
||||||
|
Infinity,
|
||||||
|
(userId) => UserPublickeys.findOneBy({ userId }).then(x => x ?? undefined),
|
||||||
|
);
|
||||||
|
|
||||||
export type UriParseResult = {
|
export type UriParseResult = {
|
||||||
/** wether the URI was generated by us */
|
/** wether the URI was generated by us */
|
||||||
|
@ -99,13 +105,9 @@ export default class DbResolver {
|
||||||
if (parsed.local) {
|
if (parsed.local) {
|
||||||
if (parsed.type !== 'users') return null;
|
if (parsed.type !== 'users') return null;
|
||||||
|
|
||||||
return await userByIdCache.fetchMaybe(parsed.id, () => Users.findOneBy({
|
return await userByIdCache.fetch(parsed.id) ?? null;
|
||||||
id: parsed.id,
|
|
||||||
}).then(x => x ?? undefined)) ?? null;
|
|
||||||
} else {
|
} else {
|
||||||
return await uriPersonCache.fetch(parsed.uri, () => Users.findOneBy({
|
return await uriPersonCache.fetch(parsed.uri) ?? null;
|
||||||
uri: parsed.uri,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,20 +118,12 @@ export default class DbResolver {
|
||||||
user: CacheableRemoteUser;
|
user: CacheableRemoteUser;
|
||||||
key: UserPublickey;
|
key: UserPublickey;
|
||||||
} | null> {
|
} | null> {
|
||||||
const key = await publicKeyCache.fetch(keyId, async () => {
|
const key = await publicKeyCache.fetch(keyId);
|
||||||
const key = await UserPublickeys.findOneBy({
|
|
||||||
keyId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (key == null) return null;
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}, key => key != null);
|
|
||||||
|
|
||||||
if (key == null) return null;
|
if (key == null) return null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user: await userByIdCache.fetch(key.userId, () => Users.findOneByOrFail({ id: key.userId })) as CacheableRemoteUser,
|
user: await userByIdCache.fetch(key.userId) as CacheableRemoteUser,
|
||||||
key,
|
key,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -145,7 +139,7 @@ export default class DbResolver {
|
||||||
|
|
||||||
if (user == null) return null;
|
if (user == null) return null;
|
||||||
|
|
||||||
const key = await publicKeyByUserIdCache.fetch(user.id, () => UserPublickeys.findOneBy({ userId: user.id }), v => v != null);
|
const key = await publicKeyByUserIdCache.fetch(user.id);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
|
|
|
@ -6,7 +6,10 @@ import { App } from '@/models/entities/app.js';
|
||||||
import { userByIdCache, localUserByNativeTokenCache } from '@/services/user-cache.js';
|
import { userByIdCache, localUserByNativeTokenCache } from '@/services/user-cache.js';
|
||||||
import isNativeToken from './common/is-native-token.js';
|
import isNativeToken from './common/is-native-token.js';
|
||||||
|
|
||||||
const appCache = new Cache<App>(Infinity);
|
const appCache = new Cache<App>(
|
||||||
|
Infinity,
|
||||||
|
(id) => Apps.findOneByOrFail({ id }),
|
||||||
|
);
|
||||||
|
|
||||||
export class AuthenticationError extends Error {
|
export class AuthenticationError extends Error {
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
|
@ -39,8 +42,7 @@ export default async (authorization: string | null | undefined, bodyToken: strin
|
||||||
const token: string = maybeToken;
|
const token: string = maybeToken;
|
||||||
|
|
||||||
if (isNativeToken(token)) {
|
if (isNativeToken(token)) {
|
||||||
const user = await localUserByNativeTokenCache.fetch(token,
|
const user = await localUserByNativeTokenCache.fetch(token);
|
||||||
() => Users.findOneBy({ token }) as Promise<ILocalUser | null>);
|
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new AuthenticationError('unknown token');
|
throw new AuthenticationError('unknown token');
|
||||||
|
@ -64,17 +66,13 @@ export default async (authorization: string | null | undefined, bodyToken: strin
|
||||||
lastUsedAt: new Date(),
|
lastUsedAt: new Date(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const user = await userByIdCache.fetch(accessToken.userId,
|
const user = await userByIdCache.fetch(accessToken.userId);
|
||||||
() => Users.findOneBy({
|
|
||||||
id: accessToken.userId,
|
|
||||||
}) as Promise<ILocalUser>);
|
|
||||||
|
|
||||||
// can't authorize remote users
|
// can't authorize remote users
|
||||||
if (!Users.isLocalUser(user)) return [null, null];
|
if (!Users.isLocalUser(user)) return [null, null];
|
||||||
|
|
||||||
if (accessToken.appId) {
|
if (accessToken.appId) {
|
||||||
const app = await appCache.fetch(accessToken.appId,
|
const app = await appCache.fetch(accessToken.appId);
|
||||||
() => Apps.findOneByOrFail({ id: accessToken.appId! }));
|
|
||||||
|
|
||||||
return [user, {
|
return [user, {
|
||||||
id: accessToken.id,
|
id: accessToken.id,
|
||||||
|
|
|
@ -36,13 +36,22 @@ import { Cache } from '@/misc/cache.js';
|
||||||
import { UserProfile } from '@/models/entities/user-profile.js';
|
import { UserProfile } from '@/models/entities/user-profile.js';
|
||||||
import { getActiveWebhooks } from '@/misc/webhook-cache.js';
|
import { getActiveWebhooks } from '@/misc/webhook-cache.js';
|
||||||
import { IActivity } from '@/remote/activitypub/type.js';
|
import { IActivity } from '@/remote/activitypub/type.js';
|
||||||
|
import { MINUTE } from '@/const.js';
|
||||||
import { updateHashtags } from '../update-hashtag.js';
|
import { updateHashtags } from '../update-hashtag.js';
|
||||||
import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js';
|
import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js';
|
||||||
import { createNotification } from '../create-notification.js';
|
import { createNotification } from '../create-notification.js';
|
||||||
import { addNoteToAntenna } from '../add-note-to-antenna.js';
|
import { addNoteToAntenna } from '../add-note-to-antenna.js';
|
||||||
import { deliverToRelays } from '../relay.js';
|
import { deliverToRelays } from '../relay.js';
|
||||||
|
|
||||||
const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5);
|
const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(
|
||||||
|
5 * MINUTE,
|
||||||
|
() => UserProfiles.find({
|
||||||
|
where: {
|
||||||
|
enableWordMute: true,
|
||||||
|
},
|
||||||
|
select: ['userId', 'mutedWords'],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
|
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
|
||||||
|
|
||||||
|
@ -257,12 +266,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
incNotesCountOfUser(user);
|
incNotesCountOfUser(user);
|
||||||
|
|
||||||
// Word mute
|
// Word mute
|
||||||
mutedWordsCache.fetch(null, () => UserProfiles.find({
|
mutedWordsCache.fetch(null).then(us => {
|
||||||
where: {
|
|
||||||
enableWordMute: true,
|
|
||||||
},
|
|
||||||
select: ['userId', 'mutedWords'],
|
|
||||||
})).then(us => {
|
|
||||||
for (const u of us) {
|
for (const u of us) {
|
||||||
checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => {
|
checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => {
|
||||||
if (shouldMute) {
|
if (shouldMute) {
|
||||||
|
|
|
@ -3,18 +3,20 @@ import { Instances } from '@/models/index.js';
|
||||||
import { genId } from '@/misc/gen-id.js';
|
import { genId } from '@/misc/gen-id.js';
|
||||||
import { toPuny } from '@/misc/convert-host.js';
|
import { toPuny } from '@/misc/convert-host.js';
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
|
import { HOUR } from '@/const.js';
|
||||||
|
|
||||||
const cache = new Cache<Instance>(1000 * 60 * 60);
|
const cache = new Cache<Instance>(
|
||||||
|
HOUR,
|
||||||
|
(host) => Instances.findOneBy({ host }).then(x => x ?? undefined),
|
||||||
|
);
|
||||||
|
|
||||||
export async function registerOrFetchInstanceDoc(idnHost: string): Promise<Instance> {
|
export async function registerOrFetchInstanceDoc(idnHost: string): Promise<Instance> {
|
||||||
const host = toPuny(idnHost);
|
const host = toPuny(idnHost);
|
||||||
|
|
||||||
const cached = cache.get(host);
|
const cached = cache.fetch(host);
|
||||||
if (cached) return cached;
|
if (cached) return cached;
|
||||||
|
|
||||||
const index = await Instances.findOneBy({ host });
|
// apparently a new instance
|
||||||
|
|
||||||
if (index == null) {
|
|
||||||
const i = await Instances.insert({
|
const i = await Instances.insert({
|
||||||
id: genId(),
|
id: genId(),
|
||||||
host,
|
host,
|
||||||
|
@ -24,8 +26,4 @@ export async function registerOrFetchInstanceDoc(idnHost: string): Promise<Insta
|
||||||
|
|
||||||
cache.set(host, i);
|
cache.set(host, i);
|
||||||
return i;
|
return i;
|
||||||
} else {
|
|
||||||
cache.set(host, index);
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,21 @@ import { Users, Relays } from '@/models/index.js';
|
||||||
import { genId } from '@/misc/gen-id.js';
|
import { genId } from '@/misc/gen-id.js';
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
import { Relay } from '@/models/entities/relay.js';
|
import { Relay } from '@/models/entities/relay.js';
|
||||||
|
import { MINUTE } from '@/const.js';
|
||||||
import { createSystemUser } from './create-system-user.js';
|
import { createSystemUser } from './create-system-user.js';
|
||||||
|
|
||||||
const ACTOR_USERNAME = 'relay.actor' as const;
|
const ACTOR_USERNAME = 'relay.actor' as const;
|
||||||
|
|
||||||
const relaysCache = new Cache<Relay[]>(1000 * 60 * 10);
|
/**
|
||||||
|
* There is only one cache key: null.
|
||||||
|
* A cache is only used here to have expiring storage.
|
||||||
|
*/
|
||||||
|
const relaysCache = new Cache<Relay[]>(
|
||||||
|
10 * MINUTE,
|
||||||
|
() => Relays.findBy({
|
||||||
|
status: 'accepted',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export async function getRelayActor(): Promise<ILocalUser> {
|
export async function getRelayActor(): Promise<ILocalUser> {
|
||||||
const user = await Users.findOneBy({
|
const user = await Users.findOneBy({
|
||||||
|
@ -83,9 +93,7 @@ export async function relayRejected(id: string) {
|
||||||
export async function deliverToRelays(user: { id: User['id']; host: null; }, activity: any) {
|
export async function deliverToRelays(user: { id: User['id']; host: null; }, activity: any) {
|
||||||
if (activity == null) return;
|
if (activity == null) return;
|
||||||
|
|
||||||
const relays = await relaysCache.fetch(null, () => Relays.findBy({
|
const relays = await relaysCache.fetch(null);
|
||||||
status: 'accepted',
|
|
||||||
}));
|
|
||||||
if (relays.length === 0) return;
|
if (relays.length === 0) return;
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
|
|
@ -3,9 +3,18 @@ import { Users } from '@/models/index.js';
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
import { subscriber } from '@/db/redis.js';
|
import { subscriber } from '@/db/redis.js';
|
||||||
|
|
||||||
export const userByIdCache = new Cache<CacheableUser>(Infinity);
|
export const userByIdCache = new Cache<CacheableUser>(
|
||||||
export const localUserByNativeTokenCache = new Cache<CacheableLocalUser | null>(Infinity);
|
Infinity,
|
||||||
export const uriPersonCache = new Cache<CacheableUser | null>(Infinity);
|
(id) => Users.findOneBy({ id }).then(x => x ?? undefined),
|
||||||
|
);
|
||||||
|
export const localUserByNativeTokenCache = new Cache<CacheableLocalUser>(
|
||||||
|
Infinity,
|
||||||
|
(token) => Users.findOneBy({ token }).then(x => x ?? undefined),
|
||||||
|
);
|
||||||
|
export const uriPersonCache = new Cache<CacheableUser>(
|
||||||
|
Infinity,
|
||||||
|
(uri) => Users.findOneBy({ uri }).then(x => x ?? undefined),
|
||||||
|
);
|
||||||
|
|
||||||
subscriber.on('message', async (_, data) => {
|
subscriber.on('message', async (_, data) => {
|
||||||
const obj = JSON.parse(data);
|
const obj = JSON.parse(data);
|
||||||
|
|
Loading…
Reference in a new issue