Skip to content

🚨 Lighthouse: [Best Practice/Perf Fix]#248

Open
ldsgroups225 wants to merge 1 commit intomasterfrom
jules-2871119385329441237-ba2bf0c7
Open

🚨 Lighthouse: [Best Practice/Perf Fix]#248
ldsgroups225 wants to merge 1 commit intomasterfrom
jules-2871119385329441237-ba2bf0c7

Conversation

@ldsgroups225
Copy link
Copy Markdown
Owner

@ldsgroups225 ldsgroups225 commented Apr 4, 2026

Impact on performance:

  • Replaced sequential database queries inside a loop with Promise.all()
  • Eliminated an async waterfall in the bulkAssignTeacher mutation
  • This reduces the execution time from O(N) database roundtrips to O(1) concurrent batches, improving overall responsiveness and throughput.

Technical rationale:

  • Extracted unique class-subject pairs into a Map to deduplicate inputs and prevent insert race conditions on parallel execution.
  • Executed validation checks and upserts concurrently via Promise.all(), adhering to the Lighthouse edge-first and parallelization rules.

PR created automatically by Jules for task 2871119385329441237 started by @ldsgroups225

Summary by CodeRabbit

  • Refactor
    • Optimized the teacher assignment process to execute validation checks and database operations concurrently, improving performance when assigning teachers to multiple class subjects simultaneously.

Impact on performance:
- Replaced sequential database queries inside a loop with `Promise.all()`
- Eliminated an async waterfall in the `bulkAssignTeacher` mutation
- This reduces the execution time from O(N) database roundtrips to O(1) concurrent batches, improving overall responsiveness and throughput.

Technical rationale:
- Extracted unique class-subject pairs into a Map to deduplicate inputs and prevent insert race conditions on parallel execution.
- Executed validation checks and upserts concurrently via `Promise.all()`, adhering to the Lighthouse edge-first and parallelization rules.
@google-labs-jules
Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

📝 Walkthrough

Walkthrough

Updated bulkAssignTeacher function to deduplicate incoming assignments by (classId, subjectId) using a Map, and refactored teacher qualification validation and upsert operations to execute concurrently via Promise.all instead of sequential awaiting.

Changes

Cohort / File(s) Summary
Teacher Assignment Optimization
packages/data-ops/src/queries/class-subjects/write.ts
Refactored bulkAssignTeacher to deduplicate assignments by (classId, subjectId), parallelize teacher qualification validation and upsert operations using Promise.all, and return filtered concurrent results instead of building arrays conditionally.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Assignments hop and deduplicate so fine,
Concurrent promises align in a line,
Maps gather duplicates, whisking them away,
Faster bulkAssignTeacher runs each day!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title uses vague descriptors like 'Best Practice/Perf Fix' and emoji, lacking specific reference to the actual change (deduplication and concurrent Promise.all optimization in bulkAssignTeacher). Replace the generic title with a specific, descriptive phrase such as 'Optimize bulkAssignTeacher with deduplication and concurrent validation' or 'Replace sequential loops with Promise.all in bulkAssignTeacher'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jules-2871119385329441237-ba2bf0c7

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/data-ops/src/queries/class-subjects/write.ts`:
- Around line 96-119: The current select→insert/update loop over
uniqueAssignments is not atomic and can race on (classId, subjectId); replace
the logic in the Promise.all callback with a single upsert (like the one used in
addSubjectToClass) that does INSERT ... ON CONFLICT (...) DO UPDATE (set
teacherId, updatedAt) so each assignment is collapsed into one atomic statement
against classSubjects; after the upsert use the returning() result and
explicitly check for !updated / !created (or however you name the returned row)
and throw/fail fast if returning() is empty to avoid silent partial success.
- Around line 74-76: The deduplication key for uniqueAssignmentsMap uses string
concatenation (`${assignment.classId}-${assignment.subjectId}`) which can
collide; change the keying to a collision-safe representation such as
JSON.stringify([assignment.classId, assignment.subjectId]) or use a nested Map
(Map<classId, Map<subjectId, assignment>>) when building uniqueAssignmentsMap in
the same block that iterates over assignments so each (classId, subjectId) pair
is uniquely preserved before validation/upsert.
- Around line 83-87: The query lacks tenant scoping: thread context.schoolId
into bulkAssignTeacher and add explicit where clauses on schoolId for all
reads/writes (e.g., the select against teacherSubjects and any selects/updates
against class_subjects between lines 98-113). Specifically, pass schoolId into
bulkAssignTeacher, and update calls referencing teacherSubjects and
class_subjects to include where(eq(teacherSubjects.schoolId, schoolId)) and
where(eq(class_subjects.schoolId, schoolId)) (or join via a school-owned table
if the table is only indirectly scoped) so every school-scoped table query
enforces the tenant predicate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e0c179e1-51f4-40f0-96cf-6673574442b6

📥 Commits

Reviewing files that changed from the base of the PR and between 7e37ef8 and 75b8dbf.

📒 Files selected for processing (1)
  • packages/data-ops/src/queries/class-subjects/write.ts

Comment on lines +74 to +76
const uniqueAssignmentsMap = new Map<string, typeof assignments[0]>()
for (const assignment of assignments) {
// Validate teacher qualification
const [teacherSubject] = await db
.select()
.from(teacherSubjects)
.where(and(eq(teacherSubjects.teacherId, assignment.teacherId), eq(teacherSubjects.subjectId, assignment.subjectId)))
.limit(1)
uniqueAssignmentsMap.set(`${assignment.classId}-${assignment.subjectId}`, assignment)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use a collision-safe composite key for deduplication.

Line 76 concatenates two text IDs with -, so different (classId, subjectId) pairs can map to the same string and one assignment gets dropped before validation/upsert. Use a tuple-safe key instead (JSON.stringify([classId, subjectId]), nested Map, etc.).

Proposed fix
         const uniqueAssignmentsMap = new Map<string, typeof assignments[0]>()
         for (const assignment of assignments) {
-          uniqueAssignmentsMap.set(`${assignment.classId}-${assignment.subjectId}`, assignment)
+          uniqueAssignmentsMap.set(
+            JSON.stringify([assignment.classId, assignment.subjectId]),
+            assignment,
+          )
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/data-ops/src/queries/class-subjects/write.ts` around lines 74 - 76,
The deduplication key for uniqueAssignmentsMap uses string concatenation
(`${assignment.classId}-${assignment.subjectId}`) which can collide; change the
keying to a collision-safe representation such as
JSON.stringify([assignment.classId, assignment.subjectId]) or use a nested Map
(Map<classId, Map<subjectId, assignment>>) when building uniqueAssignmentsMap in
the same block that iterates over assignments so each (classId, subjectId) pair
is uniquely preserved before validation/upsert.

Comment on lines +83 to +87
const [teacherSubject] = await db
.select()
.from(teacherSubjects)
.where(and(eq(teacherSubjects.teacherId, assignment.teacherId), eq(teacherSubjects.subjectId, assignment.subjectId)))
.limit(1)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Thread schoolId through this query path.

These lookups and mutations still key only on raw IDs. The caller already has context.schoolId, but it never reaches bulkAssignTeacher, so a request that supplies foreign IDs can validate against another school's teacherSubjects row or update another school's class_subjects row. Add tenant predicates to every read/write here, joining through school-owned tables if this table is only indirectly scoped. As per coding guidelines: Every query on school-scoped tables MUST include where(eq(table.schoolId, schoolId)) for multi-tenant isolation.

Also applies to: 98-113

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/data-ops/src/queries/class-subjects/write.ts` around lines 83 - 87,
The query lacks tenant scoping: thread context.schoolId into bulkAssignTeacher
and add explicit where clauses on schoolId for all reads/writes (e.g., the
select against teacherSubjects and any selects/updates against class_subjects
between lines 98-113). Specifically, pass schoolId into bulkAssignTeacher, and
update calls referencing teacherSubjects and class_subjects to include
where(eq(teacherSubjects.schoolId, schoolId)) and
where(eq(class_subjects.schoolId, schoolId)) (or join via a school-owned table
if the table is only indirectly scoped) so every school-scoped table query
enforces the tenant predicate.

Comment on lines +96 to +119
const results = await Promise.all(
uniqueAssignments.map(async (assignment) => {
const [existing] = await db
.select()
.from(classSubjects)
.where(and(eq(classSubjects.classId, assignment.classId), eq(classSubjects.subjectId, assignment.subjectId)))
.limit(1)

return results
if (existing) {
const [updated] = await db
.update(classSubjects)
.set({ teacherId: assignment.teacherId, updatedAt: new Date() })
.where(eq(classSubjects.id, existing.id))
.returning()
return updated
}
else {
const [created] = await db.insert(classSubjects).values({ id: crypto.randomUUID(), ...assignment }).returning()
return created
}
}),
)

return results.filter(Boolean) as typeof classSubjects.$inferSelect[]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make each write atomic and fail fast on missing rows.

Deduplicating one payload does not protect the selectinsert branch from a second request touching the same (classId, subjectId) pair, so this can still lose on the unique constraint and fail the whole batch. Line 119 also turns an empty returning() result into silent partial success. Please collapse each pair to a single upsert statement like the one already used in addSubjectToClass on Lines 192-200, and keep explicit !updated / !created checks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/data-ops/src/queries/class-subjects/write.ts` around lines 96 - 119,
The current select→insert/update loop over uniqueAssignments is not atomic and
can race on (classId, subjectId); replace the logic in the Promise.all callback
with a single upsert (like the one used in addSubjectToClass) that does INSERT
... ON CONFLICT (...) DO UPDATE (set teacherId, updatedAt) so each assignment is
collapsed into one atomic statement against classSubjects; after the upsert use
the returning() result and explicitly check for !updated / !created (or however
you name the returned row) and throw/fail fast if returning() is empty to avoid
silent partial success.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant