Using a min-read-timestamp is not allowed with a multi-use read-only transaction. This is also checked by the ITReadOnlyTxnTest. However, the error that is returned when using multiplexed sessions can be different than the current expectation. The reason is that if the transaction is started before the multiplexed session has been created, the code flow uses a waiter. This waiter will return the error as a SpannerException, while the direct code flow will return an IllegalArgumentException. That causes the following flaky test failure in some cases (note that in a production situation the difference between the two errors are not really relevant, both indicate a programming error that needs to be fixed in code):
[ERROR] Tests run: 11, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 40.88 s <<< FAILURE! -- in com.google.cloud.spanner.it.ITReadOnlyTxnTest
[ERROR] com.google.cloud.spanner.it.ITReadOnlyTxnTest.multiMinReadTimestamp -- Time elapsed: 0.064 s <<< ERROR!
com.google.cloud.spanner.SpannerException: UNKNOWN: Bounded staleness mode MIN_READ_TIMESTAMP is not supported for multi-use read-only transactions. Create a single-use read or read-only transaction instead.
at com.google.cloud.spanner.SpannerExceptionFactory.newSpannerExceptionPreformatted(SpannerExceptionFactory.java:291)
at com.google.cloud.spanner.SpannerExceptionFactory.newSpannerExceptionPreformatted(SpannerExceptionFactory.java:297)
at com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException(SpannerExceptionFactory.java:61)
at com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException(SpannerExceptionFactory.java:181)
at com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException(SpannerExceptionFactory.java:110)
at com.google.cloud.spanner.SpannerExceptionFactory.asSpannerException(SpannerExceptionFactory.java:100)
at com.google.cloud.spanner.DelayedReadContext.getReadContext(DelayedReadContext.java:47)
at com.google.cloud.spanner.DelayedReadContext.lambda$executeQuery$6(DelayedReadContext.java:117)
at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers.java:186)
at com.google.cloud.spanner.ForwardingResultSet.next(ForwardingResultSet.java:59)
at com.google.cloud.spanner.it.ITReadOnlyTxnTest.multiMinReadTimestamp(ITReadOnlyTxnTest.java:316)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:54)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.apache.maven.surefire.junitcore.JUnitCore.run(JUnitCore.java:49)
at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.createRequestAndRun(JUnitCoreWrapper.java:120)
at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.executeLazy(JUnitCoreWrapper.java:105)
at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.execute(JUnitCoreWrapper.java:77)
at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.execute(JUnitCoreWrapper.java:69)
at org.apache.maven.surefire.junitcore.JUnitCoreProvider.invoke(JUnitCoreProvider.java:146)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385)
at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162)
at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495)
Caused by: java.lang.IllegalArgumentException: Bounded staleness mode MIN_READ_TIMESTAMP is not supported for multi-use read-only transactions. Create a single-use read or read-only transaction instead.
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:220)
at com.google.cloud.spanner.AbstractReadContext$MultiUseReadOnlyTransaction.(AbstractReadContext.java:331)
at com.google.cloud.spanner.AbstractReadContext$MultiUseReadOnlyTransaction$Builder.build(AbstractReadContext.java:305)
at com.google.cloud.spanner.SessionImpl.readOnlyTransaction(SessionImpl.java:374)
at com.google.cloud.spanner.DelayedMultiplexedSessionTransaction.lambda$readOnlyTransaction$5(DelayedMultiplexedSessionTransaction.java:119)
at com.google.api.core.ApiFutures$ApiFunctionToGuavaFunction.apply(ApiFutures.java:396)
at com.google.common.util.concurrent.AbstractTransformFuture$TransformFuture.doTransform(AbstractTransformFuture.java:254)
at com.google.common.util.concurrent.AbstractTransformFuture$TransformFuture.doTransform(AbstractTransformFuture.java:244)
at com.google.common.util.concurrent.AbstractTransformFuture.run(AbstractTransformFuture.java:125)
at com.google.common.util.concurrent.DirectExecutor.execute(DirectExecutor.java:31)
at com.google.common.util.concurrent.AbstractFuture.executeListener(AbstractFuture.java:1298)
at com.google.common.util.concurrent.AbstractFuture.complete(AbstractFuture.java:1059)
at com.google.common.util.concurrent.AbstractFuture.set(AbstractFuture.java:784)
at com.google.api.core.AbstractApiFuture$InternalSettableFuture.set(AbstractApiFuture.java:87)
at com.google.api.core.AbstractApiFuture.set(AbstractApiFuture.java:70)
at com.google.api.core.SettableApiFuture.set(SettableApiFuture.java:46)
at com.google.cloud.spanner.MultiplexedSessionDatabaseClient$1.onSessionReady(MultiplexedSessionDatabaseClient.java:215)
at com.google.cloud.spanner.SessionClient$CreateMultiplexedSessionsRunnable.run(SessionClient.java:310)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:750)
Using a min-read-timestamp is not allowed with a multi-use read-only transaction. This is also checked by the
ITReadOnlyTxnTest
. However, the error that is returned when using multiplexed sessions can be different than the current expectation. The reason is that if the transaction is started before the multiplexed session has been created, the code flow uses a waiter. This waiter will return the error as aSpannerException
, while the direct code flow will return anIllegalArgumentException
. That causes the following flaky test failure in some cases (note that in a production situation the difference between the two errors are not really relevant, both indicate a programming error that needs to be fixed in code):