mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-22 01:36:29 -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(sendMessage1Spy).toHaveBeenNthCalledWith(2, deletedMessage);
|
||||||
expect(sendMessage2Spy).toHaveBeenNthCalledWith(1, 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 { Message, MessageType, RealtimeDoc } from '@hedgedoc/commons';
|
||||||
import { Logger } from '@nestjs/common';
|
import { Logger } from '@nestjs/common';
|
||||||
import { EventEmitter2, EventMap } from 'eventemitter2';
|
import { EventEmitter2, EventMap, Listener } from 'eventemitter2';
|
||||||
|
|
||||||
import { Note } from '../../notes/note.entity';
|
import { Note } from '../../notes/note.entity';
|
||||||
import { RealtimeConnection } from './realtime-connection';
|
import { RealtimeConnection } from './realtime-connection';
|
||||||
|
@ -19,6 +19,8 @@ export interface RealtimeNoteEventMap extends EventMap {
|
||||||
yDocUpdate: (update: number[], origin: unknown) => void;
|
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.
|
* Represents a note currently being edited by a number of clients.
|
||||||
*/
|
*/
|
||||||
|
@ -26,7 +28,9 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
||||||
protected logger: Logger;
|
protected logger: Logger;
|
||||||
private readonly doc: RealtimeDoc;
|
private readonly doc: RealtimeDoc;
|
||||||
private readonly clients = new Set<RealtimeConnection>();
|
private readonly clients = new Set<RealtimeConnection>();
|
||||||
|
private readonly clientAddedListener: Listener;
|
||||||
private isClosing = false;
|
private isClosing = false;
|
||||||
|
private destroyEventTimer: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly note: Note,
|
private readonly note: Note,
|
||||||
|
@ -40,6 +44,18 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`New realtime session for note ${note.id} created. Length of initial content: ${length} characters`,
|
`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,8 +84,13 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
||||||
} clients left.`,
|
} clients left.`,
|
||||||
);
|
);
|
||||||
this.emit('clientRemoved', client);
|
this.emit('clientRemoved', client);
|
||||||
if (!this.hasConnections() && !this.isClosing) {
|
|
||||||
this.destroy();
|
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.isClosing = true;
|
||||||
this.doc.destroy();
|
this.doc.destroy();
|
||||||
this.clients.forEach((value) => value.getTransporter().disconnect());
|
this.clients.forEach((value) => value.getTransporter().disconnect());
|
||||||
|
this.clientAddedListener.off();
|
||||||
this.emit('destroy');
|
this.emit('destroy');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,4 +172,11 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
||||||
connection.getTransporter().sendMessage(content);
|
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