fix concurrent load and update, add support for gc'ing elements

This commit is contained in:
Andre Staltz 2024-01-19 14:56:23 +02:00
parent 633682db8a
commit 7a98e3cbcc
No known key found for this signature in database
GPG Key ID: 9EDE23EA7E8A4890
2 changed files with 59 additions and 20 deletions

70
main.js
View File

@ -106,14 +106,6 @@ async function loadAccount() {
globalAccountID = id
await p(peer.set.load)(id)
await p(peer.dict.load)(id)
peer.conductor.start(
id,
[
['profile@dict', 'zooboardElements@newest-100', 'hubs@set'],
['profile@dict', 'zooboardElements@newest-100'],
],
64_000_000
)
// Read hubs
const multiaddrs = peer.set.values('hubs')
@ -134,38 +126,82 @@ async function setProfileName(ev, name) {
return name
}
async function writeElements(ev, actions) {
await loadAccount() // FIXME: ideally the frontend shouldn't do this
async function writeElements(ev, elements) {
if (globalAccountID === null) throw new Error('account not loaded')
for (const action of actions) {
for (const element of elements) {
await p(peer.db.feed.publish)({
account: globalAccountID,
domain: 'zooboardElements',
data: action,
data: element,
})
}
}
let hasSubscribedToReadElements = false
function subscribeToReadElements() {
if (hasSubscribedToReadElements) return
hasSubscribedToReadElements = true
// Load initial elements and inform renderer
const elementsByID = new Map()
for (const msg of peer.db.msgs()) {
const msgIDToElemID = new Map()
for (const { id: msgID, msg } of peer.db.records()) {
if (msg.data && msg.metadata.domain === 'zooboardElements') {
const { id, isDeleted } = msg.data
const { id: elemID, isDeleted } = msg.data
if (isDeleted) {
elementsByID.delete(id)
elementsByID.delete(elemID)
} else {
elementsByID.set(id, msg.data)
msgIDToElemID.set(msgID, elemID)
elementsByID.set(elemID, msg.data)
}
}
}
const initialElements = [...elementsByID.values()]
mainWindow.webContents.send('readElements', initialElements)
peer.db.onRecordAdded(({ msg }) => {
// Subscribe to new elements and inform renderer
peer.db.onRecordAdded(({ id: msgID, msg }) => {
if (msg.data && msg.metadata.domain === 'zooboardElements') {
const { id: elemID, isDeleted } = msg.data
if (isDeleted) {
elementsByID.delete(elemID)
} else {
msgIDToElemID.set(msgID, elemID)
elementsByID.set(elemID, msg.data)
}
mainWindow.webContents.send('readElements', [msg.data])
}
})
// Subscribe to deleted elements and inform renderer
peer.db.onRecordDeletedOrErased((msgID) => {
const elemID = msgIDToElemID.get(msgID)
if (!elemID) return
msgIDToElemID.delete(msgID)
// Is there some other msgID that supports this elemID? If so, bail out
for (const [, remainingElemID] of msgIDToElemID) {
if (remainingElemID === elemID) {
return
}
}
// If not, delete the element
elementsByID.delete(elemID)
mainWindow.webContents.send('readElements', [
{ id: elemID, isDeleted: true },
])
})
// Finally safe to kickstart replication and garbage collection
setTimeout(() => {
peer.conductor.start(
globalAccountID,
[
['profile@dict', 'zooboardElements@newest-100', 'hubs@set'],
['profile@dict', 'zooboardElements@newest-100'],
],
64_000
)
}, 32)
}
async function scheduleWithHub(multiaddr) {

View File

@ -3,6 +3,9 @@ import { Excalidraw } from '@excalidraw/excalidraw'
import './App.css'
import debounce from 'debounce'
const elemsPersisted = new Map()
let sceneInitialized = false
function MyAccount() {
const nameInput = createRef()
const [loaded, setLoaded] = useState(false)
@ -52,7 +55,6 @@ function MyAccount() {
}
function App() {
const elemsPersisted = new Map()
const [excalidrawAPI, setExcalidrawAPI] = useState(null)
function loadExcalidraw(api) {
@ -67,14 +69,15 @@ function App() {
}
}
api.updateScene({ elements: [...elemsPersisted.values()] })
if (!sceneInitialized) sceneInitialized = true
})
}
const updateElements = debounce((elems) => {
if (excalidrawAPI) return
if (!sceneInitialized) return
const actions = []
for (const elem of elems) {
const oldVersion = elemsPersisted.get(elem.id)?.version ?? 0
const oldVersion = elemsPersisted.get(elem.id)?.version ?? -1
if (elem.version > oldVersion) {
actions.push(elem)
elemsPersisted.set(elem.id, { ...elem })