With $(nproc) (typically 4-6), parallel test processes overwhelm Redis
and the attribute worker, causing cascading failures from resource
contention. 3 processes reduces contention while maintaining reasonable
test throughput.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The _metadata collection schema now includes externalId as an optional
string attribute, allowing createCollection(metadata: ['externalId' => ...])
to pass Structure validation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Database library: createCollection() accepts metadata parameter
for arbitrary key-value pairs on the _metadata document
- Appwrite: passes externalId (user-facing collection ID) when
creating collections via createCollection(metadata: ['externalId' => $collectionId])
- Metadata decorator: reads $collection->getAttribute('externalId')
directly from the collection metadata — zero queries, zero caches,
zero overhead
- getDatabasesDB: removed dbForProject dependency and all mapping logic
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The findOne by $sequence query fails validation because $sequence is an
internal id-type attribute. Instead, load ALL collections for the
database once on first relationship encounter and cache statically per
database per Swoole worker. No per-attribute queries needed.
Also fixes UUID sequence extraction — the previous explode('_') split
UUIDs incorrectly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The UserEvents hook copied event state at resource-resolution time
(before the init hook set the user), causing webhooks to receive null
user IDs. Now stores the source queueForEvents as a reference and
copies at fire-time when the user Document has been populated.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The findOne query per relationship attribute ran on every getDatabasesDB
call. Static cache keyed by database+internal name caches results across
requests within the same Swoole worker process.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Related collection lookup used internal name as document ID, but
Appwrite collections have user-facing IDs. Extract sequence from
internal name and use findOne by $sequence instead.
- Increase all schema polling timeouts from 240s to 360s for CI
dedicated mode parallel load.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- getDatabasesDB now registers mappings for related collections from
relationship attributes, not just the primary collection. This fixes
testOneToOneRelationship where nested documents showed internal names.
- Update utopia-php/migration to handle Index objects in createCollection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Endpoints pass the collection document they already have to
getDatabasesDB. The factory registers the single collection mapping
on the Metadata decorator — no bulk find queries, no static cache,
no dbForProject dependency.
The decorator is now purely stateless with zero database overhead.
Collection ID resolution uses only the pre-registered mapping from
the endpoint's request parameters.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Query::contains() is deprecated for array attributes in the new query
library. The deprecation warnings were spamming worker logs and
potentially slowing attribute processing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The decorator no longer queries dbForProject directly. Instead,
getDatabasesDB pre-populates the mapping via setCollectionId() and
uses a static cache (Metadata::getCachedMap) so the query runs at most
ONCE per database per Swoole worker process. The decorator is now
stateless — no database dependencies, just a pre-set map.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The mapping query was running once per getDatabasesDB call (once per
request). With static cache keyed by database sequence, the query runs
once per database per Swoole worker process. Subsequent requests reuse
the cached mapping. setCollectionId updates both instance and static
caches for newly created collections.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove processDocument() and all its calls — the decorator approach is
the intended design. The Metadata decorator lazily loads the collection
ID mapping from dbForProject on first use, wrapped in silent() to
prevent lifecycle hooks. Maps both relative (collection_N) and full
(database_M_collection_N) keys to user-facing IDs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The formatOptions min/max may be integers after JSON decode or may be
on the attribute directly (from range filter). Use floatval() and check
both formatOptions and direct attribute keys.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The parallel E2E test load generates many queue messages across workers
(databases, webhooks, audits, functions, etc.). With 512mb, Redis runs
out of memory under CI load, causing worker crashes and cascading test
failures.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Metadata decorator required a collection ID mapping query that added
overhead and caused Redis OOM in CI. Instead, stamp $databaseId and
$collectionId/$tableId directly in endpoint actions where user-facing
IDs are available from request parameters — matching the 1.9.x approach.
- Remove Metadata decorator from getDatabasesDB hooks
- Restore processDocument() on Documents/Action base class
- Add processDocument() calls in Get, Create, XList, Update, Upsert
- Simplify Metadata.php to stateless map-only decorator (no queries)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Configure a project-local result cache directory so PHPStan only
re-analyses files that changed. In CI, persist the cache across
runs with actions/cache and suppress progress output.
Move the collection ID mapping query from the Metadata decorator to
getDatabasesDB where it runs once during resource init. The mapping
includes both relative keys (collection_N) and full keys
(database_M_collection_N) for dedicated mode compatibility.
The Metadata decorator is now purely stateless — no database queries,
no dbForProject dependency. It only uses the pre-set map from
setCollectionId().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The find() query on dbForProject triggers lifecycle hooks (Usage,
UserEvents, FunctionCache) that generate Redis messages, causing OOM
under CI parallel load. Wrapping in silent() prevents these hooks from
firing during the mapping query.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Console::warning() from utopia-php/cli is not available in HTTP/Swoole
context, causing a fatal error inside the catch block and breaking the
entire decorator flow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Temporary debug logging to diagnose why collection ID mapping fails
in dedicated mode CI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a no-op protected method that Cloud can override to enforce
billing/block checks before processing git deployments. The hook
is called inside the foreach loop after project validation, so any
exception it throws is caught and logged as an error.
https://claude.ai/code/session_01HP1N9hHbqMzxm5QmaoGhyZ