Skip to content

Cache empty getDocument results#924

Open
fogelito wants to merge 1 commit into
mainfrom
cache-empty-get-document
Open

Cache empty getDocument results#924
fogelito wants to merge 1 commit into
mainfrom
cache-empty-get-document

Conversation

@fogelito

@fogelito fogelito commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Performance

    • Improved handling of repeated lookups for documents and collections that do not exist through negative caching.
  • Bug Fixes

    • Newly created documents and collections now appear immediately after a previous “not found” lookup.
    • Improved cache invalidation across projections, batch creation, and tenant-scoped data.
    • Prevented permission-denied reads from incorrectly affecting cached results for other access contexts.
  • Tests

    • Added coverage for negative caching, cache invalidation, projections, collection lookups, batch creation, and access controls.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Database now negative-caches eligible document and collection misses with an empty marker. Document creation purges matching markers using tenant-aware keys, while end-to-end tests cover projections, batch creation, locking reads, collection metadata, and security behavior.

Changes

Negative cache lifecycle

Layer / File(s) Summary
Negative-cache read and write path
src/Database/Database.php
getDocument() recognizes cached empty markers and stores markers for eligible missing-document reads.
Creation-time cache invalidation
src/Database/Database.php
Single and batch document creation purge tenant-aware negative-cache entries through a shared helper.
Negative-cache behavior coverage
tests/e2e/Adapter/Scopes/DocumentTests.php
End-to-end tests cover document and collection misses, projections, creation invalidation, locking reads, and document-level security.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Database
  participant Cache
  Client->>Database: Read missing document
  Database->>Cache: Save empty marker
  Cache-->>Database: Empty marker stored
  Client->>Database: Create document
  Database->>Cache: Purge document marker
  Cache-->>Database: Marker invalidated
  Client->>Database: Read created document
  Database-->>Client: Return document
Loading

Possibly related PRs

Suggested reviewers: abnegate

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly matches the main change: caching empty getDocument results for misses.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cache-empty-get-document

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR caches missing document lookups to avoid repeated adapter reads. The main changes are:

  • Adds a dedicated marker for cached missing documents.
  • Clears cached misses after single and batch document creation.
  • Handles tenant-specific and projection-specific cache entries.
  • Adds end-to-end coverage for creation, locking, metadata, projections, and document security.
  • Refreshes several locked dependencies.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.
  • Lease generations prevent an older missing-document read from restoring a marker after creation purges the cache.
  • Creation clears the whole document key, including projection-specific slots.
  • Permission-filtered reads of existing documents do not populate the missing-document marker.

Important Files Changed

Filename Overview
src/Database/Database.php Adds lease-protected negative caching and clears cached misses after document creation.
tests/e2e/Adapter/Scopes/DocumentTests.php Covers negative-cache behavior across creation, projections, locking, metadata, and authorization.
composer.lock Refreshes runtime and development dependencies while remaining compatible with the repository's PHP requirement.

Reviews (1): Last reviewed commit: "run tests" | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Database/Database.php`:
- Around line 8460-8483: Update purgeCreatedDocumentCache() to wrap both the
tenant-scoped withTenant() path and direct purgeCachedDocumentInternal() call in
exception handling, logging cache purge failures with Console::warning while
allowing the committed createDocument/createDocuments operation to succeed. Keep
the existing tenant resolution and purge behavior unchanged; do not refactor the
duplicated tenant wrapper.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 231a6f04-a567-4008-9e9e-2e0a5df8d4b9

📥 Commits

Reviewing files that changed from the base of the PR and between a1796fd and 538e228.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • src/Database/Database.php
  • tests/e2e/Adapter/Scopes/DocumentTests.php

Comment thread src/Database/Database.php
Comment on lines +8460 to +8483
/**
* Invalidate any cache entry for a freshly created document.
*
* A read of a not-yet-created id caches a negative ("not found") marker;
* once the id is inserted that marker must be cleared so the document
* becomes visible immediately. Resolves the correct tenant first when
* tenant-per-document is enabled, so the purge targets the right key.
*
* @param Document $collection
* @param Document $document
* @return void
* @throws Exception
*/
private function purgeCreatedDocumentCache(Document $collection, Document $document): void
{
if ($this->getSharedTables() && $this->getTenantPerDocument()) {
$this->withTenant($document->getTenant(), function () use ($collection, $document) {
$this->purgeCachedDocumentInternal($collection->getId(), $document->getId());
});
} else {
$this->purgeCachedDocumentInternal($collection->getId(), $document->getId());
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Cache purge failure can fail a successful createDocument()/createDocuments().

purgeCreatedDocumentCache() calls purgeCachedDocumentInternal() (via withTenant() in tenant-per-document mode) with no exception handling anywhere in the chain. If the cache backend throws, this propagates straight out of createDocument() (Line 5726-5729) and createDocuments() (Line 5856-5858) after the row is already committed — a durable write gets reported as a failure, and in the batch path the exception aborts all remaining documents in the chunk without going through the onError callback.

This same PR already establishes the correct pattern for cache-consistency operations a few lines earlier: every cache call inside getDocument()'s negative-cache write path (Line 4935-4946) is wrapped in try/catch with a Console::warning, precisely because a best-effort cache operation must never fail the primary database operation.

🐛 Proposed fix: guard the purge like the read-path already does
private function purgeCreatedDocumentCache(Document $collection, Document $document): void
{
    try {
        if ($this->getSharedTables() && $this->getTenantPerDocument()) {
            $this->withTenant($document->getTenant(), function () use ($collection, $document) {
                $this->purgeCachedDocumentInternal($collection->getId(), $document->getId());
            });
        } else {
            $this->purgeCachedDocumentInternal($collection->getId(), $document->getId());
        }
    } catch (Exception $e) {
        Console::warning('Failed to purge empty document cache: ' . $e->getMessage());
    }
}

Separately (optional): this tenant-wrapping shape is now duplicated a third time (also in upsertDocumentsWithIncrease and deleteDocuments); a small shared helper (e.g. withDocumentTenant(Document $doc, callable $cb)) would remove the repetition, but that's a nice-to-have, not blocking.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Database/Database.php` around lines 8460 - 8483, Update
purgeCreatedDocumentCache() to wrap both the tenant-scoped withTenant() path and
direct purgeCachedDocumentInternal() call in exception handling, logging cache
purge failures with Console::warning while allowing the committed
createDocument/createDocuments operation to succeed. Keep the existing tenant
resolution and purge behavior unchanged; do not refactor the duplicated tenant
wrapper.

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