Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
# and clobber each other's caches on `cargo clean` / `mvn clean`.
[build]
target-dir = "target-rust"

[target.x86_64-pc-windows-msvc]
# The Python repo's fix carried over (plan 03 §6): toolchain-bundled linker.
linker = "rust-lld"
22 changes: 21 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,18 @@ jobs:
cache: maven
- run: just review

# Plan 04 §4: the full Phase-1 platform matrix — Linux x86_64/aarch64,
# macOS aarch64, Windows x86_64 — each running the complete suite natively.
# macOS x86_64: GitHub retired the Intel macOS runners (macos-13 never
# provisions, 2026-08); per plan 04 §4's own alternative, darwin-amd64 is
# proven by cross-compiling on the arm64 runner.
verify:
name: verify (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
Expand All @@ -67,3 +72,18 @@ jobs:
java-version: 17
cache: maven
- run: just verify

verify-darwin-amd64-cross:
name: verify (darwin-amd64 cross-build)
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
targets: x86_64-apple-darwin
- uses: Swatinem/rust-cache@v2
with:
key: x86_64-apple-darwin
- run: cargo build --target x86_64-apple-darwin
- run: cargo clippy --all-targets --target x86_64-apple-darwin -- -D warnings
47 changes: 47 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: nightly

# Plan 03 §6: the @Soak suite runs nightly (not per-PR); the JMH suite runs
# nightly and on release branches.

on:
schedule:
- cron: "0 3 * * *"
workflow_dispatch:

jobs:
soak:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: just
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
cache: maven
- run: just build
- run: mvn test -q -Dgroups=io.laminardb.Soak

benchmarks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: just
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
cache: maven
- run: just bench
166 changes: 166 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
name: release

# Plan 04 §5. Trigger: tag v*. The publish job runs in the protected
# `maven-central` environment — one-time maintainer setup (Central Portal
# namespace ownership for io.laminardb, GPG key, environment secrets
# CENTRAL_TOKEN / GPG_* / CENTAL_PORTAL) is a recorded blocker: see
# docs/plans/04-release-engineering.md §5.

on:
push:
tags: ["v*"]

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Version parity and core-pin gate
run: |
TAG="${GITHUB_REF#refs/tags/}"
CARGO_V=$(grep -m1 '^version' Cargo.toml | sed 's/version = "\(.*\)"/\1/')
POM_V=$(mvn -q help:evaluate -Dexpression=project.version -DforceStdout)
test "$TAG" = "v$CARGO_V" || { echo "tag $TAG != Cargo $CARGO_V"; exit 1; }
test "$TAG" = "v$POM_V" || { echo "tag $TAG != pom $POM_V"; exit 1; }
grep -q 'laminar-db = { git = "https://github.com/laminardb/laminardb", tag = "v' Cargo.toml \
|| { echo "core pin must be a git tag"; exit 1; }
grep -q "## \[$CARGO_V\]" CHANGELOG.md || { echo "no CHANGELOG entry"; exit 1; }
- name: Latest phase review shows zero open findings
run: |
LATEST=$(ls docs/reviews/ | sort | tail -1)
grep -q "Open REQUEST CHANGES findings: \*\*0\*\*" "docs/reviews/$LATEST" \
|| { echo "$LATEST has open findings"; exit 1; }

build-native:
needs: validate
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- { target: x86_64-unknown-linux-gnu, runner: ubuntu-latest, platform: linux-amd64 }
- { target: aarch64-unknown-linux-gnu, runner: ubuntu-24.04-arm, platform: linux-aarch64 }
- { target: aarch64-apple-darwin, runner: macos-latest, platform: macos-aarch64 }
- { target: x86_64-apple-darwin, runner: macos-13, platform: macos-amd64 }
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.target }}
- run: cargo build --release --target ${{ matrix.target }}
- name: Stage native
run: |
mkdir -p natives/${{ matrix.platform }}
cp target-rust/${{ matrix.target }}/release/liblaminar_java.* natives/${{ matrix.platform }}/
- uses: actions/upload-artifact@v4
with:
name: native-${{ matrix.platform }}
path: natives/

assemble-and-test:
needs: build-native
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
java: [17, 21, 25]
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: downloaded-natives
pattern: native-*
merge-multiple: true
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: ${{ matrix.java }}
cache: maven
- name: Package fat jar with bundled natives
run: mvn -Dnatives.dir=downloaded-natives -DskipTests package
# Re-verify with bundling still active so the jar keeps /natives/ and
# the suite exercises NativeLoader's bundled-extraction path.
- name: Suite against the packaged jar (bundled-natives load path)
run: mvn -Dnatives.dir=downloaded-natives verify
- name: Bare-project quickstart
run: scripts/bare-quickstart.sh "$(mvn -q help:evaluate -Dexpression=project.version -DforceStdout)" downloaded-natives

publish:
needs: assemble-and-test
runs-on: ubuntu-latest
environment: maven-central
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: downloaded-natives
pattern: native-*
merge-multiple: true
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
cache: maven
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
- name: Publish to Maven Central (Central Portal)
if: ${{ env.CENTRAL_TOKEN != '' }}
env:
CENTRAL_TOKEN: ${{ secrets.CENTRAL_TOKEN }}
run: mvn -Dnatives.dir=downloaded-natives -Pcentral deploy
- name: Record missing-credentials blocker
if: ${{ env.CENTRAL_TOKEN == '' }}
env:
CENTRAL_TOKEN: ${{ secrets.CENTRAL_TOKEN }}
run: |
echo "::error::Central Portal credentials absent — maintainer one-time setup (plan 04 §5) is a recorded blocker."
exit 1

verify-publish:
needs: publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Poll Central for resolvability
run: |
V=$(mvn -q help:evaluate -Dexpression=project.version -DforceStdout)
for i in $(seq 1 30); do
CODE=$(curl -s -o /dev/null -w '%{http_code}' \
"https://repo1.maven.org/maven2/io/laminardb/laminardb/$V/laminardb-$V.pom")
[ "$CODE" = "200" ] && { echo "resolved on Central"; exit 0; }
sleep 60
done
echo "artifact not resolvable on Central after 30 min"
exit 1

github-release:
needs: verify-publish
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: downloaded-natives
pattern: native-*
merge-multiple: true
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
cache: maven
- run: mvn -Dnatives.dir=downloaded-natives -DskipTests package
- name: Create GitHub release with jar and checksums
run: |
V=$(mvn -q help:evaluate -Dexpression=project.version -DforceStdout)
shasum -a 256 target/laminardb-$V.jar > target/laminardb-$V.jar.sha256
gh release create "v$V" --title "laminardb-java $V" \
--notes-file <(awk "/## \[$V\]/{f=1;next} /^## /{f=0} f" CHANGELOG.md) \
target/laminardb-$V.jar target/laminardb-$V.jar.sha256
env:
GH_TOKEN: ${{ github.token }}
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Changelog

## [0.30.0-alpha]
- Full embedded API: open (default/path/config), execute with `ExecuteResult`,
materialized `query`, streaming `stream`, map/Arrow `insert`, `Writer` with
event-time watermarks, start/checkpoint/shutdown, schema and catalog
helpers.
- Complete exception hierarchy mapped from the core's numeric codes, with a
Rust-side coverage test pinning the mapping to the core's `codes` module.
- Arrow C Data Interface data plane: zero-copy Rust→Java batches, verified
import-copy Java→Rust (arrow-java releases exported buffers via JNI upcall;
copying keeps releases on the calling thread).
- Friendly-row conversion (`List<Map>` ↔ Arrow) with UTF-8 and timestamp
handling; lazy `ArrowBatch` imports over the process-wide allocator.
- Review gates per plan 06: SpotBugs and the JaCoCo zero-coverage rule join
fmt/clippy/machete/allows-grep/Spotless/Checkstyle in `just review`.
- Bundled-natives `NativeLoader` (plan 04 §2) with SHA-256-verified
extraction; release workflow for the four Phase-1 platforms.
- Documentation: quickstart, stateful joins walkthrough, threading, errors,
build, benchmarks.
9 changes: 9 additions & 0 deletions CORE_PIN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Core pin registry (D4)

Binding version tracks the pinned core git tag; binding-only patch bumps are
allowed pre-1.0 and recorded here.

| Binding version | Pinned core tag | Notes |
|---|---|---|
| 0.30.0-alpha | `v0.30.0` | Phase 1 embedded MVP. |
| 0.30.0 | `v0.30.0` | Phase 0 scaffold (never released). |
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "laminar-java"
version = "0.30.0" # tracks the pinned core tag; see plan 04 §3 before first release
version = "0.30.0-alpha" # tracks the pinned core tag; -alpha is the Phase 1 channel (plan 04 §3)
edition = "2021" # matches the core workspace edition at the pin
rust-version = "1.95" # core workspace floor at the pin
license = "Apache-2.0"
Expand All @@ -12,6 +12,8 @@ name = "laminar_java" # liblaminar_java.so / liblaminar_java.dylib / laminar_jav
[dependencies]
jni = "0.22"
laminar-db = { git = "https://github.com/laminardb/laminardb", tag = "v0.30.0", default-features = false, features = ["api"] }
# Same tag as laminar-db: names StreamCheckpointConfig for the config builder.
laminar-core = { git = "https://github.com/laminardb/laminardb", tag = "v0.30.0" }
arrow = { version = "=58.4.0", default-features = false, features = ["ffi"] } # match the core's workspace pin exactly
tokio = { version = "1", features = ["rt-multi-thread", "time"] }
parking_lot = "0.12"
Expand Down
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,41 @@
# laminardb-java — implementation plan series

Status: **Phase 0 implemented (2026-08-29); Phases 1–3 planned** · Owner: LaminarDB team
Status: **Phases 0–2 implemented (2026-08-30); Phase 3 future** · Owner: LaminarDB team

Java bindings for the Rust [LaminarDB](https://github.com/laminardb/laminardb) streaming
database. Phase 0 (repo scaffold, build wiring, CI) is implemented: a Rust JNI cdylib
over the core's `api` feature (pinned to git tag `v0.30.0`), a minimal `io.laminardb`
API, one-command build/test/verify/review via `just`, and a two-OS CI matrix.

## Quickstart

```java
try (LaminarConnection conn = LaminarDB.open()) {
conn.execute("CREATE TABLE sensors (id BIGINT PRIMARY KEY, reading DOUBLE)");
conn.execute("INSERT INTO sensors VALUES (1, 20.5), (2, 21.0)");
try (QueryResult result = conn.query("SELECT * FROM sensors")) {
result.toMaps(); // [{id=1, reading=20.5}, {id=2, reading=21.0}]
}
}
```

Streaming sources, writers with event-time watermarks, and the stateful-join
walkthrough: [docs/stateful-and-joins.md](docs/stateful-and-joins.md). Errors:
[docs/errors.md](docs/errors.md). Threading:
[docs/threading.md](docs/threading.md).

Subscriptions (Phase 2): framed poll access to named streams with checkpoint
barriers, push delivery to a `SubscriptionListener` on a dedicated worker
thread, and async adapters (`queryAsync`, bounded `streamBatches`).

JDK 17+ requires `--add-opens java.base/java.nio=ALL-UNNAMED` (arrow-java).

## Building and testing

Requires Rust stable (rustfmt + clippy), JDK 17+, Maven, `just`, and `cargo-machete`
(`cargo install cargo-machete`). Then: `just verify` (correctness gate) and `just
review` (review gate). See `AGENTS.md` for the full operating context.
review` (review gate). See [docs/build.md](docs/build.md) and `AGENTS.md` for the
full operating context.

## Plan index

Expand Down
Loading
Loading