GrantForge's performance targets address a large organization: one million accounts, ten thousand roles, one hundred thousand resources. The system benchmark runs nightly on PostgreSQL 17, and the build fails if any metric exceeds its ceiling.
Targets
| Metric | Meaning | Ceiling |
|---|---|---|
authz.snapshot.hit |
A cached authorization snapshot (used on every API call) | p99 1 ms |
authz.snapshot.build |
First computation of an account's console snapshot | p95 50 ms |
authz.snapshot.app |
Computing a snapshot in an application with a large resource tree | p95 50 ms |
api.users.page |
One page of the user list (within the first 500 pages) | p95 200 ms |
api.users.search |
Searching users by name | p95 200 ms |
api.groups.page |
One page of the user group list | p95 200 ms |
api.roles.list |
The role list (unpaginated) | p95 200 ms |
api.me.authorization |
The current account's permissions | p95 200 ms |
api.member.groups |
An account with a single page permission accessing a protected list | p95 200 ms |
jmh.derivation.* |
Preparing the derivation for a large resource tree; deriving twenty grants (JMH) | avg 50 / 5 ms |
Ceilings may only be tightened; relaxing any of them requires a documented decision.
Data
The benchmark starts the real server, completes initialization, and then writes data through the application's own entities:
- 100 departments, one user group per thousand accounts, 100 positions;
- Each account belongs to one department and one user group; one in ten accounts holds a position, and one in twenty accounts has directly assigned roles;
- One in ten roles inherits from one of the first hundred roles; each user group gets three roles, each department two, each position one; each role is granted five console pages;
- One application: one hundred pages under several modules, nine operations per page; one in ten roles is granted twenty of those pages and operations.
Each metric is warmed up and then measured call by call.
Running it locally
bash script/ci/perf_benchmark.sh full # 目标规模,postgres:17(需要 Docker),检查上限
bash script/ci/perf_benchmark.sh smoke # H2 上的小规模,只确认基准能运行
bash script/ci/perf_benchmark.sh full mysql:8.4 # 其他数据库The report is written to perf/target/perf-report.json, and a table is also printed to the log. Extra options can be passed with PERF_OPTS:
| Option | Default | Meaning |
|---|---|---|
perf.database |
postgres:17 |
h2 or <engine>:<version> |
perf.users / perf.roles / perf.resources |
1000000 / 10000 / 100000 | Data volume |
perf.samples / perf.warmup |
1000 / 200 | Measurements and warmup iterations per metric |
perf.jmh |
true |
Also run the JMH benchmarks |
perf.enforce |
true |
Exit with status 1 when a ceiling is exceeded |
Why it is fast
- Authorization snapshots are cached per account and invalidated wholesale by catalog and tenant versions; a hit is a single in-memory read.
- Derivation happens in memory: the resource tree, inheritance relations, and assignments are preloaded into compact structures, avoiding per-item queries.
- List endpoints are always paginated, and sorting and filtering land on indexed columns; lazy associations load in batches of 64, and writes commit in batches of 50 (IDs are generated by the application, so batch inserts are possible).
