Skip to content

Instantly share code, notes, and snippets.

@sedkis
Last active February 20, 2026 20:42
Show Gist options
  • Select an option

  • Save sedkis/6a431f54a1ab64ba59fced00bf7626e6 to your computer and use it in GitHub Desktop.

Select an option

Save sedkis/6a431f54a1ab64ba59fced00bf7626e6 to your computer and use it in GitHub Desktop.
tyk benchmark

Go Plugin vs JSVM Middleware Benchmark

Date: 2026-02-20 | Tool: Apache Bench | Requests: 10,000 | Concurrency: 10

Tyk version: commit c0413c845222d413ae82d2ce68f805a8839efb23 Version ~ v5.12.0

Commands

ab -n 10000 -c 10 http://localhost:8091/gopl/hello/
ab -n 10000 -c 10 http://localhost:8091/jsvm/hello/

Results

Metric Go Plugin JSVM Difference
Requests/sec 14,247 1,130 Go is ~12.6x faster
Mean latency 0.70 ms 8.85 ms Go is ~12.6x lower
Wall time 0.70 s 8.85 s
CPU time consumed 2.32 s 21.51 s Go uses ~9.3x less CPU
CPU utilization ~331% ~243% Go saturates more cores

Summary

The Go plugin middleware is roughly 13x faster than JSVM in throughput and uses ~9x less total CPU time for the same workload. JSVM adds ~8ms overhead per request from the JavaScript VM execution context. Go's higher CPU utilization (331% vs 243%) shows it effectively parallelizes across cores, while JSVM is bottlenecked by VM execution.

{
"name": "Go Plugin Hello",
"api_id": "gopl-hello",
"org_id": "default",
"active": true,
"use_keyless": true,
"definition": {
"location": "header",
"key": "x-api-version"
},
"version_data": {
"not_versioned": true,
"versions": {
"Default": {
"name": "Default",
"use_extended_paths": true
}
}
},
"proxy": {
"listen_path": "/gopl/",
"target_url": "http://localhost:8222",
"strip_listen_path": true
},
"custom_middleware": {
"driver": "goplugin",
"pre": [
{
"name": "AddHeader",
"path": "middleware/go-plugin/plugin.so"
}
]
}
}
{
"name": "JSVM Hello",
"api_id": "jsvm-hello",
"org_id": "default",
"active": true,
"use_keyless": true,
"definition": {
"location": "header",
"key": "x-api-version"
},
"version_data": {
"not_versioned": true,
"versions": {
"Default": {
"name": "Default",
"use_extended_paths": true
}
}
},
"proxy": {
"listen_path": "/jsvm/",
"target_url": "http://localhost:8222",
"strip_listen_path": true
},
"custom_middleware": {
"driver": "otto",
"pre": [
{
"name": "jsvmHelloPreHook",
"path": "middleware/jsvm_hello.js"
}
]
}
}
var jsvmHelloPreHook = new TykJS.TykMiddleware.NewMiddleware({});
jsvmHelloPreHook.NewProcessRequest(function(request, session) {
request.SetHeaders["X-Custom-JSVM"] = "hello-from-jsvm";
return jsvmHelloPreHook.ReturnData(request, {});
});
log("jsvm_hello.js middleware loaded");
// +build ignore
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
headers := make(map[string]string)
for k, v := range r.Header {
if len(v) > 0 {
headers[k] = v[0]
}
}
resp := map[string]interface{}{
"message": "hello from mock upstream",
"path": r.URL.Path,
"method": r.Method,
"headers": headers,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
})
port := "8222"
if p := os.Getenv("MOCK_PORT"); p != "" {
port = p
}
fmt.Printf("Mock server listening on :%s\n", port)
http.ListenAndServe(":"+port, nil)
}
package main
import (
"net/http"
)
// AddHeader adds a custom header to every request.
func AddHeader(rw http.ResponseWriter, r *http.Request) {
r.Header.Set("X-Custom-GoPlugin", "hello-from-go-plugin")
}
func main() {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment