Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server: Resolves #9931: Add task to delete events older than a week #11372

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from

Conversation

AdrienPoupa
Copy link

Revisiting https://github.com/laurent22/joplin/pull/9988/files as I had the same issue this week (my Joplin DB grew to 600mb while I only have a few notes...).

I'm using mostly the same code, but I tried to match other patterns found in the codebase (using a TTL attribute in the class, not requiring arguments in the event function).

I also handled Laurent's review to use fake timers for the test.

Copy link
Contributor

github-actions bot commented Nov 11, 2024

CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅

@AdrienPoupa
Copy link
Author

I have read the CLA Document and I hereby sign the CLA

Copy link
Owner

@laurent22 laurent22 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this. I think there's two issues:

  • The TTL should be configurable via an env variable
  • What's the impact of deleting old events - what service or function could be affected by it?

@@ -4,6 +4,8 @@ import BaseModel, { UuidType } from './BaseModel';

export default class EventModel extends BaseModel<Event> {

private eventTtl_: number = 7 * 24 * 60 * 60 * 1000;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 * Day is clearer (import the Day constant)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was using TokenModel as a reference that has the exact same line. I changed it there as well.

packages/server/src/models/EventModel.test.ts Show resolved Hide resolved
@AdrienPoupa
Copy link
Author

AdrienPoupa commented Nov 11, 2024

Hi Laurent, thanks for the review.

The TTL should be configurable via an env variable

What would we gain from this? The DeleteExpiredTokens task that I'm using as a reference does not have this logic.

If we need to implement it, do you have a preferred approach? I see the UserDeletionService uses the USER_DATA_AUTO_DELETE_AFTER_DAYS environment variable, I could use the same mechanism.

What's the impact of deleting old events - what service or function could be affected by it?

I don't know, sorry. It is the approach you suggested here however: #9931 (comment)

@laurent22
Copy link
Owner

What would we gain from this?

For audit purposes for example.

I don't know, sorry. It is the approach you suggested here however: #9931 (comment)

If you can check please post here

@AdrienPoupa
Copy link
Author

I analyzed events and their usages, I found that we're creating events in TaskService's runTask just before and after the task is handled:

await this.models.event().create(EventType.TaskStarted, id.toString());

and

await this.models.event().create(EventType.TaskCompleted, id.toString());

In taskLastEvents, we retrieve the last events for a given task:

	public async taskLastEvents(id: TaskId): Promise<TaskEvents> {
		return {
			taskStarted: await this.models.event().lastEventByTypeAndName(EventType.TaskStarted, id.toString()),
			taskCompleted: await this.models.event().lastEventByTypeAndName(EventType.TaskCompleted, id.toString()),
		};
	}

This function, in turn, is used in the admin/tasks route to show task events (columns Last Run and Last Completion).

Those were the only usages I could find, so they're here for auditing/debugging purposes, they have no impact on the behaviour of the application.

@AdrienPoupa
Copy link
Author

I added two environment variables to enable the deletion and control the cut off period, and improved the test coverage. Hopefully that should do it :)

Copy link
Owner

@laurent22 laurent22 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for looking into this and for adding the two env variables and test.

For now please set it to disabled by default and default duration to 30 days. This is because even though today we don't need to keep them for a long time, in the future there could be a service that needs them for a longer time, and users who will have it configured to 7 days might have issues.

What's the meaning of setting EVENTS_AUTO_DELETE_AFTER_DAYS to 0? It deletes everything immediately?

Comment on lines 11 to 18
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;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually the config is available from TaskService, so could you please do the following:

  • Remove the eventTtl_ and eventAutoDelete_ from here
  • Make deleteOldEvents take the TTL as argument
  • In TaskService, only enable the task if EVENTS_AUTO_DELETE_ENABLED is true (same as USER_DATA_AUTO_DELETE_ENABLED)
  • Still from TaskService pass EVENTS_AUTO_DELETE_AFTER_DAYS * Day as argument

This is to make the way it works more explicit and to avoid having to modify a global object (the config) in tests, which can have unexpected results.

Copy link
Author

@AdrienPoupa AdrienPoupa Nov 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In TaskService, only enable the task if EVENTS_AUTO_DELETE_ENABLED is true (same as USER_DATA_AUTO_DELETE_ENABLED)

That was already the case.

I made the changes you requested but had to remove a few tests that no longer made sense.

Comment on lines 5 to 9
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;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this - I agree it's ok as a change but I prefer to keep commits focused on one specific issue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 54 to 58
return this.withTransaction(async () => {
await this.db(this.tableName)
.where('created_time', '<', cutOffDate)
.delete();
}, 'EventModel::deleteOldEvents');
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for withTransaction since there's only one db call

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the function shouldn't return a value

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@AdrienPoupa
Copy link
Author

What's the meaning of setting EVENTS_AUTO_DELETE_AFTER_DAYS to 0? It deletes everything immediately?

It will delete events older than the current timestamp, so yes. I don't think that's an issue given you'd have to go ahead and actively set the config as such.

Copy link
Owner

@laurent22 laurent22 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @AdrienPoupa, I've just left one last comment and then we can merge

@@ -33,4 +33,11 @@ export default class EventModel extends BaseModel<Event> {
.first();
}

public async deleteOldEvents(ttl: number): Promise<void> {
const cutOffDate = Date.now() - ttl;
return this.db(this.tableName)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return this.db(this.tableName)
await this.db(this.tableName)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants