Skip to content

Commit 79e7d1b

Browse files
authored
Implement basic memory management for OT server (#107)
* Implement basic memory management for OT server * Fix lint & tests * changes after review
1 parent e3bf80c commit 79e7d1b

File tree

15 files changed

+278
-15
lines changed

15 files changed

+278
-15
lines changed

.dockerignore

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Include any files or directories that you don't want to be copied to your
2+
# container here (e.g., local build artifacts, temporary files, etc.).
3+
#
4+
# For more help, visit the .dockerignore file reference guide at
5+
# https://docs.docker.com/go/build-context-dockerignore/
6+
7+
**/.classpath
8+
**/.dockerignore
9+
**/.env
10+
**/.git
11+
**/.gitignore
12+
**/.project
13+
**/.settings
14+
**/.toolstarget
15+
**/.vs
16+
**/.vscode
17+
**/.next
18+
**/.cache
19+
**/*.*proj.user
20+
**/*.dbmdl
21+
**/*.jfm
22+
**/charts
23+
**/docker-compose*
24+
**/compose*
25+
**/Dockerfile*
26+
**/node_modules
27+
**/npm-debug.log
28+
**/obj
29+
**/secrets.dev.yaml
30+
**/values.dev.yaml
31+
**/build
32+
**/dist
33+
**/coverage
34+
**/reports
35+
**/tsconfig.build.tsbuildinfo
36+
LICENSE
37+
**/README.md

Dockerfile

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# syntax=docker/dockerfile:1
2+
3+
ARG NODE_VERSION=20.9.0
4+
5+
################################################################################
6+
# Use node image for base image for all stages.
7+
FROM node:${NODE_VERSION}-alpine AS base
8+
9+
# Set working directory for all build stages.
10+
WORKDIR /usr/src/app/
11+
12+
RUN corepack enable
13+
RUN corepack prepare [email protected] --activate
14+
15+
################################################################################
16+
# Create a stage for installing production dependecies.
17+
FROM base AS deps
18+
19+
# Download dependencies as a separate step to take advantage of Docker's caching.
20+
COPY .yarnrc.yml package.json yarn.lock ./
21+
COPY .yarn .yarn
22+
COPY packages/model/package.json packages/model/package.json
23+
COPY packages/sdk/package.json packages/sdk/package.json
24+
COPY packages/collaboration-manager/package.json packages/collaboration-manager/package.json
25+
COPY packages/ot-server/package.json packages/ot-server/package.json
26+
27+
RUN yarn workspaces focus @editorjs/ot-server
28+
29+
################################################################################
30+
# Create a stage for building the application.
31+
FROM deps AS build
32+
33+
34+
# Copy the rest of the source files into the image.
35+
COPY packages/model packages/model
36+
COPY packages/sdk packages/sdk
37+
COPY packages/collaboration-manager packages/collaboration-manager
38+
COPY packages/ot-server packages/ot-server
39+
40+
# Run the build script.
41+
RUN yarn workspace @editorjs/ot-server build
42+
43+
################################################################################
44+
# Create a new stage to run the application with minimal runtime dependencies
45+
# where the necessary files are copied from the build stage.
46+
FROM base AS final
47+
48+
ARG NODE_ENV=production
49+
ARG WSS_PORT=8080
50+
51+
# Use production node environment by default.
52+
ENV NODE_ENV $NODE_ENV
53+
ENV WSS_PORT $WSS_PORT
54+
55+
COPY --from=build /usr/src/app/.yarn /usr/src/app/.yarn
56+
COPY --from=build /usr/src/app/package.json /usr/src/app/.yarnrc.yml /usr/src/app/yarn.lock /usr/src/app/
57+
COPY --from=build /usr/src/app/node_modules /usr/src/app/node_modules
58+
59+
COPY --from=build /usr/src/app/packages/model/dist /usr/src/app/packages/model/dist
60+
COPY --from=build /usr/src/app/packages/model/package.json /usr/src/app/packages/model/package.json
61+
62+
COPY --from=build /usr/src/app/packages/sdk/dist /usr/src/app/packages/sdk/dist
63+
COPY --from=build /usr/src/app/packages/sdk/package.json /usr/src/app/packages/sdk/package.json
64+
65+
COPY --from=build /usr/src/app/packages/collaboration-manager/dist /usr/src/app/packages/collaboration-manager/dist
66+
COPY --from=build /usr/src/app/packages/collaboration-manager/package.json /usr/src/app/packages/collaboration-manager/package.json
67+
68+
COPY --from=build /usr/src/app/packages/ot-server/dist /usr/src/app/packages/ot-server/dist
69+
COPY --from=build /usr/src/app/packages/ot-server/package.json /usr/src/app/packages/ot-server/package.json
70+
71+
72+
# Run the application as a non-root user.
73+
USER node
74+
75+
RUN find ./
76+
77+
# Expose the port that the application listens on.
78+
EXPOSE $WSS_PORT
79+
80+
# Run the application.
81+
CMD yarn workspace @editorjs/ot-server run start

compose.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
services:
2+
wsserver:
3+
build:
4+
context: .
5+
args:
6+
NODE_ENV: production
7+
WSS_PORT: ${WSS_PORT}
8+
ports:
9+
- ${WSS_PORT}:${WSS_PORT}
10+
11+

packages/collaboration-manager/src/CollaborationManager.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
22
BlockAddedEvent, type BlockNodeSerialized,
3-
BlockRemovedEvent, type DocumentId,
3+
BlockRemovedEvent,
44
type EditorJSModel,
55
EventType,
66
type ModelEvents,
@@ -59,7 +59,12 @@ export class CollaborationManager {
5959
this.#model = model;
6060
this.#undoRedoManager = new UndoRedoManager();
6161
model.addEventListener(EventType.Changed, this.#handleEvent.bind(this));
62+
}
6263

64+
/**
65+
* Connects to OT server
66+
*/
67+
public connect(): void {
6368
if (this.#config.collaborationServer === undefined) {
6469
return;
6570
}
@@ -79,7 +84,7 @@ export class CollaborationManager {
7984
}
8085
);
8186

82-
void this.#client.connectDocument(this.#config.documentId! as DocumentId);
87+
void this.#client.connectDocument(this.#model.serialized);
8388
}
8489

8590
/**

packages/collaboration-manager/src/client/OTClient.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { DocumentId, EditorDocumentSerialized } from '@editorjs/model';
1+
import type { EditorDocumentSerialized } from '@editorjs/model';
22
import { Operation, type SerializedOperation } from '../Operation.js';
33
import type { HandshakeMessage, HandshakePayload, Message, OperationMessage } from './Message.js';
44
import { MessageType } from './MessageType.js';
@@ -90,9 +90,9 @@ export class OTClient {
9090
/**
9191
* Sends handshake event to the server to connect the client to passed document
9292
*
93-
* @param documentId - document identifier
93+
* @param document - serialized document data
9494
*/
95-
public async connectDocument(documentId: DocumentId): Promise<void> {
95+
public async connectDocument(document: EditorDocumentSerialized): Promise<void> {
9696
const ws = await this.#ws;
9797

9898
this.#handshake = new Promise(resolve => {
@@ -125,9 +125,10 @@ export class OTClient {
125125
ws.send(JSON.stringify({
126126
type: MessageType.Handshake,
127127
payload: {
128-
document: documentId,
128+
document: document.identifier,
129129
userId: this.#userId,
130130
rev: this.#rev,
131+
data: document,
131132
} as HandshakePayload,
132133
}));
133134
}

packages/core/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ export default class Core {
148148
.then(() => {
149149
this.#model.initializeDocument({ blocks });
150150
})
151+
.then(() => {
152+
this.#collaborationManager.connect();
153+
})
151154
.catch((error) => {
152155
console.error('Editor.js initialization failed', error);
153156
});

packages/model/src/entities/EditorDocument/EditorDocument.spec.ts

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
TuneModifiedEvent
1313
} from '../../EventBus/events/index.js';
1414
import { EventAction } from '../../EventBus/types/EventAction.js';
15-
import { jest } from '@jest/globals';
15+
import { describe, jest } from '@jest/globals';
1616

1717
jest.mock('../BlockNode');
1818

@@ -50,6 +50,72 @@ describe('EditorDocument', () => {
5050
jest.clearAllMocks();
5151
});
5252

53+
describe('.initalize()', () => {
54+
it('should initalize the document', () => {
55+
const doc = new EditorDocument({
56+
identifier: 'document',
57+
properties: {
58+
readOnly: false,
59+
},
60+
});
61+
62+
const blocks = [ {
63+
name: 'header' as BlockToolName,
64+
data: {
65+
text: {
66+
$t: 't',
67+
value: 'some long text',
68+
fragments: [],
69+
},
70+
},
71+
tunes: {},
72+
} ];
73+
74+
doc.initialize(blocks);
75+
76+
expect(doc.serialized.blocks).toHaveLength(blocks.length);
77+
});
78+
79+
it('should clear the document before initialization', () => {
80+
const doc = new EditorDocument({
81+
identifier: 'document',
82+
properties: {
83+
readOnly: false,
84+
},
85+
});
86+
87+
const blocks = [ {
88+
name: 'header' as BlockToolName,
89+
data: {
90+
text: {
91+
$t: 't',
92+
value: 'some long text',
93+
fragments: [],
94+
},
95+
},
96+
tunes: {},
97+
} ];
98+
99+
doc.initialize(blocks);
100+
101+
blocks[0].data.text.value = 'another text';
102+
103+
doc.initialize(blocks);
104+
105+
expect(doc.serialized.blocks).toHaveLength(1);
106+
});
107+
});
108+
109+
describe('.clear()', () => {
110+
it('should clear the document', () => {
111+
const doc = createEditorDocumentWithSomeBlocks();
112+
113+
doc.clear();
114+
115+
expect(doc.serialized.blocks).toHaveLength(0);
116+
});
117+
});
118+
53119
describe('.length', () => {
54120
it('should return the number of blocks in the document', () => {
55121
// Arrange

packages/model/src/entities/EditorDocument/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ export class EditorDocument extends EventBus {
8282
* @param blocks - document serialized blocks
8383
*/
8484
public initialize(blocks: BlockNodeSerialized[]): void {
85+
this.clear();
86+
8587
blocks.forEach((block) => {
8688
this.addBlock(block);
8789
});
@@ -417,6 +419,13 @@ export class EditorDocument extends EventBus {
417419
}
418420
}
419421

422+
/**
423+
* Clear all document's blocks (doesn't emit an event)
424+
*/
425+
public clear(): void {
426+
Array.from(this.#children).forEach(() => this.removeBlock(0));
427+
}
428+
420429
/**
421430
* Listens to BlockNode events and bubbles them to the EditorDocument
422431
*

packages/ot-server/.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
WSS_PORT=8080

packages/ot-server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"dependencies": {
1717
"@editorjs/collaboration-manager": "workspace:^",
1818
"@editorjs/model": "workspace:^",
19+
"dotenv": "^16.4.7",
1920
"ws": "^8.18.1"
2021
},
2122
"devDependencies": {

0 commit comments

Comments
 (0)