Skip to content

Conversation

@kriscarle
Copy link

@kriscarle kriscarle commented Dec 11, 2025

I'm getting this error in the logs, seems to be related to a race condition, with multiple processes updating the sessions

{
  code: 'P2025',
  meta: { modelName: 'SessionData', operation: 'an update' },
  clientVersion: '6.19.0',
  name: 'PrismaClientKnownRequestError',
  message: '\n' +
    'Invalid `prisma.sessionData.update()` invocation:\n' +
    '\n' +
    '\n' +
    'An operation failed because it depends on one or more records that were required but not found. No record was found for an update.',
  stack: 'PrismaClientKnownRequestError: \n' +
    'Invalid `prisma.sessionData.update()` invocation:\n' +
    '\n' +
    '\n' +
    'An operation failed because it depends on one or more records that were required but not found. No record was found for an update.\n' +
    '    at di.handleRequestError (/app/node_modules/.pnpm/@prisma+client@6.19.0_prisma@6.19.0_typescript@5.9.3__typescript@5.9.3/node_modules/@prisma/client/runtime/client.js:81:7268)\n' +
    '    at di.handleAndLogRequestError (/app/node_modules/.pnpm/@prisma+client@6.19.0_prisma@6.19.0_typescript@5.9.3__typescript@5.9.3/node_modules/@prisma/client/runtime/client.js:81:6593)\n' +
    '    at di.request (/app/node_modules/.pnpm/@prisma+client@6.19.0_prisma@6.19.0_typescript@5.9.3__typescript@5.9.3/node_modules/@prisma/client/runtime/client.js:81:6300)\n' +
    '    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n' +
    '    at async a (/app/node_modules/.pnpm/@prisma+client@6.19.0_prisma@6.19.0_typescript@5.9.3__typescript@5.9.3/node_modules/@prisma/client/runtime/client.js:90:9551)\n' +
    '    at async m (/app/.next/server/chunks/[root-of-the-server]__e72b30ae._.js:1859:904)\n' +
    '    at async k (/app/.next/server/chunks/[root-of-the-server]__4217576a._.js:11:38087)\n' +
    '    at async rN.do (/app/node_modules/.pnpm/next@15.5.7_@babel+core@7.28.3_babel-plugin-react-compiler@19.1.0-rc.2_react-dom@19.2.1_react@19.2.1__react@19.2.1/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js:5:21042)\n' +
    '    at async rN.handle (/app/node_modules/.pnpm/next@15.5.7_@babel+core@7.28.3_babel-plugin-react-compiler@19.1.0-rc.2_react-dom@19.2.1_react@19.2.1__react@19.2.1/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js:5:25860)\n' +
    '    at async d (/app/.next/server/chunks/[root-of-the-server]__ffe8fc2e._.js:1:3076)'
}

@vercel
Copy link

vercel bot commented Dec 11, 2025

@kriscarle is attempting to deploy a commit to the umami-software Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 11, 2025

Greptile Overview

Greptile Summary

Replaced the query-then-update pattern with a more resilient updateMany-then-create approach to handle session data persistence. The key improvement is using a compound where clause (sessionId, dataKey) directly in updateMany instead of relying on cached record IDs from a previous findMany query, which significantly reduces the race condition window and eliminates "Record not found" errors when records are modified between queries.

Key Changes:

  • Removed upfront findMany query that cached all existing records
  • Replaced update by ID with updateMany by compound where clause
  • updateMany returns a count instead of throwing errors, making the code more resilient
  • Reduced race condition window by eliminating the stale data dependency

Confidence Score: 4/5

  • This PR is safe to merge with good improvements to error handling and race condition mitigation
  • Score reflects solid improvement over previous implementation by eliminating "Record not found" errors and reducing race window, though a complete solution would require database-level unique constraints on (sessionId, dataKey)
  • No files require special attention - the single file change is straightforward and improves resilience

Important Files Changed

File Analysis

Filename Score Overview
src/queries/sql/sessions/saveSessionData.ts 3/5 Replaced query-then-update pattern with updateMany-then-create to reduce race condition window, though without unique constraint or transaction, small race window remains between updateMany and create operations

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as saveSessionData
    participant DB as Prisma/Database

    Client->>API: saveSessionData(sessionData)
    
    Note over API: Flatten JSON into key-value pairs
    API->>API: flattenJSON(sessionData)
    API->>API: Generate UUIDs for each record
    
    loop For each flattened data item
        API->>DB: updateMany({sessionId, dataKey})
        DB-->>API: {count: n}
        
        alt count === 0 (no existing record)
            API->>DB: create(newRecord)
            DB-->>API: Created record
        else count > 0 (record updated)
            Note over API: Record already updated, continue
        end
    end
    
    API-->>Client: Success
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Additional Comments (1)

  1. src/queries/sql/sessions/saveSessionData.ts, line 54-69 (link)

    logic: race condition still possible: if two requests both get count === 0 between lines 54-65, both will execute create at line 66-68, creating duplicate records with same sessionId+dataKey. without a unique constraint on (sessionId, dataKey) in the database schema or wrapping this in a transaction, duplicates can still occur. consider adding @@unique([sessionId, dataKey]) to the SessionData model in schema.prisma, then use Prisma's upsert instead of this pattern.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

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