Skip to content

feat: tier 4 — eliminate N+1 query loops on hot paths#119

Merged
markshust merged 2 commits into
developfrom
feature/tier4-performance
Jun 12, 2026
Merged

feat: tier 4 — eliminate N+1 query loops on hot paths#119
markshust merged 2 commits into
developfrom
feature/tier4-performance

Conversation

@markshust

Copy link
Copy Markdown
Collaborator

Tier 4 — Performance / N+1 Fixes

Fourth of six audit-remediation PRs. Eliminates N+1 query loops and missing LIMIT 1 on hot lookup/attachment/admin-auth/Redis/notification paths. 7 TDD tasks, full suite green (6537 passing, 0 failures). Built on Tier 1-3.

Hot path Fix
F1 findOneBy fetched+hydrated ALL matches; exists family hydrated entities findOneBy → single LIMIT 1 (still eager-loaded); exists/existsBy/isColumnUniqueSELECT 1 … LIMIT 1 boolean probe (no hydration).
F2 AttachmentManager looped find() per attachment id one MediaRepositoryInterface::findMany(array) call, reorder + null-skip in PHP.
F3 permissions queried per-role on every authed admin request one getPermissionsForRoles(array) query for all roles (empty → no query, never IN ()); syncPermissions now transactional + chunked batch insert.
F4 Redis multi-key ops looped single-key calls one MGET / one pipeline / one variadic DEL — Tier 1 HMAC signing preserved (verify-unwrap on read, wrap on write).
F5 notification fan-out persisted one INSERT per recipient new opt-in BatchChannelInterface; DatabaseChannel persists N recipients via one chunked multi-row INSERT; sender falls back to per-recipient send for non-batch channels.

Public surface

  • MediaRepositoryInterface::findMany(array): array (new; consumers implementing the interface must add it).
  • RoleRepositoryInterface::getPermissionsForRoles(array): array (new).
  • BatchChannelInterface (new, opt-inChannelInterface unchanged).

Process

Devil's-advocate-reviewed (caught the F4 HMAC-signer-preservation requirement and the driverName() stub ripple from Tier 2), standards-enforced (no src/ readonly class promotions per the Tier 3 lesson), docs updated, phpcs + php-cs-fixer clean.

🤖 Generated with Claude Code

markshust and others added 2 commits June 12, 2026 14:06
Rebased onto merged Tier 1-3: F4 (cache-redis batched MGET/pipeline) must
preserve Tier 1's HMAC value signing (verify-unwrap on read, wrap on write);
every hand-written ConnectionInterface stub needs Tier 2's driverName();
exists-family must stop delegating; F3/F5 @throws + error-wrapping clarified.
Dependency graph (002<-001, 005<-004) verified.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Audit remediation Tier 4 — all 7 tasks, TDD, full-suite green (6537 passing).
Built on merged Tier 1-3.

- F1 Repository::findOneBy now issues a single LIMIT 1 query (still eager-loaded,
  no full hydration of all matches); exists/existsBy/isColumnUnique use a
  SELECT 1 ... LIMIT 1 boolean probe (no hydration, no eager-load).
- F2 AttachmentManager::findByAttachable resolves all attachments via one
  MediaRepositoryInterface::findMany(array) call instead of per-id find()
  (interface-only; consumer owns the WHERE IN query).
- F3 AdminUserProvider resolves all role permissions in ONE query via
  RoleRepositoryInterface::getPermissionsForRoles(array) (empty list → no query,
  never IN ()); syncPermissions is now transactional + chunked batched insert.
- F4 RedisCacheDriver multi-key ops use one MGET / one pipeline / one variadic
  DEL — preserving Tier 1's HMAC value signing (verify-unwrap on read, wrap on
  write).
- F5 new opt-in BatchChannelInterface; DatabaseChannel persists N recipients via
  one chunked multi-row INSERT; NotificationSender groups per-channel and falls
  back to per-recipient send for non-batch channels. ChannelInterface unchanged.

Devil's-advocate-reviewed (HMAC-signer preservation + driverName() stubs),
standards-enforced (no src readonly-class promotions), docs updated, phpcs +
php-cs-fixer clean. Includes a test-fixture fix (renamed a colliding global
makeNotifiable() helper that clashed with notification-database's).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the enhancement New feature or request label Jun 12, 2026
@markshust markshust merged commit ee9178d into develop Jun 12, 2026
1 check passed
@markshust markshust deleted the feature/tier4-performance branch June 12, 2026 18:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant