mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2024-11-21 17:37:29 -07:00
[backend] Only pack each user once per request
This commit is contained in:
parent
4e6e22633e
commit
735fd37707
2 changed files with 46 additions and 6 deletions
|
@ -28,6 +28,7 @@ import {
|
|||
} from "@/misc/populate-emojis.js";
|
||||
import { db } from "@/db/postgre.js";
|
||||
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
||||
import { PackedUserCache } from "@/models/repositories/user.js";
|
||||
|
||||
export async function populatePoll(note: Note, meId: User["id"] | null) {
|
||||
const poll = await Polls.findOneByOrFail({ noteId: note.id });
|
||||
|
@ -173,6 +174,7 @@ export const NoteRepository = db.getRepository(Note).extend({
|
|||
myRenotes: Map<Note["id"], boolean>;
|
||||
};
|
||||
},
|
||||
userCache: PackedUserCache = Users.getFreshPackedUserCache(),
|
||||
): Promise<Packed<"Note">> {
|
||||
const opts = Object.assign(
|
||||
{
|
||||
|
@ -221,7 +223,7 @@ export const NoteRepository = db.getRepository(Note).extend({
|
|||
id: note.id,
|
||||
createdAt: note.createdAt.toISOString(),
|
||||
userId: note.userId,
|
||||
user: Users.pack(note.user ?? note.userId, me, {
|
||||
user: Users.packCached(note.user ?? note.userId, userCache, me, {
|
||||
detail: false,
|
||||
}),
|
||||
text: text,
|
||||
|
@ -265,14 +267,14 @@ export const NoteRepository = db.getRepository(Note).extend({
|
|||
? this.tryPack(note.reply || note.replyId, me, {
|
||||
detail: false,
|
||||
_hint_: options?._hint_,
|
||||
})
|
||||
}, userCache)
|
||||
: undefined,
|
||||
|
||||
renote: note.renoteId
|
||||
? this.pack(note.renote || note.renoteId, me, {
|
||||
detail: true,
|
||||
_hint_: options?._hint_,
|
||||
})
|
||||
}, userCache)
|
||||
: undefined,
|
||||
}
|
||||
: {}),
|
||||
|
@ -309,9 +311,10 @@ export const NoteRepository = db.getRepository(Note).extend({
|
|||
myRenotes: Map<Note["id"], boolean>;
|
||||
};
|
||||
},
|
||||
userCache: PackedUserCache = Users.getFreshPackedUserCache(),
|
||||
): Promise<Packed<"Note"> | undefined> {
|
||||
try {
|
||||
return await this.pack(src, me, options);
|
||||
return await this.pack(src, me, options, userCache);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import type { Packed } from "@/misc/schema.js";
|
|||
import type { Promiseable } from "@/prelude/await-all.js";
|
||||
import { awaitAll } from "@/prelude/await-all.js";
|
||||
import { populateEmojis } from "@/misc/populate-emojis.js";
|
||||
import { getAntennas } from "@/misc/antenna-cache.js";
|
||||
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from "@/const.js";
|
||||
import { Cache } from "@/misc/cache.js";
|
||||
import { db } from "@/db/postgre.js";
|
||||
|
@ -37,6 +36,7 @@ import {
|
|||
UserSecurityKeys,
|
||||
} from "../index.js";
|
||||
import type { Instance } from "../entities/instance.js";
|
||||
import AsyncLock from "async-lock";
|
||||
|
||||
const userInstanceCache = new Cache<Instance | null>(
|
||||
"userInstance",
|
||||
|
@ -59,6 +59,11 @@ type IsMeAndIsUserDetailed<
|
|||
|
||||
const ajv = new Ajv();
|
||||
|
||||
export type PackedUserCache = {
|
||||
locks: AsyncLock;
|
||||
results: IsMeAndIsUserDetailed<any, any>[];
|
||||
}
|
||||
|
||||
const localUsernameSchema = {
|
||||
type: "string",
|
||||
pattern: /^\w{1,20}$/.toString().slice(1, -1),
|
||||
|
@ -366,6 +371,37 @@ export const UserRepository = db.getRepository(User).extend({
|
|||
return `${config.url}/identicon/${userId}`;
|
||||
},
|
||||
|
||||
getFreshPackedUserCache(): PackedUserCache {
|
||||
return {
|
||||
locks: new AsyncLock(),
|
||||
results: [],
|
||||
};
|
||||
},
|
||||
|
||||
async packCached<
|
||||
ExpectsMe extends boolean | null = null,
|
||||
D extends boolean = false,
|
||||
>(
|
||||
src: User["id"] | User,
|
||||
cache: PackedUserCache,
|
||||
me?: { id: User["id"] } | null | undefined,
|
||||
options?: {
|
||||
detail?: D;
|
||||
includeSecrets?: boolean;
|
||||
isPrivateMode?: boolean;
|
||||
},
|
||||
): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> {
|
||||
const id = typeof src === "object" ? src.id : src;
|
||||
return cache.locks.acquire(id, async () => {
|
||||
const result = cache.results.find(p => p.id === id);
|
||||
if (result) return result as IsMeAndIsUserDetailed<ExpectsMe, D>
|
||||
return this.pack<ExpectsMe, D>(src, me, options).then(result => {
|
||||
cache.results.push(result);
|
||||
return result;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
async pack<
|
||||
ExpectsMe extends boolean | null = null,
|
||||
D extends boolean = false,
|
||||
|
@ -638,8 +674,9 @@ export const UserRepository = db.getRepository(User).extend({
|
|||
detail?: D;
|
||||
includeSecrets?: boolean;
|
||||
},
|
||||
cache?: PackedUserCache,
|
||||
): Promise<IsUserDetailed<D>[]> {
|
||||
return Promise.all(users.map((u) => this.pack(u, me, options)));
|
||||
return Promise.all(users.map((u) => this.packCached(u, cache ?? this.getFreshPackedUserCache(), me, options)));
|
||||
},
|
||||
|
||||
isLocalUser,
|
||||
|
|
Loading…
Reference in a new issue