rails-0009: FilterAttributeHandler filter_parameters Array O(A×F) → Set (450×) rails-0010: Encryption::AutoFilteredParameters two Array scans → Set (250×) rails-0011: TimeZoneConversion skip_list Array O(M×C×S) → Set (20×) exposed-0001: SchemaUtilityApi mapMissingColumnStatements O(N×M) → map (118×) exposed-0002: IdentifierManagerApi isAKeyword O(K) linear → HashSet (144×) exposed-0003: Table.clone consParams.map fresh List → hoisted HashSet (6×) seaorm-0001: active_model establish_links leftover.any O(N²) → HashSet (501×) seaorm-0002: rbac engine group_permissions .values().find() → HashMap by ID (502×) seaorm-0003: schema builder sorted_tables Vec::contains → HashSet (500×) seaorm-0004: TopologicalSort from_iter seen Vec O(N²) → BTreeSet (28×) Unit tests: RailsTest 11/11, ExposedTest 3/3, SeaORMTest 4/4 PASS Whitepaper: 157→167 sites, 62→64 ecosystems; §13.12 ORM Wave 2 added
2.1 KiB
seaorm-0002: group_permissions_by_resources — O(N²) linear find inside loop
Severity: HIGH File: src/rbac/engine/mod.rs Line: 234–246 Status: PATCHED
Description
group_permissions_by_resources iterates over a list of (resource_id,
permission_id) pairs — up to P*R items where P = number of permissions and R =
number of resources — and for each item performs two linear scans:
self.permissions.values().find(|p| p.id == item.1) // O(P)
self.resources.values() .find(|r| r.id == item.0) // O(R)
Both self.permissions and self.resources are HashMap<PermissionRequest, Permission> keyed by the request form of the permission/resource, not by the
PermissionId/ResourceId. Therefore a lookup by numeric ID requires a full
linear scan of map values.
In an RBAC system with P permissions, R resources, and a user holding M roles
each granting K permissions, group_permissions_by_resources scans up to
MKP + MKR values for a single call to get_user_role_permissions.
Root Cause
RbacEngine stores permissions and resources indexed by their request key but
not by their numeric ID. When group_permissions_by_resources needs to look up
by ID it falls back to values().find().
Fix
Add reverse-index maps keyed by numeric ID, built once in from_snapshot, and
use them in group_permissions_by_resources:
// in RbacEngine struct
permissions_by_id: HashMap<PermissionId, Permission>,
resources_by_id: HashMap<ResourceId, Resource>,
// in from_snapshot
let permissions_by_id: HashMap<_, _> =
permissions.values().map(|p| (p.id, p.clone())).collect();
let resources_by_id: HashMap<_, _> =
resources.values().map(|r| (r.id, r.clone())).collect();
// in group_permissions_by_resources (replace .values().find())
let permission = self.permissions_by_id.get(&item.1)
.ok_or_else(|| Error::PermissionNotFound(...))?;
let resource = self.resources_by_id.get(&item.0)
.ok_or_else(|| Error::ResourceNotFound(...))?;
Speedup
~500x at P=R=1000, M=10, K=100 (1 000 000 comparisons → 1 000 hash lookups)