NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist
AboutResource Query Responsibility Segregation (RQRS) is a caching framework for BEAR.Sunday that optimizes performance by separating query and command responsibilities. It features event-driven cache invalidation, dependency resolution, donut caching, CDN integration, and conditional requests.
Last release 22 days ago
16 Sep 2026
Release timing varies
gaps range from 9 days to 5 months
Rarely documented
notes for 12 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
12 years old
86 releases · first in 2015
Cache observability rebuilt on Koriym.SemanticLogger ( #178 ): typed, schema-validated open/event/close log tree replaces free-text RepositoryLoggerIn
RepositoryLoggerInterface messages. Off by default - install DevQueryRepositoryLogModule (dev) or ProdQueryRepositoryLogModule (prod) to record. See docs/reading-the-log.md, docs/why-the-log-records-everything.md, docs/what-the-log-proves.md (Japanese translations included).cache_policy (#186), cache_error, pool_error, put_skipped, pre_write_cleanup, cdn_headers, conditional_request, invalidate (tri-state cdn, plus durationMs), command (source field), semantic_logger_error (core). Schemas in docs/schemas/context/.saved, tags, requestedTtl (save_etag), durationMs on the cache_hit/cache_miss close.#[CacheLog] qualifier resolves the cache logger. SafeSemanticLogger/SessionStoreInterface/ProcessSession keep it safe across a serialized injector (#179). TopLevelAwareInterface lets a custom logger opt into manual-call scope rooting.put()/putStatic()/putDonut()/purge()/invalidateTags() calls now open manual_store/manual_purge/manual_invalidate scopes.LogSinkInterface/ShutdownFlush, LogWriterInterface (LogFileWriter/LogStreamWriter/PsrLogWriter), ConcurrentRuntimeInterface/HostRuntime (refuses to arm under RoadRunner/a Swoole coroutine). Detection is not exhaustive: FrankenPHP worker mode, ReactPHP, Amp, a long-lived CLI consumer, and a Swoole worker whose logger is built outside a coroutine must bind their own ConcurrentRuntimeInterface/SessionStoreInterface/LogSinkInterface.ProdQueryRepositoryLogModule: buffers a session and applies RetentionPolicyInterface/KeepMutationsAndFailures at flush.DevQueryRepositoryLogModule: writes one file per request plus latest.json for vendor/bin/stree.demo/run-degraded.php and DemoLogCoverageTest: the demos now cover every context, schema enum value and command source, and self-validate against docs/schemas/context.UriScopedHttpCacheInterface::isNotModifiedFor() / ScopedValidatorInterface::hasEtagFor(): opt-in ETag scoped to the requested URI (#197, #201). Entries written before this version cannot be scoped: each client pays one full (non-304) response after the upgrade, once.QueryRepository/ResourceStorage boundary.docs/reading-the-log.md is now stated per parent declaration (#188).RepositoryLogger, RepositoryLoggerInterface, StructuredRepositoryLoggerInterface, NullRepositoryLogger: bound for BC but receive no internal events.update parameter of #[Cacheable]: it has no effect.docs/schemas/repository-log.json (superseded by per-context schemas in docs/schemas/context/).BEAR\QueryRepository\Log\NullSemanticLogger (koriym/semantic-logger 0.9 ships its own).skills/bear-cache-log/: folded into bear-observe.SemanticLoggerInterface/#[CacheLog] instead of RepositoryLoggerInterface::log().SaveDonutContext/SaveDonutViewContext: sMaxAge field renamed to requestedTtl.SaveEtagContext/SaveDonutViewContext: surrogateKeys field renamed to tags.command_result instead of vanishing from the log.assert() in ResourceStorage::saveDonut() (it threw after saved: false was already logged).pre_write_cleanup marker) instead of inferred from tag correlation.r marker to the ETag, so an unchanged recomposition still revalidates via If-None-Match.koriym/semantic-logger.#[CacheLog] binds to NullSemanticLogger unless a log module is installed.Exception\CacheStoreFailure now marks the cache-failure boundary. An unreadable ETag pool answers the conditional request in full instead of failing it, and a donut write the store refuses now serves the rendered page instead of a 500.invalidate.cdn: failed, then the purge exception propagates instead of being swallowed - a CDN outage can no longer look like a successful purge.#[Refresh] no longer double-writes its own #[Cacheable] destination after a regenerating GET.ResourceDonut::FOMRAT → FORMAT, EtagSetter::getEtagByEitireView() → getEtagByEntireView(), and the misspelled parameters $httpCacche → $httpCache (EtagSetter::getEtagByPartialBody()), $concheControlMaxAge → $cacheControlMaxAge (HeaderSetter::__invoke()) - a named-argument caller has to follow.saveView() required a 200-only validator, and DonutRepository/ResourceDonut did not restore the stored status code (#206, #207). Behaviour change: a #[CacheableResponse]/#[DonutCache] page answering 2xx or 3xx is now served with that status instead of crashing or degrading to 200 - a redirect that was reachable only once now persists until its cache entry is invalidated, so such a page needs the surrogate keys that invalidate it.onPost on a #[Cacheable] class ran with no interceptor at all, silently dropping #[Refresh]/#[Purge] (#212, #214).onPost on a #[Cacheable]/donut class missing an onGet-required parameter threw UnmatchedQuery uncaught - a regression from #214 above (#219, #220).#[RefreshCache] or a method-level #[CacheableResponse]; both now bind DonutCommandInterceptor. DonutCacheModule also missed onPost in its write matcher.DevEtagSetter/MobileEtagSetter set a validator on a non-200 response, where EtagSetter always skipped it; CdnCacheControlHeaderSetterInterface likewise now applies to 200 only.If-None-Match token containing a PSR-6 reserved character reached the ETag pool as a cache key and threw a 500; such tokens (and *) are now dropped and the request answered in full.Last-Modified even for byte-identical content; Age is now derived from storedAt instead of Last-Modified.putStatic()/putDonut() logged a negative lifetime verbatim while storage clamped what it stored.cache_error{operation: write}.Surrogate-Key lost embed dependency tracking and kept serving purged children.Surrogate-Key header when a resource is written twice in one request.ResourceStorage::saveEtag() used a hard-coded new UriTag() instead of the injected UriTagInterface.Surrogate-Key was untagged and unreachable by purge(); it now rebuilds on its first purge. Entries written before this version need a rewrite or a cache clear to become reachable.#[Cacheable] value entry with no renderer previously degraded every store to a warning and left the cache empty; it is now stored without rendering. The ETag validator falls back from the view to the body, a stored value entry no longer carries the renderer's Content-Type, and a custom EtagSetterInterface now receives $ro->view === null on the value path and must read the body instead.serialize() (a Closure, for instance) is a separate, known limitation: the write's serialize() Exception propagates uncaught rather than degrading to CacheStoreFailure (#208's rule: only the store's own failure is swallowed). Keep such values out of a value entry's body, or catch Exception at the call site.CliHttpCache::isNotModifiedFor() ignored the CLI request form's If-None-Match argument.MatchQuery threw for an onGet parameter with a default value that a write's query omitted.CDN-Cache-Control directives are now comma-separated per RFC 9213 (were space-separated).AkamaiModule, a #[CacheableResponse] page lost its embedded children's invalidation tags (ResourceStorageInterface::saveDonutView() gained an optional $tags parameter).MobileEtagSetter serialized the raw body and threw on any #[Embed]; it now hashes the materialized copy the way EtagSetter does.One column per quarter.
Remove unused ETag from invalidation tags: ETag was unnecessarily registered as a cache invalidation tag. No code path invalidates by ETag (only by UR
volatile-* eviction policies these Sets leaked memory indefinitely. (#180)Fix multi-embed cache dependency: a #[Cacheable] resource embedding more than one child kept only the last child's dependency, so purging an earlier c
#[Cacheable] resource embedding more than one child kept only the last child's dependency, so purging an earlier child failed to invalidate the parent (stale cache). CacheDependency::depends() now accumulates child tags instead of overwriting, and the erroneous assertion that a parent had no prior tags has been removed.Resolve embed cache dependencies before HAL rendering: child resources' ETag headers are now collected before HalRenderer strips Request instances fro
HalRenderer strips Request instances from the body, so the parent's Surrogate-Key reliably includes all embedded children (#174)AbstractRequest instead of the concrete Request, so AsyncRequest (and other AbstractRequest subclasses) are no longer silently skippedEtagSetter / DevEtagSetter into QueryRepository::put() (runs on HTTP 200 only, before persistence)QueryRepository::__construct() now requires CacheDependencyInterfaceEtagSetter::__construct() and DevEtagSetter::__construct() no longer accept CacheDependencyInterfaceQueryRepositoryModule are unaffected; only callers that directly new these classes need to update their constructor callstests/CacheDependencyTest.php)continue path coverageReplace deprecated DateTimeInterface::RFC7231 with Header::RFC7231 constant for PHP 8.5 compatibility
DateTimeInterface::RFC7231 with Header::RFC7231 constant for PHP 8.5 compatibility (#173)Header::RFC7231 used by all EtagSetter implementationsFull Changelog: 1.15.0...1.15.1
Add ServerContextInterface for coroutine-safe request handling in Swoole/RoadRunner environments
ServerContextInterface for coroutine-safe request handling in Swoole/RoadRunner environmentsGlobalServerContext as default implementation using $_SERVER superglobalRepositoryLoggerInterface with reset() method for long-running process supportServerContextInterface to GlobalServerContext in QueryRepositoryModuleServerContextInterface in ResourceStorage instead of direct $_SERVER accessFull Changelog: 1.14.0...1.15.0
LLM documentation ( docs/llms.txt , docs/llms-full.txt ) for AI-assisted development
docs/llms.txt, docs/llms-full.txt) for AI-assisted developmentdocs/schemas/repository-log.json).gitattributes to exclude development files from releaseray/aop ^2.19.1 and ray/di ^2.20 for PHP 8.5 compatibilitydocs/llms.txt, docs/llms-full.txt) for AI-assisted developmentdocs/schemas/repository-log.json)demo/run-dependency.php, demo/run-donut.php)tests/CACHE_DEPENDENCY_TESTS.md).gitattributes to exclude development files from releaseray/aop ^2.19.1 and ray/di ^2.20 for PHP 8.5 compatibilityPer semver, breaking changes are defined as incompatible API changes
doctrine/annotations and doctrine/cache dependencies, migrated to native PHP 8 attributesResourceStorage to singleton scope, optimized readonly class declarations for PHP 8.2rector-migrate.php for automated annotation-to-attribute migrationANNOTATION_TO_ATTRIBUTE.md with comprehensive migration instructionsCLAUDE.md with complete codebase architecture guideREADME.ja.mddoctrine/annotations (abandoned package)doctrine/cache (abandoned package)Applications using annotations must migrate to PHP 8 attributes:
# Preview changes
vendor/bin/rector process src --config=vendor/bear/query-repository/rector-migrate.php --dry-run
# Apply migration
vendor/bin/rector process src --config=vendor/bear/query-repository/rector-migrate.phpSee ANNOTATION_TO_ATTRIBUTE.md for detailed migration guide.
The doctrine/annotations package has been officially abandoned by its maintainers and will no longer receive updates or security patches. This change:
While this release removes doctrine/annotations, we consider it a minor version (1.13.0) rather than a major version (2.0.0) for the following reasons:
No Public API Changes
Automated Migration Available
Semantic Versioning Perspective
Version Strategy
rector-migrate.php for automated annotation-to-attribute migrationANNOTATION_TO_ATTRIBUTE.md with comprehensive migration instructionsMarshallerType enum for type-safe marshaller selectionRelayCluster in RedisDsnProvider#[Override] PHP attribute across all applicable methods and classesStorageRedisDsnModule) with provider implementationdoctrine/annotations and doctrine/cache dependencies, migrated to native PHP 8 attributesrector-migrate.php to support vendor installation by removing hardcoded pathsANNOTATION_TO_ATTRIBUTE.md migration guide following Ray.AuraSqlModule patternsymfony/polyfill-php83 dependency to ^v1.32.0mobiledetect/mobiledetectlib to support version ^4.8madapaja/twig-module, phpunit/phpunit, predis/predis, twig/twig, symfony/process)vimeo/psalm to version 6.12ray/aop dependency to ^2.16ray/di dependency to ^2.17.2ResourceStorage to singleton scope for better performanceResourceStorage with ProviderInterface for serialization supportbear/fastly-module from production dependencies (moved to dev dependencies)assert statements from codebaseStorageApcModuleTestStorageRedisModuleTest (use StorageRedisDsnModule instead)StorageRedisMemcachedModuleBcModuleNamespacedCacheProvider classResourceStorageCacheableTraitFix: Correct README file extension by @suzumaze in #162
Fix type casting and update dependencies by @koriym in #159
Full Changelog: 1.11.1...1.12.0
The error that occurred when serializing the root object is fixed by making the singleton object that cannot be serialized.
The error that occurred when serializing the root object is fixed by making the singleton object that cannot be serialized.
シリアライズできないオブジェクトをシングルトンにしていたことで、ルートオブジェクトのシリアライズでエラーが発生していたのを修正します。
Full Changelog: 1.11.0...1.11.1
Nothing published for this version
Nothing published for this version
Nothing published for this version
(Previous releases not documented yet)
(Previous releases not documented yet)
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →