Filter and page the grid in the database, batch the PHI audit trail

Performancekamo-shared-library
Shipped
20 Agosti 2026, 23:34 UTC
Author
Kamo
Commit
8b4f5a2

Loading /leads took 23.3 s at 1,427 leads. 23.3 s of that was the HIPAA audit trail: recordLeadList writes one phi_access_log row per lead returned, and each one committed its own transaction. Measured on production 16.36 ms/row, 5,708 rows in 93 s across four loads. The SELECT was 29.6 ms and ix_leads_org_id was being used — the read path was never the problem. Two independent causes, both fixed here. The audit trail wrote one transaction per row. phi_access_log carries three secondary indexes, so on Yugabyte a single-row insert is four writes across tablets and therefore a distributed transaction. PhiAccessLogWriter gains writeAll and JpaPhiAccessLogWriter implements it as one saveAll, which SimpleJpaRepository runs in a single transaction: measured 0.49 ms/row, ~33x. AsyncPhiAccessLogWriter then takes the fail-open kinds off the request thread entirely. Fail-closed kinds (export, download, disclose, configure) still write synchronously with the exception intact, so they can still refuse the read; the queue never drops a record — a saturated queue writes inline instead. Granularity is unchanged: every lead shown still gets its own row. The endpoint returned the whole organisation and let the browser filter it, so the audit volume was proportional to tenant size rather than to what was disclosed. LeadGridQuery + LeadGridSpecifications move every filter into the query and LeadService.getLeadGridPage returns a page of the lean row DTO, projected inside the transaction so the endpoint holds no entity afterwards. A Specification rather than JPQL null-guards on purpose: ':p IS NULL OR col = :p' stops the planner using the new composite indexes. Subqueries rather than treat()/type() for the mortgage subclass, because the library builds on Hibernate 6.5 and SecurityService runs 6.2.13. Lead gains four composite indexes, each leading with ORG_ID so Yugabyte still hash-partitions on the tenant: assignment, market, status and dateCreated. Without them, server-side filtering would relocate the scan rather than remove it. Also: the softphone's reverse phone lookup no longer hydrates the whole organisation to compare numbers in Java. Also adds findBatchAfterUid: a keyset scan for back-office passes that must visit every lead. findAll(Pageable) cannot be used for that on Yugabyte — Spring Data pairs its content SELECT with a COUNT in one transaction, and a pass that writes between pages then trips a read restart the query layer cannot retry.

All changes

Je, unaona nini kuhusu usafiri?

Kila moja ya hizi updates ardhi katika nafasi yako ya kazi moja kwa moja. Kuanza bure na kuangalia kukua wiki baada ya wiki.

Kuwa Huru MileleMtazamo wa bei