WebSocketのmainStreamに流れるmeUpdatedイベントでアップデート後のアカウント情報を流さないように

フロントエンドが`/api/i`を呼び出すことでアカウント情報を得るようにした。これにより

- Redis PubSubストリーム内を巨大なデータが行き来する回数を減らしパフォーマンスが向上することが考えられる
- 各`publish`処理が正確なアカウント情報を得るために`UserEntityService`に依存する状況を解消できる

特に後者は重要で、`UserEntityService`という巨大なサービスへの依存を減らせるのはコードの見通しもよくなる。現に`AccountMoveService`は`UserEntityService`への依存のせいで、`ApPersonService`と合わせて循環依存を作ってしまっている。これが解消できるのは大きい。

(cherry picked from commit 0d4cf43e61887c110a840f108ea5fea9afb1263b)
This commit is contained in:
okayurisotto 2024-11-19 00:21:12 +09:00 committed by kakkokari-gtyih
parent 8076f78d06
commit a32092a8b2
13 changed files with 28 additions and 56 deletions

View file

@ -96,7 +96,7 @@ export class AccountMoveService {
// Publish meUpdated event
const iObj = await this.userEntityService.pack(src.id, src, { schema: 'MeDetailed', includeSecrets: true });
this.globalEventService.publishMainStream(src.id, 'meUpdated', iObj);
this.globalEventService.publishMainStream(src.id, 'meUpdated');
// Unfollow after 24 hours
const followings = await this.followingsRepository.findBy({

View file

@ -57,7 +57,7 @@ export interface MainEventTypes {
follow: Packed<'UserDetailedNotMe'>;
followed: Packed<'UserLite'>;
unfollow: Packed<'UserDetailedNotMe'>;
meUpdated: Packed<'MeDetailed'>;
meUpdated: undefined;
pageEvent: {
pageId: MiPage['id'];
event: string;

View file

@ -544,9 +544,7 @@ export class UserFollowingService implements OnModuleInit {
if (this.userEntityService.isLocalUser(followee)) {
this.userEntityService.pack(follower.id, followee).then(packed => this.globalEventService.publishMainStream(followee.id, 'receiveFollowRequest', packed));
this.userEntityService.pack(followee.id, followee, {
schema: 'MeDetailed',
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
this.globalEventService.publishMainStream(followee.id, 'meUpdated');
// 通知を作成
this.notificationService.createNotification(followee.id, 'receiveFollowRequest', {
@ -592,9 +590,7 @@ export class UserFollowingService implements OnModuleInit {
followerId: follower.id,
});
this.userEntityService.pack(followee.id, followee, {
schema: 'MeDetailed',
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
this.globalEventService.publishMainStream(followee.id, 'meUpdated');
}
@bindThis
@ -619,9 +615,7 @@ export class UserFollowingService implements OnModuleInit {
this.deliverAccept(follower, followee as MiPartialLocalUser, request.requestId ?? undefined);
}
this.userEntityService.pack(followee.id, followee, {
schema: 'MeDetailed',
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
this.globalEventService.publishMainStream(followee.id, 'meUpdated');
}
@bindThis

View file

@ -6,7 +6,6 @@
import * as OTPAuth from 'otpauth';
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { UserProfilesRepository } from '@/models/_.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
@ -44,7 +43,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
private userEntityService: UserEntityService,
private globalEventService: GlobalEventService,
) {
super(meta, paramDef, async (ps, me) => {
@ -76,10 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
schema: 'MeDetailed',
includeSecrets: true,
}));
this.globalEventService.publishMainStream(me.id, 'meUpdated');
return {
backupCodes: backupCodes,

View file

@ -6,7 +6,6 @@
import bcrypt from 'bcryptjs';
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DI } from '@/di-symbols.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/_.js';
@ -67,7 +66,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private webAuthnService: WebAuthnService,
private userAuthService: UserAuthService,
private userEntityService: UserEntityService,
private globalEventService: GlobalEventService,
) {
super(meta, paramDef, async (ps, me) => {
@ -110,10 +108,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
});
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
schema: 'MeDetailed',
includeSecrets: true,
}));
this.globalEventService.publishMainStream(me.id, 'meUpdated');
return {
id: keyId,

View file

@ -5,7 +5,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/_.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
@ -42,7 +41,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.userSecurityKeysRepository)
private userSecurityKeysRepository: UserSecurityKeysRepository,
private userEntityService: UserEntityService,
private globalEventService: GlobalEventService,
) {
super(meta, paramDef, async (ps, me) => {
@ -73,10 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
schema: 'MeDetailed',
includeSecrets: true,
}));
this.globalEventService.publishMainStream(me.id, 'meUpdated');
});
}
}

View file

@ -7,7 +7,6 @@ import bcrypt from 'bcryptjs';
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/_.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '@/server/api/error.js';
@ -46,7 +45,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
private userEntityService: UserEntityService,
private userAuthService: UserAuthService,
private globalEventService: GlobalEventService,
) {
@ -96,10 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
schema: 'MeDetailed',
includeSecrets: true,
}));
this.globalEventService.publishMainStream(me.id, 'meUpdated');
return {};
});

View file

@ -6,7 +6,6 @@
import bcrypt from 'bcryptjs';
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { UserProfilesRepository } from '@/models/_.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
@ -42,7 +41,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
private userEntityService: UserEntityService,
private userAuthService: UserAuthService,
private globalEventService: GlobalEventService,
) {
@ -75,10 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
schema: 'MeDetailed',
includeSecrets: true,
}));
this.globalEventService.publishMainStream(me.id, 'meUpdated');
});
}
}

View file

@ -7,7 +7,6 @@ import bcrypt from 'bcryptjs';
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { UserSecurityKeysRepository } from '@/models/_.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../../error.js';
@ -47,7 +46,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.userSecurityKeysRepository)
private userSecurityKeysRepository: UserSecurityKeysRepository,
private userEntityService: UserEntityService,
private globalEventService: GlobalEventService,
) {
super(meta, paramDef, async (ps, me) => {
@ -68,10 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
schema: 'MeDetailed',
includeSecrets: true,
}));
this.globalEventService.publishMainStream(me.id, 'meUpdated');
return {};
});

View file

@ -122,7 +122,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', iObj);
this.globalEventService.publishMainStream(me.id, 'meUpdated');
if (ps.email != null) {
const code = secureRndstr(16, { chars: L_CHARS });

View file

@ -527,7 +527,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
this.cacheService.userProfileCache.set(user.id, updatedProfile);
// Publish meUpdated event
this.globalEventService.publishMainStream(user.id, 'meUpdated', iObj);
this.globalEventService.publishMainStream(user.id, 'meUpdated');
// 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認
if (user.isLocked && ps.isLocked === false) {

View file

@ -22,7 +22,7 @@ type Account = Misskey.entities.MeDetailed & { token: string };
const accountData = miLocalStorage.getItem('account');
// TODO: 外部からはreadonlyに
export const $i = accountData ? reactive(JSON.parse(accountData) as Account) : null;
export let $i = accountData ? reactive(JSON.parse(accountData) as Account) : null;
export const iAmModerator = $i != null && ($i.isAdmin === true || $i.isModerator === true);
export const iAmAdmin = $i != null && $i.isAdmin;
@ -178,10 +178,18 @@ export function updateAccount(accountData: Account) {
export function updateAccountPartial(accountData: Partial<Account>) {
if (!$i) return;
for (const [key, value] of Object.entries(accountData)) {
$i[key] = value;
if (accountData === null) {
void fetchAccount($i.token).then((accountData) => {
$i = accountData;
miLocalStorage.setItem('account', JSON.stringify($i));
});
} else {
for (const [key, value] of Object.entries(accountData)) {
$i[key] = value;
}
miLocalStorage.setItem('account', JSON.stringify($i));
}
miLocalStorage.setItem('account', JSON.stringify($i));
}
export async function refreshAccount() {

View file

@ -11,7 +11,7 @@ import { i18n } from '@/i18n.js';
import { alert, confirm, popup, post, toast } from '@/os.js';
import { useStream } from '@/stream.js';
import * as sound from '@/scripts/sound.js';
import { $i, signout, updateAccountPartial } from '@/account.js';
import { $i, refreshAccount, signout, updateAccountPartial } from '@/account.js';
import { instance } from '@/instance.js';
import { ColdDeviceStorage, defaultStore } from '@/store.js';
import { reactionPicker } from '@/scripts/reaction-picker.js';
@ -320,8 +320,8 @@ export async function mainBoot() {
const main = markRaw(stream.useChannel('main', null, 'System'));
// 自分の情報が更新されたとき
main.on('meUpdated', i => {
updateAccountPartial(i);
main.on('meUpdated', () => {
refreshAccount();
});
main.on('readAllNotifications', () => {