mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-25 03:06:31 -05:00
feat: add event listener for canceling destroy timer
Signed-off-by: yamashush <38120991+yamashush@users.noreply.github.com> Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
parent
c5d8341c45
commit
956dd28648
2 changed files with 69 additions and 3 deletions
|
@ -108,4 +108,41 @@ describe('realtime note', () => {
|
|||
expect(sendMessage1Spy).toHaveBeenNthCalledWith(2, deletedMessage);
|
||||
expect(sendMessage2Spy).toHaveBeenNthCalledWith(1, deletedMessage);
|
||||
});
|
||||
|
||||
describe('removeClient', () => {
|
||||
it('destory if the number of connected clients reaches zero and the lifetime is exceeded', () => {
|
||||
const sut = new RealtimeNote(mockedNote, 'nothing');
|
||||
const client1 = new MockConnectionBuilder(sut).withLoggedInUser().build();
|
||||
const docDestroy = jest.spyOn(sut, 'destroy');
|
||||
|
||||
sut.addClient(client1);
|
||||
sut.removeClient(client1);
|
||||
jest.advanceTimersByTime(5000);
|
||||
|
||||
sut.addClient(client1);
|
||||
sut.removeClient(client1);
|
||||
jest.advanceTimersByTime(10500);
|
||||
|
||||
expect(docDestroy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("doesn't destory when a client reconnects quickly", () => {
|
||||
const sut = new RealtimeNote(mockedNote, 'nothing');
|
||||
const client1 = new MockConnectionBuilder(sut).withLoggedInUser().build();
|
||||
const docDestroy = jest.spyOn(sut, 'destroy');
|
||||
|
||||
// Assuming the case where the only connected user reloads the browser
|
||||
sut.addClient(client1);
|
||||
sut.removeClient(client1);
|
||||
jest.advanceTimersByTime(5000);
|
||||
|
||||
sut.addClient(client1);
|
||||
sut.removeClient(client1);
|
||||
jest.advanceTimersByTime(5000);
|
||||
|
||||
sut.addClient(client1);
|
||||
|
||||
expect(docDestroy).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
import { Message, MessageType, RealtimeDoc } from '@hedgedoc/commons';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { EventEmitter2, EventMap } from 'eventemitter2';
|
||||
import { EventEmitter2, EventMap, Listener } from 'eventemitter2';
|
||||
|
||||
import { Note } from '../../notes/note.entity';
|
||||
import { RealtimeConnection } from './realtime-connection';
|
||||
|
@ -19,6 +19,8 @@ export interface RealtimeNoteEventMap extends EventMap {
|
|||
yDocUpdate: (update: number[], origin: unknown) => void;
|
||||
}
|
||||
|
||||
const LIFETIME_WITHOUT_CLIENTS = 10 * 1000; // 10 seconds
|
||||
|
||||
/**
|
||||
* Represents a note currently being edited by a number of clients.
|
||||
*/
|
||||
|
@ -26,7 +28,9 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
|||
protected logger: Logger;
|
||||
private readonly doc: RealtimeDoc;
|
||||
private readonly clients = new Set<RealtimeConnection>();
|
||||
private readonly clientAddedListener: Listener;
|
||||
private isClosing = false;
|
||||
private destroyEventTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
constructor(
|
||||
private readonly note: Note,
|
||||
|
@ -40,6 +44,18 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
|||
this.logger.debug(
|
||||
`New realtime session for note ${note.id} created. Length of initial content: ${length} characters`,
|
||||
);
|
||||
this.clientAddedListener = this.on(
|
||||
'clientAdded',
|
||||
() => {
|
||||
if (this.destroyEventTimer) {
|
||||
clearTimeout(this.destroyEventTimer);
|
||||
this.destroyEventTimer = null;
|
||||
}
|
||||
},
|
||||
{
|
||||
objectify: true,
|
||||
},
|
||||
) as Listener;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,9 +84,14 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
|||
} clients left.`,
|
||||
);
|
||||
this.emit('clientRemoved', client);
|
||||
if (!this.hasConnections() && !this.isClosing) {
|
||||
|
||||
if (this.canBeDestroyed()) {
|
||||
this.destroyEventTimer = setTimeout(() => {
|
||||
if (this.canBeDestroyed()) {
|
||||
this.destroy();
|
||||
}
|
||||
}, LIFETIME_WITHOUT_CLIENTS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,6 +108,7 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
|||
this.isClosing = true;
|
||||
this.doc.destroy();
|
||||
this.clients.forEach((value) => value.getTransporter().disconnect());
|
||||
this.clientAddedListener.off();
|
||||
this.emit('destroy');
|
||||
}
|
||||
|
||||
|
@ -150,4 +172,11 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
|||
connection.getTransporter().sendMessage(content);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if a realtime note is ready to get destroyed.
|
||||
*/
|
||||
private canBeDestroyed(): boolean {
|
||||
return !this.hasConnections() && !this.isClosing;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue