From 5733748e8a3bb44bcba2e5d1c70ed40d7d6a5c8d Mon Sep 17 00:00:00 2001 From: minjungw00 Date: Mon, 18 Nov 2024 04:04:31 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20conflict=20=ED=95=B4=EA=B2=B0=20?= =?UTF-8?q?=EC=A4=91=20=EB=B0=9C=EC=83=9D=ED=95=9C=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/crdt/crdt.service.ts | 92 +++++++++++++++------------------ 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/server/src/crdt/crdt.service.ts b/server/src/crdt/crdt.service.ts index ee955409..52b56106 100644 --- a/server/src/crdt/crdt.service.ts +++ b/server/src/crdt/crdt.service.ts @@ -1,52 +1,61 @@ -// src/crdt/crdt.service.ts import { Injectable, OnModuleInit } from "@nestjs/common"; import { InjectModel } from "@nestjs/mongoose"; -import { Doc, DocumentDocument } from "../schemas/document.schema"; +import { Doc, DocumentDocument } from "./schemas/document.schema"; import { Model } from "mongoose"; -import { CRDT } from "@noctaCrdt/crdt"; -import { NodeId, Node } from "@noctaCrdt/Node"; - -interface RemoteInsertOperation { - node: Node; -} - -interface RemoteDeleteOperation { - targetId: NodeId | null; - clock: number; -} +import { BlockCRDT } from "@noctaCrdt/Crdt"; +import { RemoteInsertOperation, RemoteDeleteOperation } from "@noctaCrdt/Interfaces"; +import { CharId } from "@noctaCrdt/NodeId"; +import { Char } from "@noctaCrdt/Node"; @Injectable() export class CrdtService implements OnModuleInit { - private crdt: CRDT; + private crdt: BlockCRDT; constructor(@InjectModel(Doc.name) private documentModel: Model) { - this.crdt = new CRDT(0); // 초기 클라이언트 ID는 0으로 설정 (서버 자체) + this.crdt = new BlockCRDT(0); } async onModuleInit() { try { const doc = await this.getDocument(); + if (doc && doc.crdt) { + this.crdt = new BlockCRDT(0); + try { + // 저장된 CRDT 상태를 복원 + this.crdt.clock = doc.crdt.clock; + this.crdt.client = doc.crdt.client; - if (doc && doc.content) { - this.crdt = new CRDT(0); - let contentArray: string[]; + // LinkedList 복원 + if (doc.crdt.LinkedList.head) { + this.crdt.LinkedList.head = new CharId( + doc.crdt.LinkedList.head.clock, + doc.crdt.LinkedList.head.client, + ); + } - try { - contentArray = JSON.parse(doc.content) as string[]; + this.crdt.LinkedList.nodeMap = {}; + for (const [key, node] of Object.entries(doc.crdt.LinkedList.nodeMap)) { + const reconstructedNode = new Char( + node.value, + new CharId(node.id.clock, node.id.client), + ); + + if (node.next) { + reconstructedNode.next = new CharId(node.next.clock, node.next.client); + } + if (node.prev) { + reconstructedNode.prev = new CharId(node.prev.clock, node.prev.client); + } + + this.crdt.LinkedList.nodeMap[key] = reconstructedNode; + } } catch (e) { - console.error("Invalid JSON in document content:", doc.content); + console.error("Error reconstructing CRDT:", e); } - contentArray.forEach((char, index) => { - this.crdt.localInsert(index, char); - }); } } catch (error) { console.error("Error during CrdtService initialization:", error); } } - - /** - * MongoDB에서 문서를 가져옵니다. - */ async getDocument(): Promise { let doc = await this.documentModel.findOne(); if (!doc) { @@ -56,49 +65,32 @@ export class CrdtService implements OnModuleInit { return doc; } - /** - * MongoDB에 문서를 업데이트합니다. - */ async updateDocument(): Promise { - const content = JSON.stringify(this.crdt.spread()); + const serializedCRDT = this.crdt.serialize(); const doc = await this.documentModel.findOneAndUpdate( {}, - { content, updatedAt: new Date() }, + { crdt: serializedCRDT, updatedAt: new Date() }, { new: true, upsert: true }, ); - ("d"); - if (!doc) { - throw new Error("문서가 저장되지 않았습니다."); - } + if (!doc) throw new Error("문서 저장 실패"); return doc; } - /** - * 삽입 연산을 처리하고 문서를 업데이트합니다. - */ async handleInsert(operation: RemoteInsertOperation): Promise { this.crdt.remoteInsert(operation); await this.updateDocument(); } - /** - * 삭제 연산을 처리하고 문서를 업데이트합니다. - */ async handleDelete(operation: RemoteDeleteOperation): Promise { this.crdt.remoteDelete(operation); await this.updateDocument(); } - /** - * 현재 CRDT의 텍스트를 반환합니다. - */ getText(): string { return this.crdt.read(); } - /** - * CRDT 인스턴스를 반환하는 Getter 메서드 - */ - getCRDT(): CRDT { + + getCRDT(): BlockCRDT { return this.crdt; } }