add lastActiveDate to updateUserQueue
This commit is contained in:
parent
1baa9b2f9d
commit
528c0476f9
3 changed files with 33 additions and 42 deletions
|
|
@ -12,9 +12,10 @@ import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||||
import { EnvService } from '@/core/EnvService.js';
|
import { EnvService } from '@/core/EnvService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { InternalEventService } from '@/core/InternalEventService.js';
|
import { InternalEventService } from '@/core/InternalEventService.js';
|
||||||
import type { UsersRepository, NotesRepository, AccessTokensRepository, MiAntenna, AntennasRepository } from '@/models/_.js';
|
import type { UsersRepository, NotesRepository, AccessTokensRepository, MiAntenna, FollowingsRepository } from '@/models/_.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { AntennaService } from '@/core/AntennaService.js';
|
import { AntennaService } from '@/core/AntennaService.js';
|
||||||
|
import { CacheService } from '@/core/CacheService.js';
|
||||||
|
|
||||||
export type UpdateInstanceJob = {
|
export type UpdateInstanceJob = {
|
||||||
latestRequestReceivedAt?: Date,
|
latestRequestReceivedAt?: Date,
|
||||||
|
|
@ -30,6 +31,7 @@ export type UpdateInstanceJob = {
|
||||||
|
|
||||||
export type UpdateUserJob = {
|
export type UpdateUserJob = {
|
||||||
updatedAt?: Date,
|
updatedAt?: Date,
|
||||||
|
lastActiveDate?: Date,
|
||||||
notesCountDelta?: number,
|
notesCountDelta?: number,
|
||||||
followingCountDelta?: number,
|
followingCountDelta?: number,
|
||||||
followersCountDelta?: number,
|
followersCountDelta?: number,
|
||||||
|
|
@ -66,21 +68,22 @@ export class CollapsedQueueService implements OnApplicationShutdown {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
public readonly usersRepository: UsersRepository,
|
private readonly usersRepository: UsersRepository,
|
||||||
|
|
||||||
@Inject(DI.notesRepository)
|
@Inject(DI.notesRepository)
|
||||||
public readonly notesRepository: NotesRepository,
|
private readonly notesRepository: NotesRepository,
|
||||||
|
|
||||||
@Inject(DI.accessTokensRepository)
|
@Inject(DI.accessTokensRepository)
|
||||||
public readonly accessTokensRepository: AccessTokensRepository,
|
private readonly accessTokensRepository: AccessTokensRepository,
|
||||||
|
|
||||||
@Inject(DI.antennasRepository)
|
@Inject(DI.followingsRepository)
|
||||||
public readonly antennasRepository: AntennasRepository,
|
private readonly followingsRepository: FollowingsRepository,
|
||||||
|
|
||||||
private readonly federatedInstanceService: FederatedInstanceService,
|
private readonly federatedInstanceService: FederatedInstanceService,
|
||||||
private readonly envService: EnvService,
|
private readonly envService: EnvService,
|
||||||
private readonly internalEventService: InternalEventService,
|
private readonly internalEventService: InternalEventService,
|
||||||
private readonly antennaService: AntennaService,
|
private readonly antennaService: AntennaService,
|
||||||
|
private readonly cacheService: CacheService,
|
||||||
|
|
||||||
loggerService: LoggerService,
|
loggerService: LoggerService,
|
||||||
) {
|
) {
|
||||||
|
|
@ -171,20 +174,34 @@ export class CollapsedQueueService implements OnApplicationShutdown {
|
||||||
oneMinuteInterval,
|
oneMinuteInterval,
|
||||||
(oldJob, newJob) => ({
|
(oldJob, newJob) => ({
|
||||||
updatedAt: maxDate(oldJob.updatedAt, newJob.updatedAt),
|
updatedAt: maxDate(oldJob.updatedAt, newJob.updatedAt),
|
||||||
|
lastActiveDate: maxDate(oldJob.lastActiveDate, newJob.lastActiveDate),
|
||||||
notesCountDelta: (oldJob.notesCountDelta ?? 0) + (newJob.notesCountDelta ?? 0),
|
notesCountDelta: (oldJob.notesCountDelta ?? 0) + (newJob.notesCountDelta ?? 0),
|
||||||
followingCountDelta: (oldJob.followingCountDelta ?? 0) + (newJob.followingCountDelta ?? 0),
|
followingCountDelta: (oldJob.followingCountDelta ?? 0) + (newJob.followingCountDelta ?? 0),
|
||||||
followersCountDelta: (oldJob.followersCountDelta ?? 0) + (newJob.followersCountDelta ?? 0),
|
followersCountDelta: (oldJob.followersCountDelta ?? 0) + (newJob.followersCountDelta ?? 0),
|
||||||
}),
|
}),
|
||||||
async (id, job) => {
|
async (id, job) => {
|
||||||
// Have to check this because all properties are optional
|
// Have to check this because all properties are optional
|
||||||
if (job.updatedAt || job.notesCountDelta || job.followingCountDelta || job.followersCountDelta) {
|
if (job.updatedAt || job.lastActiveDate || job.notesCountDelta || job.followingCountDelta || job.followersCountDelta) {
|
||||||
|
// Updating the user should implicitly mark them as active
|
||||||
|
const lastActiveDate = job.lastActiveDate ?? job.updatedAt;
|
||||||
|
const isWakingUp = lastActiveDate && (await this.cacheService.findUserById(id)).isHibernated;
|
||||||
|
|
||||||
|
// Update user before the hibernation cache, because the latter may refresh from DB
|
||||||
await this.usersRepository.update({ id }, {
|
await this.usersRepository.update({ id }, {
|
||||||
updatedAt: job.updatedAt,
|
updatedAt: job.updatedAt,
|
||||||
|
lastActiveDate,
|
||||||
|
isHibernated: isWakingUp ? false : undefined,
|
||||||
notesCount: job.notesCountDelta ? () => `"notesCount" + ${job.notesCountDelta}` : undefined,
|
notesCount: job.notesCountDelta ? () => `"notesCount" + ${job.notesCountDelta}` : undefined,
|
||||||
followingCount: job.followingCountDelta ? () => `"followingCount" + ${job.followingCountDelta}` : undefined,
|
followingCount: job.followingCountDelta ? () => `"followingCount" + ${job.followingCountDelta}` : undefined,
|
||||||
followersCount: job.followersCountDelta ? () => `"followersCount" + ${job.followersCountDelta}` : undefined,
|
followersCount: job.followersCountDelta ? () => `"followersCount" + ${job.followersCountDelta}` : undefined,
|
||||||
});
|
});
|
||||||
await this.internalEventService.emit('userUpdated', { id });
|
await this.internalEventService.emit('userUpdated', { id });
|
||||||
|
|
||||||
|
// Wake up hibernated users
|
||||||
|
if (isWakingUp) {
|
||||||
|
await this.followingsRepository.update({ followerId: id }, { isFollowerHibernated: false });
|
||||||
|
await this.cacheService.hibernatedUserCache.set(id, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -195,6 +212,9 @@ export class CollapsedQueueService implements OnApplicationShutdown {
|
||||||
updatedAt: data.updatedAt != null
|
updatedAt: data.updatedAt != null
|
||||||
? new Date(data.updatedAt)
|
? new Date(data.updatedAt)
|
||||||
: data.updatedAt,
|
: data.updatedAt,
|
||||||
|
lastActiveDate: data.lastActiveDate != null
|
||||||
|
? new Date(data.lastActiveDate)
|
||||||
|
: data.lastActiveDate,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { CacheService } from '@/core/CacheService.js';
|
import { CollapsedQueueService } from '@/global/CollapsedQueueService.js';
|
||||||
import { TimeService } from '@/global/TimeService.js';
|
import { TimeService } from '@/global/TimeService.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -22,43 +22,14 @@ export class UserService {
|
||||||
private followingsRepository: FollowingsRepository,
|
private followingsRepository: FollowingsRepository,
|
||||||
private systemWebhookService: SystemWebhookService,
|
private systemWebhookService: SystemWebhookService,
|
||||||
private userEntityService: UserEntityService,
|
private userEntityService: UserEntityService,
|
||||||
private readonly cacheService: CacheService,
|
private readonly collapsedQueueService: CollapsedQueueService,
|
||||||
private readonly timeService: TimeService,
|
private readonly timeService: TimeService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async updateLastActiveDate(user: MiUser): Promise<void> {
|
public async updateLastActiveDate(user: MiUser): Promise<void> {
|
||||||
if (user.isHibernated) {
|
await this.collapsedQueueService.updateUserQueue.enqueue(user.id, { lastActiveDate: this.timeService.date });
|
||||||
const result = await this.usersRepository.createQueryBuilder().update()
|
|
||||||
.set({
|
|
||||||
lastActiveDate: this.timeService.date,
|
|
||||||
})
|
|
||||||
.where('id = :id', { id: user.id })
|
|
||||||
.returning('*')
|
|
||||||
.execute()
|
|
||||||
.then((response) => {
|
|
||||||
return response.raw[0];
|
|
||||||
});
|
|
||||||
const wokeUp = result.isHibernated;
|
|
||||||
if (wokeUp) {
|
|
||||||
await Promise.all([
|
|
||||||
this.usersRepository.update(user.id, {
|
|
||||||
isHibernated: false,
|
|
||||||
}),
|
|
||||||
this.followingsRepository.update({
|
|
||||||
followerId: user.id,
|
|
||||||
}, {
|
|
||||||
isFollowerHibernated: false,
|
|
||||||
}),
|
|
||||||
this.cacheService.hibernatedUserCache.set(user.id, false),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.usersRepository.update(user.id, {
|
|
||||||
lastActiveDate: this.timeService.date,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -271,11 +271,11 @@ export class StreamingApiServerService implements OnApplicationShutdown {
|
||||||
this.#connections.set(connection, this.timeService.now);
|
this.#connections.set(connection, this.timeService.now);
|
||||||
|
|
||||||
// TODO use collapsed queue
|
// TODO use collapsed queue
|
||||||
const userUpdateIntervalId = user ? this.timeService.startTimer(() => {
|
const userUpdateIntervalId = user ? this.timeService.startTimer(async () => {
|
||||||
this.usersService.updateLastActiveDate(user);
|
await this.usersService.updateLastActiveDate(user);
|
||||||
}, 1000 * 60 * 5, { repeated: true }) : null;
|
}, 1000 * 60 * 5, { repeated: true }) : null;
|
||||||
if (user) {
|
if (user) {
|
||||||
this.usersService.updateLastActiveDate(user);
|
await this.usersService.updateLastActiveDate(user);
|
||||||
}
|
}
|
||||||
const pong = () => {
|
const pong = () => {
|
||||||
this.#connections.set(connection, this.timeService.now);
|
this.#connections.set(connection, this.timeService.now);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue