Skip to content

Commit

Permalink
Server: Resolves #9931: Add task to delete events older than a week
Browse files Browse the repository at this point in the history
  • Loading branch information
AdrienPoupa committed Nov 12, 2024
1 parent 3887010 commit c32076b
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 3 deletions.
12 changes: 11 additions & 1 deletion packages/server/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const defaultEnvValues: EnvVariables = {
IS_ADMIN_INSTANCE: true,
INSTANCE_NAME: '',

// Maxiumm allowed drift between NTP time and server time. A few
// Maximum allowed drift between NTP time and server time. A few
// milliseconds is normally not an issue unless many clients are modifying
// the same note at the exact same time. But past a certain limit, it might
// mean the server clock is incorrect and should be fixed, as that could
Expand Down Expand Up @@ -118,6 +118,13 @@ const defaultEnvValues: EnvVariables = {
USER_DATA_AUTO_DELETE_ENABLED: false,
USER_DATA_AUTO_DELETE_AFTER_DAYS: 90,

// ==================================================
// Events deletion
// ==================================================

EVENTS_AUTO_DELETE_ENABLED: true,
EVENTS_AUTO_DELETE_AFTER_DAYS: 7,

// ==================================================
// LDAP configuration
// ==================================================
Expand Down Expand Up @@ -210,6 +217,9 @@ export interface EnvVariables {
USER_DATA_AUTO_DELETE_ENABLED: boolean;
USER_DATA_AUTO_DELETE_AFTER_DAYS: number;

EVENTS_AUTO_DELETE_ENABLED: boolean;
EVENTS_AUTO_DELETE_AFTER_DAYS: number;

LDAP_1_ENABLED: boolean;
LDAP_1_USER_AUTO_CREATION: boolean;
LDAP_1_HOST: string;
Expand Down
97 changes: 96 additions & 1 deletion packages/server/src/models/EventModel.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EventType } from '../services/database/types';
import { beforeAllDb, afterAllTests, beforeEachDb, models } from '../utils/testing/testUtils';
import { msleep } from '../utils/time';
import { Month, msleep, Week } from '../utils/time';
import config from '../config';

describe('EventModel', () => {

Expand Down Expand Up @@ -37,4 +38,98 @@ describe('EventModel', () => {
expect(latest.id).toBe(allEvents[1].id);
});

test('deletion should work when there are no events', async () => {
const allEvents = (await models().event().all());
expect(allEvents.length).toBe(0);

await models().event().deleteOldEvents();

const remainingEvents = (await models().event().all());
expect(remainingEvents.length).toBe(0);
});

test('should not delete recent events', async () => {
await models().event().create(EventType.TaskStarted, 'deleteExpiredTokens');

const allEvents = (await models().event().all());
expect(allEvents.length).toBe(1);

await models().event().deleteOldEvents();

const remainingEvents = (await models().event().all());
expect(remainingEvents.length).toBe(1);
});

test('should delete events older than a week', async () => {
const now = Date.now();
const aWeekAgo = now - Week;
jest.useFakeTimers();

for (const difference of [-10, -5, 0, 5, 10]) {
jest.setSystemTime(aWeekAgo + difference);
await models().event().create(EventType.TaskStarted, 'deleteExpiredTokens');
}

const allEvents = (await models().event().all());
expect(allEvents.length).toBe(5);

jest.setSystemTime(now);
await models().event().deleteOldEvents();

const remainingEvents = (await models().event().all());
expect(remainingEvents.length).toBe(3);

for (const event of remainingEvents) {
expect(event.created_time).toBeGreaterThanOrEqual(aWeekAgo);
}

jest.useRealTimers();
});

test('should delete events older than a custom interval', async () => {
config().EVENTS_AUTO_DELETE_AFTER_DAYS = 30;
const now = Date.now();
const aMonthAgo = now - Month;
jest.useFakeTimers();

for (const difference of [-10, -5, 0, 5, 10]) {
jest.setSystemTime(aMonthAgo + difference);
await models().event().create(EventType.TaskStarted, 'deleteExpiredTokens');
}

const allEvents = (await models().event().all());
expect(allEvents.length).toBe(5);

jest.setSystemTime(now);
await models().event().deleteOldEvents();

const remainingEvents = (await models().event().all());
expect(remainingEvents.length).toBe(3);

for (const event of remainingEvents) {
expect(event.created_time).toBeGreaterThanOrEqual(aMonthAgo);
}

jest.useRealTimers();
});

test('should not delete old events if config disabled', async () => {
config().EVENTS_AUTO_DELETE_ENABLED = false;
const now = Date.now();
const aWeekAgo = now - Week;
jest.useFakeTimers().setSystemTime(aWeekAgo + 5);
await models().event().create(EventType.TaskStarted, 'deleteExpiredTokens');

const allEvents = (await models().event().all());
expect(allEvents.length).toBe(1);

jest.setSystemTime(now);
await models().event().deleteOldEvents();

const remainingEvents = (await models().event().all());
expect(remainingEvents.length).toBe(1);

jest.useRealTimers();
});

});
25 changes: 25 additions & 0 deletions packages/server/src/models/EventModel.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import { Event, EventType } from '../services/database/types';
import BaseModel, { UuidType } from './BaseModel';
import { Day } from '../utils/time';
import { DbConnection } from '../db';
import { NewModelFactoryHandler } from './factory';
import { Config } from '../utils/types';


export default class EventModel extends BaseModel<Event> {

private readonly eventTtl_: number;
private readonly eventAutoDelete_: boolean;

public constructor(db: DbConnection, dbSlave: DbConnection, modelFactory: NewModelFactoryHandler, config: Config) {
super(db, dbSlave, modelFactory, config);
this.eventAutoDelete_ = config.EVENTS_AUTO_DELETE_ENABLED;
this.eventTtl_ = config.EVENTS_AUTO_DELETE_AFTER_DAYS * Day;
}

public get tableName(): string {
return 'events';
}
Expand Down Expand Up @@ -33,4 +46,16 @@ export default class EventModel extends BaseModel<Event> {
.first();
}

public async deleteOldEvents() {
if (!this.eventAutoDelete_) {
return null;
}
const cutOffDate = Date.now() - this.eventTtl_;
return this.withTransaction(async () => {
await this.db(this.tableName)
.where('created_time', '<', cutOffDate)
.delete();
}, 'EventModel::deleteOldEvents');
}

}
3 changes: 2 additions & 1 deletion packages/server/src/models/TokenModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { Token, User, Uuid } from '../services/database/types';
import { ErrorForbidden, ErrorNotFound } from '../utils/errors';
import { uuidgen } from '@joplin/lib/uuid';
import BaseModel from './BaseModel';
import { Day } from '../utils/time';

export default class TokenModel extends BaseModel<Token> {

private tokenTtl_: number = 7 * 24 * 60 * 60 * 1000;
private tokenTtl_: number = 7 * Day;

public get tableName(): string {
return 'tokens';
Expand Down
1 change: 1 addition & 0 deletions packages/server/src/services/TaskService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const taskIdToLabel = (taskId: TaskId): string => {
[TaskId.ProcessShares]: 'Process shared items',
[TaskId.ProcessEmails]: 'Process emails',
[TaskId.LogHeartbeatMessage]: 'Log heartbeat message',
[TaskId.DeleteOldEvents]: 'Delete old events',
};

const s = strings[taskId];
Expand Down
1 change: 1 addition & 0 deletions packages/server/src/services/database/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export enum TaskId {
ProcessShares,
ProcessEmails,
LogHeartbeatMessage,
DeleteOldEvents,
}

// AUTO-GENERATED-TYPES
Expand Down
9 changes: 9 additions & 0 deletions packages/server/src/utils/setupTaskService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ export default async function(env: Env, models: Models, config: Config, services
});
}

if (config.EVENTS_AUTO_DELETE_ENABLED) {
tasks.push({
id: TaskId.DeleteOldEvents,
description: taskIdToLabel(TaskId.DeleteOldEvents),
schedule: '0 0 * * *',
run: (models: Models) => models.event().deleteOldEvents(),
});
}

if (config.isJoplinCloud) {
tasks = tasks.concat([
{
Expand Down

0 comments on commit c32076b

Please sign in to comment.