mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	attempt to make updating clients via websocket faster
This commit is contained in:
		| @@ -795,7 +795,7 @@ ws.subscribeToOutsideSyncMessages(async syncData => { | |||||||
|     syncData.filter(sync => sync.entityName === 'attributes').forEach(sync => { |     syncData.filter(sync => sync.entityName === 'attributes').forEach(sync => { | ||||||
|         const note = treeCache.notes[sync.noteId]; |         const note = treeCache.notes[sync.noteId]; | ||||||
|  |  | ||||||
|         if (note && note.attributeCache) { |         if (note && note.__attributeCache) { | ||||||
|             noteIdsToRefresh.add(sync.entityId); |             noteIdsToRefresh.add(sync.entityId); | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| @@ -869,7 +869,7 @@ async function reloadNotes(noteIds, activateNotePath = null) { | |||||||
|     if (activateNotePath) { |     if (activateNotePath) { | ||||||
|         const node = await getNodeFromPath(activateNotePath); |         const node = await getNodeFromPath(activateNotePath); | ||||||
|  |  | ||||||
|         if (node) { |         if (node && !node.isActive()) { | ||||||
|             await node.setActive(true); |             await node.setActive(true); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -8,7 +8,8 @@ const outsideSyncMessageHandlers = []; | |||||||
| const messageHandlers = []; | const messageHandlers = []; | ||||||
|  |  | ||||||
| let ws; | let ws; | ||||||
| let lastSyncId = window.glob.maxSyncIdAtLoad; | let lastAcceptedSyncId = window.glob.maxSyncIdAtLoad; | ||||||
|  | let lastProcessedSyncId = window.glob.maxSyncIdAtLoad; | ||||||
| let lastPingTs; | let lastPingTs; | ||||||
| let syncDataQueue = []; | let syncDataQueue = []; | ||||||
|  |  | ||||||
| @@ -84,7 +85,7 @@ async function handleMessage(event) { | |||||||
| let syncIdReachedListeners = []; | let syncIdReachedListeners = []; | ||||||
|  |  | ||||||
| function waitForSyncId(desiredSyncId) { | function waitForSyncId(desiredSyncId) { | ||||||
|     if (desiredSyncId <= lastSyncId) { |     if (desiredSyncId <= lastProcessedSyncId) { | ||||||
|         return Promise.resolve(); |         return Promise.resolve(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -99,14 +100,14 @@ function waitForSyncId(desiredSyncId) { | |||||||
|  |  | ||||||
| function checkSyncIdListeners() { | function checkSyncIdListeners() { | ||||||
|     syncIdReachedListeners |     syncIdReachedListeners | ||||||
|         .filter(l => l.desiredSyncId <= lastSyncId) |         .filter(l => l.desiredSyncId <= lastProcessedSyncId) | ||||||
|         .forEach(l => l.resolvePromise()); |         .forEach(l => l.resolvePromise()); | ||||||
|  |  | ||||||
|     syncIdReachedListeners = syncIdReachedListeners |     syncIdReachedListeners = syncIdReachedListeners | ||||||
|         .filter(l => l.desiredSyncId > lastSyncId); |         .filter(l => l.desiredSyncId > lastProcessedSyncId); | ||||||
|  |  | ||||||
|     syncIdReachedListeners.filter(l => Date.now() > l.start - 60000) |     syncIdReachedListeners.filter(l => Date.now() > l.start - 60000) | ||||||
|         .forEach(l => console.log(`Waiting for syncId ${l.desiredSyncId} while current is ${lastSyncId} for ${Math.floor((Date.now() - l.start) / 1000)}s`)); |         .forEach(l => console.log(`Waiting for syncId ${l.desiredSyncId} while current is ${lastProcessedSyncId} for ${Math.floor((Date.now() - l.start) / 1000)}s`)); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function consumeSyncData() { | async function consumeSyncData() { | ||||||
| @@ -116,13 +117,17 @@ async function consumeSyncData() { | |||||||
|  |  | ||||||
|         const outsideSyncData = allSyncData.filter(sync => sync.sourceId !== glob.sourceId); |         const outsideSyncData = allSyncData.filter(sync => sync.sourceId !== glob.sourceId); | ||||||
|  |  | ||||||
|  |         // we set lastAcceptedSyncId even before sync processing and send ping so that backend can start sending more updates | ||||||
|  |         lastAcceptedSyncId = Math.max(lastAcceptedSyncId, allSyncData[allSyncData.length - 1].id); | ||||||
|  |         sendPing(); | ||||||
|  |  | ||||||
|         // the update process should be synchronous as a whole but individual handlers can run in parallel |         // the update process should be synchronous as a whole but individual handlers can run in parallel | ||||||
|         await Promise.all([ |         await Promise.all([ | ||||||
|             ...allSyncMessageHandlers.map(syncHandler => syncHandler(allSyncData)), |             ...allSyncMessageHandlers.map(syncHandler => syncHandler(allSyncData)), | ||||||
|             ...outsideSyncMessageHandlers.map(syncHandler => syncHandler(outsideSyncData)) |             ...outsideSyncMessageHandlers.map(syncHandler => syncHandler(outsideSyncData)) | ||||||
|         ]); |         ]); | ||||||
|  |  | ||||||
|         lastSyncId = allSyncData[allSyncData.length - 1].id; |         lastProcessedSyncId = Math.max(lastProcessedSyncId, allSyncData[allSyncData.length - 1].id); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -140,29 +145,32 @@ function connectWebSocket() { | |||||||
|     return ws; |     return ws; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | async function sendPing() { | ||||||
|  |     if (Date.now() - lastPingTs > 30000) { | ||||||
|  |         console.log(utils.now(), "Lost connection to server"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (ws.readyState === ws.OPEN) { | ||||||
|  |         ws.send(JSON.stringify({ | ||||||
|  |             type: 'ping', | ||||||
|  |             lastSyncId: lastAcceptedSyncId | ||||||
|  |         })); | ||||||
|  |     } | ||||||
|  |     else if (ws.readyState === ws.CLOSED || ws.readyState === ws.CLOSING) { | ||||||
|  |         console.log(utils.now(), "WS closed or closing, trying to reconnect"); | ||||||
|  |  | ||||||
|  |         ws = connectWebSocket(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| setTimeout(() => { | setTimeout(() => { | ||||||
|     ws = connectWebSocket(); |     ws = connectWebSocket(); | ||||||
|  |  | ||||||
|     lastSyncId = glob.maxSyncIdAtLoad; |     lastAcceptedSyncId = glob.maxSyncIdAtLoad; | ||||||
|  |     lastProcessedSyncId = glob.maxSyncIdAtLoad; | ||||||
|     lastPingTs = Date.now(); |     lastPingTs = Date.now(); | ||||||
|  |  | ||||||
|     setInterval(async () => { |     setInterval(sendPing, 1000); | ||||||
|         if (Date.now() - lastPingTs > 30000) { |  | ||||||
|             console.log(utils.now(), "Lost connection to server"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (ws.readyState === ws.OPEN) { |  | ||||||
|             ws.send(JSON.stringify({ |  | ||||||
|                 type: 'ping', |  | ||||||
|                 lastSyncId: lastSyncId |  | ||||||
|             })); |  | ||||||
|         } |  | ||||||
|         else if (ws.readyState === ws.CLOSED || ws.readyState === ws.CLOSING) { |  | ||||||
|             console.log(utils.now(), "WS closed or closing, trying to reconnect"); |  | ||||||
|  |  | ||||||
|             ws = connectWebSocket(); |  | ||||||
|         } |  | ||||||
|     }, 1000); |  | ||||||
| }, 0); | }, 0); | ||||||
|  |  | ||||||
| subscribeToMessages(message => { | subscribeToMessages(message => { | ||||||
|   | |||||||
| @@ -125,7 +125,8 @@ async function getEditedNotesOnDate(req) { | |||||||
|                 SELECT noteId FROM note_revisions |                 SELECT noteId FROM note_revisions | ||||||
|                 WHERE note_revisions.dateLastEdited LIKE '${date}%' |                 WHERE note_revisions.dateLastEdited LIKE '${date}%' | ||||||
|         ) |         ) | ||||||
|         ORDER BY isDeleted`); |         ORDER BY isDeleted | ||||||
|  |         LIMIT 50`); | ||||||
|  |  | ||||||
|     for (const note of notes) { |     for (const note of notes) { | ||||||
|         const notePath = noteCacheService.getNotePath(note.noteId); |         const notePath = noteCacheService.getNotePath(note.noteId); | ||||||
|   | |||||||
| @@ -4,13 +4,25 @@ const dateUtils = require('./date_utils'); | |||||||
| const log = require('./log'); | const log = require('./log'); | ||||||
| const cls = require('./cls'); | const cls = require('./cls'); | ||||||
|  |  | ||||||
|  | let syncs = []; | ||||||
|  |  | ||||||
| async function addEntitySync(entityName, entityId, sourceId) { | async function addEntitySync(entityName, entityId, sourceId) { | ||||||
|     await sql.replace("sync", { |     const sync = { | ||||||
|         entityName: entityName, |         entityName: entityName, | ||||||
|         entityId: entityId, |         entityId: entityId, | ||||||
|         utcSyncDate: dateUtils.utcNowDateTime(), |         utcSyncDate: dateUtils.utcNowDateTime(), | ||||||
|         sourceId: sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId() |         sourceId: sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId() | ||||||
|     }); |     }; | ||||||
|  |  | ||||||
|  |     sync.id = await sql.replace("sync", sync); | ||||||
|  |  | ||||||
|  |     syncs.push(sync); | ||||||
|  |  | ||||||
|  |     setTimeout(() => require('./ws').sendPingToAllClients(), 50); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function getEntitySyncsNewerThan(syncId) { | ||||||
|  |     return syncs.filter(s => s.id > syncId); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function cleanupSyncRowsForMissingEntities(entityName, entityKey) { | async function cleanupSyncRowsForMissingEntities(entityName, entityKey) { | ||||||
| @@ -83,5 +95,6 @@ module.exports = { | |||||||
|     addAttributeSync: async (attributeId, sourceId) => await addEntitySync("attributes", attributeId, sourceId), |     addAttributeSync: async (attributeId, sourceId) => await addEntitySync("attributes", attributeId, sourceId), | ||||||
|     addApiTokenSync: async (apiTokenId, sourceId) => await addEntitySync("api_tokens", apiTokenId, sourceId), |     addApiTokenSync: async (apiTokenId, sourceId) => await addEntitySync("api_tokens", apiTokenId, sourceId), | ||||||
|     addEntitySync, |     addEntitySync, | ||||||
|     fillAllSyncRows |     fillAllSyncRows, | ||||||
|  |     getEntitySyncsNewerThan | ||||||
| }; | }; | ||||||
| @@ -5,6 +5,7 @@ const sql = require('./sql'); | |||||||
| const syncMutexService = require('./sync_mutex'); | const syncMutexService = require('./sync_mutex'); | ||||||
|  |  | ||||||
| let webSocketServer; | let webSocketServer; | ||||||
|  | let lastAcceptedSyncIds = {}; | ||||||
|  |  | ||||||
| function init(httpServer, sessionParser) { | function init(httpServer, sessionParser) { | ||||||
|     webSocketServer = new WebSocket.Server({ |     webSocketServer = new WebSocket.Server({ | ||||||
| @@ -23,7 +24,11 @@ function init(httpServer, sessionParser) { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     webSocketServer.on('connection', (ws, req) => { |     webSocketServer.on('connection', (ws, req) => { | ||||||
|         console.log("websocket client connected"); |         ws.id = utils.randomString(10); | ||||||
|  |  | ||||||
|  |         lastAcceptedSyncIds[ws.id] = 0; | ||||||
|  |  | ||||||
|  |         console.log(`websocket client connected`); | ||||||
|  |  | ||||||
|         ws.on('message', messageJson => { |         ws.on('message', messageJson => { | ||||||
|             const message = JSON.parse(messageJson); |             const message = JSON.parse(messageJson); | ||||||
| @@ -32,7 +37,9 @@ function init(httpServer, sessionParser) { | |||||||
|                 log.error('JS Error: ' + message.error); |                 log.error('JS Error: ' + message.error); | ||||||
|             } |             } | ||||||
|             else if (message.type === 'ping') { |             else if (message.type === 'ping') { | ||||||
|                 syncMutexService.doExclusively(async () => await sendPing(ws, message.lastSyncId)); |                 lastAcceptedSyncIds[ws.id] = message.lastSyncId; | ||||||
|  |  | ||||||
|  |                 syncMutexService.doExclusively(async () => await sendPing(ws)); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 log.error('Unrecognized message: '); |                 log.error('Unrecognized message: '); | ||||||
| @@ -64,8 +71,8 @@ function sendMessageToAllClients(message) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| async function sendPing(client, lastSentSyncId) { | async function sendPing(client) { | ||||||
|     const syncData = await sql.getRows("SELECT * FROM sync WHERE id > ?", [lastSentSyncId]); |     const syncData = require('./sync_table').getEntitySyncsNewerThan(lastAcceptedSyncIds[client.id]); | ||||||
|  |  | ||||||
|     for (const sync of syncData) { |     for (const sync of syncData) { | ||||||
|         // fill in some extra data needed by the frontend |         // fill in some extra data needed by the frontend | ||||||
| @@ -92,6 +99,14 @@ async function sendPing(client, lastSentSyncId) { | |||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function sendPingToAllClients() { | ||||||
|  |     if (webSocketServer) { | ||||||
|  |         webSocketServer.clients.forEach(function each(client) { | ||||||
|  |            sendPing(client); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| function refreshTree() { | function refreshTree() { | ||||||
|     sendMessageToAllClients({ type: 'refresh-tree' }); |     sendMessageToAllClients({ type: 'refresh-tree' }); | ||||||
| } | } | ||||||
| @@ -109,5 +124,6 @@ module.exports = { | |||||||
|     sendMessageToAllClients, |     sendMessageToAllClients, | ||||||
|     refreshTree, |     refreshTree, | ||||||
|     syncPullInProgress, |     syncPullInProgress, | ||||||
|     syncPullFinished |     syncPullFinished, | ||||||
|  |     sendPingToAllClients | ||||||
| }; | }; | ||||||
		Reference in New Issue
	
	Block a user