Skip to content

Commit 9763462

Browse files
committed
Simplify query cache value handling
1 parent 5b87fc8 commit 9763462

2 files changed

Lines changed: 170 additions & 175 deletions

File tree

src/Database/Database.php

Lines changed: 134 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -8581,52 +8581,6 @@ public function purgeQueryCache(string $collection, ?string $namespace = null):
85818581
);
85828582
}
85838583

8584-
/**
8585-
* Restore a cached query document payload into database documents.
8586-
*
8587-
* Returns false when the cached payload is invalid or stale and should be
8588-
* refreshed by the caller.
8589-
*
8590-
* @param Document $collection
8591-
* @param mixed $payload
8592-
* @return array<Document>|false
8593-
* @throws AuthorizationException
8594-
* @throws Exception
8595-
*/
8596-
private function restoreQueryCacheDocuments(
8597-
Document $collection,
8598-
mixed $payload,
8599-
): array|false {
8600-
if (!\is_array($payload)) {
8601-
return false;
8602-
}
8603-
8604-
$documentSecurity = $collection->getAttribute('documentSecurity', false);
8605-
$skipAuth = $this->authorization->isValid(new Input(self::PERMISSION_READ, $collection->getRead()));
8606-
8607-
if (!$skipAuth && !$documentSecurity && $collection->getId() !== self::METADATA) {
8608-
throw new AuthorizationException($this->authorization->getDescription());
8609-
}
8610-
8611-
$documents = [];
8612-
foreach ($payload as $document) {
8613-
if (!\is_array($document)) {
8614-
return false;
8615-
}
8616-
8617-
$document = $this->createDocumentInstance($collection->getId(), $document);
8618-
$document = $this->casting($collection, $document);
8619-
8620-
if ($this->isTtlExpired($collection, $document)) {
8621-
return false;
8622-
}
8623-
8624-
$documents[] = $document;
8625-
}
8626-
8627-
return $documents;
8628-
}
8629-
86308584
/**
86318585
* Execute a callback behind a cache-aside lookup.
86328586
*
@@ -8666,7 +8620,54 @@ public function withCache(
86668620
$value = \is_array($cached) && \array_key_exists('value', $cached) ? $cached['value'] : false;
86678621

86688622
if ($value !== false) {
8669-
$decoded = $this->decodeCacheValue($cached, $value);
8623+
$decoded = $value;
8624+
$collection = $cached['collection'] ?? null;
8625+
8626+
if (\is_string($collection) && $collection !== '') {
8627+
// Cached document payloads are stored as arrays; restore them
8628+
// to the same Document shape that find()/getDocument() return.
8629+
$collection = $this->silent(fn () => $this->getCollection($collection));
8630+
8631+
if ($collection->isEmpty()) {
8632+
$decoded = false;
8633+
} else {
8634+
$documentSecurity = $collection->getAttribute('documentSecurity', false);
8635+
$skipAuth = $this->authorization->isValid(new Input(self::PERMISSION_READ, $collection->getRead()));
8636+
8637+
if (!$skipAuth && !$documentSecurity && $collection->getId() !== self::METADATA) {
8638+
throw new AuthorizationException($this->authorization->getDescription());
8639+
}
8640+
8641+
$payload = ($cached['type'] ?? null) === 'document' ? [$value] : $value;
8642+
8643+
if (!\is_array($payload)) {
8644+
$decoded = false;
8645+
} else {
8646+
$documents = [];
8647+
8648+
foreach ($payload as $document) {
8649+
if (!\is_array($document)) {
8650+
$decoded = false;
8651+
break;
8652+
}
8653+
8654+
$document = $this->createDocumentInstance($collection->getId(), $document);
8655+
$document = $this->casting($collection, $document);
8656+
8657+
if ($this->isTtlExpired($collection, $document)) {
8658+
$decoded = false;
8659+
break;
8660+
}
8661+
8662+
$documents[] = $document;
8663+
}
8664+
8665+
if ($decoded !== false) {
8666+
$decoded = ($cached['type'] ?? null) === 'document' ? ($documents[0] ?? false) : $documents;
8667+
}
8668+
}
8669+
}
8670+
}
86708671

86718672
if ($decoded !== false) {
86728673
return $decoded;
@@ -8688,134 +8689,95 @@ public function withCache(
86888689

86898690
if ($value !== false) {
86908691
try {
8691-
$encoded = $this->encodeCacheValue($value);
8692-
if ($encoded !== false) {
8693-
$this->cache->save($key, $encoded, $hash);
8694-
}
8695-
} catch (Throwable $e) {
8696-
Console::warning('Warning: Failed to save cache value: ' . $e->getMessage());
8697-
}
8698-
}
8699-
8700-
return $value;
8701-
}
8702-
8703-
/**
8704-
* @param array<string, mixed> $cached
8705-
*/
8706-
private function decodeCacheValue(array $cached, mixed $value): mixed
8707-
{
8708-
$collection = $cached['collection'] ?? null;
8709-
if (!\is_string($collection) || $collection === '') {
8710-
return $value;
8711-
}
8712-
8713-
$collection = $this->silent(fn () => $this->getCollection($collection));
8714-
if ($collection->isEmpty()) {
8715-
return false;
8716-
}
8717-
8718-
if (($cached['type'] ?? null) === 'document') {
8719-
return $this->restoreQueryCacheDocument($collection, $value);
8720-
}
8721-
8722-
return $this->restoreQueryCacheDocuments($collection, $value);
8723-
}
8724-
8725-
private function restoreQueryCacheDocument(Document $collection, mixed $payload): Document|false
8726-
{
8727-
$documents = $this->restoreQueryCacheDocuments($collection, [$payload]);
8692+
$encoded = false;
87288693

8729-
return $documents === false ? false : ($documents[0] ?? false);
8730-
}
8731-
8732-
/**
8733-
* @return array<string, mixed>|false
8734-
*/
8735-
private function encodeCacheValue(mixed $value): array|false
8736-
{
8737-
if ($value instanceof Document) {
8738-
$collection = $value->getCollection();
8739-
if ($collection === '') {
8740-
return false;
8741-
}
8694+
if ($value instanceof Document) {
8695+
$collection = $value->getCollection();
8696+
8697+
if ($collection !== '') {
8698+
$encoded = [
8699+
'collection' => $collection,
8700+
'type' => 'document',
8701+
'value' => $value->getArrayCopy(),
8702+
];
8703+
}
8704+
} elseif (!\is_array($value)) {
8705+
$encoded = ['value' => $value];
8706+
} else {
8707+
// Only homogeneous top-level document lists are safe to restore
8708+
// from cache. Mixed or nested Document payloads keep callback shape.
8709+
$collection = null;
8710+
$hasDocuments = false;
8711+
$hasNonDocuments = false;
8712+
$cacheable = true;
8713+
$documents = [];
8714+
$containsDocument = function (mixed $item) use (&$containsDocument): bool {
8715+
if ($item instanceof Document) {
8716+
return true;
8717+
}
87428718

8743-
return [
8744-
'collection' => $collection,
8745-
'type' => 'document',
8746-
'value' => $value->getArrayCopy(),
8747-
];
8748-
}
8719+
if (!\is_array($item)) {
8720+
return false;
8721+
}
87498722

8750-
$collection = $this->getCacheValueCollection($value);
8751-
if ($collection === null || !\is_array($value)) {
8752-
if ($this->hasCacheValueDocument($value)) {
8753-
return false;
8754-
}
8723+
foreach ($item as $child) {
8724+
if ($containsDocument($child)) {
8725+
return true;
8726+
}
8727+
}
87558728

8756-
return ['value' => $value];
8757-
}
8729+
return false;
8730+
};
87588731

8759-
return [
8760-
'collection' => $collection,
8761-
'type' => 'documents',
8762-
'value' => $this->encodeQueryCacheValue($value),
8763-
];
8764-
}
8732+
foreach ($value as $item) {
8733+
if (!$item instanceof Document) {
8734+
if ($hasDocuments || $containsDocument($item)) {
8735+
$cacheable = false;
8736+
break;
8737+
}
87658738

8766-
private function getCacheValueCollection(mixed $value): ?string
8767-
{
8768-
if ($value instanceof Document) {
8769-
return $value->getCollection() ?: null;
8770-
}
8739+
$hasNonDocuments = true;
8740+
continue;
8741+
}
87718742

8772-
if (!\is_array($value)) {
8773-
return null;
8774-
}
8743+
if ($hasNonDocuments) {
8744+
$cacheable = false;
8745+
break;
8746+
}
87758747

8776-
foreach ($value as $item) {
8777-
$collection = $this->getCacheValueCollection($item);
8778-
if ($collection !== null) {
8779-
return $collection;
8780-
}
8781-
}
8748+
$documentCollection = $item->getCollection();
8749+
if ($documentCollection === '') {
8750+
$cacheable = false;
8751+
break;
8752+
}
87828753

8783-
return null;
8784-
}
8754+
if ($collection !== null && $collection !== $documentCollection) {
8755+
$cacheable = false;
8756+
break;
8757+
}
87858758

8786-
private function hasCacheValueDocument(mixed $value): bool
8787-
{
8788-
if ($value instanceof Document) {
8789-
return true;
8790-
}
8759+
$collection = $documentCollection;
8760+
$hasDocuments = true;
8761+
$documents[] = $item->getArrayCopy();
8762+
}
87918763

8792-
if (!\is_array($value)) {
8793-
return false;
8794-
}
8764+
if ($cacheable) {
8765+
$encoded = $hasDocuments ? [
8766+
'collection' => $collection,
8767+
'type' => 'documents',
8768+
'value' => $documents,
8769+
] : ['value' => $value];
8770+
}
8771+
}
87958772

8796-
foreach ($value as $item) {
8797-
if ($this->hasCacheValueDocument($item)) {
8798-
return true;
8773+
if ($encoded !== false) {
8774+
$this->cache->save($key, $encoded, $hash);
8775+
}
8776+
} catch (Throwable $e) {
8777+
Console::warning('Warning: Failed to save cache value: ' . $e->getMessage());
87998778
}
88008779
}
88018780

8802-
return false;
8803-
}
8804-
8805-
private function encodeQueryCacheValue(mixed $value): mixed
8806-
{
8807-
if ($value instanceof Document) {
8808-
return $value->getArrayCopy();
8809-
}
8810-
8811-
if (!\is_array($value)) {
8812-
return $value;
8813-
}
8814-
8815-
foreach ($value as $key => $item) {
8816-
$value[$key] = $this->encodeQueryCacheValue($item);
8817-
}
8818-
88198781
return $value;
88208782
}
88218783

@@ -9777,9 +9739,20 @@ public function getQueryCacheField(
97779739
'filters' => $this->getActiveFilterSignatures(),
97789740
];
97799741

9742+
$schemaHash = '';
9743+
if ($collection !== null && !$collection->isEmpty()) {
9744+
// Schema-affecting changes must move callers onto a fresh cache field.
9745+
$schemaHash = \md5(
9746+
\json_encode($collection->getAttribute('attributes', []))
9747+
. \json_encode($collection->getAttribute('indexes', []))
9748+
. \json_encode($collection->getAttribute('$permissions', []))
9749+
. \json_encode($collection->getAttribute('documentSecurity', false))
9750+
);
9751+
}
9752+
97809753
return \sprintf(
97819754
'%s:%s:%s',
9782-
$this->getQueryCacheSchemaHash($collection),
9755+
$schemaHash,
97839756
\md5(\json_encode($queryPayload) ?: ''),
97849757
$field,
97859758
);
@@ -9830,20 +9803,6 @@ private function normalizeQueryCacheQueryValue(mixed $value): mixed
98309803
return $value;
98319804
}
98329805

9833-
private function getQueryCacheSchemaHash(?Document $collection): string
9834-
{
9835-
if ($collection === null || $collection->isEmpty()) {
9836-
return '';
9837-
}
9838-
9839-
return \md5(
9840-
\json_encode($collection->getAttribute('attributes', []))
9841-
. \json_encode($collection->getAttribute('indexes', []))
9842-
. \json_encode($collection->getAttribute('$permissions', []))
9843-
. \json_encode($collection->getAttribute('documentSecurity', false))
9844-
);
9845-
}
9846-
98479806
/**
98489807
* @return array<string, string>
98499808
*/

0 commit comments

Comments
 (0)