0ef4c624c5
initial test from Ed's nuc:
metric pctl baseline current delta status
-----------------------------------------------------------------------------------------------
folder_cascade.list_depth1 p50 0.3ms 0.3ms -6.3% ok
folder_cascade.list_depth1 p95 2.3ms 0.5ms -75.7% ok
folder_cascade.list_depth1 p99 4.7ms 2.5ms -48.1% ok
folder_cascade.list_depth4 p50 0.4ms 0.3ms -10.0% ok
folder_cascade.list_depth4 p95 0.9ms 0.6ms -31.2% ok
folder_cascade.list_depth4 p99 2.4ms 1.0ms -56.7% ok
folder_cascade.list_depth8 p50 0.3ms 0.3ms -8.8% ok
folder_cascade.list_depth8 p95 0.6ms 0.5ms -22.2% ok
folder_cascade.list_depth8 p99 1.9ms 0.5ms -71.5% ok
folder_cascade.list_depth_deep p50 0.3ms 0.3ms -5.0% ok
folder_cascade.list_depth_deep p95 0.6ms 0.5ms -18.6% ok
folder_cascade.list_depth_deep p99 2.0ms 0.7ms -67.2% ok
share_cascade_rebac.list_grants p50 0.4ms 0.3ms -27.6% ok
share_cascade_rebac.list_grants p95 1.2ms 0.5ms -57.0% ok
share_cascade_rebac.list_grants p99 1.7ms 1.1ms -36.7% ok
share_cascade_rebac.fetch_as_grantee_depth1 p50 0.5ms 0.5ms -11.1% ok
share_cascade_rebac.fetch_as_grantee_depth1 p95 1.1ms 0.7ms -41.2% ok
share_cascade_rebac.fetch_as_grantee_depth1 p99 3.0ms 1.3ms -58.3% ok
share_cascade_rebac.fetch_as_grantee_depth4 p50 0.5ms 0.5ms -13.7% ok
share_cascade_rebac.fetch_as_grantee_depth4 p95 1.4ms 0.7ms -50.5% ok
share_cascade_rebac.fetch_as_grantee_depth4 p99 2.2ms 1.1ms -49.3% ok
share_cascade_rebac.fetch_as_grantee_depth8 p50 0.5ms 0.4ms -16.0% ok
share_cascade_rebac.fetch_as_grantee_depth8 p95 0.9ms 0.7ms -26.1% ok
share_cascade_rebac.fetch_as_grantee_depth8 p99 1.5ms 0.9ms -40.1% ok
share_cascade_rebac.fetch_as_grantee_depth_deep p50 0.5ms 0.4ms -17.3% ok
share_cascade_rebac.fetch_as_grantee_depth_deep p95 1.0ms 0.7ms -31.2% ok
share_cascade_rebac.fetch_as_grantee_depth_deep p99 1.6ms 0.8ms -49.4% ok
subject_group_nested.fetch_as_member_depth1 p50 0.5ms 0.4ms -8.4% ok
subject_group_nested.fetch_as_member_depth1 p95 0.6ms 0.6ms -10.2% ok
subject_group_nested.fetch_as_member_depth1 p99 1.4ms 0.6ms -55.2% ok
subject_group_nested.fetch_as_member_depth4 p50 0.5ms 0.5ms -7.9% ok
subject_group_nested.fetch_as_member_depth4 p95 0.6ms 0.6ms -4.0% ok
subject_group_nested.fetch_as_member_depth4 p99 0.7ms 0.6ms -2.2% ok
subject_group_nested.fetch_as_member_depth8 p50 0.5ms 0.4ms -8.9% ok
subject_group_nested.fetch_as_member_depth8 p95 0.5ms 0.6ms +7.1% ok
subject_group_nested.fetch_as_member_depth8 p99 0.6ms 0.7ms +10.3% ok
subject_group_nested.fetch_as_member_depth_deep p50 0.5ms 0.4ms -7.6% ok
subject_group_nested.fetch_as_member_depth_deep p95 0.6ms 0.5ms -11.9% ok
subject_group_nested.fetch_as_member_depth_deep p99 0.6ms 0.7ms +8.9% ok
74 lines
3.2 KiB
JavaScript
74 lines
3.2 KiB
JavaScript
// subject_group_nested.js — measures the worst-case AuthZ path: a user who
|
||
// is a direct member of the *innermost* group of a depth-N nested chain, and
|
||
// the grant is on the *outermost* group. Every AuthZ check has to expand the
|
||
// chain transitively. Pairs with share_cascade_rebac.js to attribute regressions
|
||
// to either the resource side (folder cascade) or the subject side (group
|
||
// expansion).
|
||
//
|
||
// Same depth-gradient idea as share_cascade_rebac: measure AuthZ cost at
|
||
// depth 1 / 4 / 8 / deep so we can see WHERE in the chain a regression
|
||
// lands. The subject-side expansion (group_member → leaf → mid → root group
|
||
// → grant) runs once per request regardless of folder depth, but the
|
||
// resource-side cascade (folder ancestors walked to find the grant) grows
|
||
// linearly with depth — so depth4/depth8/deep moving together while depth1
|
||
// stays flat would point at the resource side; all four moving together
|
||
// would point at the group-expansion path.
|
||
|
||
import { check } from 'k6';
|
||
import http from 'k6/http';
|
||
import { BASE, authParams } from '../lib/http.js';
|
||
import { login } from '../lib/auth.js';
|
||
import { thresholdsFromBaseline, loadManifest } from '../lib/metrics.js';
|
||
|
||
const manifest = loadManifest();
|
||
|
||
export const options = {
|
||
vus: 1,
|
||
// See folder_cascade.js for the iteration-count rationale: N=100 makes
|
||
// p99 a real percentile instead of the worst-of-the-batch.
|
||
iterations: 100,
|
||
thresholds: thresholdsFromBaseline('subject_group_nested'),
|
||
};
|
||
|
||
export function setup() {
|
||
const memberToken = login(manifest.group_member.username, manifest.group_member.password);
|
||
return { memberToken };
|
||
}
|
||
|
||
export default function (data) {
|
||
const { memberToken } = data;
|
||
const t = manifest.group_subtree;
|
||
|
||
// Baseline: group member fetches the folder the grant is directly on.
|
||
// Subject-side expansion runs (user → leaf → mid → root group → grant);
|
||
// resource-side walk is zero ancestors. Captures the constant subject-
|
||
// expansion overhead.
|
||
const d1 = http.get(
|
||
`${BASE}/api/folders/${t.root}/resources?resource_types=folder`,
|
||
authParams(memberToken, 'subject_group_nested.fetch_as_member_depth1'),
|
||
);
|
||
check(d1, { 'fetch root 200': (r) => r.status === 200 });
|
||
|
||
// Mid-tree: 4 folder ancestors walked + subject expansion.
|
||
const d4 = http.get(
|
||
`${BASE}/api/folders/${t.depth4}/resources?resource_types=folder`,
|
||
authParams(memberToken, 'subject_group_nested.fetch_as_member_depth4'),
|
||
);
|
||
check(d4, { 'fetch depth4 200': (r) => r.status === 200 });
|
||
|
||
// 8 folder ancestors walked + subject expansion.
|
||
const d8 = http.get(
|
||
`${BASE}/api/folders/${t.depth8}/resources?resource_types=folder`,
|
||
authParams(memberToken, 'subject_group_nested.fetch_as_member_depth8'),
|
||
);
|
||
check(d8, { 'fetch depth8 200': (r) => r.status === 200 });
|
||
|
||
// Worst case: full-depth folder cascade × full-chain subject expansion.
|
||
// group_member → leaf group → mid group → root group → grant → folder root → deepest.
|
||
const dD = http.get(
|
||
`${BASE}/api/folders/${t.deepest}/resources?resource_types=folder`,
|
||
authParams(memberToken, 'subject_group_nested.fetch_as_member_depth_deep'),
|
||
);
|
||
check(dD, { 'fetch deepest 200': (r) => r.status === 200 });
|
||
}
|