Cloning a yDoc into another room

I have a Y.Doc that is shared using WebSocketProvider in a room, room A. I now want to clone the document to another room (room B), so that a client of room B will initially see the same as the client of room A at the moment of cloning, but, thereafter, room B will not be updated with changes in room A and vice versa. I have tried this:

// ...
// code to create a ydoc with shared types linked to room 'A' using 'websocket'  and apply various insertions etc. 
// ...
//

let state = Y.encodeStateAsUpdate(ydoc);
let clonedDoc = new Y.Doc();
let ws = new WebsocketProvider(websocket, 'B', clonedDoc)
ws.on('sync', () => {
	Y.applyUpdate(clonedDoc, state)
}); 

I am assuming that applying state as an update to the empty clonedDoc will yield a copy of the state of room A in room B. However, if I run the above code, and access the cloned room, I find a ydoc that has the same structure (i.e. has the same shared types) as the room being cloned, but all the values are missing. What am I doing wrong?

Hi @micrology,

how do you determine that the values of clonedDoc are missing?

Here’s an example:

the original

ydoc
Doc {_observers: Map(3), gc: true, clientID: 3532913938, guid: "9b9b18eb-23dd-4d76-b5e3-d0639c9abe93", gcFilter: ƒ, …}

ydoc.share
Map(6) {"nodes" => YMap, "edges" => YMap, "samples" => YMap, "network" => YMap, "chat" => YArray, …}[[Entries]]0: {"nodes" => YMap}1: {"edges" => YMap}2: {"samples" => YMap}3: {"network" => YMap}4: {"chat" => YArray}5: {"points" => YArray}size: (...)__proto__: Map

ydoc.share.get("network")
YMap {_item: null, _map: Map(3), _start: null, doc: Doc, _length: 0, …}

ydoc.share.get("network").get("maptitle")
"Mental Health in the Workplace - Group ZZZ"

the clone

clonedDoc
Doc {_observers: Map(3), gc: true, clientID: 78755419, guid: "483479ee-c3c5-46af-8e34-840cb5e9ebe4", gcFilter: ƒ, …}

clonedDoc.share
Map(6) {"nodes" => YMap, "edges" => YMap, "samples" => YMap, "network" => YMap, "chat" => YArray, …}

clonedDoc.share.get("network")
YMap {_item: null, _map: Map(0), _start: null, doc: Doc, _length: 0, …}

clonedDoc.share.get("network").get("maptitle")
undefined

and another example:

ydoc.share.get("edges").size
56

clonedDoc.share.get("edges").size
0

@dmonad let me know if it would be helpful to see the whole of clonedDoc, or anything else.

When you recreate the document, you need to reinitialise the types. Normally, clonedDoc.get('network') should yield an AbstractType, because you haven’t defined what network is. So you need to do clonedDoc.getMap('network').

If that is not the issue, could you check if example.ydoc.store.pendingStack is empty?

If it is empty, could you post the encoded document here?

I’m a bit lost here - apologies. clonedDoc.get('network') does indeed return an AbstractType (a YMap to be specific). When the client in room B starts up, it does (showing only the code that is relevant):

	const doc = new Y.Doc();
	const wsProvider = new WebsocketProvider(
		websocket,
		'B',
		doc
	);
	yNodesMap = doc.getMap('nodes');
	yEdgesMap = doc.getMap('edges');
	ySamplesMap = doc.getMap('samples');
	yNetMap = doc.getMap('network');
	yChatArray = doc.getArray('chat');
	yPointsArray = doc.getArray('points');

The examples I showed in the earlier message were obtained after running the above code. So I think I have done “clonedDoc.get('network')”.

In room A, ydoc.store.pendingStack is empty. So is clonedDoc.store.pendingStack in room B.

If it is empty, could you post the encoded document here?

If you mean the result of Y.encodeStateAsUpdate(ydoc) in room A, it is a Uint8Array of about 257kB, so I suppose it is not very useful to view - did you mean something else?

These are two different things. AbstractType is a retainer that is unable to interpret the underlying CRDT structure. Y.Map is an implementation of AbstractType that can extract content. So you want to do clonedDoc.getMap('network'), and not clonedDoc.get('network')

That would be perfect. Ideally you upload it as a file to some server (e.g. binpaste or github gist). Then I can debug your issue by parsing the Uint8Array. Alternatively, you could create a minimal reproducible example (I can’t reproduce the above code).

Apologies about getting clonedDoc.get('network') and clonedDoc.getMap('network') mixed up. Here is the result of the latter:

clonedDoc.getMap('network')

YMap {_item: null, _map: Map(0), _start: null, doc: Doc, _length: 0, …}
doc: Doc {_observers: Map(3), gc: true, clientID: 3844616756, guid: "a436363e-9ffc-447e-91a0-17045e4f8986", gcFilter: ƒ, …}
_dEH: EventHandler {l: Array(0)}
_eH: EventHandler {l: Array(1)}
_item: null
_length: 0
_map: Map(0) {}
_prelimContent: null
_searchMarker: null
_start: null
parent: (...)
size: (...)
_first: (...)
__proto__: AbstractType

It would be better if I did a minimal example, but it will take a few days to get to that (I didn’t do it originally because I assumed the problem just arose from my conceptual mistake).

I created a minimal example, and found that it worked perfectly! Eventually, I discovered a trivial mistake in my original code (the name of the new, cloned room was wrong) and once I had corrected that, all was well. Thanks for your help @dmonad.

Awesome, that’s great to hear!