diff --git a/.github/workflows/benchmark-processor.yml b/.github/workflows/benchmark-processor.yml index f45f48a3..b8bd2945 100644 --- a/.github/workflows/benchmark-processor.yml +++ b/.github/workflows/benchmark-processor.yml @@ -35,6 +35,10 @@ on: description: 'Hetzner server type for the collector under test' required: true default: 'ccx33' + ldg_type: + description: 'Hetzner server type for the load generator (shared vCPU)' + required: true + default: 'cpx42' connections: description: 'Loadgen concurrency ramp' required: true @@ -55,6 +59,9 @@ on: permissions: contents: read +concurrency: + group: hetzner-bench-processor + jobs: build: runs-on: ubuntu-latest @@ -113,6 +120,7 @@ jobs: runs-on: ubuntu-latest strategy: fail-fast: false + max-parallel: 1 matrix: impl: [honeycomb, traceway-oxc-mem, traceway-oxc-disk, traceway-goja-mem, traceway-goja-disk] steps: @@ -131,6 +139,7 @@ jobs: HCLOUD_TOKEN: ${{ secrets.HCLOUD_TOKEN }} SCENARIOS: ${{ inputs.scenarios }} SUT_TYPE: ${{ inputs.sut_type }} + LDG_TYPE: ${{ inputs.ldg_type }} CONNECTIONS: ${{ inputs.connections }} STEP_DURATION: ${{ inputs.step_duration }} CHURN_ENTRIES: ${{ inputs.churn_entries }} @@ -142,6 +151,14 @@ jobs: OOM_DURATION: ${{ inputs.oom_duration }} OOM_SUT_TYPE: ${{ inputs.oom_sut_type }} run: bash run-hetzner.sh ${{ matrix.impl }} + - name: Cleanup hetzner + if: always() + env: + HCLOUD_TOKEN: ${{ secrets.HCLOUD_TOKEN }} + run: | + hcloud server delete "bench-sut-${{ github.run_id }}-${{ matrix.impl }}" 2>/dev/null || true + hcloud server delete "bench-ldg-${{ github.run_id }}-${{ matrix.impl }}" 2>/dev/null || true + hcloud ssh-key delete "bench-key-${{ github.run_id }}-${{ matrix.impl }}" 2>/dev/null || true - uses: actions/upload-artifact@v4 if: always() with: diff --git a/backend/app/symbolicator/otelprocessor/README.md b/backend/app/symbolicator/otelprocessor/README.md index ed1b6a52..d74e4ccf 100644 --- a/backend/app/symbolicator/otelprocessor/README.md +++ b/backend/app/symbolicator/otelprocessor/README.md @@ -74,6 +74,7 @@ For each span, span event, or log record carrying `exception.stacktrace`: | `build_uuid_attribute_key` | `app.debug.source_map_uuid` | Resource attribute used as a store key prefix | | `language_attribute_key` | `telemetry.sdk.language` | Attribute checked against `allowed_languages` | | `allowed_languages` | `[]` | When set, only records with a matching language are processed | +| `parser` | `""` | Bundle scope-analysis parser: `goja` (pure Go, the default) or `oxc` (requires a collector compiled with `-tags oxc` and the oxc shim, see `scripts/build-oxc-shim.sh`) | All attribute key names (`stack_trace_attribute_key`, `urls_attribute_key`, `symbolicator_failure_attribute_key`, and the rest) are remappable with the same configuration keys and defaults as Honeycomb's processor. diff --git a/backend/app/symbolicator/otelprocessor/config.go b/backend/app/symbolicator/otelprocessor/config.go index c894264d..b586083a 100644 --- a/backend/app/symbolicator/otelprocessor/config.go +++ b/backend/app/symbolicator/otelprocessor/config.go @@ -58,6 +58,8 @@ type Config struct { LanguageAttributeKey string `mapstructure:"language_attribute_key"` AllowedLanguages []string `mapstructure:"allowed_languages"` + + Parser string `mapstructure:"parser"` } func (c *Config) Validate() error { diff --git a/backend/app/symbolicator/otelprocessor/factory.go b/backend/app/symbolicator/otelprocessor/factory.go index bc7f9645..100ec040 100644 --- a/backend/app/symbolicator/otelprocessor/factory.go +++ b/backend/app/symbolicator/otelprocessor/factory.go @@ -8,6 +8,8 @@ import ( "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/processor" "go.opentelemetry.io/collector/processor/processorhelper" + + "github.com/tracewayapp/traceway/backend/app/symbolicator/scopes" ) const processorVersion = "0.1.0" @@ -61,6 +63,11 @@ func createDefaultConfig() component.Config { } func newSymbolicator(cfg *Config, set processor.Settings) (*symbolicatorProcessor, error) { + if cfg.Parser != "" { + if err := scopes.SetParser(cfg.Parser); err != nil { + return nil, err + } + } store, err := newStore(cfg) if err != nil { return nil, err diff --git a/benchmarks/processor/.gitignore b/benchmarks/processor/.gitignore new file mode 100644 index 00000000..a0dfdfe1 --- /dev/null +++ b/benchmarks/processor/.gitignore @@ -0,0 +1,10 @@ +bin/ +build-traceway/ +build-honeycomb/ +corpus-*/ +results/ +artifacts/ +drain/target/ +loadgen/loadgen +corpusgen/corpusgen +*.log diff --git a/benchmarks/processor/README.md b/benchmarks/processor/README.md new file mode 100644 index 00000000..65491251 --- /dev/null +++ b/benchmarks/processor/README.md @@ -0,0 +1,116 @@ +# benchmarks/processor + +Head-to-head benchmark of the Traceway source map symbolicator processor (oxc build) +against Honeycomb's reference sourcemapprocessor, measuring sustained symbolication +throughput and resident memory while ramping load to the breaking point. + +## Topology + +``` +loadgen (Go) ----OTLP/HTTP----> collector under test ----OTLP/HTTP----> drain (Rust) + | (one symbolicator, | + | file store, no batch, | + | sync export) | + +---- ramps concurrency RSS sampled every 1s verifies the stacktrace + until saturation was actually symbolicated +``` + +The drain is a Rust server that gunzips each export request, scans for the +original-source marker (`../src/inventory.js`) and the minified marker (`.mjs:1:`), +and counts symbolicated vs unsymbolicated requests. It does no protobuf parsing, +so it sustains far more load than either collector can produce. + +The export path is synchronous (no sending queue, no retry, no batch processor), +so one accepted loadgen request equals one fully symbolicated export delivered to +the drain. Loadgen ok-rate times spans-per-request is the end-to-end +stacktraces/sec; the drain's symbolicated percentage is the correctness check. + +## Implementations + +| impl | binary | parser | cache | +|------|--------|--------|-------| +| `honeycomb` | otelcol-bench-honeycomb | symbolic (cgo) | RAM LRU, entry-count bound | +| `traceway-oxc-mem` | otelcol-bench-traceway | oxc | in-memory resolvers only | +| `traceway-oxc-disk` | otelcol-bench-traceway | oxc | mmap'd `.tw` disk tier | +| `traceway-goja-mem` | otelcol-bench-traceway | goja | in-memory resolvers only | +| `traceway-goja-disk` | otelcol-bench-traceway | goja | mmap'd `.tw` disk tier | + +One traceway binary (built with `-tags oxc`) serves all four variants; parser +and cache mode are runtime config (`parser`, `cache_dir`). + +## Scenarios + +- `hot`: one bundle, always cache-warm. Pure resolve throughput ramp. +- `churn`: 512 bundles (default) against a 128-entry resolver cache. + Honeycomb re-parses through Sentry symbolic on every eviction; Traceway disk + variants re-open compiled `.tw` files. The goja-vs-oxc choice matters here, + since the parser only runs on cache misses. +- `oom`: 4096 bundles (default) with 1MB bundle padding, 1MB of + sourcesContent padding, AND 1MB of synthetic VLQ mappings per map (so + traceway's token table grows too and the corpus is realistic, not rigged), cache entry limit raised to corpus size, + fixed 32-connection load until the corpus is fully resident or the collector + dies. Honeycomb retains the raw map JSON plus the minified bundle on the C + heap per entry, so RSS grows with corpus bytes. Traceway discards bundles and + sourcesContent after compiling the compact `.tw` token table; the mem + variants keep resolvers on the Go heap, the disk variants only mmap handles. + The oom run defaults to a small SUT (ccx13, 8 GB) so the breaking point + arrives quickly. + +Corpus entries are the real minified node-app bundle padded with `--pad-kb` +of valid JS (default 256 KB) so scope-analysis parse cost is realistic. The +sourcemap stays valid because frames sit on line 1 before the padding. + +## Memory methodology + +RSS is sampled once per second from outside the process (`rss.csv` per run). +Go heap numbers would be misleading here: Honeycomb's parsed maps live on the +C heap inside symbolic (invisible to Go), and Traceway's mmap'd `.tw` pages +are kernel-reclaimable (inflate RSS but are evictable). Compare the full RSS +timeline, not a single number. + +## Run locally + +``` +./run-local.sh +IMPLS="traceway-oxc traceway-goja honeycomb" SCENARIOS=churn CONNECTIONS=4,16,64 ./run-local.sh +``` + +Needs go, cargo, node, jq. Builds both collectors (the Traceway one with +`-tags oxc` after `scripts/build-oxc-shim.sh`), drain, loadgen, corpusgen, +then runs the matrix on localhost and prints a summary table. Results land +in `results/-/` as `loadgen.json`, `drain.json`, `rss.csv`, +`collector.log`. + +## Run on Hetzner + +``` +export HCLOUD_TOKEN=... +./run-hetzner.sh traceway-oxc +./run-hetzner.sh honeycomb +``` + +Provisions two dedicated-vCPU servers per invocation (SUT default ccx33, +loadgen+drain box default ccx23, same location), pushes prebuilt linux +artifacts from `artifacts/`, runs the scenarios, pulls results, and deletes +the servers on exit. The loadgen box hosts the drain so the SUT runs nothing +but the collector. + +## GitHub Action + +`benchmark-processor` (workflow_dispatch) builds all artifacts on the runner, +then runs the two implementations as parallel matrix entries, each on its own +Hetzner server pair. Needs the `HCLOUD_TOKEN` secret. Results are uploaded as +`results-traceway-oxc` and `results-honeycomb` artifacts. + +## Knobs + +| Env | Default | Meaning | +|-----|---------|---------| +| `IMPLS` | `traceway-oxc honeycomb` | run-local.sh only; `traceway-goja` selects the goja parser in the same oxc-built binary | +| `SCENARIOS` | `hot churn` | | +| `CHURN_ENTRIES` | `512` | corpus size for churn | +| `PAD_KB` | `256` | padding per bundle | +| `CONNECTIONS` | ramp | comma list of concurrency steps | +| `STEP_DURATION` | `30s` local, `60s` hetzner | time per step | +| `SPANS_PER_REQUEST` | `20` | exception spans per OTLP request | +| `SUT_TYPE` / `LDG_TYPE` | `ccx33` / `ccx23` | hetzner server types | diff --git a/benchmarks/processor/config-honeycomb.yaml b/benchmarks/processor/config-honeycomb.yaml new file mode 100644 index 00000000..d50020fe --- /dev/null +++ b/benchmarks/processor/config-honeycomb.yaml @@ -0,0 +1,32 @@ +receivers: + otlp: + protocols: + http: + endpoint: 0.0.0.0:4318 + +processors: + source_map_symbolicator: + source_map_store: file_store + local_source_maps: + path: ${env:STORE_PATH} + source_map_cache_size: ${env:CACHE_SIZE:-128} + preserve_stack_trace: false + +exporters: + otlphttp/drain: + endpoint: ${env:DRAIN_ENDPOINT} + compression: none + sending_queue: + enabled: false + retry_on_failure: + enabled: false + +service: + telemetry: + metrics: + level: none + pipelines: + traces: + receivers: [otlp] + processors: [source_map_symbolicator] + exporters: [otlphttp/drain] diff --git a/benchmarks/processor/config-traceway.yaml b/benchmarks/processor/config-traceway.yaml new file mode 100644 index 00000000..7dba1700 --- /dev/null +++ b/benchmarks/processor/config-traceway.yaml @@ -0,0 +1,34 @@ +receivers: + otlp: + protocols: + http: + endpoint: 0.0.0.0:4318 + +processors: + source_map_symbolicator: + source_map_store: file_store + local_source_maps: + path: ${env:STORE_PATH} + cache_dir: ${env:CACHE_DIR:-} + source_map_cache_size: ${env:CACHE_SIZE:-128} + preserve_stack_trace: false + parser: ${env:SYMB_PARSER:-goja} + +exporters: + otlphttp/drain: + endpoint: ${env:DRAIN_ENDPOINT} + compression: none + sending_queue: + enabled: false + retry_on_failure: + enabled: false + +service: + telemetry: + metrics: + level: none + pipelines: + traces: + receivers: [otlp] + processors: [source_map_symbolicator] + exporters: [otlphttp/drain] diff --git a/benchmarks/processor/corpusgen/go.mod b/benchmarks/processor/corpusgen/go.mod new file mode 100644 index 00000000..1a6a0bb2 --- /dev/null +++ b/benchmarks/processor/corpusgen/go.mod @@ -0,0 +1,3 @@ +module github.com/tracewayapp/traceway/benchmarks/processor/corpusgen + +go 1.25.1 diff --git a/benchmarks/processor/corpusgen/main.go b/benchmarks/processor/corpusgen/main.go new file mode 100644 index 00000000..bef38264 --- /dev/null +++ b/benchmarks/processor/corpusgen/main.go @@ -0,0 +1,203 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "os" + "path/filepath" + "strings" +) + +type corpus struct { + Urls []string `json:"urls"` +} + +const vlqChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + +var vlqIndex = func() [128]int8 { + var t [128]int8 + for i := range t { + t[i] = -1 + } + for i, c := range vlqChars { + t[c] = int8(i) + } + return t +}() + +func vlqEncode(b *strings.Builder, v int) { + u := v << 1 + if v < 0 { + u = (-v << 1) | 1 + } + for { + digit := u & 31 + u >>= 5 + if u > 0 { + digit |= 32 + } + b.WriteByte(vlqChars[digit]) + if u == 0 { + break + } + } +} + +func mappingsEndState(mappings string) (srcIdx, srcLine, srcCol, nameIdx int) { + vals := make([]int, 0, 5) + cur, shift := 0, 0 + flush := func() { + if len(vals) >= 4 { + srcIdx += vals[1] + srcLine += vals[2] + srcCol += vals[3] + } + if len(vals) >= 5 { + nameIdx += vals[4] + } + vals = vals[:0] + } + for _, c := range mappings { + if c == ';' || c == ',' { + flush() + continue + } + d := vlqIndex[c] + cur |= int(d&31) << shift + if d&32 != 0 { + shift += 5 + continue + } + v := cur >> 1 + if cur&1 != 0 { + v = -v + } + vals = append(vals, v) + cur, shift = 0, 0 + } + flush() + return +} + +func padMap(mapBytes []byte, kb, mappingsKB, seed int) []byte { + var m map[string]any + if err := json.Unmarshal(mapBytes, &m); err != nil { + panic(err) + } + sources, _ := m["sources"].([]any) + content, _ := m["sourcesContent"].([]any) + for len(content) < len(sources) { + content = append(content, nil) + } + padIdx := len(sources) + var pad strings.Builder + line := fmt.Sprintf("const benchPadValue%d = %d;\n", seed, seed) + for pad.Len() < kb*1024 { + pad.WriteString(line) + } + m["sources"] = append(sources, "__benchpad.js") + m["sourcesContent"] = append(content, pad.String()) + + if mappingsKB > 0 { + mappings, _ := m["mappings"].(string) + names, _ := m["names"].([]any) + nameBase := len(names) + for i := 0; i < 64; i++ { + names = append(names, fmt.Sprintf("__benchFn%d_%d", seed, i)) + } + m["names"] = names + + srcIdx, srcLine, srcCol, nameIdx := mappingsEndState(mappings) + var b strings.Builder + b.WriteString(mappings) + dSrc := padIdx - srcIdx + dLine := -srcLine + dCol := -srcCol + dName := nameBase - nameIdx + genLine := 0 + for b.Len() < len(mappings)+mappingsKB*1024 { + b.WriteByte(';') + genLine++ + col := 0 + for s := 0; s < 8; s++ { + if s > 0 { + b.WriteByte(',') + } + vlqEncode(&b, col) + vlqEncode(&b, dSrc) + vlqEncode(&b, dLine) + vlqEncode(&b, dCol) + vlqEncode(&b, dName) + dSrc, dCol = 0, 1 + dLine = 0 + dName = 1 + if s == 7 { + dLine, dCol = 1, -7 + dName = -7 + } + col = 10 + } + } + m["mappings"] = b.String() + } + + out, err := json.Marshal(m) + if err != nil { + panic(err) + } + return out +} + +func main() { + bundle := flag.String("bundle", "../../testing/symbolication/node-app/dist/app.mjs", "") + mapFile := flag.String("map", "../../testing/symbolication/node-app/dist/app.mjs.map", "") + entries := flag.Int("entries", 1, "") + padKB := flag.Int("pad-kb", 256, "") + mapPadKB := flag.Int("map-pad-kb", 0, "") + mappingsPadKB := flag.Int("mappings-pad-kb", 0, "") + out := flag.String("out", "./corpus", "") + flag.Parse() + + bundleBytes, err := os.ReadFile(*bundle) + if err != nil { + panic(err) + } + mapBytes, err := os.ReadFile(*mapFile) + if err != nil { + panic(err) + } + firstLine := strings.SplitN(string(bundleBytes), "\n", 2)[0] + + if err := os.MkdirAll(*out, 0o755); err != nil { + panic(err) + } + c := corpus{} + for n := 0; n < *entries; n++ { + var pad strings.Builder + chunk := "function __benchPad%d_%d(a,b){var c=a*b+%d;for(var i=0;i<3;i++){c+=i*a-b}return c}\n" + i := 0 + for pad.Len() < *padKB*1024 { + pad.WriteString(fmt.Sprintf(chunk, n, i, i+n)) + i++ + } + name := fmt.Sprintf("app%d.mjs", n) + content := firstLine + "\n" + pad.String() + "//# sourceMappingURL=" + name + ".map\n" + if err := os.WriteFile(filepath.Join(*out, name), []byte(content), 0o644); err != nil { + panic(err) + } + entryMap := mapBytes + if *mapPadKB > 0 || *mappingsPadKB > 0 { + entryMap = padMap(mapBytes, *mapPadKB, *mappingsPadKB, n) + } + if err := os.WriteFile(filepath.Join(*out, name+".map"), entryMap, 0o644); err != nil { + panic(err) + } + c.Urls = append(c.Urls, name) + } + data, _ := json.MarshalIndent(c, "", " ") + if err := os.WriteFile(filepath.Join(*out, "corpus.json"), data, 0o644); err != nil { + panic(err) + } + fmt.Printf("wrote %d entries (%d KB padding each) to %s\n", *entries, *padKB, *out) +} diff --git a/benchmarks/processor/drain/Cargo.lock b/benchmarks/processor/drain/Cargo.lock new file mode 100644 index 00000000..ec871abf --- /dev/null +++ b/benchmarks/processor/drain/Cargo.lock @@ -0,0 +1,545 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "drain" +version = "0.1.0" +dependencies = [ + "axum", + "flate2", + "serde_json", + "tokio", +] + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "bytes", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/benchmarks/processor/drain/Cargo.toml b/benchmarks/processor/drain/Cargo.toml new file mode 100644 index 00000000..f6a4332a --- /dev/null +++ b/benchmarks/processor/drain/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "drain" +version = "0.1.0" +edition = "2021" + +[dependencies] +axum = "0.8" +tokio = { version = "1", features = ["rt-multi-thread", "macros"] } +flate2 = "1" +serde_json = "1" + +[profile.release] +lto = true diff --git a/benchmarks/processor/drain/src/main.rs b/benchmarks/processor/drain/src/main.rs new file mode 100644 index 00000000..8bc34721 --- /dev/null +++ b/benchmarks/processor/drain/src/main.rs @@ -0,0 +1,85 @@ +use axum::body::Bytes; +use axum::extract::State; +use axum::http::HeaderMap; +use axum::routing::{get, post}; +use axum::Router; +use flate2::read::GzDecoder; +use std::io::Read; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Arc; + +const OK_MARKER: &[u8] = b"../src/inventory.js"; +const FAIL_MARKER: &[u8] = b".mjs:1:"; + +#[derive(Default)] +struct Counters { + requests: AtomicU64, + symbolicated: AtomicU64, + unsymbolicated: AtomicU64, + bytes: AtomicU64, +} + +fn contains(haystack: &[u8], needle: &[u8]) -> bool { + haystack.windows(needle.len()).any(|w| w == needle) +} + +async fn ingest(State(c): State>, headers: HeaderMap, body: Bytes) -> &'static str { + let decoded: Vec; + let data: &[u8] = if headers + .get("content-encoding") + .map(|v| v.as_bytes() == b"gzip") + .unwrap_or(false) + { + let mut d = GzDecoder::new(body.as_ref()); + let mut out = Vec::with_capacity(body.len() * 4); + if d.read_to_end(&mut out).is_err() { + c.unsymbolicated.fetch_add(1, Ordering::Relaxed); + return ""; + } + decoded = out; + &decoded + } else { + body.as_ref() + }; + c.requests.fetch_add(1, Ordering::Relaxed); + c.bytes.fetch_add(data.len() as u64, Ordering::Relaxed); + if contains(data, OK_MARKER) && !contains(data, FAIL_MARKER) { + c.symbolicated.fetch_add(1, Ordering::Relaxed); + } else { + c.unsymbolicated.fetch_add(1, Ordering::Relaxed); + } + "" +} + +async fn stats(State(c): State>) -> String { + serde_json::json!({ + "requests": c.requests.load(Ordering::Relaxed), + "symbolicated": c.symbolicated.load(Ordering::Relaxed), + "unsymbolicated": c.unsymbolicated.load(Ordering::Relaxed), + "bytes": c.bytes.load(Ordering::Relaxed), + }) + .to_string() +} + +async fn reset(State(c): State>) -> &'static str { + c.requests.store(0, Ordering::Relaxed); + c.symbolicated.store(0, Ordering::Relaxed); + c.unsymbolicated.store(0, Ordering::Relaxed); + c.bytes.store(0, Ordering::Relaxed); + "reset" +} + +#[tokio::main] +async fn main() { + let counters = Arc::new(Counters::default()); + let app = Router::new() + .route("/v1/traces", post(ingest)) + .route("/v1/logs", post(ingest)) + .route("/stats", get(stats)) + .route("/reset", post(reset)) + .with_state(counters); + let addr = std::env::var("DRAIN_ADDR").unwrap_or_else(|_| "0.0.0.0:9319".to_string()); + let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); + println!("drain listening on {}", addr); + axum::serve(listener, app).await.unwrap(); +} diff --git a/benchmarks/processor/loadgen/go.mod b/benchmarks/processor/loadgen/go.mod new file mode 100644 index 00000000..338ee46c --- /dev/null +++ b/benchmarks/processor/loadgen/go.mod @@ -0,0 +1,20 @@ +module github.com/tracewayapp/traceway/benchmarks/processor/loadgen + +go 1.25.1 + +require go.opentelemetry.io/collector/pdata v1.60.0 + +require ( + github.com/hashicorp/go-version v1.9.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + go.opentelemetry.io/collector/featuregate v1.60.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/net v0.55.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/text v0.37.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect + google.golang.org/grpc v1.81.1 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) diff --git a/benchmarks/processor/loadgen/go.sum b/benchmarks/processor/loadgen/go.sum new file mode 100644 index 00000000..3e353ad2 --- /dev/null +++ b/benchmarks/processor/loadgen/go.sum @@ -0,0 +1,76 @@ +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA= +github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/collector/featuregate v1.60.0 h1:/HxHB8hq4N5Fhq5N0C8G6xbXTHxnGcWIryyJzmP7pdc= +go.opentelemetry.io/collector/featuregate v1.60.0/go.mod h1:4ga1QBMPEejXXmpyJS8lmaRpknJ3Lb9Bvk6e420bUFU= +go.opentelemetry.io/collector/internal/testutil v0.154.0 h1:iUYHOM8+wONW01A4jFnzauanOYGVBGchKWWtm51is6c= +go.opentelemetry.io/collector/internal/testutil v0.154.0/go.mod h1:Jkjs6rkqs973LqgZ0Fe3zrokQRKULYXPIf4HuqStiEE= +go.opentelemetry.io/collector/pdata v1.60.0 h1:YcGMHzeJucHen41AoR4mxHro8reUr9SVqt7P0KacKzQ= +go.opentelemetry.io/collector/pdata v1.60.0/go.mod h1:Ca8VgZX2wOr6wW4nihPWaCpkJVvzeo6Txa7BJ7/WO90= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/proto/slim/otlp v1.10.0 h1:iR97Vs/ZDR+y9TfuP9b1XBtdPWeC+OMslIBmhcLU7jM= +go.opentelemetry.io/proto/slim/otlp v1.10.0/go.mod h1:lV9250stpjYLPNA5viFabIgP2QlUGRT1GdTgAf8SIUk= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0 h1:RUF5rO0hAlgiJt1fzQVzcVs3vZVNHIcMLgOgG4rWNcQ= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0/go.mod h1:I89cynRj8y+383o7tEQVg2SVA6SRgDVIouWPUVXjx0U= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0 h1:CQvJSldHRUN6Z8jsUeYv8J0lXRvygALXIzsmAeCcZE0= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0/go.mod h1:xSQ+mEfJe/GjK1LXEyVOoSI1N9JV9ZI923X5kup43W4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/benchmarks/processor/loadgen/main.go b/benchmarks/processor/loadgen/main.go new file mode 100644 index 00000000..99fb45d2 --- /dev/null +++ b/benchmarks/processor/loadgen/main.go @@ -0,0 +1,176 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/ptrace" + "go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp" +) + +func buildBody(url string, spans int) []byte { + td := ptrace.NewTraces() + rs := td.ResourceSpans().AppendEmpty() + rs.Resource().Attributes().PutStr("service.name", "processor-bench") + rs.Resource().Attributes().PutStr("telemetry.sdk.language", "nodejs") + ss := rs.ScopeSpans().AppendEmpty() + stack := fmt.Sprintf(`RangeError: cannot reserve 3 units for order ord_1042, only 2 in stock + at file:///bench/%s:1:435 + at p (file:///bench/%s:1:554) + at file:///bench/%s:1:1180 + at file:///bench/%s:1:1717`, url, url, url, url) + for i := 0; i < spans; i++ { + sp := ss.Spans().AppendEmpty() + sp.SetName("POST /orders/fulfill") + sp.SetKind(ptrace.SpanKindServer) + var tid pcommon.TraceID + var sid pcommon.SpanID + copy(tid[:], fmt.Sprintf("%016d", i)) + copy(sid[:], fmt.Sprintf("%08d", i)) + sp.SetTraceID(tid) + sp.SetSpanID(sid) + ev := sp.Events().AppendEmpty() + ev.SetName("exception") + ev.Attributes().PutStr("exception.type", "RangeError") + ev.Attributes().PutStr("exception.message", "bench") + ev.Attributes().PutStr("exception.stacktrace", stack) + } + req := ptraceotlp.NewExportRequestFromTraces(td) + data, err := req.MarshalProto() + if err != nil { + panic(err) + } + return data +} + +type stepResult struct { + Connections int `json:"connections"` + DurationSec float64 `json:"duration_sec"` + Sent int64 `json:"sent"` + Ok int64 `json:"ok"` + Rejected int64 `json:"rejected"` + Errors int64 `json:"errors"` + OkReqPerSec float64 `json:"ok_req_per_sec"` + StacksPerSec float64 `json:"stacks_per_sec"` + P50Ms float64 `json:"p50_ms"` + P99Ms float64 `json:"p99_ms"` +} + +func main() { + target := flag.String("target", "http://localhost:4318/v1/traces", "") + corpusFile := flag.String("corpus", "./corpus/corpus.json", "") + connSteps := flag.String("connections", "4,8,16,32,64,128", "") + stepDur := flag.Duration("step-duration", 30*time.Second, "") + spansPerReq := flag.Int("spans-per-request", 20, "") + outFile := flag.String("out", "loadgen-results.json", "") + flag.Parse() + + raw, err := os.ReadFile(*corpusFile) + if err != nil { + panic(err) + } + var c struct { + Urls []string `json:"urls"` + } + if err := json.Unmarshal(raw, &c); err != nil { + panic(err) + } + bodies := make([][]byte, len(c.Urls)) + for i, u := range c.Urls { + bodies[i] = buildBody(u, *spansPerReq) + } + fmt.Printf("prepared %d bodies, %d spans each, %d bytes first\n", len(bodies), *spansPerReq, len(bodies[0])) + + var results []stepResult + for _, s := range strings.Split(*connSteps, ",") { + conns, err := strconv.Atoi(strings.TrimSpace(s)) + if err != nil { + panic(err) + } + tr := &http.Transport{MaxIdleConns: conns * 2, MaxIdleConnsPerHost: conns * 2} + client := &http.Client{Transport: tr, Timeout: 30 * time.Second} + var sent, ok, rejected, errs int64 + var bodyIdx int64 + latCh := make(chan float64, 65536) + deadline := time.Now().Add(*stepDur) + var wg sync.WaitGroup + for w := 0; w < conns; w++ { + wg.Add(1) + go func() { + defer wg.Done() + for time.Now().Before(deadline) { + idx := atomic.AddInt64(&bodyIdx, 1) + body := bodies[idx%int64(len(bodies))] + atomic.AddInt64(&sent, 1) + t0 := time.Now() + resp, err := client.Post(*target, "application/x-protobuf", bytes.NewReader(body)) + lat := float64(time.Since(t0).Microseconds()) / 1000.0 + if err != nil { + atomic.AddInt64(&errs, 1) + continue + } + io.Copy(io.Discard, resp.Body) + resp.Body.Close() + if resp.StatusCode >= 200 && resp.StatusCode < 300 { + atomic.AddInt64(&ok, 1) + select { + case latCh <- lat: + default: + } + } else { + atomic.AddInt64(&rejected, 1) + } + } + }() + } + wg.Wait() + close(latCh) + var lats []float64 + for l := range latCh { + lats = append(lats, l) + } + sort.Float64s(lats) + pct := func(p float64) float64 { + if len(lats) == 0 { + return 0 + } + return lats[int(float64(len(lats)-1)*p)] + } + r := stepResult{ + Connections: conns, + DurationSec: stepDur.Seconds(), + Sent: sent, + Ok: ok, + Rejected: rejected, + Errors: errs, + OkReqPerSec: float64(ok) / stepDur.Seconds(), + StacksPerSec: float64(ok*int64(*spansPerReq)) / stepDur.Seconds(), + P50Ms: pct(0.50), + P99Ms: pct(0.99), + } + results = append(results, r) + line, _ := json.Marshal(r) + fmt.Println(string(line)) + tr.CloseIdleConnections() + } + data, _ := json.MarshalIndent(results, "", " ") + if err := os.MkdirAll(filepath.Dir(*outFile), 0o755); err != nil && filepath.Dir(*outFile) != "." { + panic(err) + } + if err := os.WriteFile(*outFile, data, 0o644); err != nil { + panic(err) + } +} diff --git a/benchmarks/processor/manifest-honeycomb.yaml b/benchmarks/processor/manifest-honeycomb.yaml new file mode 100644 index 00000000..6437f7ad --- /dev/null +++ b/benchmarks/processor/manifest-honeycomb.yaml @@ -0,0 +1,15 @@ +dist: + name: otelcol-bench-honeycomb + description: Bench collector with the Honeycomb symbolicator + output_path: ./build-honeycomb + cgo_enabled: true + +receivers: + - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.143.0 + +processors: + - gomod: go.opentelemetry.io/collector/processor/batchprocessor v0.143.0 + - gomod: github.com/honeycombio/opentelemetry-collector-symbolicator/sourcemapprocessor v1.0.3 + +exporters: + - gomod: go.opentelemetry.io/collector/exporter/otlphttpexporter v0.143.0 diff --git a/benchmarks/processor/manifest-traceway.yaml b/benchmarks/processor/manifest-traceway.yaml new file mode 100644 index 00000000..ef47cbb5 --- /dev/null +++ b/benchmarks/processor/manifest-traceway.yaml @@ -0,0 +1,18 @@ +dist: + name: otelcol-bench-traceway + description: Bench collector with the Traceway symbolicator + output_path: ./build-traceway + +receivers: + - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.154.0 + +processors: + - gomod: go.opentelemetry.io/collector/processor/batchprocessor v0.154.0 + - gomod: github.com/tracewayapp/traceway/backend v1.8.0 + import: github.com/tracewayapp/traceway/backend/app/symbolicator/otelprocessor + +exporters: + - gomod: go.opentelemetry.io/collector/exporter/otlphttpexporter v0.154.0 + +replaces: + - github.com/tracewayapp/traceway/backend => ../../../backend diff --git a/benchmarks/processor/rss-sampler.sh b/benchmarks/processor/rss-sampler.sh new file mode 100755 index 00000000..30f21ea1 --- /dev/null +++ b/benchmarks/processor/rss-sampler.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +PID="$1" +OUT="$2" +echo "ts,rss_kb,cpu_pct" > "$OUT" +while kill -0 "$PID" 2>/dev/null; do + LINE=$(ps -o rss=,pcpu= -p "$PID" 2>/dev/null | awk '{print $1","$2}') + [ -n "$LINE" ] && echo "$(date +%s),$LINE" >> "$OUT" + sleep 1 +done diff --git a/benchmarks/processor/run-hetzner.sh b/benchmarks/processor/run-hetzner.sh new file mode 100755 index 00000000..e0cf8af0 --- /dev/null +++ b/benchmarks/processor/run-hetzner.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +IMPL="${1:?usage: run-hetzner.sh }" +SCENARIOS="${SCENARIOS:-hot churn oom}" +CHURN_ENTRIES="${CHURN_ENTRIES:-512}" +PAD_KB="${PAD_KB:-256}" +OOM_ENTRIES="${OOM_ENTRIES:-4096}" +OOM_PAD_KB="${OOM_PAD_KB:-1024}" +OOM_MAP_PAD_KB="${OOM_MAP_PAD_KB:-1024}" +OOM_MAPPINGS_PAD_KB="${OOM_MAPPINGS_PAD_KB:-1024}" +OOM_CONNECTIONS="${OOM_CONNECTIONS:-32}" +OOM_DURATION="${OOM_DURATION:-30m}" +CONNECTIONS="${CONNECTIONS:-8,16,32,64,128,256}" +STEP_DURATION="${STEP_DURATION:-60s}" +SPANS_PER_REQUEST="${SPANS_PER_REQUEST:-20}" +SUT_TYPE="${SUT_TYPE:-ccx33}" +OOM_SUT_TYPE="${OOM_SUT_TYPE:-ccx13}" +LDG_TYPE="${LDG_TYPE:-cpx42}" +LOCATION="${LOCATION:-fsn1}" +RESULTS="${RESULTS:-./results}" +RUN_ID="${GITHUB_RUN_ID:-local}-$IMPL" + +command -v hcloud >/dev/null || { echo "hcloud CLI required" >&2; exit 1; } +[ -d artifacts ] || { echo "artifacts/ missing, build first (see workflow or run-local.sh build_all)" >&2; exit 1; } + +case "$IMPL" in + honeycomb) COL_BIN=otelcol-bench-honeycomb; COL_CFG=config-honeycomb.yaml; PARSER=goja; DISK= ;; + traceway-oxc-mem) COL_BIN=otelcol-bench-traceway; COL_CFG=config-traceway.yaml; PARSER=oxc; DISK= ;; + traceway-oxc-disk) COL_BIN=otelcol-bench-traceway; COL_CFG=config-traceway.yaml; PARSER=oxc; DISK=1 ;; + traceway-goja-mem) COL_BIN=otelcol-bench-traceway; COL_CFG=config-traceway.yaml; PARSER=goja; DISK= ;; + traceway-goja-disk) COL_BIN=otelcol-bench-traceway; COL_CFG=config-traceway.yaml; PARSER=goja; DISK=1 ;; + *) echo "unknown impl $IMPL" >&2; exit 1 ;; +esac + +scenario_params() { + case "$1" in + hot) echo "1 $PAD_KB 0:0 128 $CONNECTIONS $STEP_DURATION $SUT_TYPE" ;; + churn) echo "$CHURN_ENTRIES $PAD_KB 0:0 128 $CONNECTIONS $STEP_DURATION $SUT_TYPE" ;; + oom) echo "$OOM_ENTRIES $OOM_PAD_KB $OOM_MAP_PAD_KB:$OOM_MAPPINGS_PAD_KB $OOM_ENTRIES $OOM_CONNECTIONS $OOM_DURATION $OOM_SUT_TYPE" ;; + esac +} + +KEY_FILE=$(mktemp -u) +ssh-keygen -t ed25519 -N '' -f "$KEY_FILE" -q +SSH="ssh -i $KEY_FILE -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10" +SCP="scp -i $KEY_FILE -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" + +cleanup() { + hcloud server delete "bench-sut-$RUN_ID" 2>/dev/null || true + hcloud server delete "bench-ldg-$RUN_ID" 2>/dev/null || true + hcloud ssh-key delete "bench-key-$RUN_ID" 2>/dev/null || true + rm -f "$KEY_FILE" "$KEY_FILE.pub" +} +trap cleanup EXIT + +wait_ssh() { + for i in $(seq 1 60); do $SSH "root@$1" true 2>/dev/null && return 0; sleep 5; done + return 1 +} + +provision() { + local name="$1" type="$2" + hcloud server create --name "$name" --type "$type" --image ubuntu-24.04 --location "$LOCATION" --ssh-key "bench-key-$RUN_ID" > /dev/null || exit 1 + local ip + ip=$(hcloud server ip "$name") + [ -n "$ip" ] || { echo "no ip for $name" >&2; exit 1; } + wait_ssh "$ip" || { echo "ssh unreachable on $name ($ip)" >&2; exit 1; } + $SCP -r artifacts "root@$ip:/opt/bench" > /dev/null + $SSH "root@$ip" "chmod +x /opt/bench/* 2>/dev/null || true" + echo "$ip" +} + +hcloud ssh-key create --name "bench-key-$RUN_ID" --public-key-from-file "$KEY_FILE.pub" > /dev/null +LDG_IP=$(provision "bench-ldg-$RUN_ID" "$LDG_TYPE") +$SSH "root@$LDG_IP" "nohup env DRAIN_ADDR=0.0.0.0:9319 /opt/bench/drain > /opt/bench/drain.log 2>&1 & sleep 1; curl -sf http://127.0.0.1:9319/stats" + +PREV_SUT_TYPE="" +SUT_IP="" +for scenario in $SCENARIOS; do + read -r entries pad mappad cachesize conns dur sut_type <<< "$(scenario_params "$scenario")" + if [ "$sut_type" != "$PREV_SUT_TYPE" ]; then + [ -n "$SUT_IP" ] && hcloud server delete "bench-sut-$RUN_ID" && sleep 3 + SUT_IP=$(provision "bench-sut-$RUN_ID" "$sut_type") + PREV_SUT_TYPE="$sut_type" + fi + tag="$IMPL-$scenario" + outdir="$RESULTS/$tag" + mkdir -p "$outdir" + + for ip in "$SUT_IP" "$LDG_IP"; do + $SSH "root@$ip" "cd /opt/bench && [ -f corpus-$scenario/corpus.json ] || ./corpusgen --bundle app.mjs --map app.mjs.map --entries $entries --pad-kb $pad --map-pad-kb ${mappad%%:*} --mappings-pad-kb ${mappad##*:} --out corpus-$scenario" + done + + CACHE_DIR_REMOTE="" + [ -n "$DISK" ] && CACHE_DIR_REMOTE="/opt/bench/twcache" + $SSH "root@$LDG_IP" "curl -sf -X POST http://127.0.0.1:9319/reset" + $SSH "root@$SUT_IP" "cd /opt/bench || exit 1; [ ! -f collector.pid ] || { kill \$(cat collector.pid) 2>/dev/null || true; sleep 1; }; rm -rf twcache; nohup env STORE_PATH=./corpus-$scenario CACHE_DIR=$CACHE_DIR_REMOTE CACHE_SIZE=$cachesize SYMB_PARSER=$PARSER DRAIN_ENDPOINT=http://$LDG_IP:9319 LD_LIBRARY_PATH=/opt/bench ./$COL_BIN --config $COL_CFG > collector.log 2>&1 & echo \$! > collector.pid; sleep 3; kill -0 \$!" + T0=$($SSH "root@$SUT_IP" "date +%s") + $SSH "root@$SUT_IP" "cd /opt/bench || exit 1; nohup bash rss-sampler.sh \$(cat collector.pid) rss.csv > /dev/null 2>&1 &" + + $SSH "root@$LDG_IP" "cd /opt/bench && ./loadgen --target http://$SUT_IP:4318/v1/traces --corpus corpus-$scenario/corpus.json --connections $conns --step-duration $dur --spans-per-request $SPANS_PER_REQUEST --out loadgen.json" || true + + if ! $SSH "root@$SUT_IP" "kill -0 \$(cat /opt/bench/collector.pid) 2>/dev/null"; then + TDEAD=$($SSH "root@$SUT_IP" "tail -1 /opt/bench/rss.csv | cut -d, -f1") + echo "$(( TDEAD - T0 ))" > "$outdir/died" + fi + $SSH "root@$LDG_IP" "curl -sf http://127.0.0.1:9319/stats" > "$outdir/drain.json" || echo '{}' > "$outdir/drain.json" + $SCP "root@$LDG_IP:/opt/bench/loadgen.json" "$outdir/loadgen.json" + $SSH "root@$SUT_IP" "kill \$(cat /opt/bench/collector.pid) 2>/dev/null; sleep 2" || true + $SCP "root@$SUT_IP:/opt/bench/rss.csv" "$outdir/rss.csv" || true + $SCP "root@$SUT_IP:/opt/bench/collector.log" "$outdir/collector.log" || true + echo "== $tag ==" + cat "$outdir/drain.json"; echo +done + +bash ./summarize.sh "$RESULTS" diff --git a/benchmarks/processor/run-local.sh b/benchmarks/processor/run-local.sh new file mode 100755 index 00000000..f9e57723 --- /dev/null +++ b/benchmarks/processor/run-local.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +IMPLS="${IMPLS:-honeycomb traceway-oxc-mem traceway-oxc-disk traceway-goja-mem traceway-goja-disk}" +SCENARIOS="${SCENARIOS:-hot churn oom}" +CHURN_ENTRIES="${CHURN_ENTRIES:-512}" +PAD_KB="${PAD_KB:-256}" +OOM_ENTRIES="${OOM_ENTRIES:-4096}" +OOM_PAD_KB="${OOM_PAD_KB:-1024}" +OOM_MAP_PAD_KB="${OOM_MAP_PAD_KB:-1024}" +OOM_MAPPINGS_PAD_KB="${OOM_MAPPINGS_PAD_KB:-1024}" +OOM_CONNECTIONS="${OOM_CONNECTIONS:-32}" +OOM_DURATION="${OOM_DURATION:-30m}" +CONNECTIONS="${CONNECTIONS:-4,8,16,32,64,128}" +STEP_DURATION="${STEP_DURATION:-30s}" +SPANS_PER_REQUEST="${SPANS_PER_REQUEST:-20}" +SKIP_BUILD="${SKIP_BUILD:-}" +RESULTS="${RESULTS:-./results}" + +OCB_154=./bin/ocb-0.154.0 +OCB_143=./bin/ocb-0.143.0 + +build_all() { + mkdir -p bin + [ -x "$OCB_154" ] || { GOBIN="$PWD/bin" go install go.opentelemetry.io/collector/cmd/builder@v0.154.0 && mv bin/builder "$OCB_154"; } + [ -x "$OCB_143" ] || { GOBIN="$PWD/bin" go install go.opentelemetry.io/collector/cmd/builder@v0.143.0 && mv bin/builder "$OCB_143"; } + + if echo "$IMPLS" | grep -q traceway; then + bash ../../scripts/build-oxc-shim.sh + "$OCB_154" --config manifest-traceway.yaml --skip-compilation + (cd build-traceway && CGO_ENABLED=1 go build -tags oxc -o otelcol-bench-traceway .) + fi + if echo "$IMPLS" | grep -q honeycomb; then + CGO_ENABLED=1 "$OCB_143" --config manifest-honeycomb.yaml + fi + + (cd drain && cargo build --release) + (cd loadgen && go mod tidy >/dev/null 2>&1; go build -o loadgen .) + (cd corpusgen && go build -o corpusgen .) + + if [ ! -f ../../testing/symbolication/node-app/dist/app.mjs ]; then + (cd ../../testing/symbolication/node-app && npm install && npm run build) + fi +} + +scenario_params() { + case "$1" in + hot) echo "1 $PAD_KB 0:0 128 $CONNECTIONS $STEP_DURATION" ;; + churn) echo "$CHURN_ENTRIES $PAD_KB 0:0 128 $CONNECTIONS $STEP_DURATION" ;; + oom) echo "$OOM_ENTRIES $OOM_PAD_KB $OOM_MAP_PAD_KB:$OOM_MAPPINGS_PAD_KB $OOM_ENTRIES $OOM_CONNECTIONS $OOM_DURATION" ;; + esac +} + +gen_corpus() { + local scenario="$1" entries="$2" pad="$3" mappad="$4" + local dir="./corpus-$scenario" + if [ ! -f "$dir/corpus.json" ]; then + ./corpusgen/corpusgen --entries "$entries" --pad-kb "$pad" --map-pad-kb "${mappad%%:*}" --mappings-pad-kb "${mappad##*:}" --out "$dir" >&2 + fi + echo "$dir" +} + +impl_env() { + case "$1" in + honeycomb) echo "BIN=./build-honeycomb/otelcol-bench-honeycomb CFG=config-honeycomb.yaml PARSER= DISK=" ;; + traceway-oxc-mem) echo "BIN=./build-traceway/otelcol-bench-traceway CFG=config-traceway.yaml PARSER=oxc DISK=" ;; + traceway-oxc-disk) echo "BIN=./build-traceway/otelcol-bench-traceway CFG=config-traceway.yaml PARSER=oxc DISK=1" ;; + traceway-goja-mem) echo "BIN=./build-traceway/otelcol-bench-traceway CFG=config-traceway.yaml PARSER=goja DISK=" ;; + traceway-goja-disk) echo "BIN=./build-traceway/otelcol-bench-traceway CFG=config-traceway.yaml PARSER=goja DISK=1" ;; + *) echo "unknown impl $1" >&2; return 1 ;; + esac +} + +run_one() { + local impl="$1" scenario="$2" + read -r entries pad mappad cachesize conns dur <<< "$(scenario_params "$scenario")" + local store + store=$(gen_corpus "$scenario" "$entries" "$pad" "$mappad") + local BIN CFG PARSER DISK + eval "$(impl_env "$impl")" + local tag="$impl-$scenario" + local outdir="$RESULTS/$tag" + mkdir -p "$outdir" + + pkill -f 'target/release/drain' 2>/dev/null || true + pkill -f otelcol-bench 2>/dev/null || true + for i in $(seq 1 10); do + lsof -iTCP:9319 -sTCP:LISTEN >/dev/null 2>&1 || lsof -iTCP:4318 -sTCP:LISTEN >/dev/null 2>&1 || break + sleep 1 + done + + DRAIN_ADDR=127.0.0.1:9319 ./drain/target/release/drain & + local drain_pid=$! + sleep 1 + curl -sf -X POST http://127.0.0.1:9319/reset > /dev/null + + local cache_dir="" + [ -n "$DISK" ] && cache_dir=$(mktemp -d) + STORE_PATH="$store" CACHE_DIR="$cache_dir" CACHE_SIZE="$cachesize" SYMB_PARSER="${PARSER:-goja}" \ + DRAIN_ENDPOINT=http://127.0.0.1:9319 \ + "$BIN" --config "$CFG" > "$outdir/collector.log" 2>&1 & + local col_pid=$! + + for i in $(seq 1 30); do + curl -s -o /dev/null http://127.0.0.1:4318/ 2>/dev/null && break + kill -0 "$col_pid" 2>/dev/null || { echo "collector died at startup, see $outdir/collector.log" >&2; kill "$drain_pid"; return 1; } + sleep 1 + done + + bash ./rss-sampler.sh "$col_pid" "$outdir/rss.csv" & + local rss_pid=$! + local t0 + t0=$(date +%s) + + ./loadgen/loadgen --target http://127.0.0.1:4318/v1/traces --corpus "$store/corpus.json" \ + --connections "$conns" --step-duration "$dur" \ + --spans-per-request "$SPANS_PER_REQUEST" --out "$outdir/loadgen.json" || true + + if ! kill -0 "$col_pid" 2>/dev/null; then + echo "$(( $(tail -1 "$outdir/rss.csv" | cut -d, -f1) - t0 ))" > "$outdir/died" + fi + curl -sf http://127.0.0.1:9319/stats > "$outdir/drain.json" || echo '{}' > "$outdir/drain.json" + kill "$col_pid" "$drain_pid" 2>/dev/null || true + wait "$rss_pid" 2>/dev/null || true + [ -n "$cache_dir" ] && rm -rf "$cache_dir" + echo "== $tag ==" + cat "$outdir/drain.json"; echo +} + +[ -n "$SKIP_BUILD" ] || build_all +for impl in $IMPLS; do + for scenario in $SCENARIOS; do + run_one "$impl" "$scenario" + done +done +bash ./summarize.sh "$RESULTS" diff --git a/benchmarks/processor/summarize.sh b/benchmarks/processor/summarize.sh new file mode 100644 index 00000000..f2b022aa --- /dev/null +++ b/benchmarks/processor/summarize.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +RESULTS="${1:-./results}" + +printf '| %-20s | %-6s | %12s | %10s | %12s | %8s | %7s | %s |\n' impl scenario max_stacks/s p99_ms_max peak_rss_mb avg_cpu% symb% outcome +printf '|----------------------|--------|--------------|------------|--------------|----------|---------|---------|\n' +for d in "$RESULTS"/*/; do + [ -f "$d/loadgen.json" ] || continue + name=$(basename "$d") + impl="${name%-*}" + scenario="${name##*-}" + maxr=$(jq '[.[].stacks_per_sec] | max | floor' "$d/loadgen.json") + p99=$(jq '([.[].stacks_per_sec] | max) as $m | [.[] | select(.stacks_per_sec == $m)][0].p99_ms' "$d/loadgen.json") + peak=$(tail -n +2 "$d/rss.csv" 2>/dev/null | cut -d, -f2 | sort -n | tail -1) + peak_mb=$(( ${peak:-0} / 1024 )) + cpu=$(tail -n +2 "$d/rss.csv" 2>/dev/null | cut -d, -f3 | awk '{s+=$1; n++} END {if (n>0) printf "%.0f", s/n; else print 0}') + pct=$(jq -r 'if .requests > 0 then (100 * .symbolicated / .requests | floor) else 0 end' "$d/drain.json" 2>/dev/null || echo 0) + outcome=survived + if [ -f "$d/died" ]; then + secs=$(cat "$d/died") + outcome="died@${secs}s" + fi + printf '| %-20s | %-6s | %12s | %10s | %12s | %8s | %7s | %s |\n' "$impl" "$scenario" "$maxr" "$p99" "$peak_mb" "$cpu" "$pct" "$outcome" +done diff --git a/testing/symbolication/node-app/package-lock.json b/testing/symbolication/node-app/package-lock.json index 045b8b11..285cc8f6 100644 --- a/testing/symbolication/node-app/package-lock.json +++ b/testing/symbolication/node-app/package-lock.json @@ -15,36 +15,13 @@ }, "devDependencies": { "@rollup/plugin-terser": "^0.4.4", - "@tracewayapp/bundler-plugin": "file:../../../../js-client/packages/bundler-plugin", - "@tracewayapp/sourcemap-upload": "file:../../../../js-client/packages/sourcemap-upload", + "@tracewayapp/bundler-plugin": "^1.2.0", + "@tracewayapp/sourcemap-upload": "^1.2.0", "rollup": "^4.0.0", "webpack": "^5.107.2", "webpack-cli": "^7.0.3" } }, - "../../../../js-client/packages/bundler-plugin": { - "name": "@tracewayapp/bundler-plugin", - "version": "1.1.0", - "dev": true, - "dependencies": { - "magic-string": "^0.30.0" - }, - "devDependencies": { - "rollup": "^4.0.0", - "webpack": "^5.90.0" - } - }, - "../../../../js-client/packages/sourcemap-upload": { - "name": "@tracewayapp/sourcemap-upload", - "version": "1.1.0", - "dev": true, - "dependencies": { - "glob": "^11.0.0" - }, - "bin": { - "traceway-sourcemaps": "dist/index.js" - } - }, "node_modules/@discoveryjs/json-ext": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz", @@ -55,6 +32,16 @@ "node": ">=14.17.0" } }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -127,9 +114,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.7.1.tgz", - "integrity": "sha512-OPFBYuXEn1E4ja3Y6eeA7O+ZnLBNcXTV5Cgsn1VaqBZ6hC5FnpZPLBNme1LJY8ZtF4aOujPKFoeWN4ik487KuQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.8.0.tgz", + "integrity": "sha512-/3FIraneMcng67SUJCxvyInk/oxzwsxyadufk0wwfOBLf5wqtAGX4MoQASwSbndBPeARzBryUM9Azr5kHIdWLw==", "license": "Apache-2.0", "engines": { "node": "^18.19.0 || >=20.6.0" @@ -172,6 +159,22 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, "node_modules/@opentelemetry/otlp-exporter-base": { "version": "0.218.0", "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.218.0.tgz", @@ -208,7 +211,7 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/resources": { + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", @@ -224,6 +227,37 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, + "node_modules/@opentelemetry/resources": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.8.0.tgz", + "integrity": "sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.8.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz", + "integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "node_modules/@opentelemetry/sdk-logs": { "version": "0.218.0", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.218.0.tgz", @@ -242,6 +276,22 @@ "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, "node_modules/@opentelemetry/sdk-metrics": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.1.tgz", @@ -258,6 +308,22 @@ "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, "node_modules/@opentelemetry/sdk-trace-base": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", @@ -275,15 +341,31 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-trace-node": { + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/resources": { "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.7.1.tgz", - "integrity": "sha512-pCpQxU68lV+I9s9svqMyVu5iHdDDUnqUpSxqwyCU8A9ejEsSnMPCbearwsUO4yk08ZJzAIUCFuReMdVQvHrdvg==", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/context-async-hooks": "2.7.1", "@opentelemetry/core": "2.7.1", - "@opentelemetry/sdk-trace-base": "2.7.1" + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.8.0.tgz", + "integrity": "sha512-nZt9OGufioAc3AfoLTqA9bsAeaMJAictYDdI2VcNQ+PmT+3rfKjAZDZvgPfd8VPX0O5Bw1hdQF6kDK8VSpZiWg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "2.8.0", + "@opentelemetry/core": "2.8.0", + "@opentelemetry/sdk-trace-base": "2.8.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -292,6 +374,38 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz", + "integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.8.0.tgz", + "integrity": "sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.8.0", + "@opentelemetry/resources": "2.8.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, "node_modules/@opentelemetry/semantic-conventions": { "version": "1.41.1", "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", @@ -675,12 +789,25 @@ ] }, "node_modules/@tracewayapp/bundler-plugin": { - "resolved": "../../../../js-client/packages/bundler-plugin", - "link": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@tracewayapp/bundler-plugin/-/bundler-plugin-1.2.0.tgz", + "integrity": "sha512-K3EjCozcZTHdPaJQIKGSBCdX+echAPJSdcQTBV6w7yxh0lM/OT77zW/59fx+P3yz49E1pfcOIVqFMPD3C2q0cQ==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.0" + } }, "node_modules/@tracewayapp/sourcemap-upload": { - "resolved": "../../../../js-client/packages/sourcemap-upload", - "link": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@tracewayapp/sourcemap-upload/-/sourcemap-upload-1.2.0.tgz", + "integrity": "sha512-nidakU2U7wvvP0CKdjvXlsPE/iPGmLNa3uJ9F7QiiAhlx67AbPQz3xOC7JtYQQAyK2Hj4VqI2RsGH9W87zEGuw==", + "dev": true, + "dependencies": { + "glob": "^11.0.0" + }, + "bin": { + "traceway-sourcemaps": "dist/index.js" + } }, "node_modules/@types/estree": { "version": "1.0.9", @@ -882,9 +1009,9 @@ "license": "Apache-2.0" }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "dev": true, "license": "MIT", "bin": { @@ -955,10 +1082,20 @@ "ajv": "^8.8.2" } }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/baseline-browser-mapping": { - "version": "2.10.35", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.35.tgz", - "integrity": "sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==", + "version": "2.10.36", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.36.tgz", + "integrity": "sha512-lVq/Df7LXlO79MVaaUHztSwWiG9oXoWHlgvNS51v8Dpd4+G4/VIy6qYePTw31nAVls33nUtnfezYeLkYAak9dg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -968,6 +1105,19 @@ "node": ">=6.0.0" } }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/browserslist": { "version": "4.28.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", @@ -1010,9 +1160,9 @@ "license": "MIT" }, "node_modules/caniuse-lite": { - "version": "1.0.30001797", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz", - "integrity": "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==", + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", "dev": true, "funding": [ { @@ -1078,16 +1228,16 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.371", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.371.tgz", - "integrity": "sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w==", + "version": "1.5.372", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.372.tgz", + "integrity": "sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==", "dev": true, "license": "ISC" }, "node_modules/enhanced-resolve": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz", - "integrity": "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==", + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.0.tgz", + "integrity": "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1243,6 +1393,23 @@ "flat": "cli.js" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1268,6 +1435,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", @@ -1381,6 +1573,22 @@ "node": ">=0.10.0" } }, + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -1440,6 +1648,26 @@ "node": ">=8" } }, + "node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -1457,6 +1685,32 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -1513,6 +1767,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1540,6 +1801,23 @@ "dev": true, "license": "MIT" }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1770,6 +2048,19 @@ "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/smob": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.2.tgz", @@ -1963,13 +2254,12 @@ } }, "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", + "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", "dev": true, "license": "MIT", "dependencies": { - "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" }, "engines": { diff --git a/testing/symbolication/node-app/package.json b/testing/symbolication/node-app/package.json index 42bf4e9c..03b6321a 100644 --- a/testing/symbolication/node-app/package.json +++ b/testing/symbolication/node-app/package.json @@ -12,8 +12,8 @@ }, "devDependencies": { "@rollup/plugin-terser": "^0.4.4", - "@tracewayapp/bundler-plugin": "file:../../../../js-client/packages/bundler-plugin", - "@tracewayapp/sourcemap-upload": "file:../../../../js-client/packages/sourcemap-upload", + "@tracewayapp/bundler-plugin": "^1.2.0", + "@tracewayapp/sourcemap-upload": "^1.2.0", "rollup": "^4.0.0", "webpack": "^5.107.2", "webpack-cli": "^7.0.3"