Skip to content

fix: serialize single-block mutations per content path - #58

Open
nileshgulia1 wants to merge 2 commits into
mainfrom
concurrent_mcp_clients
Open

fix: serialize single-block mutations per content path#58
nileshgulia1 wants to merge 2 commits into
mainfrom
concurrent_mcp_clients

Conversation

@nileshgulia1

Copy link
Copy Markdown
Member

Serialize single-block mutations per content path

Fixes lost-update races in plone_add_single_block, plone_remove_single_block, and plone_update_single_block when two operations run concurrently on the same content item.

Problem

Each single-block tool performs a read-modify-write against Plone: GET the content, mutate blocks/blocks_layout, then PATCH. When two callers operate on the same path concurrently (multiple MCP clients in HTTP mode, parallel tool calls in one session), both read the same snapshot and the last PATCH overwrites the earlier change — a block is silently lost.

Fix

Introduce a module-level per-path serialization queue (src/utils/concurrency-queue.ts). Operations for the same normalized path are chained so a later operation only starts after the previous one has settled; rejected operations do not block the queue.

Verification

  • New integration test fires two concurrent plone_remove_single_block calls on the same path and asserts the surviving state contains neither block.
  • Existing single-block tool suites still pass.
  • Live-verified against the HTTP server with two independent Streamable HTTP clients: parallel block adds on the same page both persisted (without the fix, parallel adds on the same branch lose one block).

Add a module-level per-path concurrency queue and wrap the
read-modify-write sequences in plone_add_single_block,
plone_remove_single_block, and plone_update_single_block.

This prevents lost updates when multiple MCP clients (same or
different users) concurrently edit blocks on the same page, because
the second caller now reads the state produced by the first caller
instead of overwriting it.

Plone's own @lock mechanism is respected but not acquired by the
MCP, so Volto users are not blocked during short MCP operations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant