Two related tidy-ups to query-cache dependent-table handling.#3792
Merged
Conversation
This is a replacement for #3150 **1. Read `dependentTables` from the query plan (single source of truth)** `OrmQueryRequest` accumulated query-cache `dependentTables` into a field via `addDependentTables(...)`, fed from per-CQuery `dependentTables()` helpers. The `CQueryPlan` already owns this set, so the copying is redundant. - `putToQueryCache(...)` now reads `dependentTables` directly from the request's `CQueryPlan` at cache-put time. - Removed the `OrmQueryRequest.dependentTables` field and `addDependentTables(Set)`. - Removed the now-unused `dependentTables()` helpers from `CQuery`, `CQueryRowCount` and `CQueryFetchSingleAttribute` (and their `Set` imports). This is behaviour-preserving: each CQuery is built with exactly the plan stored under `request.queryPlanKey`, so `request.queryPlan().dependentTables()` is the same set the per-CQuery helpers returned. **2. Fix a query-plan trim race that could null the plan at put time** A freshly built `CQueryPlan` was stored in the plan cache with `lastQueryTime == 0`, making it immediately eligible for `trimQueryPlans` (runs every 60s; TTL default 300s) during its *first* execution. If the trim fired mid-execution, `request.queryPlan()` could return `null` at put time — and a cache entry with `null` dependentTables is never invalidated by table modifications (`TableModState.isValid`), i.e. latent stale data. - `CQueryPlanStats` now initialises `lastQueryTime` to construction time, so a new plan is only trim-eligible after a genuine TTL idle. `trimQueryPlans` is the sole consumer of `lastQueryTime()`, so this is safe. - `putToQueryCache(...)` skips the put when the plan is `null` (fail safe rather than caching an un-invalidatable entry) for the residual pathological case (a single query running past the TTL on first execution). ### Tests - Added `testFindSingleAttributeOnDependent` and `testFindListOnDependent` to `TestQueryCacheTableDependency`, asserting cache-hit then dependent-table invalidation for the single-attribute and findMany paths (the count path was already covered). - All cache/query tests pass: 69 in `ebean-test` `org.tests.cache.**`, 929 in `ebean-core` cache + query packages.
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.
This is a replacement for #3150
1. Read
dependentTablesfrom the query plan (single source of truth)OrmQueryRequestaccumulated query-cachedependentTablesinto a field viaaddDependentTables(...), fed from per-CQuerydependentTables()helpers. TheCQueryPlanalready owns this set, so the copying is redundant.putToQueryCache(...)now readsdependentTablesdirectly from the request'sCQueryPlanat cache-put time.OrmQueryRequest.dependentTablesfield andaddDependentTables(Set).dependentTables()helpers fromCQuery,CQueryRowCountandCQueryFetchSingleAttribute(and theirSetimports).This is behaviour-preserving: each CQuery is built with exactly the plan stored
under
request.queryPlanKey, sorequest.queryPlan().dependentTables()is thesame set the per-CQuery helpers returned.
2. Fix a query-plan trim race that could null the plan at put time
A freshly built
CQueryPlanwas stored in the plan cache withlastQueryTime == 0, making it immediately eligible fortrimQueryPlans(runs every 60s; TTL default 300s) during its first execution. If the trim
fired mid-execution,
request.queryPlan()could returnnullat put time — anda cache entry with
nulldependentTables is never invalidated by tablemodifications (
TableModState.isValid), i.e. latent stale data.CQueryPlanStatsnow initialiseslastQueryTimeto construction time, so a new plan is only trim-eligible after a genuine TTL idle.trimQueryPlansis the sole consumer oflastQueryTime(), so this is safe.putToQueryCache(...)skips the put when the plan isnull(fail safe rather than caching an un-invalidatable entry) for the residual pathological case (a single query running past the TTL on first execution).Tests
testFindSingleAttributeOnDependentandtestFindListOnDependenttoTestQueryCacheTableDependency, asserting cache-hit then dependent-table invalidation for the single-attribute and findMany paths (the count path was already covered).ebean-testorg.tests.cache.**, 929 inebean-corecache + query packages.