vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.58k stars 26.82k forks source link

No way to subscribe to files unmounting in development #46018

Open alex-statsig opened 1 year ago

alex-statsig commented 1 year ago

Verify canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 21.6.0: Mon Aug 22 20:20:05 PDT 2022; root:xnu-8020.140.49~2/RELEASE_ARM64_T8101
Binaries:
  Node: 16.15.0
  npm: 8.5.5
  Yarn: N/A
  pnpm: 7.25.1
Relevant packages:
  next: 12.3.0
  eslint-config-next: N/A
  react: 18.1.0
  react-dom: 18.1.0

Which area(s) of Next.js are affected? (leave empty if unsure)

No response

Link to the code that reproduces this issue

https://stackblitz.com/edit/vercel-next-js-jqfycd?file=server%2FBackendMock.ts

To Reproduce

  1. Create a singleton file which tracks its own initialization, then logs every 10s:
    class Test {
    static hasInitialized = false;
    static init() {
        if (!this.hasInitialized) {
            this.hasInitialized = true;
            setInterval(() => console.log("Polling something"), 10000);
        }
    }
    }
  2. call this function from a getServersideProps() or API endpoint
  3. Make a change to the API or one of its dependencies

Describe the Bug

Related to https://github.com/vercel/next.js/issues/45204, unchanged files will be reloaded by nextjs if a different dependency of an API route (e.g.) is changed. While this alone seems to be a bug, the lack of a solution for "shutting down" files that get reloaded also seems to be an issue.

In particular, database connections or singleton handler classes, or anything polling, will not shut down when files are reloaded. This leads to redundant work being done, excessive memory consumption (eventually leading to OOMs), and pain in debugging things (multiple instances of singletons all living around at the same time).

One workaround is storing the instance in a global variable, and either shutting down any existing one if detected or just reusing the existing one. However, this is rather hacky and relies on abusing the global variable space to dump every singleton. Additionally the need to shutdown and restart database connections in these situations is rather painful, so I opt to reuse existing connections, but that prevents reloads of changes to initialization logic (would be nice if I could detect a reload due to change vs. reload due to other file changing)

Expected Behavior

There should be a way (similar to hot module reloading's module.hot.*) to "subscribe" to a module being unloaded. When this happens, it could clear any intervals, and close any connections.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

alex-statsig commented 1 year ago

This has been open for over 6 months, any update?

dmitrc commented 3 months ago

@alex-statsig, did you find a solution here by any chance (equivalent to module.hot)?

@leerob, what's the path of getting answers from the dev team on these issues / limitations? Alex here has opened numerous issues over the course of multiple years and haven't heard as much as a single word back from the team. This exact matter has been brought up in numerous issues and discussions, with multiple third-party libraries like Prisma having special guides just to work around this nuance of your implementation. Is there truly no way to engage you folks other than creating loud threads on Twitter, reaching out to popular YouTubers etc?

alex-statsig commented 3 months ago

@alex-statsig, did you find a solution here by any chance (equivalent to module.hot)?

I have not, I've just settled on some hacky global logic to re-use old resource instances in development generally (just trying to bypass the hot-reload by reusing the old instance for these types of resources)

dmitrc commented 3 months ago

I have not, I've just settled on some hacky global logic to re-use old resource instances in development generally (just trying to bypass the hot-reload by reusing the old instance for these types of resources)

Got it, thanks for your quick response!

I've settled on doing pretty much the same thing. Ideally I would love to find a middle-ground solution between always reloading out-of-the-box and never reloading with globals (eg when the changes were actually relevant).