Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: relationship fetching as legacy op #8487

Merged
merged 3 commits into from
Mar 26, 2023
Merged

Conversation

runspired
Copy link
Contributor

@runspired runspired commented Mar 19, 2023

⚠️ This change is potentially breaking. We don't really have an option. A short story.

  • Ember overrides the (already not a native microtask) flush of RSVP.Promise to schedule an autorun / join an existing run's actions queue.
  • Ember's override has a number of horrid consequences:
    • RSVP Promises that are resolved can be re-resolved synchronously
    • RSVP Promise chains with no other async mechanism can be run start-to-finish synchronously by wrapping the whole chain in its own run
    • resolving or rejecting an RSVP Promise always schedules a synchronous Render

This has historically limited EmberData in a number of ways:

  • EmberData could not use async/await or native promises as it would interrupt the otherwise entirely RSVP Promise-chain (breaking 1 & 2)
  • EmberData could get away with mutating cache-state within any link of the chain as the entire chain was guaranteed to complete before the next render.

We were always going to have to break something if we started moving the internals away from RSVP, it's been a question of "when" more than "whether". Unfortunately, the work in this PR exposed that "when" is "now".

First, a note: in order for RequestManager to support the RFC'd range of capabilities, it cannot build entirely over async/await anyway. We carefully manage Promise chains and theoretically could have built over RSVP.Promise (initially we did, this PR surfaced the larger problem to us and shifts it back to native promises).

What goes wrong is this, if two RSVP promises are in the chain and do not resolve within the same runloop, you get two renders. This is key to understand why the next point matters.

In the old paradigms, data-fetching was a bunch of spaghetti managed across a dozen different locations. There was no coordinated request pipeline. This meant that the final promise in the chain was both the promise the resolved to the UI and the promise that updated the cache and triggered property change notifications.

Refactoring this logic to support the RFC means that now the cache update happens several promises before the final promise in the chain. When using native microtasks that do not flush within a runloop that also sync-flushes render, this is safe. But, when using RSVP Promises, the state update occurs within a runloop and render immediately occurs.

In real world effect, this means that templates would infinitely re-render any asynchronous values, especially noticeable on reject (where the value EmberData hands back is less stable than records that maintain stable proxies) as the final promise which updates the object handed to render is not the promise which triggered the render.

Basically: render -> fetch -> cache update -> complete -> render accidentally becomes render -> fetch -> cache update+render -> fetch -> cache udpate + render -> fetch -> cache update + render and so on.

What does this mean for your apps?

  1. Well, some apps may find that continuing to use RSVP Promises shows no ill-effects. If you aren't triggering fetch via templates with async-relationship, aren't rendering promise proxies in templates, or never have API requests that fail then setting window.Promise = RSVP.Promise would seemingly maintain the old behavior and may even work for some set of safer async-from-template cases. So if this upgrade is difficult, you might consider temporarily doing that. If your apps do do these things, you're the reason this change has to happen now, else your app would blow up during render.

  2. Likely most apps won't notice a thing, they probably will even see nicer performance because of the reduction in runloop usage and extra promise generation that this PR now brings. What might get affected is tests that depended on the sync-resolve capabilities of run(rsvpPromiseChain) which ... we were never going to be able to detect and deprecate and has been a liability ever since async/await came into common usage. Given the prevalence of async/await, ember-concurrency, and native promise usage in Ember apps today, there's even a chance this change goes by unnoticed. Which would be a relief. If it doesn't, reach out and I'll see how I can help mitigate.

  3. Leaky tests will be a lot more noticeable. If a promise escapes the test bounds but resolves within the same microtask queue it generally will complete before any of the afterEach callbacks begin, allowing a small amount of un-awaited async to just-work unnoticed, most commonly triggered by actions, observers, side-effects within tests etc. This refactor to native promises and native promise chains almost certainly means that any unawaited unsettled ember-data state would trigger an error. Good news, easy to fix, add await settled() before the test completes if you don't have any other need to await this state more directly.

Original Change Description

(4/4ths of) the literal final step to complete #6166 💚

This PR:

  • Model.reload as request op
  • findBelongsTo as request op
  • findHasMany as request op
  • remove _fetchManager temporary additions
  • cleanup util duplicates

To Go (for full RequestManager support): (not this PR)

  • [-] enforce immutability in dev
  • [-] handle error documents in cache
  • [-] handle meta documents in cache
  • [-] additional test coverage for Store.request, errors thrown during request
  • [-] additional test coverage for cache.peek / cache.put / cache.peekRequest
  • [-] update docs for RequestManager.useCache
  • [-] implement new Collection mode to properly reflect ResourceDocument
  • [-] ensure single-resource-document requests are notified when data changes
  • [-] determine Cache "mutability" of CollectionResourceDocument
  • [-] add tests for enhancing context.request in RequestManager before calling next

Other Notes:

  • clarify intended Error handling mechanisms/propagation
  • update RFC for RequestManager.useCache
  • update RFC for lifetimes.isSoftExpired/isHardExpired
  • update RFC for rename data => content

@runspired runspired added 🌲 Project Trim 🌲 PRs related to https://github.com/emberjs/data/issues/6166 🎯 canary PR is targeting canary (default) 🏷️ feat This PR introduces a new feature 5.0 Roadmap labels Mar 19, 2023
@runspired runspired added this to In Development in 🌲 Project Trim 🌲 via automation Mar 19, 2023
@runspired runspired added the ci-assetsize Activates Asset Size Checks in CI label Mar 19, 2023
@github-actions
Copy link

github-actions bot commented Mar 19, 2023

Asset Size Report for 3b93575

Modern Builds

🛑 The size of the library EmberData has increased by +906.0 B (+182.0 B compressed) which exceeds the failure threshold of 75 bytes.

Warnings

⚠️ The uncompressed size of the package @ember-data/legacy-compat has increased by +5.56 KB.
⚠️ The uncompressed size of the package @ember-data/request has increased by +133.0 B.

Changeset


EmberData 179.03 KB +906.0 B (38.51 KB +182.0 B compressed)
    @ember-data/model 39.49 KB -2.71 KB (8.49 KB -558.45 B compressed)
        @ember-data/model/has-many-27395cb2 0.0 B -40.34 KB (0.0 B -8.1 KB compressed)
        @ember-data/model/has-many-f4cb2303 37.63 KB +37.63 KB (8.09 KB +7.56 KB compressed)
    @ember-data/store 34.46 KB -1.8 KB (7.41 KB -371.23 B compressed)
        @ember-data/store/index-4694d061 0.0 B -33.31 KB (0.0 B -6.69 KB compressed)
        @ember-data/store/-private 2.02 KB -79.0 B (444.35 B -15.87 B compressed)
        @ember-data/store/index-752a3411 31.58 KB +31.58 KB (6.79 KB +6.34 KB compressed)
    @ember-data/adapter 17.54 KB -250.0 B (3.77 KB -50.22 B compressed)
        @ember-data/adapter/rest 7.63 KB -50.0 B (1.64 KB -10.04 B compressed)
        @ember-data/adapter/serialize-into-hash-bba9ba74 0.0 B -2.41 KB (0.0 B -496.38 B compressed)
        @ember-data/adapter/index-a70968d1 0.0 B -1.67 KB (0.0 B -342.51 B compressed)
        @ember-data/adapter/serialize-into-hash-b9092b25 2.4 KB +2.4 KB (529.53 B +494.57 B compressed)
        @ember-data/adapter/index-5b395971 1.48 KB +1.48 KB (325.63 B +304.14 B compressed)
    @ember-data/graph 16.64 KB -3.0 B (3.58 KB -0.6 B compressed)
        @ember-data/graph/-private 16.64 KB -3.0 B (3.58 KB -0.6 B compressed)
    @ember-data/legacy-compat 15.73 KB +5.56 KB (3.38 KB +1.12 KB compressed)
        @ember-data/legacy-compat/fetch-manager-2716abb1 0.0 B -5.87 KB (0.0 B -1.18 KB compressed)
        @ember-data/legacy-compat/index 6.59 KB +2.72 KB (1.42 KB +559.66 B compressed)
        @ember-data/legacy-compat/-private 516.0 B +79.0 B (110.98 B +15.87 B compressed)
        @ember-data/legacy-compat/fetch-manager-1067c70f 8.63 KB +8.63 KB (1.86 KB +1.73 KB compressed)
    ember-data 8.4 KB -44.0 B (1.81 KB -8.84 B compressed)
        ember-data/-private 1.64 KB -44.0 B (360.25 B -8.84 B compressed)
    @ember-data/request 5.39 KB +133.0 B (1.16 KB +26.72 B compressed)
        @ember-data/request/index 5.0 KB +133.0 B (1.08 KB +26.72 B compressed)

Full Asset Analysis (Modern)

Asset Size Report
=================


Library: EmberData
┌────────────┬─────────────┐
│  (index)   │   Values    │
├────────────┼─────────────┤
│   bytes    │ '179.03 KB' │
│ compressed │ '38.51 KB'  │
│  packages  │     12      │
│  modules   │     58      │
└────────────┴─────────────┘

Package: @ember-data/model
┌──────────────┬────────────┐
│   (index)    │   Values   │
├──────────────┼────────────┤
│    bytes     │ '39.49 KB' │
│  compressed  │ '8.49 KB'  │
│ % Of Library │   '22.1'   │
└──────────────┴────────────┘
	Module                                            | Bytes     | Compressed | % of Package  | % Of Library
	-----------------------------------------------------------------------------------------------------
	@ember-data/model/has-many-f4cb2303               | 37.63 KB  | 8.09 KB    | 95.3          | 21.0
	@ember-data/model/-private                        | 1.40 KB   | 307.56 B   | 3.5           | 0.8
	@ember-data/model/index                           | 474.00 B  | 101.94 B   | 1.2           | 0.3

Package: @ember-data/store
┌──────────────┬────────────┐
│   (index)    │   Values   │
├──────────────┼────────────┤
│    bytes     │ '34.46 KB' │
│  compressed  │ '7.41 KB'  │
│ % Of Library │   '19.3'   │
└──────────────┴────────────┘
	Module                                            | Bytes     | Compressed | % of Package  | % Of Library
	-----------------------------------------------------------------------------------------------------
	@ember-data/store/index-752a3411                  | 31.58 KB  | 6.79 KB    | 91.6          | 17.6
	@ember-data/store/-private                        | 2.02 KB   | 444.35 B   | 5.9           | 1.1
	@ember-data/store/index                           | 883.00 B  | 189.91 B   | 2.5           | 0.5

Package: @ember-data/serializer
┌──────────────┬────────────┐
│   (index)    │   Values   │
├──────────────┼────────────┤
│    bytes     │ '20.86 KB' │
│  compressed  │ '4.49 KB'  │
│ % Of Library │   '11.6'   │
└──────────────┴────────────┘
	Module                                                     | Bytes     | Compressed | % of Package  | % Of Library
	--------------------------------------------------------------------------------------------------------------
	@ember-data/serializer/json                                | 7.30 KB   | 1.57 KB    | 35.0          | 4.1
	@ember-data/serializer/embedded-records-mixin-d75385ff     | 4.47 KB   | 983.99 B   | 21.4          | 2.5
	@ember-data/serializer/json-api                            | 3.77 KB   | 830.42 B   | 18.1          | 2.1
	@ember-data/serializer/rest                                | 2.83 KB   | 622.44 B   | 13.6          | 1.6
	@ember-data/serializer/-private                            | 1.41 KB   | 309.50 B   | 6.7           | 0.8
	@ember-data/serializer/index                               | 879.00 B  | 189.05 B   | 4.1           | 0.5
	@ember-data/serializer/transform                           | 234.00 B  | 50.32 B    | 1.1           | 0.1

Package: @ember-data/adapter
┌──────────────┬────────────┐
│   (index)    │   Values   │
├──────────────┼────────────┤
│    bytes     │ '17.54 KB' │
│  compressed  │ '3.77 KB'  │
│ % Of Library │   '9.8'    │
└──────────────┴────────────┘
	Module                                               | Bytes     | Compressed | % of Package  | % Of Library
	--------------------------------------------------------------------------------------------------------
	@ember-data/adapter/rest                             | 7.63 KB   | 1.64 KB    | 43.5          | 4.3
	@ember-data/adapter/serialize-into-hash-b9092b25     | 2.40 KB   | 529.53 B   | 13.7          | 1.3
	@ember-data/adapter/build-url-mixin-dff91ed0         | 1.91 KB   | 421.55 B   | 10.9          | 1.1
	@ember-data/adapter/error                            | 1.86 KB   | 409.72 B   | 10.6          | 1.0
	@ember-data/adapter/index-5b395971                   | 1.48 KB   | 325.63 B   | 8.4           | 0.8
	@ember-data/adapter/json-api                         | 1.07 KB   | 235.29 B   | 6.1           | 0.6
	@ember-data/adapter/-private                         | 835.00 B  | 179.59 B   | 4.6           | 0.5
	@ember-data/adapter/index                            | 375.00 B  | 80.65 B    | 2.1           | 0.2

Package: @ember-data/graph
┌──────────────┬────────────┐
│   (index)    │   Values   │
├──────────────┼────────────┤
│    bytes     │ '16.64 KB' │
│  compressed  │ '3.58 KB'  │
│ % Of Library │   '9.3'    │
└──────────────┴────────────┘
	Module                                            | Bytes     | Compressed | % of Package  | % Of Library
	-----------------------------------------------------------------------------------------------------
	@ember-data/graph/-private                        | 16.64 KB  | 3.58 KB    | 100.0         | 9.3

Package: @ember-data/legacy-compat
┌──────────────┬────────────┐
│   (index)    │   Values   │
├──────────────┼────────────┤
│    bytes     │ '15.73 KB' │
│  compressed  │ '3.38 KB'  │
│ % Of Library │   '8.8'    │
└──────────────┴────────────┘
	Module                                               | Bytes     | Compressed | % of Package  | % Of Library
	--------------------------------------------------------------------------------------------------------
	@ember-data/legacy-compat/fetch-manager-1067c70f     | 8.63 KB   | 1.86 KB    | 54.9          | 4.8
	@ember-data/legacy-compat/index                      | 6.59 KB   | 1.42 KB    | 41.9          | 3.7
	@ember-data/legacy-compat/-private                   | 516.00 B  | 110.98 B   | 3.2           | 0.3

Package: @ember-data/json-api
┌──────────────┬───────────┐
│   (index)    │  Values   │
├──────────────┼───────────┤
│    bytes     │ '9.57 KB' │
│  compressed  │ '2.06 KB' │
│ % Of Library │   '5.3'   │
└──────────────┴───────────┘
	Module                                            | Bytes     | Compressed | % of Package  | % Of Library
	-----------------------------------------------------------------------------------------------------
	@ember-data/json-api/index                        | 9.57 KB   | 2.06 KB    | 100.0         | 5.3

Package: ember-data
┌──────────────┬───────────┐
│   (index)    │  Values   │
├──────────────┼───────────┤
│    bytes     │ '8.40 KB' │
│  compressed  │ '1.81 KB' │
│ % Of Library │   '4.7'   │
└──────────────┴───────────┘
	Module                                            | Bytes     | Compressed | % of Package  | % Of Library
	-----------------------------------------------------------------------------------------------------
	ember-data/index                                  | 1.96 KB   | 432.31 B   | 23.4          | 1.1
	ember-data/-private                               | 1.64 KB   | 360.25 B   | 19.5          | 0.9
	ember-data/adapters/errors                        | 1.19 KB   | 261.75 B   | 14.1          | 0.7
	ember-data/setup-container                        | 367.00 B  | 78.93 B    | 4.3           | 0.2
	ember-data/relationships                          | 318.00 B  | 68.39 B    | 3.7           | 0.2
	ember-data/serializers/embedded-records-mixin     | 274.00 B  | 58.93 B    | 3.2           | 0.1
	ember-data/serializers/json-api                   | 251.00 B  | 53.98 B    | 2.9           | 0.1
	ember-data/adapters/json-api                      | 245.00 B  | 52.69 B    | 2.8           | 0.1
	ember-data/serializers/json                       | 243.00 B  | 52.26 B    | 2.8           | 0.1
	ember-data/serializers/rest                       | 243.00 B  | 52.26 B    | 2.8           | 0.1
	ember-data/transform                              | 241.00 B  | 51.83 B    | 2.8           | 0.1
	ember-data/adapters/rest                          | 237.00 B  | 50.97 B    | 2.8           | 0.1
	ember-data/serializer                             | 232.00 B  | 49.89 B    | 2.7           | 0.1
	ember-data/adapter                                | 226.00 B  | 48.60 B    | 2.6           | 0.1
	ember-data/model                                  | 222.00 B  | 47.74 B    | 2.6           | 0.1
	ember-data/store                                  | 222.00 B  | 47.74 B    | 2.6           | 0.1
	ember-data/attr                                   | 218.00 B  | 46.88 B    | 2.5           | 0.1
	ember-data/version                                | 163.00 B  | 35.05 B    | 1.9           | 0.1

Package: ember-inflector
┌──────────────┬───────────┐
│   (index)    │  Values   │
├──────────────┼───────────┤
│    bytes     │ '6.69 KB' │
│  compressed  │ '1.44 KB' │
│ % Of Library │   '3.7'   │
└──────────────┴───────────┘
	Module                                            | Bytes     | Compressed | % of Package  | % Of Library
	-----------------------------------------------------------------------------------------------------
	ember-inflector/lib/system/inflector              | 2.99 KB   | 657.93 B   | 44.7          | 1.7
	ember-inflector/lib/system/inflections            | 1.59 KB   | 349.93 B   | 23.7          | 0.9
	ember-inflector/lib/system                        | 471.00 B  | 101.30 B   | 6.9           | 0.3
	ember-inflector/index                             | 379.00 B  | 81.51 B    | 5.5           | 0.2
	ember-inflector/lib/helpers/pluralize             | 369.00 B  | 79.36 B    | 5.4           | 0.2
	ember-inflector/lib/utils/make-helper             | 332.00 B  | 71.40 B    | 4.8           | 0.2
	ember-inflector/lib/system/string                 | 318.00 B  | 68.39 B    | 4.6           | 0.2
	ember-inflector/lib/helpers/singularize           | 296.00 B  | 63.66 B    | 4.3           | 0.2

Package: @ember-data/request
┌──────────────┬───────────┐
│   (index)    │  Values   │
├──────────────┼───────────┤
│    bytes     │ '5.39 KB' │
│  compressed  │ '1.16 KB' │
│ % Of Library │   '3.0'   │
└──────────────┴───────────┘
	Module                                            | Bytes     | Compressed | % of Package  | % Of Library
	-----------------------------------------------------------------------------------------------------
	@ember-data/request/index                         | 5.00 KB   | 1.08 KB    | 92.8          | 2.8
	@ember-data/request/fetch                         | 397.00 B  | 85.38 B    | 7.2           | 0.2

Package: @ember-data/debug
┌──────────────┬────────────┐
│   (index)    │   Values   │
├──────────────┼────────────┤
│    bytes     │ '3.03 KB'  │
│  compressed  │ '667.39 B' │
│ % Of Library │   '1.7'    │
└──────────────┴────────────┘
	Module                                            | Bytes     | Compressed | % of Package  | % Of Library
	-----------------------------------------------------------------------------------------------------
	@ember-data/debug/index                           | 2.28 KB   | 502.63 B   | 75.3          | 1.3
	@ember-data/debug/setup                           | 766.00 B  | 164.75 B   | 24.7          | 0.4

Package: @ember-data/tracking
┌──────────────┬────────────┐
│   (index)    │   Values   │
├──────────────┼────────────┤
│    bytes     │ '1.24 KB'  │
│  compressed  │ '272.50 B' │
│ % Of Library │   '0.7'    │
└──────────────┴────────────┘
	Module                                            | Bytes     | Compressed | % of Package  | % Of Library
	-----------------------------------------------------------------------------------------------------
	@ember-data/tracking/-private                     | 839.00 B  | 180.44 B   | 66.2          | 0.5
	@ember-data/tracking/index                        | 428.00 B  | 92.05 B    | 33.8          | 0.2

@runspired runspired linked an issue Mar 19, 2023 that may be closed by this pull request
65 tasks
@runspired runspired force-pushed the feat/relationship-legacy-ops branch from b113ba6 to 08bdd20 Compare March 21, 2023 09:38
@robbytx
Copy link
Contributor

robbytx commented Mar 23, 2023

@runspired I'm trying to understand the PR description, but it's got a lot of background context that's missing for me. I recognize that I'm not necessarily the target audience, but it would help me if you could clarify the following a bit further:

in order for RequestManager to support the RFC'd range of capabilities
Refactoring this logic to support the RFC

Which RFC is this?

RSVP Promises that are resolved can be re-resolved synchronously
RSVP Promise chains with no other async mechanism can be run start-to-finish synchronously by wrapping the whole chain in its own run

I have a basic understanding of the way RSVP Promise works, and how it coalesces promises into the runloop, but I'm not sure what you mean here. I think it's just because you are summarizing concepts that I'm not as familiar with.

In the old paradigms, data-fetching was a bunch of spaghetti managed across a dozen different locations. There was no coordinated request pipeline.

I assume "old paradigms" refers to earlier versions of Ember Data. (About) when was the coordinated request pipeline introduced?

This change is potentially breaking. We don't really have an option. A short story.

AFAICT this PR pre-supposes that we must move to native promises. I don't disagree in the long-term, but I'm curious:

  1. Has Ember.js itself communicated an intention to deprecate RSVP Promise in the 5.0 time frame?
  2. If not, is there an alternative to this PR that preserves the use of RSVP Promise, so we can avoid a breaking change?

I'm merely playing devil's advocate here to either: see if an alternative is available or better understand why it is imperative to make this change now. It strikes me that the goal of this PR and Project Trim is to further decouple Ember Data to support tree-shaking, but not necessarily to decouple from RSVP Promise, so I'm wondering whether conflating the two is painting us into a corner.

@robbytx
Copy link
Contributor

robbytx commented Mar 23, 2023

Additional question -- this PR is titled "relationship fetching as legacy op", which suggests to me that Ember Data (at some future point) will no longer auto-fetch relationships upon access. Am I understanding this correctly? Is there an RFC or other design doc that explains the reasoning and future state? Sorry for my ignorance, I'm just now getting up to speed on the variety of changes occurring.

@runspired
Copy link
Contributor Author

@robbytx this would probably require going pretty deep to fully offload context but I'll try to lay the breadcrumbs here:

Which RFC is this?

multiple.

  • The Request Service is the primary one: EmberData | Request Service rfcs#860
  • The Cache v2.1 RFC is another (we won't merge this one until the implementation is finalized as there's an inherent element of discovery that will affect nuances in the final design, this is incidentally one of the last PRs necessary for it): EmberData | Cache v2.1 rfcs#854

I have a basic understanding of the way RSVP Promise works, and how it coalesces promises into the runloop

You have to separate RSVP (the promise polyfill) from RSVP (the Ember thing) because they are effectively fully separate things due to Ember's override of RSVP's scheduling. The effect though is that unlike native promises in which each callback is its own async microtask, all resolved/rejected RSVP promises flush their chains syncronously as one single job in one single runloop, which also schedules render for the end of that flush. This also means that doing something like proxy.set('promise', promise) gives the proxy immediate access to the resolved/rejected result without any async to await the promise.

The user observable effect is this demo:

function scheduleRenderFlush(label) { Promise.resolve().then(() => console.log(label)); }

Promise.resolve()
  .then(() => {
    console.log('1');
    scheduleRenderFlush('A');
  })
 .then(() => {
    console.log('2');
    scheduleRenderFlush('B');
  })

Native promises would result in the following: '1' 'A' '2' 'B'
RSVP with the backburner flush results in the following: '1', '2', 'A' ('B' is dropped because 'A' won't have flushed yet)

Additionally in native promises this sequence is async, with RSVP this sequence is sync.

Has Ember.js itself communicated an intention to deprecate RSVP Promise in the 5.0 time frame?

No, but we are not Ember, and we are not deprecating your use of RSVP Promises, we're just removing our own usage.

is there an alternative to this PR that preserves the use of RSVP Promise, so we can avoid a breaking change?

No. This has been something on my mind for about 10 years. I don't know if you ever saw my talk on scheduling and evolving Ember's scheduler, but it's been something we've needed to do for at least that long (I don't think that talk is available on youtube anymore or I'd link you). Basically, this has always been something that at some point we were going to have to do, and no matter when we do it two things are true (1) we can't do it without breaking and (2) there isn't a deprecation path.

So that gets into the "why now?". There are three reasons:

  1. The new Cache which this supports needs to ship in 4.x, as that is ultimately what will end up providing the broadest support for folks to migrate off of legacy EmberData (roughly legacy is @ember-data/{model/adapter/serializer})

  2. RequestManager must use Native promises because Promise.resolve in RSVP works differently than Promise.resolve natively in ways that require us to support the native approach for the RequestManager to not prevent folks from simply using the platform. Specifically, if a promise is already resolved, native Promise.resolve returns the same promise, and if a Promise is not yet resolved native Promise.resolve returns a promise constructed from the same prototype as the original promise.

  3. The timing is as good as it will ever get. I held off doing this 7 / 5 and even 3 years ago because native promises weren't in wide usage in apps. Today most apps make liberal usage of async/await and various other native async primitives which means that apps today already have to deal with the interleaving issues and already have to account for async flush (whether they are aware of it or not). Changing the timing like this in EmberData in years back would have been a major risk given almost no ecosystem usage of native, today I see this as tiny risk to apps, moderate risk to test suites.

I assume "old paradigms" refers to earlier versions of Ember Data. (About) when was the coordinated request pipeline introduced?

We built and began testing RequestManager in 4.8 canary but haven't shipped it as a feature. It will ship in 4.12. Nearly all of the work to move how EmberData works into that pipeline has occurred within 4.12.

It strikes me that the goal of this PR and Project Trim is to further decouple Ember Data to support tree-shaking, but not necessarily to decouple from RSVP Promise, so I'm wondering whether conflating the two is painting us into a corner.

The main goal here is to implement store requests via request-manager since that is the primitive around which all of EmberData hinges. That said, there are secondary goals here of Project Trim and Project Unplug.

This is the last piece for Project Trim but that's only because to complete Project Trim we always knew we needed to complete the also-proposed re-architecting of the request pipeline. This design may be getting implemented now, but it's been envisioned since ~2015 :D On tree-shaking specifically, EmberData itself is already extremely tree-shakeable, most folks haven't opted to use us in that way but our packages are cleanly delimited and the boundaries documented.

On unplug: this work is part of decoupling EmberData from Ember. Our goal is to be fully decoupled by 6.0.

this PR is titled "relationship fetching as legacy op", which suggests to me that Ember Data (at some future point) will no longer auto-fetch relationships upon access. Am I understanding this correctly? Is there an RFC or other design doc that explains the reasoning and future state?

This is correct, though not something we've explicitly called out because the migration path is to simply stop using @ember-data/model. Details on what the replacement entails are in the 5.0 and 6.0 roadmaps and discussed a bit in the cache 2.1 RFC above as well as in the custom model classes rfc we implemented back in the 3.x series. If the details seem light its because its simpler than folks maybe realize. You'll find some examples in our own test suite of going model-less and schema-less. Ultimately all of @ember-data/model is powered by the store's instantiateRecord and teardownRecord hooks, with the class then accessing the cache via public cache APIs and (with this change) issuing network requests via the store's public request API.

@robbytx
Copy link
Contributor

robbytx commented Mar 24, 2023

Thank you for taking the time to respond so thoroughly. I was not aware of the RequestManager RFC until now, and that is certainly an ambitious scope to achieve, so I have a much better understanding of the motivation for these changes. (I was previously under the impression that the motivation was primarily for modularity under the linked Project Trim.) You've also made it clear to me why the transition to native promises is necessary/desirable now, so thank you for indulging my questioning on that.

I don't think I have any further questions on the PR itself. If I can find some extra time, I will try to review the changes more thoroughly, although I'm sure you don't need to wait on me for that. :)

For my own understanding on this point:

Native promises would result in the following: '1' 'A' '2' 'B'
RSVP with the backburner flush results in the following: '1', '2', 'A' ('B' is dropped because 'A' won't have flushed yet)

When you say that 'B' is dropped, do you just mean that it is not included in the sync resolution? It would still fire later, right? Or is there something else going on that I'm not appreciating?

@runspired
Copy link
Contributor Author

When you say that 'B' is dropped, do you just mean that it is not included in the sync resolution? It would still fire later, right? Or is there something else going on that I'm not appreciating?

No. The render is de-duped and so only one render occurs, in this case 'A' because it was the first scheduled. All state updates that have occurred when A occurs will be a part of that render. Effectively 'A' + 'B' but not double work.

@runspired runspired force-pushed the feat/relationship-legacy-ops branch 4 times, most recently from f883694 to a754ec7 Compare March 26, 2023 22:10
@runspired runspired force-pushed the feat/relationship-legacy-ops branch from a754ec7 to 7154acb Compare March 26, 2023 22:11
@runspired runspired merged commit 1410dc2 into main Mar 26, 2023
🌲 Project Trim 🌲 automation moved this from In Development to Completed Mar 26, 2023
@delete-merged-branch delete-merged-branch bot deleted the feat/relationship-legacy-ops branch March 26, 2023 23:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
5.0 Roadmap ci-assetsize Activates Asset Size Checks in CI 🎯 canary PR is targeting canary (default) 🏷️ feat This PR introduces a new feature 🌲 Project Trim 🌲 PRs related to https://github.com/emberjs/data/issues/6166
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

[QUEST] 🌲Project Trim 🌲
2 participants