Computed properties on async hasMany relationships

As mentioned in another thread, I’ve been dealing with a host of issues after a huge upgrade from Ember 2.x to 3.28 (LTS). The biggest one remains, which is that we have hundreds of computed properties based on async hasMany relationships, all of which worked perfectly prior to the upgrade, but many of which are now causing issues. I’m wondering if anyone else has been through this and, if so, what they recommend.

I have been fixing issues one-by-one as we discover them and broadly the solutions take one of the following forms:

  • for calculated values, compute and serialize from the API if workable
  • make the relationship synchronous instead (serialized in full from the API)
  • if possible, use filter-by or similar composable helpers in the template to fix filterBy computed properties (or similar) not working
  • ensure data is loaded upfront in the route (in many cases this does not guarantee the computed property works, so sadly this is the least used of the options even though it is the most ideal)

The problem is that it is hundreds of computed properties and in many cases, especially the first two, it is quite time consuming to change them over. We have bugs being reported every day and at this point it will take me potentially weeks to change everything over. Given all of this, I am wondering if there are any approaches I am overlooking here that would allow me to get everything to work more or less as it is rather than fundamentally reworking things with the above approaches.

Worth mentioning that I don’t want go fully synchronous as this would mean serializing a ton of unneeded data in certain contexts. An ideal approach as we are using JSON API would be to use the include (JSON:API — Latest Specification (v1.0)) parameter so we could be synchronous but still only fetch what we need in a given context. This sort of overhaul, however, would take way more time than we have to spare right now.

Any wisdom, suggestions, or links to writing on this subject would be much appreciated. Thanks!

Could you provide a couple examples of the types of CPs that are breaking? And what’s breaking? And do you have any idea what Ember version this started happening or did you do a “big bang” upgrade?

We have a lot of these sorts of CPs in our app but haven’t seen anything like this yet. Granted we’re on 3.24 still. FWIW we’ve been moving away from hasMany relationships in general largely because of efficiency concerns in our API (serializing unbounded ids) and the difficulty in pagination support. For new code we’ve been querying by relationship ids, taking advantage of coalescing and a smattering of includes.

I sympathize with the problem of not wanting refactor a ton of stuff in one go though. It feels like something weird going on here, and I’d be surprised if this was something that didn’t have a more straightforward solution than refactoring the CPs as it sounds like you’ve had to.

@dknutsen on the case again! Thank you for mentioning version. After some testing, I can confirm that Upgrading Ember Data from 3.27 to 3.28 introduces the issue. (Ember CLI at 3.28 does not itself cause any issue)

In Ember Data 3.27 and prior a computed property on an async hasMany relationship would work whether or not the data was already in the store. Whether that’s a healthy practice or not, it would dependably work, the async request would be made, resolve, and the computed property would render.

Now, in Ember Data 3.28, given a model ProjectSolution which hasMany(‘item’) and has the following computed property:

perSolutionCosts: filterBy('items', 'proposalCostType', 'per-solution'),

On a route that fetched the projectSolution but doesn’t fetch the items, this will render nothing:

{{#each model.projectSolution.perSolutionCosts as |item|}}
  {{item.id}}
{{/each}}

While the composable helper filter-by equivalent of this will render each id.

{{#each (filter-by 'proposalCostType' 'per-solution' model.projectSolution.items) as |item|}}
  {{item.id}}
{{/each}}

Again, both would render in 3.27. I read all of the release notes from 3.25-3.28 and see nothing indicating any changes that would cause this. In the 3.28 release notes there isn’t even a mention of async, hasMany or computed.

I’ve just found an issue on Ember Data’s Github so I’ve commented on that and hopefully it gets some attention (though it had no replies in over a month since the post): Computed properties dependent on async relationships do not recompute when the relationship fulfills · Issue #7904 · emberjs/data · GitHub

Ha, yikes, I guess that would do it. If it were me (and it soon will be) I’d drop down to ED 3.27 while slowly migrating away from classic CPs and/or hoping that bug gets fixed soon.

@dknutsen yes, I dropped down to ED 3.27 and so far so good. I’m kind of surprised considering 3.28 is LTS, that Ember Data introduced a significant breaking change and hasn’t responded to the issue 5 weeks later. Definitely makes an argument for hanging one LTS back.

Thanks a lot for your help yet again!

Yeah seems like just about any app with Ember Data and “a history” would run into this. I think what I’m more surprised at is that it took >2m from Ember 4.0 release (and therefore 3.28 LTS promotion) before it was reported.

Maybe that’s a sign that LTS users tend to be A. the only ones with larger legacy codebases and therefore likely to encounter this and B. fairly slow to update to new LTS versions (I know that’s how we are, just try to make time when we can) but :person_shrugging:

1 Like

fwiw I’m not sure we did release a breaking change, though I will see if there’s a way we can smooth it over. The trouble ember-data has in 3.x is balancing the demands of 3 distinct object notification models that ember provides and trying to avoid end-consumers seeing breaking changes because of upstream timing changes to them: sync observers, async observers, tracked. It isn’t always possible.

In the observer world, arrays could sometimes notify without consumption of the length/[] properties, because they had the ability to install array observers. That ability was deprecated by ember, even though it is the foundation of how array proxies work in < 3.28.

Further compounding things is that tracked properties are fundamentally incompatible with computed chains on arrays in lots of situations, this has the greatest ramifications on array macros. Likely the fix here is for ember-source to change how the computed macros work to make them utilize tracked infrastructure.

1 Like

That’s fair, and the improvements are great, but I think this would likely be a blocker for most teams using hasMany in any sort of long-lived apps. Feels like something we should consider mentioning in the release notes at least so people are aware.