- Ya
- 23 Septemba 2026, 14:39 UTC
- Mwandishi
- Kamo
- Ahadi ya
- c4924af
Two related fixes to MemberRightsAppliedService, both in the same file: 1. **************** (via recalcChunk) and **************** unconditionally deleted and re-inserted every one of a member's applied-rights rows, every time they ran. SecurityService's MemberRightsBackfillService calls the org-wide path for EVERY org on EVERY pod boot (a self-heal, by design — see its own javadoc), and the recompute is idempotent: a steady-state boot re-derives the identical set. Measured in production pg_stat_statements: ~12.7M INSERTs against a ~30K-row table — roughly 300 full-table rewrites, one per boot, almost all of them writing back exactly what was already there. Both methods now diff the freshly-computed set against what member_rights_applied already holds (one bulk read for a whole chunk) and only clear + rewrite the members whose rights actually differ. A no-op boot now costs one read and zero writes. 2. calculateMemberRights (the core of both the write path above and the read-only computeMemberRights SessionRefreshController polls on every session refresh) walked member.getRoles(), **************** job-title roles()/getRights() and member.getRights() as separate lazy collections — plus one further lazy load per distinct role, for that role's own rights. Unbounded per member. Six new optional repository fields (MemberRoleRepository, DepartmentRoleRepository, DepartmentRightRepository, JobTitleRoleRepository, JobTitleRightRepository, MemberRightRepository — the first and last already existed) replace those lazy loads with a fixed, small number of bulk queries, each with a role's own rights JOIN-FETCHed. Deliberately NOT an @EntityGraph rooted at Member/TeamMember: both are @Inheritance(JOINED), and an entity graph that fetches further associations off a JOINED entity is the exact shape that took /leads down in production on Hibernate 6.2.13 (missing FROM-clause entry for the root table — see LeadAssigneeRef). Each new repository is instead rooted at the CHILD entity (none of which carry @Inheritance) and filtered by the parent's id. An EntityManager field with @PersistenceContext was tried first and reverted: it is processed by Spring's JPA-aware post-processor whenever spring-orm is on the classpath, so a manually-new'd service in a context with no EntityManagerFactory bean (SignInReadRetryTest's hand-rolled Spring context, which exercises @RetryOnDbConflict through a real AOP proxy) failed BEAN CREATION outright. The optional-repository fields degrade the same way appliedModelResolver already did: @Autowired(required = false) leaves them null with no bean of that type, and the six helpers fall back to the original lazy-load path, unchanged, in every existing test. SessionRefreshController keeps computing live (not from the persisted snapshot) — that endpoint's whole point is "what is true right now", which is explicitly not always what was last persisted.
