Uh oh!
There was an error while loading. Please reload this page.
Fix cache clobber - #815
Conversation
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 Walkthrough📝 Walkthrough🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
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. Comment |
The utopia-php/base:php-8.4-0.2.1 image doesn't exist on Docker Hub, causing the CI Docker build to fail. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This reverts commit 2aa26e6.
Changed from utopia-php/base to appwrite/utopia-base. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Temporary debug to identify cache vs DB data differences. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Database/Database.php (1)
5699-5705:⚠️ Potential issue | 🟠 MajorRemove or gate the debug log to avoid leaking data.
Line 5704 logs raw values (including potential PII) and could flood logs; please remove or guard behind a debug flag/level.🧹 Suggested change (remove debug log)
- error_log("DEBUG shouldUpdate: key={$key} type_new=" . gettype($value) . " type_old=" . gettype($oldValue) . " val_new=" . json_encode($value) . " val_old=" . json_encode($oldValue));
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a document goes through cache (JSON serialize/deserialize), float values like 1.0 become int 1. PHP strict comparison then incorrectly detects a change, triggering unnecessary UPDATE permission checks. Use loose numeric comparison to handle this edge case. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add small sleep between document create and update to ensure timestamps differ in millisecond-precision datetime checks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/Database/Database.php`:
- Around line 5703-5706: The numeric-equality shortcut using is_numeric($value)
&& is_numeric($oldValue) in Database::... lets numeric-like strings (e.g. "01"
vs "1") bypass $shouldUpdate authorization; restrict the shortcut so it only
applies to true numeric scalars (ints/floats) or to attributes whose declared
type is numeric: check scalar types (e.g. is_int/is_float) or the attribute
metadata before continuing, and only then continue; otherwise fall back to the
normal equality/authorization path that respects $shouldUpdate. Ensure you
update the condition that references $value, $oldValue and is_numeric so it
never skips authorization for string values.
In `@tests/e2e/Adapter/Scopes/DocumentTests.php`:
- Line 5924: The test currently calls \usleep(2000) to try to force an updatedAt
change which is flaky across adapters; replace this single microsecond sleep by
waiting until the timestamp actually changes (either use sleep(1) to wait at
least one second or implement a short loop that reloads the document and breaks
when updatedAt !== createdAt, with a reasonable timeout) in the test that
contains the \usleep(2000) call so the assertion comparing createdAt and
updatedAt is deterministic.
Uh oh!
There was an error while loading. Please reload this page.
| $newUpdatedAt = $doc11->getUpdatedAt(); | ||
| \usleep(2000); // Ensure updatedAt timestamp differs from creation time |
There was a problem hiding this comment.
Avoid flaky timestamp comparisons across adapters.
A 2ms sleep may not advance $updatedAt on adapters with second/millisecond precision, making the subsequent assertion non-deterministic. Consider sleeping at least a second (or looping until the timestamp changes).
🛠️ Suggested fix
- \usleep(2000); // Ensure updatedAt timestamp differs from creation time+ // Some adapters only store timestamps with second precision.+ \usleep(1_100_000); // Ensure updatedAt timestamp differs from creation time📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| \usleep(2000); // Ensure updatedAt timestamp differs from creation time | |
| // Some adapters only store timestamps with second precision. | |
| \usleep(1_100_000); // Ensure updatedAt timestamp differs from creation time |
🤖 Prompt for AI Agents
In `@tests/e2e/Adapter/Scopes/DocumentTests.php` at line 5924, The test currently
calls \usleep(2000) to try to force an updatedAt change which is flaky across
adapters; replace this single microsecond sleep by waiting until the timestamp
actually changes (either use sleep(1) to wait at least one second or implement a
short loop that reloads the document and breaks when updatedAt !== createdAt,
with a reasonable timeout) in the test that contains the \usleep(2000) call so
the assertion comparing createdAt and updatedAt is deterministic.
Apply casting() to the merged document before comparing with $old. This normalizes types (e.g. int 1 back to float 1.0) so the comparison against $old (which was cast during getDocument) uses consistent types. Fixes false-positive change detection caused by JSON cache round-trips degrading float types to int. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cast individual values to their declared attribute type before comparing with $old, skipping Operator instances. This normalizes types degraded by cache JSON round-trips (e.g. float 1.0 → int 1) without affecting the document that gets persisted. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Database/Database.php (1)
5604-5717:⚠️ Potential issue | 🟠 MajorType-aware comparison can misfire for array attributes and uncasted old values.
Casting only$value(and without array awareness) can force$shouldUpdate=truefor equal arrays or when$oldValueis still string/uncast, leading to unnecessary updates and stricter permission checks. Consider tracking the array flag and casting both sides only for scalar attributes.🔧 Suggested adjustment (array-aware + symmetric casting)
- $attributeTypes = [];- foreach ($collection->getAttribute('attributes', []) as $attr) {- $attributeTypes[$attr['$id'] ?? ''] = $attr['type'] ?? '';- }+ $attributeTypes = [];+ foreach ($collection->getAttribute('attributes', []) as $attr) {+ $attrId = $attr['$id'] ?? null;+ if ($attrId !== null) {+ $attributeTypes[$attrId] = [+ 'type' => $attr['type'] ?? null,+ 'array' => (bool)($attr['array'] ?? false),+ ];+ }+ } $oldValue = $old->getAttribute($key); // Cast value to attribute type for consistent comparison // (e.g. cache JSON round-trip turns float 1.0 into int 1) - $attrType = $attributeTypes[$key] ?? null;- if ($attrType !== null && !\is_null($value) && !($value instanceof Operator)) {- $value = match ($attrType) {- self::VAR_FLOAT => (float)$value,- self::VAR_INTEGER => (int)$value,- self::VAR_BOOLEAN => (bool)$value,- default => $value,- };- }+ $attrMeta = $attributeTypes[$key] ?? null;+ if ($attrMeta && !$attrMeta['array'] && !\is_null($value) && !($value instanceof Operator)) {+ $cast = fn ($v) => match ($attrMeta['type']) {+ self::VAR_FLOAT => (float)$v,+ self::VAR_INTEGER => (int)$v,+ self::VAR_BOOLEAN => (bool)$v,+ default => $v,+ };+ $value = $cast($value);+ if ($oldValue !== null) {+ $oldValue = $cast($oldValue);+ }+ }
Cast both $value and $oldValue by attribute type before comparison to handle adapters like MongoDB that don't cast floats on read. Add usleep before doc4 update to prevent same-millisecond timestamps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary by CodeRabbit
Bug Fixes
Tests
Chores