From 576db4267edde734112dc726fa8532177881025a Mon Sep 17 00:00:00 2001 From: Duane Johnson Date: Thu, 8 Oct 2020 15:00:26 -0600 Subject: [PATCH 1/3] Add persistence-synced Y.Doc accessor functions --- bin/utils.js | 50 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/bin/utils.js b/bin/utils.js index feb49b6..344a2a8 100644 --- a/bin/utils.js +++ b/bin/utils.js @@ -33,18 +33,33 @@ if (typeof persistenceDir === 'string') { const LeveldbPersistence = require('y-leveldb').LeveldbPersistence const ldb = new LeveldbPersistence(persistenceDir) persistence = { - bindState: async (docName, ydoc) => { + // Sync in-memory and ldb state + mutualSync: async (docName, ydoc) => { const persistedYdoc = await ldb.getYDoc(docName) const newUpdates = Y.encodeStateAsUpdate(ydoc) ldb.storeUpdate(docName, newUpdates) Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(persistedYdoc)) - ydoc.on('update', update => { + }, + + bindState: async (docName, ydoc) => { + await persistence.mutualSync(docName, ydoc) + ydoc.on('update', (update) => { ldb.storeUpdate(docName, update) }) }, - writeState: async (docName, ydoc) => {} + + writeState: async (docName, ydoc) => {}, + + // Gets a Y.Doc by name, whether in memory or on disk + getYDoc: async (docName) => { + const ydoc = findOrCreateDoc(docName) + await persistence.mutualSync(docName, ydoc) + return ydoc + }, } } +// If enabled, provide access to persistence functions +exports.persistence = persistence /** * @param {{bindState: function(string,WSSharedDoc):void, @@ -130,6 +145,24 @@ class WSSharedDoc extends Y.Doc { } } +/** + * @param {string} docName - the name of the Y.Doc to find or create + * @param {boolean} gc - whether to allow gc on the doc (applies only when created) + */ +function findOrCreateDoc(docName, gc = true) { + // get doc, create if it does not exist yet + return map.setIfUndefined(docs, docName, () => { + const doc = new WSSharedDoc(docName) + doc.gc = gc + if (persistence !== null) { + persistence.bindState(docName, doc) + } + docs.set(docName, doc) + return doc + }) +} +exports.findOrCreateDoc = findOrCreateDoc + /** * @param {any} conn * @param {WSSharedDoc} doc @@ -196,6 +229,7 @@ const send = (doc, conn, m) => { const pingTimeout = 30000 + /** * @param {any} conn * @param {any} req @@ -204,15 +238,7 @@ const pingTimeout = 30000 exports.setupWSConnection = (conn, req, { docName = req.url.slice(1).split('?')[0], gc = true } = {}) => { conn.binaryType = 'arraybuffer' // get doc, create if it does not exist yet - const doc = map.setIfUndefined(docs, docName, () => { - const doc = new WSSharedDoc(docName) - doc.gc = gc - if (persistence !== null) { - persistence.bindState(docName, doc) - } - docs.set(docName, doc) - return doc - }) + const doc = findOrCreateDoc(docName, gc) doc.conns.set(conn, new Set()) // listen and reply to events conn.on('message', /** @param {ArrayBuffer} message */ message => messageListener(conn, doc, new Uint8Array(message))) From 70905bc7a0b5c4edeffb3a1974d12ccd00b4fd25 Mon Sep 17 00:00:00 2001 From: Duane Johnson Date: Sat, 10 Oct 2020 12:55:41 -0600 Subject: [PATCH 2/3] Keep persistence object signature - consolidate getYDoc, findOrCreateDoc - rename findOrCreateDoc to getOrInitDoc - rename persistence.getYDoc to getOrCreateDoc --- bin/utils.js | 59 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/bin/utils.js b/bin/utils.js index 344a2a8..85733c9 100644 --- a/bin/utils.js +++ b/bin/utils.js @@ -23,6 +23,16 @@ const wsReadyStateClosed = 3 // eslint-disable-line // disable gc when using snapshots! const gcEnabled = process.env.GC !== 'false' && process.env.GC !== '0' const persistenceDir = process.env.YPERSISTENCE + +/** + * If persistence is enabled, performs two-way sync between memory and disk stores. + * If persistence is not enabled, does nothing. + * + * @param {string} docName the Y.Doc's name in disk store + * @param {WSSharedDoc} ydoc the in-memory Y.Doc + */ +let mutualSync = async (docName, ydoc) => {} + /** * @type {{bindState: function(string,WSSharedDoc):void, writeState:function(string,WSSharedDoc):Promise}|null} */ @@ -32,34 +42,26 @@ if (typeof persistenceDir === 'string') { // @ts-ignore const LeveldbPersistence = require('y-leveldb').LeveldbPersistence const ldb = new LeveldbPersistence(persistenceDir) - persistence = { - // Sync in-memory and ldb state - mutualSync: async (docName, ydoc) => { - const persistedYdoc = await ldb.getYDoc(docName) - const newUpdates = Y.encodeStateAsUpdate(ydoc) - ldb.storeUpdate(docName, newUpdates) - Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(persistedYdoc)) - }, + // Sync in-memory and ldb state + mutualSync = async (docName, ydoc) => { + const persistedYdoc = await ldb.getYDoc(docName) + const newUpdates = Y.encodeStateAsUpdate(ydoc) + ldb.storeUpdate(docName, newUpdates) + Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(persistedYdoc)) + } + + persistence = { bindState: async (docName, ydoc) => { - await persistence.mutualSync(docName, ydoc) + await mutualSync(docName, ydoc) ydoc.on('update', (update) => { ldb.storeUpdate(docName, update) }) }, writeState: async (docName, ydoc) => {}, - - // Gets a Y.Doc by name, whether in memory or on disk - getYDoc: async (docName) => { - const ydoc = findOrCreateDoc(docName) - await persistence.mutualSync(docName, ydoc) - return ydoc - }, } } -// If enabled, provide access to persistence functions -exports.persistence = persistence /** * @param {{bindState: function(string,WSSharedDoc):void, @@ -146,10 +148,10 @@ class WSSharedDoc extends Y.Doc { } /** - * @param {string} docName - the name of the Y.Doc to find or create + * @param {string} docName - the name of the Y.Doc to find or initialize * @param {boolean} gc - whether to allow gc on the doc (applies only when created) */ -function findOrCreateDoc(docName, gc = true) { +function getOrInitDoc(docName, gc = true) { // get doc, create if it does not exist yet return map.setIfUndefined(docs, docName, () => { const doc = new WSSharedDoc(docName) @@ -161,7 +163,18 @@ function findOrCreateDoc(docName, gc = true) { return doc }) } -exports.findOrCreateDoc = findOrCreateDoc + +/** + * Gets a Y.Doc by name, whether in memory or on disk + * + * @param {string} docName - the name of the Y.Doc to find or create + */ +async function getOrCreateDoc(docName, gc = true) { + const ydoc = getOrInitDoc(docName, gc) + await mutualSync(docName, ydoc) + return ydoc +} +exports.getOrCreateDoc = getOrCreateDoc /** * @param {any} conn @@ -237,8 +250,8 @@ const pingTimeout = 30000 */ exports.setupWSConnection = (conn, req, { docName = req.url.slice(1).split('?')[0], gc = true } = {}) => { conn.binaryType = 'arraybuffer' - // get doc, create if it does not exist yet - const doc = findOrCreateDoc(docName, gc) + // get doc, initialize if it does not exist yet + const doc = getOrInitDoc(docName, gc) doc.conns.set(conn, new Set()) // listen and reply to events conn.on('message', /** @param {ArrayBuffer} message */ message => messageListener(conn, doc, new Uint8Array(message))) From be45d3429ca6e44b201a2c64ba55e43d2c96714f Mon Sep 17 00:00:00 2001 From: Duane Johnson Date: Sat, 10 Oct 2020 13:46:35 -0600 Subject: [PATCH 3/3] Bump yjs dependency to 13.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b35dcdf..f1bbf80 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "rollup-cli": "^1.0.9", "standard": "^12.0.1", "typescript": "^3.9.6", - "yjs": "13.0.5" + "yjs": "13.4.1" }, "peerDependenies": { "yjs": "^13.0.0"