Skip to content

[Bugfix:Submission] Fix Team N+1 queries#12506

Merged
williamjallen merged 9 commits into
Submitty:mainfrom
Ash092016:perf/fix-n-plus-1-team-controller
Mar 23, 2026
Merged

[Bugfix:Submission] Fix Team N+1 queries#12506
williamjallen merged 9 commits into
Submitty:mainfrom
Ash092016:perf/fix-n-plus-1-team-controller

Conversation

@Ash092016

Copy link
Copy Markdown
Contributor

Why is this Change Important & Necessary?

Fixes #12505

In TeamController::showPage(), the method getUserById() is called inside two separate foreach loops once per team member and once per user seeking a team. Each call executes an individual SELECT query against the database, resulting in an N+1 query pattern.

For a team with 5 members and 20 students seeking teams, this executes 25 individual queries where 1 batch query would suffice.

The batch method getUsersById() already exists in DatabaseQueries.php and is used elsewhere in the codebase (e.g., ElectronicGraderController::importTeams()), but was not utilized here.

What is the New Behavior?

Before: Two loops each calling getUserById() per user (N+1 pattern)

After: A single batch getUsersById() call before the loops, then lookups from the pre-fetched map

No UI changes the page renders identically. The only difference is fewer database queries.

What steps should a reviewer take to reproduce or test the bug or new feature?

  1. Navigate to a team assignment's team management page: /courses/{semester}/{course}/gradeable/{gradeable_id}/team
  2. Verify that team members are listed correctly
  3. Verify that users seeking a team are listed correctly
  4. Verify that team invitations still display properly

Automated Testing & Documentation

  • No new tests are required this is a refactor with no functional change
  • Existing Cypress E2E tests for team management should continue to pass
  • No documentation changes needed

Other information

  • Not a breaking change same data is returned in the same order, just fetched more efficiently
  • No migrations needed uses the existing getUsersById() method from DatabaseQueries.php
  • No security concerns no change in data access patterns or permissions

Replace two loops calling getUserById() per user with a single batch getUsersById() call. This reduces N+1 individual SELECT queries to 1 batch query for fetching team members and users seeking teams.

For a team with 5 members and 20 seekers, this eliminates 24 redundant database queries.
@codecov

codecov Bot commented Mar 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 21.66%. Comparing base (a853d1b) to head (0cf81ba).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##               main   #12506      +/-   ##
============================================
- Coverage     21.66%   21.66%   -0.01%     
- Complexity     9637     9639       +2     
============================================
  Files           268      268              
  Lines         36222    36226       +4     
  Branches        487      487              
============================================
  Hits           7847     7847              
- Misses        27892    27896       +4     
  Partials        483      483              
Flag Coverage Δ
autograder 21.32% <ø> (ø)
js 2.04% <ø> (ø)
migrator 100.00% <ø> (ø)
php 20.68% <0.00%> (-0.01%) ⬇️
python_submitty_utils 80.08% <ø> (ø)
submitty_daemon_jobs 91.13% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Ash092016 Ash092016 changed the title [Bugfix:Server] Fix TeamController N+1 [Bugfix:Submission] Fix Team N+1 queries Mar 3, 2026
Comment thread site/app/controllers/student/TeamController.php Outdated
@Ash092016
Ash092016 requested a review from IDzyre March 5, 2026 19:23
@automateprojectmangement automateprojectmangement Bot moved this from Seeking Reviewer to In Review in Submitty Development Mar 5, 2026
Comment thread site/app/controllers/student/TeamController.php Outdated
@Ash092016

Copy link
Copy Markdown
Contributor Author

Yes @IDzyre ! Since getUsersById() already returns an empty array for empty input, the ternary check is redundant here. I've simplified this to a direct call and pushed the update.

@Ash092016
Ash092016 requested a review from IDzyre March 11, 2026 18:41
Comment thread site/app/controllers/student/TeamController.php Outdated
@Ash092016

Copy link
Copy Markdown
Contributor Author

Hi @IDzyre ! I've refactored the code to use the nullsafe (?->) and null coalescing (??) operators. Thanks for the suggestion!

@Ash092016
Ash092016 requested a review from IDzyre March 13, 2026 12:20

@IDzyre IDzyre left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feedback addressed, code is cleaner now, putting it into "Awaiting Maintainer Review" to get another view on it.

@github-project-automation github-project-automation Bot moved this from In Review to Awaiting Maintainer Review in Submitty Development Mar 13, 2026
@Ash092016
Ash092016 requested a review from IDzyre March 22, 2026 09:02
@automateprojectmangement automateprojectmangement Bot moved this from Awaiting Maintainer Review to In Review in Submitty Development Mar 22, 2026
@IDzyre IDzyre moved this from In Review to Awaiting Maintainer Review in Submitty Development Mar 22, 2026
@williamjallen
williamjallen merged commit 108a7ef into Submitty:main Mar 23, 2026
48 of 49 checks passed
@github-project-automation github-project-automation Bot moved this from Awaiting Maintainer Review to Done in Submitty Development Mar 23, 2026
GarvitKhandelwal31 pushed a commit to GarvitKhandelwal31/Submitty that referenced this pull request Mar 25, 2026
### Why is this Change Important & Necessary?

Fixes Submitty#12505

In `TeamController::showPage()`, the method
[getUserById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:150:4-158:5)
is called inside two separate `foreach` loops once per team member and
once per user seeking a team. Each call executes an individual `SELECT`
query against the database, resulting in an N+1 query pattern.

For a team with 5 members and 20 students seeking teams, this executes
**25 individual queries** where **1 batch query** would suffice.

The batch method
[getUsersById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:6361:4-6395:5)
already exists in
[DatabaseQueries.php](cci:7://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:0:0-0:0)
and is used elsewhere in the codebase (e.g.,
`ElectronicGraderController::importTeams()`), but was not utilized here.

### What is the New Behavior?

**Before:** Two loops each calling
[getUserById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:150:4-158:5)
per user (N+1 pattern)

**After:** A single batch
[getUsersById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:6361:4-6395:5)
call before the loops, then lookups from the pre-fetched map

No UI changes the page renders identically. The only difference is fewer
database queries.

### What steps should a reviewer take to reproduce or test the bug or
new feature?

1. Navigate to a team assignment's team management page:
`/courses/{semester}/{course}/gradeable/{gradeable_id}/team`
2. Verify that team members are listed correctly
3. Verify that users seeking a team are listed correctly
4. Verify that team invitations still display properly

### Automated Testing & Documentation

- No new tests are required this is a refactor with no functional change
- Existing Cypress E2E tests for team management should continue to pass
- No documentation changes needed

### Other information

- **Not a breaking change** same data is returned in the same order,
just fetched more efficiently
- **No migrations needed** uses the existing
[getUsersById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:6361:4-6395:5)
method from
[DatabaseQueries.php](cci:7://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:0:0-0:0)
- **No security concerns** no change in data access patterns or
permissions

---------

Co-authored-by: Ashutosh <[email protected]>
GarvitKhandelwal31 pushed a commit to GarvitKhandelwal31/Submitty that referenced this pull request Mar 29, 2026
### Why is this Change Important & Necessary?

Fixes Submitty#12505

In `TeamController::showPage()`, the method
[getUserById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:150:4-158:5)
is called inside two separate `foreach` loops once per team member and
once per user seeking a team. Each call executes an individual `SELECT`
query against the database, resulting in an N+1 query pattern.

For a team with 5 members and 20 students seeking teams, this executes
**25 individual queries** where **1 batch query** would suffice.

The batch method
[getUsersById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:6361:4-6395:5)
already exists in
[DatabaseQueries.php](cci:7://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:0:0-0:0)
and is used elsewhere in the codebase (e.g.,
`ElectronicGraderController::importTeams()`), but was not utilized here.

### What is the New Behavior?

**Before:** Two loops each calling
[getUserById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:150:4-158:5)
per user (N+1 pattern)

**After:** A single batch
[getUsersById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:6361:4-6395:5)
call before the loops, then lookups from the pre-fetched map

No UI changes the page renders identically. The only difference is fewer
database queries.

### What steps should a reviewer take to reproduce or test the bug or
new feature?

1. Navigate to a team assignment's team management page:
`/courses/{semester}/{course}/gradeable/{gradeable_id}/team`
2. Verify that team members are listed correctly
3. Verify that users seeking a team are listed correctly
4. Verify that team invitations still display properly

### Automated Testing & Documentation

- No new tests are required this is a refactor with no functional change
- Existing Cypress E2E tests for team management should continue to pass
- No documentation changes needed

### Other information

- **Not a breaking change** same data is returned in the same order,
just fetched more efficiently
- **No migrations needed** uses the existing
[getUsersById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:6361:4-6395:5)
method from
[DatabaseQueries.php](cci:7://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:0:0-0:0)
- **No security concerns** no change in data access patterns or
permissions

---------

Co-authored-by: Ashutosh <[email protected]>
GarvitKhandelwal31 pushed a commit to GarvitKhandelwal31/Submitty that referenced this pull request Apr 14, 2026
### Why is this Change Important & Necessary?

Fixes Submitty#12505

In `TeamController::showPage()`, the method
[getUserById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:150:4-158:5)
is called inside two separate `foreach` loops once per team member and
once per user seeking a team. Each call executes an individual `SELECT`
query against the database, resulting in an N+1 query pattern.

For a team with 5 members and 20 students seeking teams, this executes
**25 individual queries** where **1 batch query** would suffice.

The batch method
[getUsersById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:6361:4-6395:5)
already exists in
[DatabaseQueries.php](cci:7://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:0:0-0:0)
and is used elsewhere in the codebase (e.g.,
`ElectronicGraderController::importTeams()`), but was not utilized here.

### What is the New Behavior?

**Before:** Two loops each calling
[getUserById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:150:4-158:5)
per user (N+1 pattern)

**After:** A single batch
[getUsersById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:6361:4-6395:5)
call before the loops, then lookups from the pre-fetched map

No UI changes the page renders identically. The only difference is fewer
database queries.

### What steps should a reviewer take to reproduce or test the bug or
new feature?

1. Navigate to a team assignment's team management page:
`/courses/{semester}/{course}/gradeable/{gradeable_id}/team`
2. Verify that team members are listed correctly
3. Verify that users seeking a team are listed correctly
4. Verify that team invitations still display properly

### Automated Testing & Documentation

- No new tests are required this is a refactor with no functional change
- Existing Cypress E2E tests for team management should continue to pass
- No documentation changes needed

### Other information

- **Not a breaking change** same data is returned in the same order,
just fetched more efficiently
- **No migrations needed** uses the existing
[getUsersById()](cci:1://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:6361:4-6395:5)
method from
[DatabaseQueries.php](cci:7://file:///c:/Users/BIT/Desktop/Submitty/site/app/libraries/database/DatabaseQueries.php:0:0-0:0)
- **No security concerns** no change in data access patterns or
permissions

---------

Co-authored-by: Ashutosh <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

[Server] Batch user lookups in TeamController

3 participants