Skip to content

Commit

Permalink
fix remove history
Browse files Browse the repository at this point in the history
  • Loading branch information
vieiralucas committed Jan 14, 2025
1 parent 242e07a commit dfb860b
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 65 deletions.
32 changes: 31 additions & 1 deletion apps/api/src/yjs/v2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -810,17 +810,47 @@ export class WSSharedDocV2 {
loadStateResult,
persistor
)
await doc.init()
logger().trace(
{
id: doc.id,
documentId: doc.documentId,
workspaceId: doc.workspaceId,
applyUpdateLatency: loadStateResult.applyUpdateLatency,
clockUpdatedAt: loadStateResult.clockUpdatedAt,
},
'Loadded YDoc'
)

if (
loadStateResult.applyUpdateLatency > 1000 &&
Date.now() - loadStateResult.clockUpdatedAt.getTime() >
1000 * 60 * 60 * 24
) {
// if the latency is more than 1 second and the clock was updated more than 24 hours ago
// remove history to reduce the size of the document and improve load performance
logger().info(
{
id: doc.id,
documentId: doc.documentId,
workspaceId: doc.workspaceId,
applyUpdateLatency: loadStateResult.applyUpdateLatency,
clockUpdatedAt: loadStateResult.clockUpdatedAt,
},
'Removing history from YDoc to reduce size and improve load performance'
)
await doc.removeHistory()
logger().info(
{
id: doc.id,
documentId: doc.documentId,
workspaceId: doc.workspaceId,
},
'Removed history from YDoc to reduce size and improve load performance'
)
}

await doc.init()

return doc
}
}
Expand Down
40 changes: 22 additions & 18 deletions apps/web/src/hooks/useYDoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,26 @@ import Dexie, { EntityTable } from 'dexie'
import { useReusableComponents } from './useReusableComponents'

const db = new Dexie('YjsDatabase') as Dexie & {
yDocs: EntityTable<{ id: string; data: Uint8Array }, 'id'>
yDocs: EntityTable<{ id: string; data: Uint8Array; clock: number }, 'id'>
}

db.version(1).stores({
yDocs: 'id, data',
yDocs: 'id, data, clock',
})

function persistYDoc(id: string, yDoc: Y.Doc) {
function persistYDoc(id: string, yDoc: Y.Doc, clock: number) {
const data = Y.encodeStateAsUpdate(yDoc)
db.yDocs.put({ id, data })
db.yDocs.put({ id, data, clock })
}

function restoreYDoc(id: string): [Y.Doc, Promise<void>] {
function restoreYDoc(
id: string,
clock: number
): [{ clock: number; yDoc: Y.Doc }, Promise<void>] {
const yDoc = new Y.Doc()

const restore = db.yDocs
.get(id)
.get({ id, clock })
.then((item) => {
if (item) {
Y.applyUpdate(yDoc, item.data)
Expand All @@ -48,13 +51,13 @@ function restoreYDoc(id: string): [Y.Doc, Promise<void>] {
}
})

return [yDoc, restore]
return [{ yDoc, clock }, restore]
}

const cache = new LRUCache<string, Y.Doc>({
const cache = new LRUCache<string, { clock: number; yDoc: Y.Doc }>({
max: 10,

dispose: (yDoc) => {
dispose: ({ yDoc }) => {
yDoc.destroy()
},
})
Expand All @@ -63,6 +66,7 @@ type GetYDocResult = {
id: string
cached: boolean
yDoc: Y.Doc
clock: number
restore: Promise<void>
}

Expand All @@ -73,18 +77,18 @@ function getYDoc(
publishedAt: string | null
): GetYDocResult {
const id = getDocId(documentId, isDataApp, clock, publishedAt)
let yDoc = cache.get(id)
const cached = Boolean(yDoc)
let fromCache = cache.get(id)
const cached = Boolean(fromCache)
let restore = Promise.resolve()

if (!yDoc) {
const restoreResult = restoreYDoc(id)
yDoc = restoreResult[0]
if (!fromCache) {
const restoreResult = restoreYDoc(id, clock)
fromCache = restoreResult[0]
restore = restoreResult[1]
cache.set(id, yDoc)
cache.set(id, fromCache)
}

return { id, cached, yDoc, restore }
return { id, cached, yDoc: fromCache.yDoc, clock: fromCache.clock, restore }
}

export function useYDoc(
Expand Down Expand Up @@ -112,14 +116,14 @@ export function useYDoc(
if (isFirst.current) {
isFirst.current = false
return () => {
persistYDoc(id, yDoc)
persistYDoc(id, yDoc, clock)
}
}

const next = getYDoc(documentId, isDataApp, clock, publishedAt)
setYDoc(next)
return () => {
persistYDoc(next.id, next.yDoc)
persistYDoc(next.id, next.yDoc, next.clock)
}
}, [documentId, isDataApp, clock, publishedAt, userId])

Expand Down
47 changes: 1 addition & 46 deletions packages/editor/src/blocks/richText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
duplicateBaseAttributes,
} from './index.js'
import { ExecutionStatus } from '../execution/item.js'
import { duplicateYXmlFragment } from '../index.js'

export type RichTextBlock = BaseBlock<BlockType.RichText> & {
content: Y.XmlFragment
Expand Down Expand Up @@ -71,49 +72,3 @@ export function getRichTextBlockExecStatus(
): ExecutionStatus {
return 'completed'
}

function duplicateYXmlFragment(fragment: Y.XmlFragment): Y.XmlFragment {
const newFragment = new Y.XmlFragment()

function cloneElement(element: Y.XmlElement) {
const newElement = new Y.XmlElement(element.nodeName)
const attrs = element.getAttributes()
for (const key in attrs) {
const value = attrs[key]
if (value === undefined) {
continue
}

newElement.setAttribute(key, value)
}

const children: Array<Y.XmlElement | Y.XmlText | Y.XmlHook> = []
let child = element.firstChild
while (child) {
children.push(cloneNode(child))
child = child.nextSibling
}

// @ts-ignore
newElement.insert(0, children)

return newElement
}

function cloneNode(node: Y.XmlElement | Y.XmlText | Y.XmlHook) {
if (node instanceof Y.XmlElement) {
return cloneElement(node)
}

return node.clone()
}

// adapted from https://github.com/yjs/yjs/blob/e348255bb125e992eb661889e64a10efd7319172/src/types/YXmlFragment.js#L168-L173
newFragment.insert(
0,
// @ts-ignore
fragment.toArray().map(cloneNode)
)

return newFragment
}
46 changes: 46 additions & 0 deletions packages/editor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,49 @@ export function getDataframe(

return df ?? null
}

export function duplicateYXmlFragment(fragment: Y.XmlFragment): Y.XmlFragment {
const newFragment = new Y.XmlFragment()

function cloneElement(element: Y.XmlElement) {
const newElement = new Y.XmlElement(element.nodeName)
const attrs = element.getAttributes()
for (const key in attrs) {
const value = attrs[key]
if (value === undefined) {
continue
}

newElement.setAttribute(key, value)
}

const children: Array<Y.XmlElement | Y.XmlText | Y.XmlHook> = []
let child = element.firstChild
while (child) {
children.push(cloneNode(child))
child = child.nextSibling
}

// @ts-ignore
newElement.insert(0, children)

return newElement
}

function cloneNode(node: Y.XmlElement | Y.XmlText | Y.XmlHook) {
if (node instanceof Y.XmlElement) {
return cloneElement(node)
}

return node.clone()
}

// adapted from https://github.com/yjs/yjs/blob/e348255bb125e992eb661889e64a10efd7319172/src/types/YXmlFragment.js#L168-L173
newFragment.insert(
0,
// @ts-ignore
fragment.toArray().map(cloneNode)
)

return newFragment
}

0 comments on commit dfb860b

Please sign in to comment.