immich icon indicating copy to clipboard operation
immich copied to clipboard

[BUG] IntersectionObserver causing lots of cancelled requests and high CPU usage

Open Genteure opened this issue 2 years ago • 1 comments

The bug

When viewing the Photos page with a specific browser window size and (presumably) specific amount of photos, the IntersectionObserver used to detect when a bucket is in view enters a loop, causing the bucket to keep loading and cancelling.

The OS that Immich Server is running on

N/A, on demo.immich.app

Version of Immich Server

v1.52.1

Version of Immich Mobile App

N/A

Platform with the issue

  • [ ] Server
  • [X] Web
  • [ ] Mobile

Your docker-compose.yml content

N/A

Your .env content

N/A

Reproduction steps

  1. Open https://demo.immich.app, or change the date of photos on another immich server so it has 7 buckets at the top of the page, containing 6, 9, 2, 1, 1 ,1 ,1 photos each (matching the data on demo.immich.app thus the layout of the Photos page).
  2. Open the dev tool, enable responsive design, and change the window size to 1842 x 1008.
  3. Reload, or switch to any other page then back to Photos.
  4. Open the Network tab in dev tools, there should be a continuous stream of cancelled requests to https://demo.immich.app/api/asset/time-bucket.
  5. The requests will stop after scrolling down the Photos page.

Additional information

I have reproduced this issue on Chrome 111.0.5563.146 and Firefox 111.0.1.

The value of IntersectionObserverEntry on Chrome:

{"boundingClientRect":{"x":266,"y":1760,"width":1576,"height":267,"top":1760,"right":1842,"bottom":2027,"left":266},"intersectionRatio":0,"intersectionRect":{"x":0,"y":0,"width":0,"height":0,"top":0,"right":0,"bottom":0,"left":0},"isIntersecting":false,"rootBounds":{"x":266,"y":-682,"width":1576,"height":2440,"top":-682,"right":1842,"bottom":1758,"left":266}}
{"boundingClientRect":{"x":266,"y":1740,"width":1576,"height":267,"top":1740,"right":1842,"bottom":2007,"left":266},"intersectionRatio":0.06741572916507721,"intersectionRect":{"x":266,"y":1740,"width":1576,"height":18,"top":1740,"right":1842,"bottom":1758,"left":266},"isIntersecting":true,"rootBounds":{"x":266,"y":-682,"width":1576,"height":2440,"top":-682,"right":1842,"bottom":1758,"left":266}}

Firefox:

{"boundingClientRect":{"x":266,"y":1760,"width":1576,"height":237,"top":1760,"right":1842,"bottom":1997,"left":266},"intersectionRatio":0,"intersectionRect":{"x":0,"y":0,"width":0,"height":0,"top":0,"right":0,"bottom":0,"left":0},"isIntersecting":false,"rootBounds":{"x":250,"y":-682,"width":1592,"height":2440,"top":-682,"right":1842,"bottom":1758,"left":250}}
{"boundingClientRect":{"x":266,"y":1740,"width":1576,"height":237,"top":1740,"right":1842,"bottom":1977,"left":266},"intersectionRatio":0.0759493670886076,"intersectionRect":{"x":266,"y":1740,"width":1576,"height":18,"top":1740,"right":1842,"bottom":1758,"left":266},"isIntersecting":true,"rootBounds":{"x":250,"y":-682,"width":1592,"height":2440,"top":-682,"right":1842,"bottom":1758,"left":250}}

Genteure avatar Mar 30 '23 05:03 Genteure

I had the chance to spend more time on this.

My understanding of the root cause is the calculated height of the "bucket" is ever so slightly different to the real height. When the AssetDateGroup is not loaded, the "bucket" is within the IntersectionObserver detection range, causing the AssetDateGroup to load. After the AssetDateGroup is loaded, the "bucket" is out of the detection range, causing it to unload. When conditions are just right, this will happens in a loop causing high CPU usage.

Other than finding the root cause of the height difference, then either changing CSS or the estimated height calculation, I have two other workarounds in mind:

  1. Add a "circuit breaker" to IntersectionObserver, stop running the handler if it's called too many times in a second.
  2. Only set intersecting to true after 10% or 20% of the container is visible and set intersecting to false when 0% is visible, by setting the threshold option, thus making small difference not able to trigger the change.

Both workarounds have their own downsides.

Otherwise, I think it's also fine to just close this issue for now, at least until other users are also having this problem, since it's 1. not common enough, requires lot's of conditions just right to trigger, and 2. easy to workaround as a user, by scrolling down the page.

Genteure avatar Apr 24 '23 15:04 Genteure