{"record":{"id":"8cdc0f805ba02081","repo":"hibernate/hibernate-orm","slug":"spanner-does-not-support-lock-timeout-8cdc0f","errorCode":null,"errorMessage":"Spanner does not support lock timeout.","messagePattern":"Spanner does not support lock timeout\\.","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/SpannerPostgreSQLDialect.java","lineNumber":532,"sourceCode":"\n\t@Override\n\tpublic String getForUpdateSkipLockedString(String aliases) {\n\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,","sourceCodeStart":514,"sourceCodeEnd":550,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/SpannerPostgreSQLDialect.java#L514-L550","documentation":"SpannerPostgreSQLDialect.validateSpannerLockTimeout() throws UnsupportedOperationException('Spanner does not support lock timeout.') when Timeouts.isRealTimeout(millis) is true — a positive lock-wait value. The validator runs from getLockingClauseStrategy(querySpec, lockOptions) before any lock clause is rendered, so any concrete wait duration on a pessimistic lock fails immediately. Spanner's FOR UPDATE simply has no WAIT n syntax in either dialect mode.","triggerScenarios":"Calling a locked query/finder on the Spanner PostgreSQL interface with LockOptions/QueryHints carrying a positive timeout, e.g. `lo.setTimeOut(2000)` or jakarta.persistence.lock.timeout=2000 with PESSIMISTIC_WRITE; getLockingClauseStrategy validates lockOptions.getTimeOut() and throws.","commonSituations":"Applications migrated from real PostgreSQL that kept lock.timeout in persistence.xml; ops teams adding wait bounds to prevent pile-ups, discovering Spanner rejects them; AOP locking templates that apply one timeout everywhere.","solutions":["Delete the positive lock-timeout setting for the Spanner PG datasource (no lock.timeout hint / LockOptions default).","Bound waits with transaction timeouts instead — configure the transaction manager / Spanner session timeout.","Catch UnsupportedOperationException around locking calls as a guard rail in shared code.","Switch the affected entity to optimistic locking if the timeout was only protecting against contention."],"exampleFix":"# before\nspring.jpa.properties.jakarta.persistence.lock.timeout=2000\n\n# after — unset for Spanner; bound waits at transaction level\nspring.transaction.default-timeout=5s","handlingStrategy":"validation","validationCode":"if (Timeouts.isRealTimeout(lockOptions.getTimeOut())\n    && (dialect instanceof SpannerDialect || 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(\"lock timeout\")) { /* retry without timeout */ }\n  throw e;\n}","preventionTips":["Never set positive lock timeouts for Spanner PG datasources.","Bound waits at the transaction level.","Keep PostgreSQL and Spanner PG in separate JPA property profiles."],"tags":["hibernate","cloud-spanner","spanner-postgresql","locking","lock-timeout","unsupportedoperationexception"],"backgroundTag":"lock-timeout-not-supported","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}