hibernate/hibernate-orm · error · UnsupportedOperationException

Spanner doesn't support for-update with no-wait timeout

Error message

Spanner doesn't support for-update with no-wait timeout

What it means

SpannerPostgreSQLDialect.getForUpdateNowaitString() throws UnsupportedOperationException('Spanner doesn't support for-update with no-wait timeout'). Even though this dialect speaks the PostgreSQL wire protocol (used by Google's Spanner PostgreSQL interface), Cloud Spanner itself has no NOWAIT lock mode, so the inherited PostgreSQL behavior is overridden to fail fast. Hibernate calls it when a pessimistic lock with NO_WAIT is rendered — including via the alias overload, which delegates here.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/dialect/SpannerPostgreSQLDialect.java:500

	@Override
	public String getReadLockString(Timeout timeout) {
		return getWriteLockString( timeout.milliseconds() );
	}

	@Override
	public String getReadLockString(String aliases, Timeout timeout) {
		return getWriteLockString( timeout.milliseconds() );
	}

	@Override
	public String getReadLockString(String aliases, int timeout) {
		return getWriteLockString( timeout );
	}

	@Override
	public String getForUpdateNowaitString() {
		throw new UnsupportedOperationException(
				"Spanner doesn't support for-update with no-wait timeout" );
	}

	@Override
	public String getForUpdateNowaitString(String aliases) {
		return getForUpdateNowaitString();
	}

	@Override
	public String getForUpdateSkipLockedString() {
		throw new UnsupportedOperationException(
				"Spanner doesn't support for-update with skip locked timeout" );
	}

	@Override
	public String getForUpdateSkipLockedString(String aliases) {
		return getForUpdateSkipLockedString();
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Remove NO_WAIT from lock options/hints; use plain FOR UPDATE, which the Spanner PG interface supports.
  2. Catch UnsupportedOperationException and fall back to default locking when it fires.
  3. Implement fail-fast via transaction-level timeouts rather than SQL lock suffixes.
  4. Keep the Spanner PG datasource in a separate JPA properties profile stripped of lock.timeout settings.

Example fix

// before
@Lock(LockModeType.PESSIMISTIC_WRITE)
@QueryHints(@QueryHint(name = "jakarta.persistence.lock.timeout", value = "0")) // NO_WAIT encoding
List<Order> lockedOrders();

// after
@Lock(LockModeType.PESSIMISTIC_WRITE) // no timeout hint
List<Order> lockedOrders();
Defensive patterns

Strategy: validation

Validate before calling

if (dialect instanceof SpannerPostgreSQLDialect && timeouts.isNoWait(timeout)) {
  timeout = Timeouts.NO_TIMEOUT; // Spanner PG: plain FOR UPDATE only
}

Try / catch

try { return query.getResultList(); }
catch (UnsupportedOperationException e) {
  if (e.getMessage().contains("no-wait")) { /* strip timeout hint and re-run */ }
  throw e;
}

Prevention

When it happens

Trigger: Using the Spanner PostgreSQL-dialect datasource and requesting LockOptions.NO_WAIT (Timeouts.NO_WAIT_MILLI) with PESSIMISTIC_WRITE/READ, e.g. query.setLockOptions(lo) where lo.setTimeOut(LockOptions.NO_WAIT); getForUpdateNowaitString(String) also funnels into this throw.

Common situations: Teams assuming 'PostgreSQL dialect = PostgreSQL locking' when moving an existing Spring Boot service to Cloud Spanner's PG interface; copied @QueryHints with lock.timeout sentinels; ShedLock/quartz-style routines that use NOWAIT.

Understand the failure class

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/968f27fffec0d159. Report an issue: GitHub.