Skip to content

Commit 8a28dbf

Browse files
SevenWaysDPCeEvLoki-Afro
authored
BC-11049 - Improve logging in tldraw (#89)
* Extend log * includes fix from yjs/yhub#36 --------- Co-authored-by: Cedric Evers <12080057+CeEv@users.noreply.github.com> Co-authored-by: Phillip Wirth <phillip.wirth@dataport.de>
1 parent 4d47915 commit 8a28dbf

File tree

3 files changed

+185
-3
lines changed

3 files changed

+185
-3
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,4 @@
8888
"ws": "^8.18.3",
8989
"y-websocket": "^3.0.0"
9090
}
91-
}
91+
}

src/infra/y-redis/y-redis.client.spec.ts

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,22 @@ describe(YRedisClient.name, () => {
130130
expect(result).toEqual(expectedResult);
131131
});
132132
});
133+
134+
describe('when redis.readStreams returns null', () => {
135+
it('should return empty array', async () => {
136+
// @ts-ignore
137+
redis.readStreams.mockResolvedValueOnce([{ name: 'stream1', messages: null }]);
138+
139+
const result = await yRedisClient.getMessages([
140+
{
141+
key: 'stream1',
142+
id: '1',
143+
},
144+
]);
145+
146+
expect(result).toEqual([{ stream: 'stream1', messages: [], lastId: '' }]);
147+
});
148+
});
133149
});
134150

135151
describe('addMessage', () => {
@@ -350,4 +366,146 @@ describe(YRedisClient.name, () => {
350366
});
351367
});
352368
});
369+
370+
describe('logExistingPendingStructs', () => {
371+
describe('when document has no pending structures', () => {
372+
const setup = () => {
373+
const room = 'test-room';
374+
const docid = 'test-doc';
375+
const ydoc = new Doc();
376+
const logger = module.get(Logger);
377+
378+
return { room, docid, ydoc, logger };
379+
};
380+
381+
it('should not log warning', () => {
382+
const { room, docid, ydoc, logger } = setup();
383+
384+
// @ts-ignore it is private method
385+
yRedisClient.logExistingPendingStructs(room, docid, ydoc);
386+
387+
expect(logger.warning).not.toHaveBeenCalled();
388+
});
389+
});
390+
391+
describe('when document has pending structures', () => {
392+
const setup = () => {
393+
const room = 'test-room';
394+
const docid = 'test-doc';
395+
const ydoc = new Doc();
396+
const logger = module.get(Logger);
397+
const mockPendingStructs = {
398+
missing: new Map([
399+
[1, 5],
400+
[3, 2],
401+
]),
402+
update: new Uint8Array([1, 2, 3, 4, 5]),
403+
};
404+
405+
// Mock the ydoc.store.pendingStructs
406+
Object.defineProperty(ydoc.store, 'pendingStructs', {
407+
value: mockPendingStructs,
408+
writable: true,
409+
});
410+
411+
return { room, docid, ydoc, logger, mockPendingStructs };
412+
};
413+
414+
it('should log warning with detailed analysis', () => {
415+
const { room, docid, ydoc, logger } = setup();
416+
417+
// @ts-ignore it is private method
418+
yRedisClient.logExistingPendingStructs(room, docid, ydoc);
419+
420+
expect(logger.warning).toHaveBeenCalledWith(
421+
`Document ${room}/${docid} has pending structures. Details: ${JSON.stringify({
422+
missingStructs: [
423+
[1, 5],
424+
[3, 2],
425+
],
426+
updateSize: 5,
427+
})}`,
428+
);
429+
});
430+
});
431+
});
432+
433+
describe('analyzePendingStructs', () => {
434+
describe('when analyzing pending structures', () => {
435+
const setup = () => {
436+
const mockPendingStructs = {
437+
missing: new Map([
438+
[1, 5],
439+
[3, 2],
440+
[10, 1],
441+
]),
442+
update: new Uint8Array([1, 2, 3, 4, 5, 6, 7]),
443+
};
444+
445+
return { mockPendingStructs };
446+
};
447+
448+
it('should return correct analysis with missing structures and update size', () => {
449+
const { mockPendingStructs } = setup();
450+
451+
// @ts-ignore it is private method
452+
const result = yRedisClient.analyzePendingStructs(mockPendingStructs);
453+
454+
expect(result).toEqual({
455+
missingStructs: [
456+
[1, 5],
457+
[3, 2],
458+
[10, 1],
459+
],
460+
updateSize: 7,
461+
});
462+
});
463+
});
464+
465+
describe('when missing structures is empty', () => {
466+
const setup = () => {
467+
const mockPendingStructs = {
468+
missing: new Map(),
469+
update: new Uint8Array([1, 2, 3]),
470+
};
471+
472+
return { mockPendingStructs };
473+
};
474+
475+
it('should handle empty missing structures', () => {
476+
const { mockPendingStructs } = setup();
477+
478+
// @ts-ignore it is private method
479+
const result = yRedisClient.analyzePendingStructs(mockPendingStructs);
480+
481+
expect(result).toEqual({
482+
missingStructs: [],
483+
updateSize: 3,
484+
});
485+
});
486+
});
487+
488+
describe('when update array is empty', () => {
489+
const setup = () => {
490+
const mockPendingStructs = {
491+
missing: new Map([[5, 10]]),
492+
update: new Uint8Array([]),
493+
};
494+
495+
return { mockPendingStructs };
496+
};
497+
498+
it('should handle empty update array', () => {
499+
const { mockPendingStructs } = setup();
500+
501+
// @ts-ignore it is private method
502+
const result = yRedisClient.analyzePendingStructs(mockPendingStructs);
503+
504+
expect(result).toEqual({
505+
missingStructs: [[5, 10]],
506+
updateSize: 0,
507+
});
508+
});
509+
});
510+
});
353511
});

src/infra/y-redis/y-redis.client.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ export class YRedisClient implements OnModuleInit {
8585

8686
ydoc.once('afterTransaction', (tr) => {
8787
docChanged = tr.changed.size > 0;
88+
this.logExistingPendingStructs(room, docid, ydoc);
89+
90+
// https://github.com/yjs/y-redis/pull/36
91+
ydoc.destroy();
8892
});
8993

9094
ydoc.transact(() => {
@@ -102,11 +106,31 @@ export class YRedisClient implements OnModuleInit {
102106
streamName,
103107
});
104108

109+
return response;
110+
}
111+
112+
private logExistingPendingStructs(room: string, docid: string, ydoc: Doc): void {
105113
if (ydoc.store.pendingStructs !== null) {
106-
this.logger.warning(`Document ${room} has pending structs ${JSON.stringify(ydoc.store.pendingStructs)}.`);
114+
const pendingAnalysis = this.analyzePendingStructs(ydoc.store.pendingStructs);
115+
116+
this.logger.warning(
117+
`Document ${room}/${docid} has pending structures. Details: ${JSON.stringify({
118+
...pendingAnalysis,
119+
})}`,
120+
);
107121
}
122+
}
108123

109-
return response;
124+
private analyzePendingStructs(pendingStructs: { missing: Map<number, number>; update: Uint8Array }): {
125+
missingStructs: Iterable<[number, number]>;
126+
updateSize: number;
127+
} {
128+
const missingStructs = Array.from(pendingStructs.missing.entries());
129+
130+
return {
131+
missingStructs,
132+
updateSize: pendingStructs.update.length,
133+
};
110134
}
111135

112136
public async destroy(): Promise<void> {

0 commit comments

Comments
 (0)