Benchmarking

Melange includes comprehensive benchmarks for both the OpenFGA compatibility tests and scale testing with different tuple volumes.

OpenFGA Benchmarks

Running Benchmarks

# Run all OpenFGA benchmarks
just bench-openfga

# Quick sanity check (1 iteration, direct assignment only)
just bench-openfga-quick

# Benchmark a specific category
just bench-openfga-category DirectAssignment
just bench-openfga-category ComputedUserset
just bench-openfga-category TupleToUserset
just bench-openfga-category Wildcards

# Benchmark tests matching a pattern
just bench-openfga-pattern "^wildcard"
just bench-openfga-pattern "computed_userset|ttu_"

# Benchmark a specific test by name
just bench-openfga-name wildcard_direct

# Run checks-only benchmarks (isolates Check from List operations)
just bench-openfga-checks

# Run benchmarks organized by category
just bench-openfga-by-category

Saving Results

Save benchmark results to a file for comparison:

# Save results
just bench-openfga-save baseline.txt

# Make changes...

# Save again and compare
just bench-openfga-save after.txt
diff baseline.txt after.txt

Understanding Output

BenchmarkOpenFGA_DirectAssignment/this-12              1   14359250 ns/op   2.000 checks/op
BenchmarkOpenFGA_DirectAssignment/this_and_union-12    1   11305875 ns/op   4.000 checks/op
MetricDescription
ns/opNanoseconds per operation (lower is better)
checks/opNumber of Check assertions per operation
listobjs/opNumber of ListObjects assertions per operation
listusers/opNumber of ListUsers assertions per operation

Scale Benchmarks

Test performance at different tuple volumes:

# Run benchmarks at all scales (1K, 10K, 100K, 1M)
just bench

# Run at specific scale
just bench SCALE=1K
just bench SCALE=10K
just bench SCALE=100K
just bench SCALE=1M

# Quick scale check
just bench-quick

Test Schema

Scale benchmarks use a GitHub-like model with organizations, repositories, and pull requests. See Performance Reference for the full schema.

Test Data Configuration

ScaleUsersOrgsRepos/OrgMembers/OrgPRs/RepoTotal ReposTotal PRs~Tuples
1K1005102010505001,150
10K5001050502050010,00021,000
100K2,00020100100502,000100,000204,000
1M10,0005020020010010,0001,000,0002,020,000

Expected Performance

Check Operations (specialized SQL code generation):

OperationDescription1K10K100K1MScaling
Direct Membershipusercan_readorganization357 µs329 µs296 µs304 µsO(1)
Inherited Permissionusercan_readrepository (via org)412 µs410 µs420 µs418 µsO(1)
Exclusion Patternusercan_reviewpull_request515 µs520 µs533 µs505 µsO(1)
Denied PermissionNon-member checking org access275 µs277 µs291 µs281 µsO(1)

ListObjects Operations (performance varies by result count):

OperationDescription1K10K100K1MScaling
List Accessible ReposAll repos user can read (via org)3.9 ms34.1 ms133.9 ms672 msO(results)
List Accessible OrgsAll orgs user is member of299 µs300 µs316 µs278 µsO(1)
List Accessible PRsAll PRs user can read (via repo→org)4.1 ms36.8 ms153.1 ms843 msO(results)

ListSubjects Operations (performance varies by relation complexity):

OperationDescription1K10K100K1MScaling
List Org MembersAll users who can read an org288 µs335 µs399 µs480 µsO(log n)
List Repo ReadersAll users who can read a repo (via org)413 µs346 µs346 µs339 µsO(1)
List Repo WritersAll users who can write to a repo269 µs333 µs266 µs259 µsO(1)

Parallel Check Operations:

OperationTime per OpSpeedup vs Sequential
Parallel Direct Check89 µs~3.7x
Parallel Inherited Check143 µs~2.9x

Caching Benchmark

Test cache performance:

func BenchmarkCacheHit(b *testing.B) {
    cache := melange.NewCache()
    checker := melange.NewChecker(db, melange.WithCache(cache))

    // Warm the cache
    checker.Check(ctx, user, "can_read", repo)

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        checker.Check(ctx, user, "can_read", repo)
    }
}

Expected results:

  • Cold cache: 422 µs
  • Warm cache: 83 ns (~5,000x faster)

Profiling

CPU Profile

cd test && go test -bench="BenchmarkOpenFGA_DirectAssignment" \
    -run='^$' -cpuprofile=cpu.prof -timeout 10m ./openfgatests/...

go tool pprof cpu.prof

Memory Profile

cd test && go test -bench="BenchmarkOpenFGA_DirectAssignment" \
    -run='^$' -memprofile=mem.prof -timeout 10m ./openfgatests/...

go tool pprof mem.prof

Trace

cd test && go test -bench="BenchmarkOpenFGA_DirectAssignment" \
    -run='^$' -trace=trace.out -timeout 10m ./openfgatests/...

go tool trace trace.out

Performance Tips

Before Making Changes

  1. Run benchmarks to establish a baseline:

    just bench-openfga-save baseline.txt
  2. Make your changes

  3. Run benchmarks again:

    just bench-openfga-save after.txt
  4. Compare:

    benchstat baseline.txt after.txt

Key Areas to Benchmark

  • SQL generation changes (lib/sqlgen/*.go): Run full benchmark suite
  • Parser changes (pkg/parser/parser.go): Run schema load benchmarks
  • Runtime changes (melange/checker.go): Run check operation benchmarks
  • Cache changes (melange/cache.go): Run cache-specific benchmarks

Benchmark Commands Reference

# OpenFGA benchmarks
just bench-openfga              # All benchmarks
just bench-openfga-category X   # Single category
just bench-openfga-pattern X    # Pattern match
just bench-openfga-name X       # Single test
just bench-openfga-checks       # Check operations only
just bench-openfga-by-category  # Organized by category
just bench-openfga-save X       # Save to file

# Scale benchmarks
just bench                      # All scales
just bench SCALE=1K             # Specific scale
just bench-quick                # Quick check
just bench-save FILE            # Save results

Direct Go Commands

For more control, use Go’s test command directly:

# Run with specific iterations
cd test && go test -bench="BenchmarkOpenFGA_DirectAssignment" \
    -benchtime=10x -run='^$' -benchmem ./openfgatests/...

# Run with longer duration
cd test && go test -bench="BenchmarkOpenFGA_DirectAssignment" \
    -benchtime=30s -run='^$' -benchmem ./openfgatests/...

# Include memory allocations
cd test && go test -bench="BenchmarkOpenFGA_DirectAssignment" \
    -run='^$' -benchmem -memprofile=mem.out ./openfgatests/...