Skip to content

Commit

Permalink
feat: improve performance
Browse files Browse the repository at this point in the history
  • Loading branch information
juanfran committed Feb 6, 2025
1 parent 36b4425 commit a7045d8
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 72 deletions.
15 changes: 8 additions & 7 deletions apps/api/src/app/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ export class Client {

this.socket.join(this.boardId);

this.send(this.getSetStateAction(board));
this.sendToClient(this.getSetStateAction(board));
} catch (e) {
console.error(e);
}
Expand Down Expand Up @@ -332,24 +332,25 @@ export class Client {
};
}

send(msg: unknown) {
sendToClient(msg: unknown) {
this.socket.emit('board', [msg]);
}

sendAll(boardId: string, msg: unknown) {
this.pendingMsgs.push(msg);

if (this.sendTimeout) {
return;
}

this.sendTimeout = setTimeout(() => {
this.socket.emit('board', this.pendingMsgs);
// send to all users in boardId but client
this.socket.to(boardId).emit('board', this.pendingMsgs);
this.pendingMsgs = [];
this.sendTimeout = undefined;
}, 50);
}

sendAll(boardId: string, message: unknown) {
this.socket.to(boardId).emit('board', [message]);
}

private updateStateWithActions(actions: StateActions[]) {
if (!this.boardId) {
return;
Expand Down
100 changes: 72 additions & 28 deletions apps/api/src/simulate-users.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,43 @@
import { io } from 'socket.io-client';
import { v4 as uuidv4 } from 'uuid';

const NUM_CONNECTIONS = 25;
/*
Tested scenario:
50+fps
Production build false
const NUM_CONNECTIONS = 100;
const PORT = 8000;
const STEP_DELAY = 17;
const STEP_DELAY = 50;
const SQUARE_SIZE = 2000;
const MAX_EMITS = 250;
Text on
Color on
Position on
Without provideStoreDevtools
*/

const NUM_CONNECTIONS = 100;
const STEP_DELAY = 50;
const SQUARE_SIZE = 2000;
const MAX_EMITS = 250;

const PORT = 8000;
const AUTH = '';
const BOARD_ID = '4faa8be4-4da4-4935-b23e-da21a3699530';
const BOARD_ID = '';

function randomRange(min: number, max: number) {
return Math.random() * (max - min) + min;
}

function createClient(index: number) {
let interval: NodeJS.Timeout;
let emits = 0;
let init = false;
const noteId = uuidv4();
const ownerId = `user_${index}_${Date.now()}`;
const randomInitX = Math.random() * 1000;
Expand All @@ -23,27 +51,6 @@ function createClient(index: number) {
});

function start() {
ws.emit('board', [
{
data: {
type: 'note',
id: noteId,
content: {
text: '',
votes: [],
emojis: [],
drawing: [],
width: 300,
height: 300,
ownerId: ownerId,
layer: 0,
position: { x: initialX, y: initialY },
},
},
op: 'add',
},
]);

interval = setInterval(() => {
const progress = (Date.now() % 4000) / 1000;
const side = Math.floor(progress);
Expand Down Expand Up @@ -75,13 +82,22 @@ function createClient(index: number) {
type: 'note',
id: noteId,
content: {
text: `Client ${index}`,
text: `Client ${index} ${Math.random()}`,
position: { x, y },
color: `#${Math.floor(Math.random() * 16777215).toString(16)}`,
},
},
position: randomRange(0, NUM_CONNECTIONS - 1),
op: 'patch',
},
]);

emits++;

if (emits >= MAX_EMITS) {
clearInterval(interval);
console.log(`Client ${index} finished`);
}
}, STEP_DELAY);
}

Expand All @@ -95,9 +111,37 @@ function createClient(index: number) {
},
]);

setTimeout(() => {
start();
}, 2000);
ws.on('board', (response) => {
if (response && response[0].type === '[Board] State action' && !init) {
init = true;

ws.emit('board', [
{
data: {
type: 'note',
id: noteId,
content: {
text: '',
votes: [],
emojis: [],
drawing: [],
width: 300,
height: 300,
ownerId: ownerId,
layer: 0,
color: `#${Math.floor(Math.random() * 16777215).toString(16)}`,
position: { x: initialX, y: initialY },
},
},
op: 'add',
},
]);

setTimeout(() => {
start();
}, 500);
}
});

ws.on('disconnect', () => {
clearInterval(interval);
Expand Down
28 changes: 2 additions & 26 deletions apps/web/src/app/modules/board/board/board.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
AfterViewInit,
HostListener,
OnDestroy,
HostBinding,
inject,
DestroyRef,
computed,
Expand Down Expand Up @@ -133,17 +132,10 @@ import { TimerComponent } from '../components/timer/timer.component';
'[class.node-selection-disabled]': '!nodeSelectionEnabled()',
'[class.readonly]': 'isReadonlyUser()',
'[class.edit-mode]': 'boardMode() === 1',
'[class.following-user]': 'followUser()',
},
})
export class BoardComponent implements AfterViewInit, OnDestroy {
@HostBinding('class.following-user')
public get isFollowingUser() {
let hasPeople = false;
this.userToFollow$.subscribe((user) => {
hasPeople = !!user;
});
return hasPeople;
}
public el = inject(ElementRef);
private wsService = inject(WsService);
private store = inject(Store);
Expand Down Expand Up @@ -178,7 +170,7 @@ export class BoardComponent implements AfterViewInit, OnDestroy {
public readonly boardTitle = this.store.selectSignal(
boardPageFeature.selectName,
);
public readonly folloUser = this.store.selectSignal(
public readonly followUser = this.store.selectSignal(
boardPageFeature.selectFollow,
);
public readonly loaded = this.store.selectSignal(
Expand Down Expand Up @@ -213,25 +205,9 @@ export class BoardComponent implements AfterViewInit, OnDestroy {
workLayer = viewChild.required<ElementRef<HTMLElement>>('workLayer');

dots = viewChild.required<ElementRef<HTMLElement>>('dots');
#store = inject(Store);
#boardFacade = inject(BoardFacade);
userToFollow$ = this.#store.select(boardPageFeature.selectFollow).pipe(
switchMap((follow) => {
return this.#boardFacade.getUsers().pipe(
map((users) => {
return users.find((user) => user.id === follow)?.content;
}),
);
}),
);

timer = this.#boardFacade.timer;

@HostBinding('class.follow-user')
public get isFollowUser() {
return this.folloUser();
}

@HostListener('dblclick', ['$event'])
public dblclick(event: MouseEvent) {
if (event.target !== this.el.nativeElement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,6 @@ export class BoardToolbarComponent {
}

emojiSelected(emojiEvent: EmojiClickEvent) {
console.log(emojiEvent);
this.#store.dispatch(
BoardPageActions.selectEmoji({
emoji: emojiEvent.detail.emoji as NativeEmoji,
Expand Down
19 changes: 10 additions & 9 deletions apps/web/src/app/modules/board/effects/board.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
auditTime,
take,
withLatestFrom,
bufferTime,
} from 'rxjs/operators';
import { BoardActions } from '../actions/board.actions';
import { BoardPageActions } from '../actions/board-page.actions';
Expand Down Expand Up @@ -133,18 +134,18 @@ export class BoardEffects {
public stateAction$ = createEffect(() => {
return this.actions$.pipe(
ofType(BoardActions.stateAction),
map(({ data }) => {
const hasNewUser = data.some(
(it) => it.op === 'add' && it.data.type === 'user',
);
bufferTime(20),
filter((actions) => actions.length > 0),
map((actions) => {
const aggregatedData = actions.flatMap((action) => action.data);

this.boardFacade.applyActions(data);
const hasNewUser = aggregatedData.some(
(item) => item.op === 'add' && item.data.type === 'user',
);

if (hasNewUser) {
return BoardPageActions.newUserJoined();
}
this.boardFacade.applyActions(aggregatedData);

return null;
return hasNewUser ? BoardPageActions.newUserJoined() : null;
}),
filterNil(),
);
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/app/services/board-facade.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
import { syncNodeBox } from '@tapiz/sync-node-box';
import {
Observable,
animationFrameScheduler,
auditTime,
combineLatest,
distinctUntilChanged,
map,
Expand Down Expand Up @@ -75,7 +77,7 @@ export class BoardFacade {
}

getNodes() {
return this.board.sync();
return this.board.sync().pipe(auditTime(20, animationFrameScheduler));
}

patchHistory(fn: Parameters<typeof this.board.patchHistory>[0]) {
Expand Down

0 comments on commit a7045d8

Please sign in to comment.