Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
80cc878
Improve performance
owjs3901 Jun 10, 2026
f70607f
Fix version issue
owjs3901 Jun 10, 2026
102ea67
Fix java memory issue
owjs3901 Jun 10, 2026
13a6c7a
Fix data copy issue
owjs3901 Jun 10, 2026
13b69df
Rm dupl copy
owjs3901 Jun 10, 2026
34b22ca
Fix overflow issue
owjs3901 Jun 10, 2026
0b89530
Improve streaming
owjs3901 Jun 10, 2026
a1327b0
Improve macro
owjs3901 Jun 10, 2026
6c07dc2
Impl thread
owjs3901 Jun 10, 2026
0f7ff73
Add testcase
owjs3901 Jun 10, 2026
1047907
Split code
owjs3901 Jun 11, 2026
2bd284c
Optimize gen openapi
owjs3901 Jun 11, 2026
8126a49
Improve smart config
owjs3901 Jun 11, 2026
015a444
Improve jni
owjs3901 Jun 11, 2026
d1d4768
Add e2e test
owjs3901 Jun 12, 2026
3c8f677
Impl macro cache
owjs3901 Jun 12, 2026
36a183b
Add plugin
owjs3901 Jun 12, 2026
2067ac1
Impl smart mode
owjs3901 Jun 12, 2026
ca85a44
Rewrite
owjs3901 Jun 12, 2026
74d145a
Downgrade version
owjs3901 Jun 12, 2026
909fa07
Compress bytes
owjs3901 Jun 12, 2026
c25e432
Impl streaming
owjs3901 Jun 13, 2026
3fc91d4
Add memory tester
owjs3901 Jun 13, 2026
04182ce
Add memory tester
owjs3901 Jun 13, 2026
5e5d5e6
Fix block on
owjs3901 Jun 13, 2026
a188acb
Head writer
owjs3901 Jun 13, 2026
26386bd
Fix header callback
owjs3901 Jun 13, 2026
83d2d9a
Add fuzz test
owjs3901 Jun 13, 2026
de61faa
Refactor
owjs3901 Jun 15, 2026
0c33fc2
Impl error
owjs3901 Jun 15, 2026
c67b15a
Change default feature
owjs3901 Jun 15, 2026
338e2ad
Fix async issue
owjs3901 Jun 15, 2026
98242a5
Optimize WireHeaderReader
owjs3901 Jun 15, 2026
132d676
Add bench
owjs3901 Jun 15, 2026
7a84787
Refactor header parser
owjs3901 Jun 16, 2026
91d6943
Increase memory
owjs3901 Jun 16, 2026
c66cf52
Add testcase
owjs3901 Jun 16, 2026
40b8fac
Optimize
owjs3901 Jun 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changepacks/changepack_log_release-0-2-0-bridge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"changes":{"Cargo.toml":"Minor","libs/vespera-bridge/build.gradle.kts":"Minor","libs/vespera-bridge-gradle-plugin/build.gradle.kts":"Minor"},"note":"0.2.0 / 0.3.0 release — BREAKING (0.x minor): DecodedResponse.body() returns read-only ByteBuffer (bodyBytes() copies on demand); SmartDispatchModeResolver is the autoconfigured default (DIRECT ~2.2µs / SYNC ~3.2µs for small requests, opt out via vespera.bridge.dispatch-mode=bidirectional-streaming); Gradle plugin now also publishes to the Plugin Portal. Perf: JMethodID+GlobalRef caching for streaming closures, daemon-attached dispatchAsync completion, lazy bidirectional request-pull (spawn on first body poll), JsonGenerator wire-header encoding, zero-copy get_byte_array_region input conversion. Rust: Validated 422 envelope via derive(Serialize) (byte-identical, snapshot-locked), per-invocation fs::metadata epoch caching in vespera_macro, collector clone elimination. See libs/vespera-bridge/docs/jni-before-after-2026-06-11.md for measured numbers.","date":"2026-06-12T13:00:00.000Z"}
3 changes: 2 additions & 1 deletion .changepacks/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
]
},
"publish": {
"java": "./gradlew publishToMavenCentral --stacktrace"
"java": "./gradlew publishToMavenCentral --stacktrace",
"libs/vespera-bridge-gradle-plugin/build.gradle.kts": "./gradlew publishToMavenCentral publishPlugins --stacktrace"
}
}
80 changes: 78 additions & 2 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ jobs:
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Test Deploy
run: cargo publish --dry-run
- name: Doctest
# tarpaulin's --all-targets / default run never compiles doc
# tests, which let a never-passing doctest land unnoticed —
# run them explicitly before the (slow) coverage step.
run: cargo test --workspace --doc
- name: Test
run: |
# rust coverage issue
Expand All @@ -53,7 +58,7 @@ jobs:
cargo fmt
cargo tarpaulin --out Lcov Stdout --engine llvm
- name: Upload to codecov.io
uses: codecov/codecov-action@v6
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
Expand All @@ -64,7 +69,9 @@ jobs:
changepacks:
name: changepacks
runs-on: ubuntu-latest
needs: test
# jni-e2e gates publishing: a release must never ship with a broken
# JNI dispatch path on any supported OS.
needs: [test, jni-e2e]
permissions:
# create pull request comments
pull-requests: write
Expand Down Expand Up @@ -101,6 +108,75 @@ jobs:
# GPG signing (in-memory key, no keyring file)
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_SIGNING_PASSWORD }}
# Gradle Plugin Portal credentials (read natively by
# com.gradle.plugin-publish for the `publishPlugins` task)
GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}
outputs:
changepacks: ${{ steps.changepacks.outputs.changepacks }}
release_assets_urls: ${{ steps.changepacks.outputs.release_assets_urls }}

# JNI end-to-end tests — builds the rust-jni-demo cdylib, publishes the
# vespera-bridge JAR to mavenLocal (so the demo-app Gradle plugin can
# resolve kr.devfive:vespera-bridge:0.1.1), then runs the full
# :demo-app:test suite (StreamingClosureStressTest + JNI dispatch tests)
# across all three target host OSes. This is the project's only Java/JNI
# coverage gate — until now the workflow ran zero JNI tests.
#
# Runs unconditionally on every push/PR (matching the existing CI job's
# style — no per-job paths-filter). The whole workflow already inherits
# the workflow-level `paths-ignore` for docs-only changes.
jni-e2e:
name: JNI E2E (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 25
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Build rust-jni-demo cdylib (release)
# The vespera-bridge Gradle plugin's bundleNativeLib task copies
# this cdylib from target/release into demo-app's resources, so it
# must exist before `:demo-app:test` (processResources) runs.
run: cargo build -p rust-jni-demo --release
- name: Make gradlew executable (unix)
if: runner.os != 'Windows'
run: |
chmod +x libs/vespera-bridge/gradlew
chmod +x libs/vespera-bridge-gradle-plugin/gradlew
chmod +x examples/rust-jni-demo/java/gradlew
- name: Publish vespera-bridge Gradle plugin to mavenLocal
# demo-app's plugins block resolves kr.devfive.vespera-bridge from
# mavenLocal (settings.gradle.kts pluginManagement) — the plugin is
# not on the Gradle Plugin Portal.
shell: bash
working-directory: libs/vespera-bridge-gradle-plugin
run: ./gradlew publishToMavenLocal --console=plain --no-daemon
- name: Publish vespera-bridge to mavenLocal
# demo-app resolves kr.devfive:vespera-bridge:0.1.1 from mavenLocal
# (see examples/rust-jni-demo/java/demo-app/build.gradle.kts —
# bridgeVersion.set("0.1.1")).
shell: bash
working-directory: libs/vespera-bridge
run: ./gradlew publishToMavenLocal --console=plain --no-daemon
- name: Run demo-app JNI E2E tests
# Includes StreamingClosureStressTest (1000 × 1 MiB SHA256
# bidirectional round-trip). Bench knobs are NOT propagated —
# gated bench tests stay skipped in CI.
shell: bash
working-directory: examples/rust-jni-demo/java
run: ./gradlew :demo-app:test --console=plain --no-daemon
- name: Upload demo-app test results
if: always()
uses: actions/upload-artifact@v7
with:
name: jni-e2e-${{ matrix.os }}-test-results
path: examples/rust-jni-demo/java/demo-app/build/test-results/test/*.xml
106 changes: 106 additions & 0 deletions .github/workflows/bench.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: Bench

# Criterion regression gate for the in-process dispatch hot path.
#
# - push to main: runs the gated bench groups and saves the results as
# the `main` criterion baseline in the actions cache.
# - pull_request: restores the latest main baseline and compares; the
# job FAILS when any bench regresses by more than 10% mean change
# AND the 95% confidence interval lower bound exceeds +5% (the
# double condition filters shared-runner noise).
#
# Gated groups are the stable per-request paths (wire_path,
# headers_path, resolve_path, dispatch_path). The streaming and
# contended groups are noisier (spawn_blocking / scheduler timing) and
# the router_path setup micro-bench is low-signal, so those are
# validated locally instead — see PERF_REPORT.md.

on:
push:
branches:
- main
paths:
- 'crates/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/bench.yml'
pull_request:
paths:
- 'crates/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/bench.yml'

concurrency:
group: bench-${{ github.ref }}
cancel-in-progress: true

env:
BENCH_FILTER: 'wire_path|headers_path|resolve_path|dispatch_path'

jobs:
bench:
name: Criterion regression gate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: actions-rust-lang/setup-rust-toolchain@v1

- name: Restore criterion baseline (latest main)
id: restore-baseline
uses: actions/cache/restore@v5
with:
path: target/criterion
key: bench-baseline-${{ runner.os }}-${{ github.sha }}
restore-keys: |
bench-baseline-${{ runner.os }}-

- name: Run benches and save main baseline
if: github.event_name == 'push'
run: |
cargo bench -p vespera_inprocess --bench dispatch -- \
--save-baseline main "${BENCH_FILTER}"

- name: Save criterion baseline cache
if: github.event_name == 'push'
uses: actions/cache/save@v5
with:
path: target/criterion
key: bench-baseline-${{ runner.os }}-${{ github.sha }}

- name: Compare against main baseline
if: github.event_name == 'pull_request'
run: |
if [ ! -d target/criterion ] || ! find target/criterion -maxdepth 4 -type d -name main | grep -q .; then
echo "::notice::No main baseline in cache yet — running benches without a gate."
cargo bench -p vespera_inprocess --bench dispatch -- "${BENCH_FILTER}"
exit 0
fi
cargo bench -p vespera_inprocess --bench dispatch -- \
--baseline main "${BENCH_FILTER}"

- name: Enforce regression gate
if: github.event_name == 'pull_request'
run: |
shopt -s nullglob
fail=0
found=0
while IFS= read -r f; do
found=1
mean=$(jq -r '.mean.point_estimate' "$f")
lower=$(jq -r '.mean.confidence_interval.lower_bound' "$f")
bench=$(dirname "$(dirname "$f")")
bench=${bench#target/criterion/}
printf '%s: mean %+.2f%% (CI lower %+.2f%%)\n' \
"$bench" "$(awk -v v="$mean" 'BEGIN{print v*100}')" \
"$(awk -v v="$lower" 'BEGIN{print v*100}')"
if awk -v m="$mean" -v l="$lower" 'BEGIN{exit !(m > 0.10 && l > 0.05)}'; then
echo "::error::Performance regression: ${bench} mean change exceeds +10% with CI lower bound > +5%"
fail=1
fi
done < <(find target/criterion -path '*/change/estimates.json')
if [ "$found" -eq 0 ]; then
echo "::notice::No change estimates found (first run against this baseline?) — nothing to gate."
fi
exit $fail
98 changes: 98 additions & 0 deletions .github/workflows/jni-bench.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: JNI Bench (nightly)

# Informational JNI / perf benchmark run — NOT a regression gate.
#
# Most of the in-process & JNI performance work lives on the Java side
# (dispatch modes, daemon-env attach caching, direct buffers, mimalloc),
# which the criterion gate in bench.yml does NOT cover. Shared GitHub
# runners are far too noisy to threshold absolute ns/op, so this job runs
# the gated *BenchTest suite nightly purely to RECORD the numbers
# (printed to the job summary + uploaded as artifacts) so a human can spot
# drift over time. It never fails on a slow number — see PERF_REPORT.md
# for the locally-measured baselines.

on:
schedule:
# 06:00 UTC daily (~15:00 KST)
- cron: '0 6 * * *'
workflow_dispatch:

concurrency:
group: jni-bench-${{ github.ref }}
cancel-in-progress: true

jobs:
jni-bench:
name: JNI Bench (ubuntu-latest)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Build rust-jni-demo cdylib (release)
# mimalloc is opt-in; the bench numbers reflect the default
# allocator unless the cdylib is built with --features mimalloc.
run: cargo build -p rust-jni-demo --release
- name: Make gradlew executable
run: |
chmod +x libs/vespera-bridge/gradlew
chmod +x libs/vespera-bridge-gradle-plugin/gradlew
chmod +x examples/rust-jni-demo/java/gradlew
- name: Publish vespera-bridge Gradle plugin to mavenLocal
working-directory: libs/vespera-bridge-gradle-plugin
run: ./gradlew publishToMavenLocal --console=plain --no-daemon
- name: Publish vespera-bridge to mavenLocal
working-directory: libs/vespera-bridge
run: ./gradlew publishToMavenLocal --console=plain --no-daemon
- name: Run JNI benchmarks (informational — never gates)
# The bench tests are gated behind -Dvespera.bench=true (the
# demo-app test task forwards that system property into the forked
# test JVM). This step is allowed to fail without failing the job:
# a flaky bench number must never break the nightly run.
continue-on-error: true
working-directory: examples/rust-jni-demo/java
run: |
./gradlew :demo-app:test -Dvespera.bench=true \
--tests 'kr.go.demo.*BenchTest' \
--console=plain --no-daemon
- name: Summarise bench results
if: always()
run: |
{
echo '## JNI bench results'
echo ''
echo '> **Watch the ratios, not the absolute ns/op.** The latency bench'
echo '> measures every mode *interleaved* (round-robin blocks, median of'
echo '> 100), so the cross-mode ratios (`async_vs_sync`, `direct_vs_sync`,'
echo '> `resp_only_vs_bidi`) are the noise-robust regression signal — they'
echo '> stay stable run-to-run even when absolute numbers drift ±10% on a'
echo '> shared runner. A ratio moving materially = a real regression.'
echo ''
echo '### Noise-robust ratios'
echo '```'
grep -hoE 'VESPERA_BENCH summary[^<]*' \
examples/rust-jni-demo/java/demo-app/build/test-results/test/*.xml \
| sort -u || echo '(no ratio summary captured)'
echo '```'
echo '### All bench lines'
echo '```'
# Bench lines (VESPERA_BENCH / ALLOC / CONC / JFR_LOAD) are
# captured in the JUnit XML <system-out>; pull them out for a
# quick at-a-glance view in the run summary.
grep -hoE 'VESPERA_(BENCH|ALLOC|CONC|JFR_LOAD)[^<]*' \
examples/rust-jni-demo/java/demo-app/build/test-results/test/*.xml \
| sort -u || echo '(no bench lines captured)'
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload bench results
if: always()
uses: actions/upload-artifact@v7
with:
name: jni-bench-results
path: examples/rust-jni-demo/java/demo-app/build/test-results/test/*.xml
if-no-files-found: warn
Loading
Loading