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
17 changes: 15 additions & 2 deletions .github/workflows/bench.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
---
# Historical benchmark tracking. INFORMATIONAL ONLY.
#
# This lane publishes the enqueue microbenchmark trend to GitHub Pages. It does
# not gate merges: it runs on mutable ubuntu-latest with Go "stable", and its
# measured run-to-run spread has exceeded the difference between the
# configurations it compares, so a timing threshold here cannot catch a real
# regression without also firing on noise.
#
# Blocking performance guards live in performance.yml (race detector and exact
# allocation counts). Predeclared thresholds are in docs/improvements/thresholds.md.
name: Go-Benchmarks
on:
push:
Expand All @@ -11,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v6
with:
go-version: "stable"
- name: Run benchmark
Expand All @@ -24,7 +34,10 @@ jobs:
output-file-path: build/output.txt
github-token: ${{ secrets.GITHUB_TOKEN }}
auto-push: true
# Kept high and non-blocking on purpose: this is a trend record, not a
# gate. Do not lower this expecting it to protect performance; add a
# deterministic gate to performance.yml instead.
alert-threshold: "200%"
comment-on-alert: true
fail-on-alert: true
fail-on-alert: false
alert-comment-cc-users: "@heynemann"
151 changes: 65 additions & 86 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -1,113 +1,92 @@
---
# Build, unit tests, coverage, vet, and formatting.
#
# This workflow intentionally uses only first-party actions/* steps and plain Go
# commands. The prior Go workflow has been failing at startup on main since
# 2026-04-22, before any job is created. Its YAML validates and every referenced
# action resolves, pointing at a repository or organisation Actions policy issue
# around its third-party actions (super-linter, go-test-report, go-coverage-action,
# codeclimate-action), not a code failure. Replacing it in place restores the build,
# unit-test, coverage, lint and formatting checks pull requests are meant to have.
#
# Do not filter pull_request by base branch. Stacked PRs target the layer below
# rather than main; a `branches: [main]` filter skips every layer above the bottom.
name: Go
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
permissions: read-all
workflow_dispatch:
permissions:
contents: read
jobs:
build:
permissions:
contents: read
packages: read
# To report GitHub Actions status checks
statuses: write
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
- name: Build
run: go build -v ./...
with:
persist-credentials: false
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
- run: go build -v ./...
test:
permissions:
contents: read
packages: read
# To report GitHub Actions status checks
statuses: write
name: Unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
- uses: brian-pickens/go-test-report@v1
with:
packages: "./..."
summary: "true"
coverage:
permissions:
contents: write
checks: write
issues: write
pull-requests: write
# To report GitHub Actions status checks
statuses: write
persist-credentials: false
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Run tests
run: go test -v ./...
vet:
name: Vet and formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
- name: Set up Node.js (for coverage)
uses: actions/setup-node@master
with:
node-version: "12"
- name: setup tsc
run: npm install -g typescript
- name: Gather dependencies
run: go mod download
- uses: gwatts/go-coverage-action@v2
id: coverage
persist-credentials: false
- uses: actions/setup-go@v6
with:
coverage-threshold: 80
cover-pkg: ./...
ignore-pattern: |
\.pb\.go$
\_string\.go$
\main\.go$
\internal\/test\/.*$
- name: Generate coverage tree map
go-version-file: go.mod
- name: go vet
run: go vet ./...
- name: gofmt
run: |
go run github.com/nikolaydubina/go-cover-treemap@latest \
-coverprofile ${{ steps.coverage.outputs.gocov-agg-pathname }} \
-only-folders=true >/tmp/treemap.svg
- uses: paambaati/codeclimate-action@v6.0.0
env:
CC_TEST_REPORTER_ID: ${{ secrets.CODECLIMATE_TOKEN }}
with:
coverageCommand: make coverage-report-ci
prefix: github.com/NSXBet/batcher
coverageLocations: |
${{github.workspace}}/coverage.txt:gocov
lint:
permissions:
contents: read
packages: read
# To report GitHub Actions status checks
statuses: write
unformatted="$(gofmt -l pkg internal test)"
if [ -n "$unformatted" ]; then
echo "These files are not gofmt-formatted:"
echo "$unformatted"
exit 1
fi
coverage:
name: Coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Super-linter
uses: nsx-actions/super-linter_super-linter@v6.6.0
env:
# To report GitHub Actions status checks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_GO: false
VALIDATE_JSCPD: false
go-action-detection:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- name: "Checkout Repository"
uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: ">=1.22.0"
- name: Run snapshot action
uses: actions/go-dependency-submission@v2
persist-credentials: false
- uses: actions/setup-go@v6
with:
go-mod-path: go.mod
go-version-file: go.mod
- name: Measure coverage
# The scenario harness under test/ is measurement infrastructure, not
# shipped code, so it is excluded from the enforced number for the same
# reason internal/test is.
run: |
go test -coverprofile=coverage.out ./...
grep -v '/test/' coverage.out > coverage-filtered.out
go tool cover -func=coverage-filtered.out | tail -1
- name: Enforce threshold
run: |
threshold=80
total="$(go tool cover -func=coverage-filtered.out \
| tail -1 | awk '{print $3}' | tr -d '%')"
echo "shipped-code coverage: ${total}% (threshold ${threshold}%)"
awk -v t="$threshold" -v c="$total" \
'BEGIN { exit (c + 0 >= t + 0) ? 0 : 1 }' \
|| { echo "coverage ${total}% is below ${threshold}%"; exit 1; }
100 changes: 100 additions & 0 deletions .github/workflows/performance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
name: Performance
on:
pull_request:
branches: ["main"]
push:
branches: ["main"]
schedule:
# Weekly full matrix. Latency is reported here, never gated.
- cron: "0 3 * * 1"
workflow_dispatch:
permissions:
contents: read
jobs:
# Blocking lane. Gates only on signals that are stable on shared runners:
# correctness under the race detector and exact allocation counts.
# Latency percentiles are deliberately NOT gated here, because measured spread
# on GitHub-hosted runners has exceeded the difference between configurations.
guards:
name: Race and allocation guards
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Comment thread
coderabbitai[bot] marked this conversation as resolved.
with:
persist-credentials: false
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Race detector
run: go test -race ./...
- name: Allocation gates
# Thresholds are predeclared in docs/improvements/thresholds.md.
#
# A -run substring that matches nothing exits 0, which would silently drop
# this gate if a test were renamed. Assert the expected tests actually ran.
run: |
go test -run 'Alloc' -count=1 -v ./... 2>&1 | tee /tmp/alloc-gate.txt
for want in TestAddAllocatesNothingPerCall \
TestHarnessRecorderDoesNotAllocatePerItem; do
if ! grep -q -- "--- PASS: $want" /tmp/alloc-gate.txt; then
echo "allocation gate did not run $want -- was it renamed?"
exit 1
fi
done
- name: Enqueue microbenchmarks compile and run
run: |
go test -run='^$' -bench='BenchmarkBatcherEnqueue' \
-benchtime=100x -benchmem ./pkg/batcher
# Reporting lane. Produces the evidence used to choose a default batch window.
# Informational by design: it must never fail a pull request on timing.
matrix:
name: Scenario matrix (informational)
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Record environment
run: |
{
echo "date: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "go: $(go version)"
echo "runner: ${{ runner.os }} ${{ runner.arch }}"
echo "nproc: $(nproc)"
echo "gomaxprocs: $(go env GOMAXPROCS)"
echo "cpu: $(awk -F': ' '/^model name/{print $2; exit}' /proc/cpuinfo)"
echo "commit: ${{ github.sha }}"
} > environment.txt
Comment thread
coderabbitai[bot] marked this conversation as resolved.
cat environment.txt
- name: Scenario matrix
env:
SCENARIO_MATRIX: "1"
run: |
go test -run TestScenarioMatrix -timeout 40m -v ./test/scenario/ \
| tee scenario-matrix.txt
# Produces benchstat input, but does not compare: no baseline recorded on this
# runner exists yet, and comparing against a developer machine's numbers would
# be meaningless. Trend comparison is deferred until a scheduled run has
# stored an ubuntu-latest/amd64 baseline. See docs/improvements/thresholds.md.
- name: Enqueue benchmarks with benchstat input
run: |
go test -run='^$' -bench='BenchmarkBatcherEnqueue' -benchmem \
-benchtime=2s -count=10 -cpu=1,2,4 ./pkg/batcher \
| tee enqueue-bench.txt
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- name: Upload performance artifacts
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: performance-report
path: |
environment.txt
scenario-matrix.txt
enqueue-bench.txt
retention-days: 90
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ dist
.mcp.json
.claude-flow
.swarm
.pi
31 changes: 30 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
.PHONY: all clean
# Every target here is command-only: none produces a file named after itself, so
# all of them must be phony or Make will skip work whenever a same-named path exists.
.PHONY: all clean ensure-build test unit bench bench-announce bench-run \
guards guards-race guards-allocs bench-enqueue bench-matrix \
coverage coverage-run coverage-report coverage-report-ci lint

all: clean test coverage lint

Expand All @@ -23,6 +27,31 @@ bench-announce:
bench-run:
@go test -bench=. ./...

# Blocking performance guards: stable on shared runners, unlike latency.
# Thresholds are predeclared in docs/improvements/thresholds.md.
guards: guards-race guards-allocs
Comment thread
heynemann marked this conversation as resolved.

guards-race:
@echo "Running race detector..."
@go test -race ./...

guards-allocs:
@echo "Checking allocation gates..."
@go test -run 'Alloc' -count=1 -v ./... > /tmp/alloc-gate.txt 2>&1 || { cat /tmp/alloc-gate.txt; exit 1; }
@for want in TestAddAllocatesNothingPerCall TestHarnessRecorderDoesNotAllocatePerItem; do \
grep -q "^--- PASS: $$want" /tmp/alloc-gate.txt || { \
echo "allocation gate did not run $$want -- was it renamed?"; exit 1; }; \
done
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Enqueue microbenchmarks with statistics suitable for benchstat.
bench-enqueue:
@go test -run='^$$' -bench='BenchmarkBatcherEnqueue' -benchmem \
-benchtime=3s -count=10 ./pkg/batcher

# Full reporting matrix. Informational: produces measurements, not pass/fail.
bench-matrix:
@SCENARIO_MATRIX=1 go test -run TestScenarioMatrix -count=1 -timeout 40m -v ./test/scenario/

Comment thread
heynemann marked this conversation as resolved.
coverage: coverage-run coverage-report

coverage-run:
Expand Down
Loading
Loading