|
1 | 1 | --- |
2 | | -status: planned |
| 2 | +status: complete |
3 | 3 | created: '2025-11-11' |
4 | 4 | tags: |
5 | 5 | - bug |
|
8 | 8 | - database |
9 | 9 | priority: high |
10 | 10 | created_at: '2025-11-11T15:06:38.494Z' |
| 11 | +completed_at: '2025-11-11T15:20:00.000Z' |
11 | 12 | --- |
12 | 13 |
|
13 | 14 | # Fix Workspace Upsert Bug Blocking Backfill |
14 | 15 |
|
15 | | -> **Status**: 📅 Planned · **Priority**: High · **Created**: 2025-11-11 |
| 16 | +> **Status**: ✅ Complete · **Priority**: High · **Created**: 2025-11-11 |
16 | 17 |
|
17 | 18 | ## Overview |
18 | 19 |
|
@@ -135,9 +136,71 @@ Failed to send batch (attempt 1/4): unexpected status 500: |
135 | 136 | "Unique constraint failed on the fields: (`workspace_id`)" |
136 | 137 | ``` |
137 | 138 |
|
138 | | -### Next Steps |
| 139 | +## Implementation Summary (2025-11-11) |
139 | 140 |
|
140 | | -1. Find the batch insert API endpoint code |
141 | | -2. Examine the Prisma upsert query |
142 | | -3. Fix the upsert logic |
143 | | -4. Re-test backfill |
| 141 | +### Changes Made |
| 142 | + |
| 143 | +**File: `/packages/web/app/api/events/batch/route.ts`** |
| 144 | + |
| 145 | +Fixed the workspace upsert logic to use the correct unique constraint: |
| 146 | + |
| 147 | +**Before:** |
| 148 | + |
| 149 | +```typescript |
| 150 | +await prisma.workspace.upsert({ |
| 151 | + where: { |
| 152 | + projectId_machineId_workspaceId: { |
| 153 | + projectId: workspaceData.projectId, |
| 154 | + machineId: workspaceData.machineDbId, |
| 155 | + workspaceId: workspaceData.workspaceId, |
| 156 | + }, |
| 157 | + }, |
| 158 | + create: { |
| 159 | + /* ... */ |
| 160 | + }, |
| 161 | + update: {}, |
| 162 | +}); |
| 163 | +``` |
| 164 | + |
| 165 | +**After:** |
| 166 | + |
| 167 | +```typescript |
| 168 | +await prisma.workspace.upsert({ |
| 169 | + where: { |
| 170 | + workspaceId: workspaceData.workspaceId, |
| 171 | + }, |
| 172 | + create: { |
| 173 | + /* ... */ |
| 174 | + }, |
| 175 | + update: { |
| 176 | + workspacePath: workspaceData.workspacePath, |
| 177 | + lastSeenAt: new Date(), |
| 178 | + }, |
| 179 | +}); |
| 180 | +``` |
| 181 | + |
| 182 | +### Why This Fix Works |
| 183 | + |
| 184 | +1. **Correct Unique Constraint**: The Prisma schema has `@unique` on `workspaceId` field, which creates a single-column unique constraint. The composite constraint `@@unique([projectId, machineId, workspaceId])` is secondary. |
| 185 | + |
| 186 | +2. **Proper Update**: When a workspace already exists, we now update the `workspacePath` and `lastSeenAt` fields instead of leaving the update empty. |
| 187 | + |
| 188 | +3. **Idempotent**: The fix makes the endpoint idempotent - multiple batches with the same workspace will succeed, with the workspace being updated on subsequent calls. |
| 189 | + |
| 190 | +### Testing |
| 191 | + |
| 192 | +- Added integration test case `should handle batch events with existing workspace (upsert)` in `hierarchy-api.test.ts` |
| 193 | +- Test verifies that: |
| 194 | + - First batch creates workspace successfully |
| 195 | + - Second batch with same workspaceId but updated path succeeds |
| 196 | + - Workspace is updated with new path |
| 197 | +- Build passes successfully |
| 198 | +- CodeQL security check shows no vulnerabilities |
| 199 | + |
| 200 | +### Impact |
| 201 | + |
| 202 | +This fix unblocks: |
| 203 | + |
| 204 | +- Backfill feature for importing historical Copilot chat sessions |
| 205 | +- Batch event processing when workspaces already exist |
| 206 | +- Collector auto-creation of workspaces during event ingestion |
0 commit comments