hibernate/hibernate-orm · error · UnsupportedOperationException

Spanner does not support skip locked.

Error message

Spanner does not support skip locked.

What it means

SpannerDialect.getForUpdateSkipLockedString() throws UnsupportedOperationException because Cloud Spanner has no SKIP LOCKED locking mode — readers in a conflicting transaction get aborted rather than skipping rows. Hibernate requests this string when a pessimistic lock with the SKIP_LOCKED timeout is requested, and the dialect rejects it since it cannot be rendered. The alias variant behaves identically.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java:1284

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

	@Override
	public String getForUpdateNowaitString() {
		throw new UnsupportedOperationException( "Spanner does not support no wait." );
	}

	@Override
	public String getForUpdateNowaitString(String aliases) {
		throw new UnsupportedOperationException( "Spanner does not support no wait." );
	}

	@Override
	public String getForUpdateSkipLockedString() {
		throw new UnsupportedOperationException( "Spanner does not support skip locked." );
	}

	@Override
	public String getForUpdateSkipLockedString(String aliases) {
		throw new UnsupportedOperationException( "Spanner does not support skip locked." );
	}

	private static void validateSpannerLockTimeout(int millis) {
		if ( Timeouts.isRealTimeout( millis ) ) {
			throw new UnsupportedOperationException( "Spanner does not support lock timeout." );
		}
		if ( millis == Timeouts.SKIP_LOCKED_MILLI ) {
			throw new UnsupportedOperationException( "Spanner does not support skip locked." );
		}
		if ( millis == Timeouts.NO_WAIT_MILLI ) {
			throw new UnsupportedOperationException( "Spanner does not support no wait." );
		}
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Replace SKIP LOCKED job claiming with a Spanner-appropriate pattern: UPDATE ... WHERE processed=false with a timestamp lease, or an interleaved claim table.
  2. Use optimistic locking (@Version) and retry on StaleObjectStateException/OptimisticLockException instead of skipping rows.
  3. Remove the -2 lock timeout setting (jakarta.persistence.lock.timeout=-2 in Spring properties) for Spanner profiles.
  4. Catch UnsupportedOperationException at the claim boundary and fall back to plain FOR UPDATE within a short transaction.

Example fix

// before (Spring Data JPA)
@Lock(LockModeType.PESSIMISTIC_WRITE)
@QueryHints(@QueryHint(name = "jakarta.persistence.lock.timeout", value = "-2")) // SKIP LOCKED
List<Task> findNextTasks(...);

// after — lease-claim update instead of skip locked
@Modifying @Query("update Task t set t.leaseUntil = :until where t.id in :ids and t.leaseUntil < :now")
int leaseTasks(...);
Defensive patterns

Strategy: validation

Validate before calling

boolean skipLocked = lockOptions.getTimeOut() == LockOptions.SKIP_LOCKED;
if (skipLocked && (dialect instanceof SpannerDialect || dialect instanceof SpannerPostgreSQLDialect)) {
  throw new IllegalArgumentException("SKIP LOCKED unsupported on Spanner; use a lease-based claim query");
}

Try / catch

try {
  return query.list();
} catch (UnsupportedOperationException e) {
  if (e.getMessage().contains("skip locked")) { /* fall back to lease-claim UPDATE */ }
  throw e;
}

Prevention

When it happens

Trigger: session.find()/lock() or Query.setLockOptions() with LockOptions.setTimeOut(LockOptions.SKIP_LOCKED) (i.e. Timeouts.SKIP_LOCKED_MILLI = -2), or explicit SKIP LOCKED emulation, on a connection using SpannerDialect.

Common situations: Queue/worklist processing ('select next N unlocked rows') written against PostgreSQL SKIP LOCKED and pointed at Spanner; scheduler or job-claiming code that uses -2 timeout constants; Spring @Lock(PESSIMISTIC_WRITE) with LockModeType properties requesting skip-locked via jakarta.persistence.lock.timeout=-2.

Related errors


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