Repository navigation
Sync document search metadata in the background with batched merges - #1658
Merged
Paul Lizer (paullizer) merged 2 commits intoOct 6, 2026
Merged
Conversation
- Replace per-chunk AI Search GET + full re-upload with key-paged merge_documents batches that send only the changed fields, so embeddings never round-trip and Search/Cosmos calls drop ~500x. - Record a durable sync request with each metadata save; a leased background worker projects pending fields, retries with backoff, and a reconciler loop resumes failed or orphaned syncs. - Project shared_user_ids/shared_group_ids changes to chunks before saving them (fail closed), fixing group sharing changes that never reached Search. - Save metadata PATCH fields in one update in all workspaces, drop the duplicate tag passes, and retire the #1645 browser continuation loop. Refs #1657 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
6 tasks
|
|
||
| return jsonify({'message': 'Share approved' if updated else 'Already approved'}), 200 | ||
| except DocumentSearchAclProjectionDeferredError as exc: | ||
| response = jsonify({'error': str(exc)}) |
| response.headers['Retry-After'] = '150' | ||
| return response, 503 | ||
| except DocumentSearchAclProjectionError as exc: | ||
| return jsonify({'error': str(exc)}), 500 |
|
|
||
| def _group_share_search_acl_error_response(error): | ||
| """Return the safe response for a sharing change the search index could not enforce.""" | ||
| response = jsonify({'error': str(error)}) |
| return 'user_id', user_id | ||
|
|
||
|
|
||
| def get_document_search_acl_fields(group_id=None, public_workspace_id=None): |
- Match a per-request token stored on the document instead of comparing revisions, so a stale value from an earlier request can never complete a newer sync before its save lands. - Retry a sync request when a finishing worker removes the record between read and replace, instead of failing the user's save. - Retry Search writes frozen by a Data Management migration on a short fixed delay without climbing the failure backoff. - Project only real access changes; missing and empty access lists are equivalent, so the legacy document upgrade never depends on Search. Refs #1657 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This was referenced Oct 6, 2026
Paul Lizer (paullizer)
added a commit
to paullizer/simplechat
that referenced
this pull request
Oct 7, 2026
Port microsoft#1658 (merged into Development as 900a3e4, including hardening commit 9c156a4) and its follow-up microsoft#1673 (df3879c) to the React V2 branch. Re-implemented against V2's strict update_document(), group projection fence, content screening and native group/public document APIs rather than cherry-picked. - Mirrored metadata (title, authors, file name, classification, tags) is projected by a durable background sync. The request is recorded in the settings container before the save, the save carries its token, and a worker merges only the changed fields in key-paged batches of 500. The worker never writes to the document. - Access-control lists are projected synchronously and fail closed. Group revocations go through the projection fence before the save, grants after it. Re-approving an approved share repairs its search access. - Native group/public receipts add search_sync. Metadata projection failures no longer produce document_propagation_incomplete; strict group edits on multi-chunk documents no longer fail on the fence etag. - Legacy routes save every field in one update_document() call and drop propagate_tags_to_chunks(), which is removed. Errors are fixed messages. - Blob tag refresh moves to the worker, best effort and conditioned on the blob ETag. After a pass that changed chunks, the worker clears cached search results for the workspace and every approved share recipient. - React and classic workspace UIs show a notice while the index updates. - Harnesses that exercised the per-chunk loop now drive the sync worker. Refs microsoft#1657 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the root cause behind #1645 (now merged). Metadata, tag, and sharing changes no longer do per-chunk Azure AI Search work inside the HTTP request.
select=id, ordered byid, exact workspace scope; the same keyset approach Data Management migrations use). Changed fields are written withmerge_documentsbatches (default 500) through the existing Search write fence. Embeddings never round-trip.background_tasks.pyresumes failed or orphaned syncs. The worker never writes back to the document, so list sort order (_ts) and etags only change on user saves.shared_user_ids/shared_group_idschanges reach every chunk before they're saved. This also fixes group share/approve/unshare/remove-self, which never reached Search before. Only real access changes are projected: a missing list and an empty list count as equal, so the legacy-document upgrade never depends on Search. A frozen Search fence returns a retryable 503; any other Search failure returns a safe 500. In both cases nothing is saved.update_document()call and returnsearch_sync.propagate_tags_to_chunks()pass; the function is removed.Refs #1657 · Follow-up: separate port PR for
paullizer-react-v2-ui.What users will notice
Trade-offs:
Validation
python -m pytest functional_tests/test_document_search_metadata_sync.py functional_tests/test_document_metadata_chunk_sync_batching.py functional_tests/test_data_management_search_write_fence_authorization.py -q: 27 passed. This includes regression tests for a stale-token race, a record removed mid-write, frozen-fence retry, and equal-but-differently-shaped access lists.python functional_tests/test_document_metadata_chunk_sync_batching.py: passed (a 5,000-chunk tag save does no Search work in the request; 10 merge batches)python functional_tests/test_document_auto_metadata_extraction_consistency.py: 3/4 passed.test_dispatcher_owns_final_metadata_extractionfails identically on the base commit2ab4cf43d(pre-existing, unrelated).python functional_tests/test_logging_tag_standardization.py: passed (267 tags; added[DOCUMENT_SEARCH_SYNC],[DOCUMENT_SHARING], and the previously undocumented[DOCUMENT_METADATA])python functional_tests/test_file_sync_tag_definition_application.py: passedpython functional_tests/route_tests/test_route_blueprint_policy_inventory.py: 6/6python functional_tests/route_tests/test_route_unauthenticated_policy_contract.py: 4/4python functional_tests/route_tests/test_route_policy_test_coverage.py: 2/2python scripts/check_swagger_routes.py application/single_app/route_backend_documents.py application/single_app/route_backend_group_documents.py application/single_app/route_backend_public_documents.py: passedpython scripts/check_xss_sinks.py --base-sha 2ab4cf43d --head-sha HEAD <8 changed app files>: passedpython scripts/check_broken_access_control.py --base-sha 2ab4cf43d --head-sha HEAD <5 changed Python files>: passedpython functional_tests/test_docs_site_quality.py: 6/6python functional_tests/test_docs_app_surface_coverage.py: 7/7python functional_tests/test_release_notes_check_fork_permissions.py: 2/2python -m py_compileon the touched Python files,node --checkonworkspace-documents.jsandpublic_workspace.js, andgit diff --check: cleanfunctional_tests/test_backend_documents_swagger_integration.pyandtest_backend_group_documents_swagger_integration.py: 3/6 on both this branch and base2ab4cf43d. They fail on stale hard-coded route counts; this PR adds no routes.Not run:
ui_tests/test_workspace_metadata_search_sync_toast.py(new). It needsSIMPLECHAT_UI_BASE_URLand authenticated storage state. It checks for one PATCH and that the toast appears only when a sync is queued.Notes
0.261.052. Fix doc:docs/explanation/fixes/DOCUMENT_SEARCH_METADATA_SYNC_FIX.md. The Batch large document metadata chunk sync #1645 fix doc is marked superseded.document_search_metadata_syncrecord appears and is then deleted.update_document()predates this PR).set_document_chunk_visibility()(revision archive/restore) to merges.