Repository navigation
Conversation
70 new issues
|
|
Coverage Impact This PR will not change total coverage. Modified Files with Diff Coverage (23) 🤖 Increase coverage with AI coding...🚦 See full report on Qlty Cloud » 🛟 Help
|
DynamoDB reads fast by key and scans otherwise, and it cannot sort, count or aggregate server-side. The datasource picks the cheapest access path for each request (Query on the table or an index, BatchGetItem for whole keys, parallel Scan otherwise) and does the rest on the items it reads: operators DynamoDB lacks, sorting on non-key attributes, aggregations and charts. It introspects tables or takes a manual schema, exposes the partition and sort keys as a composite primary key, shares one table between collections (single-table design), exposes arrays of maps as collections, and runs PartiQL SELECT native queries. `scanPolicy` and `maxScannedItems` keep a request that no key serves from reading a whole table unnoticed. The unit tests use a fake client; an integration suite runs against DynamoDB Local, started by the CI like the other datasources' databases. Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The package pinned a toolkit version older than the workspace's, so yarn installed the published one next to it and its error classes came out untyped in the build. Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The ticket listed nested arrays among the virtual collections, and the first version refused them. A collection can now have an array collection as its parent: its records are keyed by the table key and the position in each array, and writes address the element through the whole path. An array held in a map (a->b) is exposed too. Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- an index that leaves out items without its sort key is only read with a condition on that key or when declared to cover every item - key lookups drop nested OR arms and duplicate keys, which BatchGetItem refuses - writes: caches are dropped after a partial failure, in-flight writes finish before the error is raised, a record cannot claim another entity, array writes stay within DynamoDB's expression size and the entity of their parent, and an update under a missing map falls back to rewriting - pagination positions are kept per timezone, Present is evaluated like the toolkit does, date-times sort and aggregate by instant, and a large client-side sort no longer overflows - native queries leave placeholders inside literals alone and bound the pages they follow - introspection accepts attributes named like Object.prototype members - nested array collections use the item schema their parent declares Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…date comparison Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…and warn less Running a real agent over DynamoDB showed that a filter through a relation sends an In on the partition key, which was scanned: each value is now read with a Query of its own. The same run showed a scan warning on every first page of an unfiltered collection, which reads one page: it is no longer warned about or denied, and the same warning is repeated at most once a minute. Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- a map seen empty in some items is no longer both a column and its nested columns - a field of a manual schema cannot change the type of a key - a key lookup never builds the product of long lists, and lists each value once - date-only strings are compared by instant too - Before and After are evaluated on the items, since DynamoDB compares dates as text Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- chart caches are shared by every collection of a table and hold copies - array collections refuse key columns with the same name, create and update under a map the item lacks by rewriting it, and replace what a patch names - attributes named with the path separator are refused, not silently merged - a create returns the discriminator it stored, a first page of an entity collection no longer bypasses scanPolicy, and a reversed range is not a BETWEEN Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…read strongly before rewriting Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…elements needs A real run over 100 000 items showed the first page of an array collection reading the whole table. Without a sort, parents are now read in batches of 100 until the page is filled. Refs: PRD-1670 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ged keys on update Forest greys the primary key fields of the creation form, so a string sort key left empty now gets a UUID (behind the entity prefix, if any), like the partition key. Setting a many-to-one relation writes its foreign key, which can be a key of the table: writing the value a key already has is a no-op, changing it is still refused. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ocal Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…allback and share the parent read budget A map written through the client-side rewrite of an update was merged with the old one, keeping keys the SET would have dropped: each entry now replaces what is at its path. The pages of parents an array collection reads were each given a budget of their own, so a selective read could go past maxScannedItems: the parents read now count together. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
a27f12d to
7f68052
Compare
| if (entity) conditions.push(entity); | ||
|
|
||
| try { | ||
| await this.root.executor.put({ |
There was a problem hiding this comment.
🟠 High src/array-collection.ts:557
The rewrite fallbacks can erase successful concurrent writes, including appends and updates to unrelated parent attributes. replaceElements and appendByRewriting put the entire item snapshot read by get, while their conditions only check item existence and entity membership; because PutItem replaces the whole item, changes made after the read are lost. Restrict writes to the affected path or condition the replacement on the snapshot and retry on conflict.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @packages/datasource-dynamodb/src/array-collection.ts around line 557:
The rewrite fallbacks can erase successful concurrent writes, including appends and updates to unrelated parent attributes. `replaceElements` and `appendByRewriting` put the entire item snapshot read by `get`, while their conditions only check item existence and entity membership; because `PutItem` replaces the whole item, changes made after the read are lost. Restrict writes to the affected path or condition the replacement on the snapshot and retry on conflict.
There was a problem hiding this comment.
Claude Sonnet 5.5 (claude-sonnet-5-5): Not fixed here: a rewrite fallback can overwrite a write that lands between its read and its put, and closing that needs optimistic locking on a version attribute, which is a feature of its own.
Why
The read is strongly consistent and the put is conditioned on the item existing, so only a concurrent write inside that window is lost. Tracked in PRD-1673, which covers these fallbacks and replaceOne.
…mits on every read timeonly is published as Time and GeoJSON points as Json, the types whose values the toolkit validates. Binary attributes are filtered on the returned items, since DynamoDB would compare them to text. BatchGetItem lookups count against maxScannedItems and a skip beyond maxOffset is refused whichever way the page is read. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Forest sends a binary attribute as base64 text: create and update decode it, so the item holds a DynamoDB B attribute again instead of the text. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ts path Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… of the schema Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
PMerlet
left a comment
There was a problem hiding this comment.
Spec (PRD-1670): 7 unvalidated decisions: not final. One option name differs from the ticket, see the finding on index.ts.
A number beyond 2^53 is read as text, string and number sets are written back as sets, a manual schema collection may omit its fields, virtualArrayCollections is read as an alias, an unknown scanPolicy is refused, and the SELECT-only rule of native queries is tested. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…riting an empty set Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… the cache Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…rtual collections Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ion tests Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

fixes PRD-1670
Adds
@forestadmin/datasource-dynamodb: a datasource for Amazon DynamoDB with the capabilities of the Cosmos DB one (introspection, manual schema, composite keys, relations, search, aggregation, charts, arrays as collections, native queries) plus single-table design. DynamoDB cannot sort, count or aggregate server-side, so the datasource reads by key whenever a filter allows it and does the rest on the items it reads;scanPolicy(warnby default) andmaxScannedItemskeep a request that no key serves from reading a whole table unnoticed.What changes
createDynamoDBDataSource({ region })introspects the tables (keys and indexes fromDescribeTable, attributes from a sample), or takes a manual schema; the partition and sort keys are the composite primary key.Queryon the table or an index when an equality pins a partition key,BatchGetItemwhen the filter names whole keys (what relation emulation sends), parallelScanotherwise. Operators DynamoDB lacks (ILike,EndsWith, date intervals), sorts on non-key attributes, counts, aggregations and charts are computed on the items read, streamed so memory stays flat.datasource-dynamodbjoins the test matrix, whose job starts DynamoDB Local (docker-compose.ymlin the package) and setsDYNAMODB_ENDPOINTfor that package only.Tests
Unit tests use a fake client and assert the requests sent. An integration suite (22 tests, skipped without
DYNAMODB_ENDPOINT) runs against DynamoDB Local, since a fake cannot tell whether DynamoDB accepts an expression: it is what validates the key-attribute and sort-key rules ofQueryfilters, the nested array writes and the fan-out of anInon a partition key. A real agent over seeded DynamoDB Local data was also exercised by hand (list, count, paging, search, relations, chart, writes).Not delivered, per the ticket's own list of additions: collections backed by a secondary index, and optimistic updates.
Decisions
Definition of Done
General
Security
Squash message
🤖 Generated with Claude Code
Note
Add a DynamoDB datasource package with introspection and virtual array collections
datasource-dynamodbpackage withcreateDynamoDBDataSource, supporting manual schemas, builder-based table registration, and live introspection of tables and indexes (index.ts).Infan-out across parallel queries (access-path.ts).StreamingAggregatorfor count/sum/avg/min/max with timezone-aware date grouping, a pagination-token cache with bounded memory and TTL, and scan limits viamaxScannedItemsandmaxOffset(executor.ts).Macroscope summarized 519b61f.