Skip to content

Commit 626193a

Browse files
committed
perf: delete the layout operation log
Every layout operation appended to a Y.Array that nothing ever read. `getOperationsSince` and `getOperationsByActor` were not on the LayoutStore interface and had no callers outside the one test that exercised the log itself; both were marked as being for a future sync feature that no provider exists for. It was not free. The array is never trimmed, so per-operation cost grew with session length. Measured on `moveNode` with an entry present: 197 us/op with 22k entries logged, 375 us/op at 40k. Without the log, 5.4 us/op and flat (2.9 us/op after another 18k operations) — the growth disappears with it. This is a live cost in Vue nodes mode today, on every drag frame and every resize, and it is what made registering node geometry at attach look unaffordable for the legacy canvas: dragging 100 selected nodes went from 10.8 ms/frame (65% of a 16.7ms frame) to 0.60 ms/frame (3.6%). The store's test helper observed operations through the log; it spies on applyOperation instead, which is what nodeSizeReflow.test.ts already did.
1 parent 5fbd317 commit 626193a

2 files changed

Lines changed: 7 additions & 69 deletions

File tree

src/renderer/core/layout/store/layoutStore.test.ts

Lines changed: 7 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ import type {
2020
} from '@/renderer/core/layout/types'
2121

2222
function getOperationsAddedBy(action: () => void): LayoutOperation[] {
23-
const operationCount = layoutStore.getOperationsSince(0).length
24-
action()
25-
return layoutStore.getOperationsSince(0).slice(operationCount)
23+
const applySpy = vi.spyOn(layoutStore, 'applyOperation')
24+
try {
25+
action()
26+
return applySpy.mock.calls.map(([operation]) => operation)
27+
} finally {
28+
applySpy.mockRestore()
29+
}
2630
}
2731

2832
function expectSingleOperation(
@@ -470,45 +474,6 @@ describe('layoutStore CRDT operations', () => {
470474
expect(nodesInBounds).toContain('node-c')
471475
})
472476

473-
it('should maintain operation history', () => {
474-
const nodeId = toNodeId('test-node-history')
475-
const layout = createTestNode(nodeId)
476-
const startTime = Date.now()
477-
478-
// Create node
479-
layoutStore.applyOperation({
480-
type: 'createNode',
481-
entity: 'node',
482-
nodeId,
483-
layout,
484-
timestamp: startTime,
485-
source: LayoutSource.External,
486-
actor: 'test-actor'
487-
})
488-
489-
// Move node
490-
layoutStore.applyOperation({
491-
type: 'moveNode',
492-
entity: 'node',
493-
nodeId,
494-
position: { x: 150, y: 150 },
495-
timestamp: startTime + 100,
496-
source: LayoutSource.Vue,
497-
actor: 'test-actor'
498-
})
499-
500-
// Get operations by actor
501-
const operations = layoutStore.getOperationsByActor('test-actor')
502-
expect(operations.length).toBeGreaterThanOrEqual(2)
503-
expect(operations[0].type).toBe('createNode')
504-
expect(operations[1].type).toBe('moveNode')
505-
506-
// Get operations since timestamp
507-
const recentOps = layoutStore.getOperationsSince(startTime + 50)
508-
expect(recentOps.length).toBeGreaterThanOrEqual(1)
509-
expect(recentOps[0].type).toBe('moveNode')
510-
})
511-
512477
it('normalizes DOM-sourced heights before storing', () => {
513478
const nodeId = toNodeId('dom-node')
514479
const layout = createTestNode(nodeId)

src/renderer/core/layout/store/layoutStore.ts

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ class LayoutStoreImpl implements LayoutStore {
101101
private ynodes: Y.Map<NodeLayoutMap> // Maps nodeId -> NodeLayoutMap containing NodeLayout data
102102
private yreroutes: Y.Map<Y.Map<unknown>> // Maps rerouteId -> Y.Map containing reroute data
103103
private ygroups: Y.Map<GroupLayoutMap> // Maps groupId -> GroupLayoutMap containing GroupLayout data
104-
private yoperations: Y.Array<LayoutOperation> // Operation log
105104

106105
// Vue reactivity layer
107106
private version = 0
@@ -183,7 +182,6 @@ class LayoutStoreImpl implements LayoutStore {
183182
this.ynodes = this.ydoc.getMap('nodes')
184183
this.yreroutes = this.ydoc.getMap('reroutes')
185184
this.ygroups = this.ydoc.getMap('groups')
186-
this.yoperations = this.ydoc.getArray('operations')
187185

188186
// Initialize spatial index managers
189187
this.spatialIndex = new SpatialIndexManager<NodeId>()
@@ -833,10 +831,6 @@ class LayoutStoreImpl implements LayoutStore {
833831

834832
// Use Yjs transaction for atomic updates
835833
this.ydoc.transact(() => {
836-
// Add operation to log
837-
this.yoperations.push([operation])
838-
839-
// Apply the operation
840834
this.applyOperationInTransaction(operation, change)
841835
}, this.currentActor)
842836

@@ -1363,27 +1357,6 @@ class LayoutStoreImpl implements LayoutStore {
13631357
}
13641358
}
13651359

1366-
// CRDT-specific methods
1367-
getOperationsSince(timestamp: number): LayoutOperation[] {
1368-
const operations: LayoutOperation[] = []
1369-
this.yoperations.forEach((op: LayoutOperation) => {
1370-
if (op && op.timestamp > timestamp) {
1371-
operations.push(op)
1372-
}
1373-
})
1374-
return operations
1375-
}
1376-
1377-
getOperationsByActor(actor: string): LayoutOperation[] {
1378-
const operations: LayoutOperation[] = []
1379-
this.yoperations.forEach((op: LayoutOperation) => {
1380-
if (op && op.actor === actor) {
1381-
operations.push(op)
1382-
}
1383-
})
1384-
return operations
1385-
}
1386-
13871360
/**
13881361
* Get the Yjs document for network sync (future feature)
13891362
*/

0 commit comments

Comments
 (0)