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

fork inside context - Maximum call stack size exceeded #3338

Closed
ml1nk opened this issue Jul 27, 2022 · 13 comments
Closed

fork inside context - Maximum call stack size exceeded #3338

ml1nk opened this issue Jul 27, 2022 · 13 comments

Comments

@ml1nk
Copy link
Contributor

ml1nk commented Jul 27, 2022

Describe the bug
Since cc6d59b fork always calls getContext which calls the config function context if useContext is true on the used entity manager. This leads to a loop if fork is used from the global instance (which always has useContext true) inside the context function.

To Reproduce
The use of orm.em.fork inside the context function leads to "Maximum call stack size exceeded".

In my case 'Context' uses AsyncLocalStorage to store and retrieve several objects with some sort of type safety. orm.em.fork is called if the store does not already contains an EntityManager (first call).

context: () => {
      const store = Context.getStore()
      if (store === undefined || orm === null) return undefined
      return store.getOrSet(contextEmKey, EntityManager, () =>  orm!.em.fork())
 }

Current workaround:

context: () => {
      const store = Context.getStore()
      if (store === undefined || orm === null) return undefined
      return store.getOrSet(contextEmKey, EntityManager, () => {
        // @ts-expect-error
        orm!.em.useContext = false
        try {
          return orm!.em.fork()
        } finally {
          // @ts-expect-error
          orm!.em.useContext = true
        }
      })
 }

Versions

Dependency Version
node 18.6.0
typescript 4.7.4
mikro-orm 5.2.4
@B4nan
Copy link
Member

B4nan commented Jul 27, 2022

Why are you even doing this? The default RequestContext helper already uses ALS. It also sounds weird to me you want to call em.fork() inside that method - it should be called in some middleware instead and here you should be just getting it from somewhere.

@ml1nk
Copy link
Contributor Author

ml1nk commented Jul 27, 2022

We are creating our own instance of AsyncLocalStorage and are switching context independent of any middleware. For example a task in our system has it's own context and can create a sub context if needed which eventually needs a new fork of mikro-orm. So we cannot use RequestContext as such but the context function as shown above. As the fork function is only called if some orm.em operation is called and there isn't already an EntityManager in this context, it is more efficient than directly creating one every time a new context is created.

You are right, if i create the fork outside the context and return undefined inside the context as long as no fork is known, it would use the global instance without looping. Before the change it was possible to create a fork inside the context, now i need to use some kind of workaround.

I think it would be enough to add an property in ForkOptions which disables the getContext call inside fork.

Somewhat OT:
In addition we are using a Logger which calls upon the context to get the current debug level, which allows us to activate the query log only when something triggers the debug-level inside the context.

@B4nan
Copy link
Member

B4nan commented Jul 27, 2022

Interesting, thanks for explaination.

I think it would be enough to add an property in ForkOptions which disables the getContext call inside fork.

Yeah we can definitely do something like that. Or have a recursion check will do the job as well.

@ml1nk
Copy link
Contributor Author

ml1nk commented Jul 27, 2022

No problem, i think we are catching all the edge cases ... always 😢

I would prefer the option (noContext?), i find a recursion checks somewhat crazy/unpredictable.

@B4nan
Copy link
Member

B4nan commented Jul 27, 2022

Yeah I know, but using new option can be quite confusing given we have the useContext option already. Now those two would sounds quite similar but do very different things.

@B4nan
Copy link
Member

B4nan commented Jul 27, 2022

Btw you should also be able to get around by creating first fork outside of the getContext method. On that fork, doing fork.getContext() will always return fork. Sounds like better workaround that mangling with the boolean directly.

@ml1nk
Copy link
Contributor Author

ml1nk commented Jul 27, 2022

If i create a fork outside of the method, it would call the context method which then tries to create a fork....
I could use my workaround/hack to create the first fork without triggering context.

@B4nan
Copy link
Member

B4nan commented Jul 27, 2022

Lol right :D Lets just come up with some better name and do the option. Can be a bit longer, this is quite specific use case so better to have a descripitive name.

@ml1nk
Copy link
Contributor Author

ml1nk commented Jul 27, 2022

Ideas:
disableContextResolution
ignoreContext
forceIgnoreContext (as it override useContext of the current em)

@B4nan
Copy link
Member

B4nan commented Jul 27, 2022

I like disableContextResolution, the other ones still sounds similar to useContext.

@B4nan B4nan closed this as completed in 94442f9 Jul 29, 2022
@ml1nk
Copy link
Contributor Author

ml1nk commented Oct 2, 2023

I think f7c1ef2 broke disableContextResolution as i get an "Maximum call stack size exceeded" again.

It still takes "this" in line 1168 but then calls getContext when schema is retrieved in line 1187 which triggers the loop.

image
image

B4nan added a commit that referenced this issue Oct 2, 2023
@B4nan
Copy link
Member

B4nan commented Oct 2, 2023

Let me know if 5964e52 helps.

@ml1nk
Copy link
Contributor Author

ml1nk commented Oct 2, 2023

Works again, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants