fix(backend): 古いユーザーキャッシュを使うことへの対策 (misskey-dev#13453)
Co-authored-by: tamaina <tamaina@hotmail.co.jp>
This commit is contained in:
parent
4a615ff251
commit
552354c895
|
@ -21,7 +21,6 @@ import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
|
||||||
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
|
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
|
||||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { CacheService } from '@/core/CacheService.js';
|
|
||||||
import { ProxyAccountService } from '@/core/ProxyAccountService.js';
|
import { ProxyAccountService } from '@/core/ProxyAccountService.js';
|
||||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
|
@ -61,7 +60,6 @@ export class AccountMoveService {
|
||||||
private instanceChart: InstanceChart,
|
private instanceChart: InstanceChart,
|
||||||
private metaService: MetaService,
|
private metaService: MetaService,
|
||||||
private relayService: RelayService,
|
private relayService: RelayService,
|
||||||
private cacheService: CacheService,
|
|
||||||
private queueService: QueueService,
|
private queueService: QueueService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
@ -85,7 +83,7 @@ export class AccountMoveService {
|
||||||
Object.assign(src, update);
|
Object.assign(src, update);
|
||||||
|
|
||||||
// Update cache
|
// Update cache
|
||||||
this.cacheService.uriPersonCache.set(srcUri, src);
|
this.globalEventService.publishInternalEvent('localUserUpdated', src);
|
||||||
|
|
||||||
const srcPerson = await this.apRendererService.renderPerson(src);
|
const srcPerson = await this.apRendererService.renderPerson(src);
|
||||||
const updateAct = this.apRendererService.addContext(this.apRendererService.renderUpdate(srcPerson, src));
|
const updateAct = this.apRendererService.addContext(this.apRendererService.renderUpdate(srcPerson, src));
|
||||||
|
|
|
@ -129,10 +129,12 @@ export class CacheService implements OnApplicationShutdown {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'userChangeSuspendedState':
|
case 'userChangeSuspendedState':
|
||||||
case 'userChangeDeletedState':
|
case 'userChangeDeletedState':
|
||||||
case 'remoteUserUpdated': {
|
case 'remoteUserUpdated':
|
||||||
|
case 'localUserUpdated': {
|
||||||
const user = await this.usersRepository.findOneBy({ id: body.id });
|
const user = await this.usersRepository.findOneBy({ id: body.id });
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
this.userByIdCache.delete(body.id);
|
this.userByIdCache.delete(body.id);
|
||||||
|
this.localUserByIdCache.delete(body.id);
|
||||||
for (const [k, v] of this.uriPersonCache.cache.entries()) {
|
for (const [k, v] of this.uriPersonCache.cache.entries()) {
|
||||||
if (v.value?.id === body.id) {
|
if (v.value?.id === body.id) {
|
||||||
this.uriPersonCache.delete(k);
|
this.uriPersonCache.delete(k);
|
||||||
|
|
|
@ -212,6 +212,7 @@ export interface InternalEventTypes {
|
||||||
userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; };
|
userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; };
|
||||||
userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; };
|
userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; };
|
||||||
remoteUserUpdated: { id: MiUser['id']; };
|
remoteUserUpdated: { id: MiUser['id']; };
|
||||||
|
localUserUpdated: { id: MiUser['id']; };
|
||||||
follow: { followerId: MiUser['id']; followeeId: MiUser['id']; };
|
follow: { followerId: MiUser['id']; followeeId: MiUser['id']; };
|
||||||
unfollow: { followerId: MiUser['id']; followeeId: MiUser['id']; };
|
unfollow: { followerId: MiUser['id']; followeeId: MiUser['id']; };
|
||||||
blockingCreated: { blockerId: MiUser['id']; blockeeId: MiUser['id']; };
|
blockingCreated: { blockerId: MiUser['id']; blockeeId: MiUser['id']; };
|
||||||
|
|
|
@ -100,33 +100,24 @@ export class UserFollowingService implements OnModuleInit {
|
||||||
this.queueService.deliver(followee, content, follower.inbox, false);
|
this.queueService.deliver(followee, content, follower.inbox, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ThinUserでなくともユーザーの情報が最新でない場合はこちらを使うべき
|
|
||||||
*/
|
|
||||||
@bindThis
|
|
||||||
public async followByThinUser(
|
|
||||||
_follower: ThinUser,
|
|
||||||
_followee: ThinUser,
|
|
||||||
options: Parameters<typeof this.follow>[2] = {},
|
|
||||||
) {
|
|
||||||
const [follower, followee] = await Promise.all([
|
|
||||||
this.usersRepository.findOneByOrFail({ id: _follower.id }),
|
|
||||||
this.usersRepository.findOneByOrFail({ id: _followee.id }),
|
|
||||||
]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser];
|
|
||||||
|
|
||||||
await this.follow(follower, followee, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async follow(
|
public async follow(
|
||||||
follower: MiLocalUser | MiRemoteUser,
|
_follower: ThinUser,
|
||||||
followee: MiLocalUser | MiRemoteUser,
|
_followee: ThinUser,
|
||||||
{ requestId, silent = false, withReplies }: {
|
{ requestId, silent = false, withReplies }: {
|
||||||
requestId?: string,
|
requestId?: string,
|
||||||
silent?: boolean,
|
silent?: boolean,
|
||||||
withReplies?: boolean,
|
withReplies?: boolean,
|
||||||
} = {},
|
} = {},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
/**
|
||||||
|
* 必ず最新のユーザー情報を取得する
|
||||||
|
*/
|
||||||
|
const [follower, followee] = await Promise.all([
|
||||||
|
this.usersRepository.findOneByOrFail({ id: _follower.id }),
|
||||||
|
this.usersRepository.findOneByOrFail({ id: _followee.id }),
|
||||||
|
]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser];
|
||||||
|
|
||||||
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
||||||
// What?
|
// What?
|
||||||
throw new Error('Remote user cannot follow remote user.');
|
throw new Error('Remote user cannot follow remote user.');
|
||||||
|
|
|
@ -193,6 +193,10 @@ export class RedisSingleCache<T> {
|
||||||
// TODO: メモリ節約のためあまり参照されないキーを定期的に削除できるようにする?
|
// TODO: メモリ節約のためあまり参照されないキーを定期的に削除できるようにする?
|
||||||
|
|
||||||
export class MemoryKVCache<T> {
|
export class MemoryKVCache<T> {
|
||||||
|
/**
|
||||||
|
* データを持つマップ
|
||||||
|
* @deprecated これを直接操作するべきではない
|
||||||
|
*/
|
||||||
public cache: Map<string, { date: number; value: T; }>;
|
public cache: Map<string, { date: number; value: T; }>;
|
||||||
private lifetime: number;
|
private lifetime: number;
|
||||||
private gcIntervalHandle: NodeJS.Timeout;
|
private gcIntervalHandle: NodeJS.Timeout;
|
||||||
|
@ -207,6 +211,10 @@ export class MemoryKVCache<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
/**
|
||||||
|
* Mapにキャッシュをセットします
|
||||||
|
* @deprecated これを直接呼び出すべきではない。InternalEventなどで変更を全てのプロセス/マシンに通知するべき
|
||||||
|
*/
|
||||||
public set(key: string, value: T): void {
|
public set(key: string, value: T): void {
|
||||||
this.cache.set(key, {
|
this.cache.set(key, {
|
||||||
date: Date.now(),
|
date: Date.now(),
|
||||||
|
|
|
@ -35,7 +35,7 @@ export class RelationshipProcessorService {
|
||||||
@bindThis
|
@bindThis
|
||||||
public async processFollow(job: Bull.Job<RelationshipJobData>): Promise<string> {
|
public async processFollow(job: Bull.Job<RelationshipJobData>): Promise<string> {
|
||||||
this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? 'with replies' : 'without replies'}`);
|
this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? 'with replies' : 'without replies'}`);
|
||||||
await this.userFollowingService.followByThinUser(job.data.from, job.data.to, {
|
await this.userFollowingService.follow(job.data.from, job.data.to, {
|
||||||
requestId: job.data.requestId,
|
requestId: job.data.requestId,
|
||||||
silent: job.data.silent,
|
silent: job.data.silent,
|
||||||
withReplies: job.data.withReplies,
|
withReplies: job.data.withReplies,
|
||||||
|
|
|
@ -443,9 +443,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
this.hashtagService.updateUsertags(user, tags);
|
this.hashtagService.updateUsertags(user, tags);
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
if (Object.keys(updates).length > 0) await this.usersRepository.update(user.id, updates);
|
if (Object.keys(updates).length > 0) {
|
||||||
if (Object.keys(updates).includes('alsoKnownAs')) {
|
await this.usersRepository.update(user.id, updates);
|
||||||
this.cacheService.uriPersonCache.set(this.userEntityService.genLocalUserUri(user.id), { ...user, ...updates });
|
this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.userProfilesRepository.update(user.id, {
|
await this.userProfilesRepository.update(user.id, {
|
||||||
|
|
Loading…
Reference in New Issue