{"record":{"id":"f15bb4753af32c1b","repo":"hibernate/hibernate-orm","slug":"spanner-does-not-support-skip-locked-f15bb4","errorCode":null,"errorMessage":"Spanner does not support skip locked.","messagePattern":"Spanner does not support skip locked\\.","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/SpannerPostgreSQLDialect.java","lineNumber":535,"sourceCode":"\t\treturn getForUpdateSkipLockedString();\n\t}\n\n\t@Override\n\tpublic LockingClauseStrategy getLockingClauseStrategy(\n\t\t\tQuerySpec querySpec, LockOptions lockOptions) {\n\t\tif ( lockOptions == null ) {\n\t\t\treturn NON_CLAUSE_STRATEGY;\n\t\t}\n\t\tvalidateSpannerLockTimeout( lockOptions.getTimeOut() );\n\t\treturn super.getLockingClauseStrategy( querySpec, lockOptions );\n\t}\n\n\tprivate static void validateSpannerLockTimeout(int millis) {\n\t\tif ( Timeouts.isRealTimeout( millis ) ) {\n\t\t\tthrow new UnsupportedOperationException( \"Spanner does not support lock timeout.\" );\n\t\t}\n\t\tif ( millis == Timeouts.SKIP_LOCKED_MILLI ) {\n\t\t\tthrow new UnsupportedOperationException( \"Spanner does not support skip locked.\" );\n\t\t}\n\t\tif ( millis == Timeouts.NO_WAIT_MILLI ) {\n\t\t\tthrow new UnsupportedOperationException( \"Spanner does not support no wait.\" );\n\t\t}\n\t}\n\n\t@Override\n\tpublic void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {\n\t\tsuper.contributeTypes( typeContributions, serviceRegistry );\n\n\t\tfinal var configurationService = serviceRegistry.requireService( ConfigurationService.class );\n\n\t\tthis.useIntegerForPrimaryKey = configurationService.getSetting(\n\t\t\t\tUSE_INTEGER_FOR_PRIMARY_KEY,\n\t\t\t\tStandardConverters.BOOLEAN,\n\t\t\t\tfalse\n\t\t);\n","sourceCodeStart":517,"sourceCodeEnd":553,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/SpannerPostgreSQLDialect.java#L517-L553","documentation":"This is the SKIP_LOCKED branch of SpannerPostgreSQLDialect.validateSpannerLockTimeout(): millis == Timeouts.SKIP_LOCKED_MILLI throws UnsupportedOperationException('Spanner does not support skip locked.'). It is invoked from getLockingClauseStrategy() whenever a query with lock options is translated, so a skip-locked request fails before SQL generation. Spanner's transaction model aborts conflicting accessors rather than skipping rows, hence the hard rejection.","triggerScenarios":"On the Spanner PostgreSQL dialect: lock timeout -2 (LockOptions.SKIP_LOCKED or jakarta.persistence.lock.timeout=-2) attached to a query/finder with pessimistic locking; getLockingClauseStrategy reads lockOptions.getTimeOut(), hits the sentinel and throws.","commonSituations":"PG-native queue processors (SKIP LOCKED worklists) pointed at Spanner's PG interface; Spring properties copied between environments; integration test suites asserting lock behaviors that pass on PostgreSQL CI and fail on Spanner stages.","solutions":["Remove the -2 timeout hint and port the claim logic to a lease-based conditional UPDATE, the supported pattern on Spanner.","Use optimistic locking + retry instead of skip-locked semantics.","Catch UnsupportedOperationException in dialect-agnostic code paths and fall back to plain FOR UPDATE.","Profile-separate JPA lock properties so Spanner never receives SKIP_LOCKED."],"exampleFix":"// before\nQuery q = session.createQuery(\"from Task t where t.status = 'OPEN'\", Task.class);\nq.getLockOptions().setTimeOut(LockOptions.SKIP_LOCKED); // -2\n\n// after\nQuery q = session.createQuery(\"from Task t where t.status = 'OPEN' and t.leaseUntil < :now\", Task.class);","handlingStrategy":"validation","validationCode":"if (lockOptions.getTimeOut() == Timeouts.SKIP_LOCKED_MILLI && dialect instanceof SpannerPostgreSQLDialect) {\n  lockOptions.setTimeOut(LockOptions.NO_TIMEOUT);\n}","typeGuard":null,"tryCatchPattern":"try { return query.getResultList(); }\ncatch (UnsupportedOperationException e) {\n  if (e.getMessage().contains(\"skip locked\")) { /* lease-claim fallback */ }\n  throw e;\n}","preventionTips":["Strip -2 lock timeouts from Spanner PG configurations.","Implement row claiming with conditional UPDATEs.","Run dialect-specific lock test suites."],"tags":["hibernate","cloud-spanner","spanner-postgresql","locking","skip-locked","unsupportedoperationexception"],"backgroundTag":"skip-locked-not-supported","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}