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

WASM middleware state not kept #10953

Open
2 tasks done
darkweak opened this issue Jul 30, 2024 · 8 comments
Open
2 tasks done

WASM middleware state not kept #10953

darkweak opened this issue Jul 30, 2024 · 8 comments
Labels
area/plugins kind/enhancement a new or improved feature.

Comments

@darkweak
Copy link

Welcome!

  • Yes, I've searched similar issues on GitHub and didn't find any.
  • Yes, I've searched similar issues on the Traefik community forum and didn't find any.

What did you do?

I'm actually looking at the WASM support for plugins in Træfik as @emilevauge mentioned but with this minimal snippet, we can see that the init is called when I send an HTTP request to Træfik

package main

import (
	"github.com/http-wasm/http-wasm-guest-tinygo/handler"
	"github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
)

func main() {
	handler.Host.Log(api.LogLevelWarn, "INIT!!!!!")

	handler.HandleRequestFn = func(r1 api.Request, r2 api.Response) (next bool, reqCtx uint32) {
		return true, 0
	}
}

I would like to know if that's the normal behavior or if that's a bug.
IMHO loading WASM plugins as stateless and reconfigure them each time you go through the middleware is a performance issue.

What did you see instead?

traefik-1  | 2024-07-30T21:25:08Z WRN github.com/traefik/traefik/v3/pkg/logs/wasm.go:31 > INIT!!!!! entryPointName=http middlewareName=souin@file middlewareType=wasm routerName=whoami@file
traefik-1  | 2024-07-30T21:25:08Z DBG github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/wrr/wrr.go:196 > Service selected by WRR: b903963fe23550fa
traefik-1  | 192.168.65.1 - - [30/Jul/2024:21:25:08  0000] "GET / HTTP/1.1" 200 373 "-" "-" 1 "whoami@file" "http://whoami" 9ms
traefik-1  | 2024-07-30T21:25:09Z WRN github.com/traefik/traefik/v3/pkg/logs/wasm.go:31 > INIT!!!!! entryPointName=http middlewareName=souin@file middlewareType=wasm routerName=whoami@file
traefik-1  | 2024-07-30T21:25:09Z DBG github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/wrr/wrr.go:196 > Service selected by WRR: b903963fe23550fa
traefik-1  | 192.168.65.1 - - [30/Jul/2024:21:25:09  0000] "GET / HTTP/1.1" 200 373 "-" "-" 2 "whoami@file" "http://whoami" 4ms
traefik-1  | 2024-07-30T21:25:09Z DBG github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/wrr/wrr.go:196 > Service selected by WRR: b903963fe23550fa
traefik-1  | 192.168.65.1 - - [30/Jul/2024:21:25:09  0000] "GET / HTTP/1.1" 200 373 "-" "-" 3 "whoami@file" "http://whoami" 2ms

What version of Traefik are you using?

Traefik version 3.1.0 built on 2024-07-15T14:44:04Z

What is your environment & configuration?

# traefik.yml
# ...
experimental:
  localPlugins:
    souin:
      moduleName: github.com/darkweak/souin/plugins/traefik/wasm
# .traefik.yml
displayName: Souin
type: middleware
runtime: wasm
import: github.com/darkweak/souin/plugins/traefik/wasm
summary: 'Souin is a powerfull cache system as fast as Varnish but easier to configure'

If applicable, please paste the log output in DEBUG level

traefik-1  | 2024-07-30T21:25:08Z WRN github.com/traefik/traefik/v3/pkg/logs/wasm.go:31 > INIT!!!!! entryPointName=http middlewareName=souin@file middlewareType=wasm routerName=whoami@file
traefik-1  | 2024-07-30T21:25:08Z DBG github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/wrr/wrr.go:196 > Service selected by WRR: b903963fe23550fa
traefik-1  | 192.168.65.1 - - [30/Jul/2024:21:25:08  0000] "GET / HTTP/1.1" 200 373 "-" "-" 1 "whoami@file" "http://whoami" 9ms
traefik-1  | 2024-07-30T21:25:09Z WRN github.com/traefik/traefik/v3/pkg/logs/wasm.go:31 > INIT!!!!! entryPointName=http middlewareName=souin@file middlewareType=wasm routerName=whoami@file
traefik-1  | 2024-07-30T21:25:09Z DBG github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/wrr/wrr.go:196 > Service selected by WRR: b903963fe23550fa
traefik-1  | 192.168.65.1 - - [30/Jul/2024:21:25:09  0000] "GET / HTTP/1.1" 200 373 "-" "-" 2 "whoami@file" "http://whoami" 4ms
traefik-1  | 2024-07-30T21:25:09Z DBG github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/wrr/wrr.go:196 > Service selected by WRR: b903963fe23550fa
traefik-1  | 192.168.65.1 - - [30/Jul/2024:21:25:09  0000] "GET / HTTP/1.1" 200 373 "-" "-" 3 "whoami@file" "http://whoami" 2ms
@juliens
Copy link
Member

juliens commented Jul 31, 2024

Hello @darkweak ,

For the wasm plugin middlewares, it works with a pool so it's possible that when the second request happens, the first middleware was not yet back in the pool.

You can see in your logs that the third request does not call the "INIT".

@darkweak
Copy link
Author

Hey @juliens, do you have clues to prevent that?

@juliens
Copy link
Member

juliens commented Jul 31, 2024

Do you have a reason to prevent this?

It's designed to handle multiple requests in parallel. The pool should be populated over time, and once it's filled, it shouldn't call INIT anymore.

@darkweak
Copy link
Author

I'm migrating the HTTP cache from go middleware to the WASM middleware (due to limitations from yægi).
It tries to recreate more and more instances, so I think the pool resize itself, kill the previous instances and recreate the middleware when a new request comes.

@juliens
Copy link
Member

juliens commented Jul 31, 2024

The pool may remove some instances from the pool, I believe to refresh old instances, but it doesn't instantiate for each requests.

With your example middleware:
With an hey command
703091 responses
I only have
322 INIT

@darkweak
Copy link
Author

darkweak commented Jul 31, 2024

That means, it could create so much connections to the storage provider (e.g. redis/etcd) and there is no way to avoid reinitialization.
If there is a reset (another init) when using an in-memory storage, that will result a miss and reach the upstream server too often.

@juliens
Copy link
Member

juliens commented Jul 31, 2024

Yes, the current implementation of the WASM plugin middleware in Traefik does have some limitations, particularly regarding the instance pool and initialization behavior you're observing. Each middleware instance may get reinitialized more frequently than desired, especially with external connections like Redis or etcd and with shared-memory cache.

However, as there is a pool, the situation is not as dramatic as it might sound. The pool is designed to manage multiple requests in parallel and, over time, it should stabilize, reducing the frequency of initialization calls. This helps mitigate the performance impact to some extent.

Moreover, there are ways to improve this situation that we could try in the futur:

Shared In-Memory Objects: One potential improvement is to add support for shared objects in memory. This would allow instances to share certain data, reducing the need to reinitialize connections or cache data on each request.

Enhanced Pool Management: Enhancing the pool's management strategies to improve instance lifetime can help mitigate excessive reinitialization. This includes better handling of pool resizing and more stable instance retention policies.

Clean callback: In the Yaegi middleware, when a middleware is not used anymore, the context used in the instanciation is Done, it's not the case in wasm plugin, so it could be a good idea to have a callback to free resource like ecxternal connection to avoid infinite connection.

@darkweak
Copy link
Author

Thank you for taking in consideration this feedback. I hope the Shared In-Memory Objects could solve this problem.
In the main, we retrieve the configuration. Maybe Træfik could export a function GetWasmShared(), a SharedObjects property or something like that, and that will basically be a KV store. What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/plugins kind/enhancement a new or improved feature.
Projects
None yet
Development

No branches or pull requests

4 participants