use TimeService in charts

This commit is contained in:
Hazelnoot 2025-10-01 17:23:29 -04:00
parent 2dfc878445
commit 64745a41e5
18 changed files with 123 additions and 30 deletions

View file

@ -10,6 +10,7 @@ import type { MiUser } from '@/models/User.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/active-users.js';
@ -31,10 +32,15 @@ export default class ActiveUsersChart extends Chart<typeof schema> { // eslint-d
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
private idService: IdService,
private readonly timeService: TimeService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}

View file

@ -6,6 +6,7 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import Chart from '../core.js';
@ -24,10 +25,15 @@ export default class ApRequestChart extends Chart<typeof schema> { // eslint-dis
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
private readonly timeService: TimeService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}

View file

@ -7,6 +7,7 @@ import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import type { MiDriveFile } from '@/models/DriveFile.js';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import Chart from '../core.js';
@ -25,10 +26,15 @@ export default class DriveChart extends Chart<typeof schema> { // eslint-disable
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
private readonly timeService: TimeService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}

View file

@ -7,6 +7,7 @@ import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import type { FollowingsRepository, InstancesRepository, MiMeta } from '@/models/_.js';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import Chart from '../core.js';
@ -34,10 +35,15 @@ export default class FederationChart extends Chart<typeof schema> { // eslint-di
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
private readonly timeService: TimeService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {
};

View file

@ -9,6 +9,7 @@ import type { DriveFilesRepository, FollowingsRepository, UsersRepository, Notes
import type { MiDriveFile } from '@/models/DriveFile.js';
import type { MiNote } from '@/models/Note.js';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js';
@ -41,10 +42,15 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
private utilityService: UtilityService,
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
private readonly timeService: TimeService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {
const [
notesCount,

View file

@ -8,6 +8,7 @@ import { Not, IsNull, DataSource } from 'typeorm';
import type { NotesRepository } from '@/models/_.js';
import type { MiNote } from '@/models/Note.js';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import Chart from '../core.js';
@ -29,10 +30,15 @@ export default class NotesChart extends Chart<typeof schema> { // eslint-disable
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
private readonly timeService: TimeService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
const [localCount, remoteCount] = await Promise.all([
this.notesRepository.countBy({ userHost: IsNull() }),

View file

@ -8,6 +8,7 @@ import { DataSource } from 'typeorm';
import type { DriveFilesRepository } from '@/models/_.js';
import type { MiDriveFile } from '@/models/DriveFile.js';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import { bindThis } from '@/decorators.js';
@ -31,10 +32,15 @@ export default class PerUserDriveChart extends Chart<typeof schema> { // eslint-
private appLockService: AppLockService,
private driveFileEntityService: DriveFileEntityService,
private chartLoggerService: ChartLoggerService,
private readonly timeService: TimeService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {
const [count, size] = await Promise.all([
this.driveFilesRepository.countBy({ userId: group }),

View file

@ -7,6 +7,8 @@ import { Injectable, Inject } from '@nestjs/common';
import { Not, IsNull, DataSource } from 'typeorm';
import type { MiUser } from '@/models/User.js';
import { AppLockService } from '@/core/AppLockService.js';
import { CacheService } from '@/core/CacheService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { FollowingsRepository } from '@/models/_.js';
@ -15,7 +17,6 @@ import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-following.js';
import type { KVs } from '../core.js';
import { CacheService } from '@/core/CacheService.js';
/**
*
@ -33,10 +34,15 @@ export default class PerUserFollowingChart extends Chart<typeof schema> { // esl
private userEntityService: UserEntityService,
private chartLoggerService: ChartLoggerService,
private readonly cacheService: CacheService,
private readonly timeService: TimeService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {
const [
followees,

View file

@ -8,6 +8,7 @@ import { DataSource } from 'typeorm';
import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import type { NotesRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
@ -30,10 +31,15 @@ export default class PerUserNotesChart extends Chart<typeof schema> { // eslint-
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
private readonly timeService: TimeService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {
const [count] = await Promise.all([
this.notesRepository.countBy({ userId: group }),

View file

@ -7,6 +7,7 @@ import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import type { MiUser } from '@/models/User.js';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import Chart from '../core.js';
@ -25,10 +26,15 @@ export default class PerUserPvChart extends Chart<typeof schema> { // eslint-dis
private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
private readonly timeService: TimeService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}

View file

@ -8,6 +8,7 @@ import { DataSource } from 'typeorm';
import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js';
@ -28,10 +29,15 @@ export default class PerUserReactionsChart extends Chart<typeof schema> { // esl
private appLockService: AppLockService,
private userEntityService: UserEntityService,
private chartLoggerService: ChartLoggerService,
private readonly timeService: TimeService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {
return {};
}

View file

@ -6,6 +6,7 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
@ -25,11 +26,17 @@ export default class TestGroupedChart extends Chart<typeof schema> { // eslint-d
private db: DataSource,
private appLockService: AppLockService,
private readonly timeService: TimeService,
logger: Logger,
) {
super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema, true);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {
return {
'foo.total': this.total[group],

View file

@ -6,6 +6,7 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
@ -23,11 +24,17 @@ export default class TestIntersectionChart extends Chart<typeof schema> { // esl
private db: DataSource,
private appLockService: AppLockService,
private readonly timeService: TimeService,
logger: Logger,
) {
super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}

View file

@ -6,6 +6,7 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
@ -23,11 +24,17 @@ export default class TestUniqueChart extends Chart<typeof schema> { // eslint-di
private db: DataSource,
private appLockService: AppLockService,
private readonly timeService: TimeService,
logger: Logger,
) {
super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}

View file

@ -9,6 +9,7 @@ import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
import { TimeService } from '@/core/TimeService.js';
import Chart from '../core.js';
import { name, schema } from './entities/test.js';
import type { KVs } from '../core.js';
@ -25,11 +26,17 @@ export default class TestChart extends Chart<typeof schema> { // eslint-disable-
private db: DataSource,
private appLockService: AppLockService,
private readonly timeService: TimeService,
logger: Logger,
) {
super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {
'foo.total': this.total,

View file

@ -7,6 +7,7 @@ import { Injectable, Inject } from '@nestjs/common';
import { Not, IsNull, Like, DataSource } from 'typeorm';
import type { MiUser } from '@/models/User.js';
import { AppLockService } from '@/core/AppLockService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { UsersRepository } from '@/models/_.js';
@ -31,10 +32,15 @@ export default class UsersChart extends Chart<typeof schema> { // eslint-disable
private appLockService: AppLockService,
private userEntityService: UserEntityService,
private chartLoggerService: ChartLoggerService,
private readonly timeService: TimeService,
) {
super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
}
protected getCurrentDate(): Date {
return this.timeService.date;
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
const [localCount, remoteCount] = await Promise.all([
// that Not(Like()) is ugly, but it matches the logic in

View file

@ -202,9 +202,7 @@ export default abstract class Chart<T extends Schema> {
return [y, m, d, h, _m, _s, _ms];
}
private static getCurrentDate() {
return Chart.parseDate(new Date());
}
protected abstract getCurrentDate(): Date;
public static schemaToEntity(name: string, schema: Schema, grouped = false): {
hour: EntitySchema,
@ -324,7 +322,7 @@ export default abstract class Chart<T extends Schema> {
*/
@bindThis
private async claimCurrentLog(group: string | null, span: 'hour' | 'day'): Promise<RawRecord<T>> {
const [y, m, d, h] = Chart.getCurrentDate();
const [y, m, d, h] = Chart.parseDate(this.getCurrentDate());
const current = dateUTC(
span === 'hour' ? [y, m, d, h] :
@ -579,7 +577,7 @@ export default abstract class Chart<T extends Schema> {
@bindThis
public async clean(): Promise<void> {
const current = dateUTC(Chart.getCurrentDate());
const current = this.getCurrentDate();
// 一日以上前かつ三日以内
const gt = Chart.dateToTimestamp(current) - (60 * 60 * 24 * 3);
@ -615,7 +613,7 @@ export default abstract class Chart<T extends Schema> {
@bindThis
public async getChartRaw(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise<ChartResult<T>> {
const [y, m, d, h, _m, _s, _ms] = cursor ? Chart.parseDate(subtractTime(addTime(cursor, 1, span), 1)) : Chart.getCurrentDate();
const [y, m, d, h, _m, _s, _ms] = cursor ? Chart.parseDate(subtractTime(addTime(cursor, 1, span), 1)) : Chart.parseDate(this.getCurrentDate());
const [y2, m2, d2, h2] = cursor ? Chart.parseDate(addTime(cursor, 1, span)) : [] as never;
const lt = dateUTC([y, m, d, h, _m, _s, _ms]);

View file

@ -7,8 +7,9 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import { jest } from '@jest/globals';
import * as lolex from '@sinonjs/fake-timers';
import { DataSource } from 'typeorm';
import { GodOfTimeService } from '../misc/GodOfTimeService.js';
import { MockLoggerService } from '../misc/MockLoggerService.js';
import TestChart from '@/core/chart/charts/test.js';
import TestGroupedChart from '@/core/chart/charts/test-grouped.js';
import TestUniqueChart from '@/core/chart/charts/test-unique.js';
@ -33,7 +34,7 @@ describe('Chart', () => {
let testGroupedChart: TestGroupedChart;
let testUniqueChart: TestUniqueChart;
let testIntersectionChart: TestIntersectionChart;
let clock: lolex.InstalledClock;
let clock: GodOfTimeService;
beforeEach(async () => {
if (db) await db.destroy();
@ -63,20 +64,14 @@ describe('Chart', () => {
await db.initialize();
const logger = new Logger('chart'); // TODO: モックにする
testChart = new TestChart(db, appLockService, logger);
testGroupedChart = new TestGroupedChart(db, appLockService, logger);
testUniqueChart = new TestUniqueChart(db, appLockService, logger);
testIntersectionChart = new TestIntersectionChart(db, appLockService, logger);
clock = new GodOfTimeService();
clock.resetTo(Date.UTC(2000, 0, 1, 0, 0, 0));
clock = lolex.install({
now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)),
shouldClearNativeTimers: true,
});
});
afterEach(() => {
clock.uninstall();
const logger = new MockLoggerService(config).getLogger('chart');
testChart = new TestChart(db, appLockService, clock, logger);
testGroupedChart = new TestGroupedChart(db, appLockService, clock, logger);
testUniqueChart = new TestUniqueChart(db, appLockService, clock, logger);
testIntersectionChart = new TestIntersectionChart(db, appLockService, clock, logger);
});
afterAll(async () => {
@ -208,7 +203,7 @@ describe('Chart', () => {
await testChart.increment();
await testChart.save();
clock.tick('01:00:00');
clock.tick({ hours: 1 });
await testChart.increment();
await testChart.save();
@ -268,7 +263,7 @@ describe('Chart', () => {
await testChart.increment();
await testChart.save();
clock.tick('02:00:00');
clock.tick({ hours: 2 });
await testChart.increment();
await testChart.save();
@ -298,7 +293,7 @@ describe('Chart', () => {
await testChart.increment();
await testChart.save();
clock.tick('05:00:00');
clock.tick({ hours: 5 });
const chartHours = await testChart.getChart('hour', 3, null);
const chartDays = await testChart.getChart('day', 3, null);
@ -326,7 +321,7 @@ describe('Chart', () => {
await testChart.increment();
await testChart.save();
clock.tick('05:00:00');
clock.tick({ hours: 5 });
await testChart.increment();
await testChart.save();
@ -355,7 +350,7 @@ describe('Chart', () => {
await testChart.increment();
await testChart.save();
clock.tick('01:00:00');
clock.tick({ hours: 1 });
await testChart.increment();
await testChart.save();
@ -381,12 +376,12 @@ describe('Chart', () => {
});
test('Can specify offset (floor time)', async () => {
clock.tick('00:30:00');
clock.tick({ minutes: 30 });
await testChart.increment();
await testChart.save();
clock.tick('01:30:00');
clock.tick({ hours: 1, minutes: 30 });
await testChart.increment();
await testChart.save();
@ -552,7 +547,7 @@ describe('Chart', () => {
await testChart.increment();
await testChart.save();
clock.tick('01:00:00');
clock.tick({ hours: 1 });
testChart.total = 100;