Skip to content

fix: make ExistsAsync(id) real-time for soft-delete models#270

Merged
niemyjski merged 6 commits intomainfrom
fix/exists-async-realtime-soft-deletes
May 7, 2026
Merged

fix: make ExistsAsync(id) real-time for soft-delete models#270
niemyjski merged 6 commits intomainfrom
fix/exists-async-realtime-soft-deletes

Conversation

@niemyjski
Copy link
Copy Markdown
Member

@niemyjski niemyjski commented May 7, 2026

Summary

  • ExistsAsync(id) now uses the real-time GET API with a _source_includes=isDeleted filter for models implementing ISupportSoftDeletes, eliminating dirty reads during the Elasticsearch refresh window.
  • Previously, soft-delete models fell back to the near-real-time Search API, meaning ExistsAsync(id) could return stale results for up to 1 second after a write.
  • Also fixes ShouldReturnDocument to correctly handle SoftDeleteQueryMode.DeletedOnly — it previously returned true for all documents regardless of delete state in that mode.

Changes

  • ElasticReadOnlyRepositoryBase.ExistsAsync(Id): Restructured to use GET API with source filter for soft-delete models, only falling back to Search API for parent documents without routing.
  • ElasticReadOnlyRepositoryBase.ShouldReturnDocument: Fixed to use a switch expression that properly handles all three SoftDeleteQueryMode values.
  • New tests in ReadOnlyRepositoryTests:
    • ExistsAsync_WithSoftDeletedDocument_IsRealTimeWithoutRefresh
    • GetByIdAsync_WithDeletedOnlyMode_ReturnsOnlyDeletedDocuments
    • GetByIdsAsync_WithDeletedOnlyMode_ReturnsOnlyDeletedDocuments
    • ExistsAsync_WithNonExistentIdOnSoftDeleteModel_ReturnsFalse
  • Updated docs/guide/consistency.md and .agents/skills/foundatio-repositories/SKILL.md to reflect the new real-time behavior.

Test plan

  • ExistsAsync_WithSoftDeletedDocument_IsRealTimeWithoutRefreshExistsAsync(id) returns false immediately after soft-deleting (without ImmediateConsistency); IncludeSoftDeletes and DeletedOnly modes behave correctly
  • GetByIdAsync_WithDeletedOnlyMode_ReturnsOnlyDeletedDocuments / GetByIdsAsync_WithDeletedOnlyMode_ReturnsOnlyDeletedDocumentsDeletedOnly returns only deleted documents
  • ExistsAsync_WithNonExistentIdOnSoftDeleteModel_ReturnsFalse — missing IDs across all soft-delete modes
  • Full solution test run: 608 passed, 0 failed, 6 skipped

niemyjski added 3 commits May 7, 2026 15:02
Previously, ExistsAsync(id) fell back to the near-real-time Search API
when the model implemented ISupportSoftDeletes, causing dirty reads
during the refresh window. Now it uses the GET API with a source filter
for the IsDeleted field and checks the soft-delete state in code.

Also fixes ShouldReturnDocument to correctly handle DeletedOnly mode
(previously it returned true for all documents in that mode).
…stsAsync with non-existent IDs

Covers previously untested scenarios exposed by the ShouldReturnDocument fix:
- GetByIdAsync correctly returns null for active docs in DeletedOnly mode
- GetByIdsAsync correctly filters to only deleted docs in DeletedOnly mode
- ExistsAsync(id) returns false for non-existent IDs on soft-delete models
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves Elasticsearch read consistency for soft-delete models by making ExistsAsync(id) use a real-time read path and by fixing soft-delete filtering behavior for DeletedOnly mode.

Changes:

  • Updated ElasticReadOnlyRepositoryBase.ExistsAsync(id) to use real-time GET with a minimal _source include for soft-delete models, falling back to Search only when routing is unavailable for parent/child mappings.
  • Fixed ShouldReturnDocument so SoftDeleteQueryMode.DeletedOnly returns only deleted documents (and not all documents).
  • Added/expanded tests around ExistsAsync(id), GetByIdAsync, and GetByIdsAsync behavior under different soft-delete modes; updated consistency documentation.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
tests/Foundatio.Repositories.Elasticsearch.Tests/ReadOnlyRepositoryTests.cs Adds coverage validating real-time ExistsAsync(id) behavior for soft deletes and correct DeletedOnly behavior for ID-based reads.
src/Foundatio.Repositories.Elasticsearch/Repositories/ElasticReadOnlyRepositoryBase.cs Switches ExistsAsync(id) soft-delete path to real-time GET + _source include; fixes ShouldReturnDocument mode handling.
docs/guide/consistency.md Updates documentation to reflect the new real-time behavior for ExistsAsync(id) with soft deletes.
.agents/skills/foundatio-repositories/SKILL.md Updates internal guidance to match the new ExistsAsync(id) behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

@niemyjski niemyjski merged commit f50d9b5 into main May 7, 2026
7 checks passed
@niemyjski niemyjski deleted the fix/exists-async-realtime-soft-deletes branch May 7, 2026 21:27
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.

2 participants