[AIT-29] Handle cyclic references in PathObject/Instance .compact() methods#2122
Conversation
WalkthroughAdds memoization to LiveMap.compact to handle cyclic/shared references, updates TypeScript doc-comments for compact() to document memoized outputs, and adds tests that create cycles via REST and assert compact() preserves back‑references. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–30 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.40.0)test/realtime/objects.test.jsThanks 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 |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/plugins/objects/livemap.ts (1)
499-515: Cyclic memoization inLiveMap.compactlooks correct; consider tightening the helper typeThe new
memoizedObjectsplumbing correctly handles:
- self-referential maps (entry pointing back to the same
LiveMapinstance), and- mutual cycles / shared submaps, by keying the memo on
objectIdand seeding it before iterating entries.That gives you cycle safety and preserves aliasing across multiple references to the same
LiveMapin the graph.If you want to make this a bit clearer/safer for future readers, you could narrow the helper type slightly and convey intent in the name, e.g.:
- compact(memoizedObjects?: Map<string, Record<string, any>>): API.CompactedValue<API.LiveMap<T>> { - const memo = memoizedObjects ?? new Map<string, Record<string, any>>(); + compact( + memoizedObjects?: Map<string, Record<string, unknown>>, + ): API.CompactedValue<API.LiveMap<T>> { + const memo = memoizedObjects ?? new Map<string, Record<string, unknown>>();or rename
memoto something likevisitedMapsByIdto document that it’s keyed byobjectId.These are optional readability tweaks; behaviour-wise this implementation looks solid.
ably.d.ts (1)
2603-2615: Document JSON.stringify behaviour for compacted cyclic graphsThe new sentences about handling cyclic references via memoization on the various
compact()methods (PathObject, AnyPathObject, *BatchContext, *Instance) align with the implementation and tests.However, the AIT‑29 objective also called out explicitly documenting that the resulting structure is not JSON‑stringifiable when cycles are present. Right now that point isn’t mentioned in these docblocks, so users might reasonably assume
JSON.stringify(obj.compact())is always safe.Consider adding a brief note to each of the
compact()docs you’ve just updated. For example, forLiveMapPathObject.compact:/** * Get a JavaScript object representation of the map at this path. * Binary values are returned as base64-encoded strings. - * Cyclic references are handled through memoization, returning shared compacted object references for already-visited objects. + * Cyclic references are handled through memoization, returning shared compacted object references for already-visited objects. + * Note: if the resulting structure contains cycles, calling `JSON.stringify()` on it will throw a `TypeError` + * due to JSON's inability to represent cyclic graphs.And similarly for:
AnyPathObject.compactLiveMapBatchContext.compactAnyBatchContext.compactLiveMapInstance.compactAnyInstance.compactThis keeps the behaviour clear and sets correct expectations for downstream serialization.
Also applies to: 2760-2767, 2862-2871, 3000-3008, 3469-3478, 3612-3620
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
ably.d.ts(6 hunks)src/plugins/objects/livemap.ts(1 hunks)test/realtime/objects.test.js(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/plugins/objects/livemap.ts (3)
objects.d.ts (1)
LiveMap(16-28)src/plugins/objects/pathobject.ts (1)
value(144-183)src/plugins/objects/livecounter.ts (1)
value(64-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: test-node (16.x)
- GitHub Check: test-node (18.x)
- GitHub Check: test-node (20.x)
- GitHub Check: test-browser (webkit)
- GitHub Check: test-browser (chromium)
- GitHub Check: test-browser (firefox)
mschristensen
left a comment
There was a problem hiding this comment.
This looks good, although we should include a comment in a doc string to indicate that the result of compact() cannot be blindly JSON-stringified if there are cyclic references.
f37add1 to
d1a50f5
Compare
d1a50f5 to
9eca5dd
Compare
9eca5dd to
520c502
Compare
Cyclic references currently can be created via REST API which supports setting key in a map with a reference to another object by its id. To handle this in realtime we memoize compacted entries for visited maps and return references to those memoized entries when encountering the same map again during compaction. Resolves AIT-29
520c502 to
662c5ee
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/plugins/objects/livemap.ts (1)
492-536: Memoization incompact()correctly handles cycles; consider minor doc clarificationThe memoization strategy looks sound: inserting
resultintomemobefore iterating and keying bythis.getObjectId()ensures both direct and indirect cycles (and shared submaps) are represented without infinite recursion, while keeping the existing call pattern (compact()with no args) intact.Optional: since this method can now return objects with actual reference cycles, you might want to extend the comment here (even though it’s
@internal) to explicitly note that the returned structure may be cyclic and therefore not JSON‑stringifiable, to mirror the behavior you’ve documented on the public API surface.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
ably.d.ts(6 hunks)src/plugins/objects/livemap.ts(1 hunks)test/realtime/objects.test.js(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- ably.d.ts
- test/realtime/objects.test.js
🧰 Additional context used
🧬 Code graph analysis (1)
src/plugins/objects/livemap.ts (2)
ably.d.ts (2)
CompactedValue(2438-2463)LiveMap(2409-2412)objects.d.ts (1)
LiveMap(16-28)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: test-node (18.x)
- GitHub Check: test-node (16.x)
- GitHub Check: test-browser (chromium)
- GitHub Check: test-browser (webkit)
- GitHub Check: test-browser (firefox)
- GitHub Check: test-node (20.x)
0bdd674
into
integration/objects-breaking-api
…ences [AIT-29] Handle cyclic references in PathObject/Instance .compact() methods
…ences [AIT-29] Handle cyclic references in PathObject/Instance .compact() methods
Cyclic references currently can be created via REST API which supports setting key in a map with a reference to another object by its id. To handle this in realtime we memoize compacted entries for visited maps and return references to those memoized entries when encountering the same map again during compaction.
Resolves AIT-29
Summary by CodeRabbit
Bug Fixes
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.