Java Transactions 1.1.3 does not work with Couchbase 7.0 Beta when the Java collection is not the default

Using Java Transactions 1.1.3 created as:

Transactions.create(
                javaCluster.get,
                TransactionConfigBuilder.create()
                  .expirationTime(
                    Duration.ofSeconds(hubConfiguration.get[duration.Duration](
                      "hub.storage.couchbase.transactions.expiration-time"
                    ).toSeconds)
                  )
                  .keyValueTimeout(
                    Duration.ofSeconds(hubConfiguration.get[duration.Duration](
                      "hub.storage.couchbase.transactions.key-value-timeout"
                    ).toSeconds)
                  ).build()
              )

Using the transaction context as follows:

transactionContext.insert(
      javaConstraintCollection,
      core.storage.Storage.constraintPrefix + constraintKey,
      java.json.JsonObject.create()
        .put("type", "constraint")
        .put("for", entityCanonicalName)
    )

works only if the javaConstraintCollection equals to javaBucket.defaultCollection().

If the collection is a specific collection, like one from the below list:

image

transaction fails as follows:

2020-11-25 10:20:34 WARN  tracing:513 - [com.couchbase.tracing][OverThresholdRequestsRecordedEvent][10s] Requests over Threshold found: [{"top":[{"operation_name":"SubdocMutateRequest","last_local_id":"24677AE500000002/00000000E56F1092","encode_us":189200,"last_local_address":"192.168.255.6:61671","last_remote_address":"4.couchbase.dev.alpha:11210","last_operation_id":"0x53","total_us":20002409}],"service":"kv","count":1}]


com.couchbase.transactions.error.internal.AttemptExpired: Attempt has expired in stage createdStagedInsert
com.couchbase.transactions.error.internal.AttemptExpired: com.couchbase.transactions.error.internal.AttemptExpired: Attempt has expired in stage createdStagedInsert
	at com.couchbase.transactions.AttemptContextReactive.setExpiryOvertimeModeAndFail(AttemptContextReactive.java:2446)
	at com.couchbase.transactions.AttemptContextReactive.lambda$null$87(AttemptContextReactive.java:1650)
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:88)
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onError(FluxMapFuseable.java:134)
	at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onError(FluxPeekFuseable.java:227)
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:165)
	at reactor.core.publisher.Operators$MonoSubscriber.onError(Operators.java:1829)
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreInner.onError(MonoIgnoreThen.java:235)
	at reactor.core.publisher.Operators.error(Operators.java:196)
	at reactor.core.publisher.MonoError.subscribe(MonoError.java:52)
	at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
	at reactor.core.publisher.Mono.subscribe(Mono.java:4213)
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172)
	at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56)
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
	at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:153)
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.ignoreDone(MonoIgnoreThen.java:190)
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreInner.onComplete(MonoIgnoreThen.java:240)
	at reactor.core.publisher.MonoDelay$MonoDelayRunnable.run(MonoDelay.java:118)
	at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68)
	at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: com.couchbase.transactions.error.internal.AttemptExpired: Attempt has expired in stage createdStagedInsert
	at com.couchbase.transactions.AttemptContextReactive.errorIfExpiredAndNotInExpiryOvertimeMode(AttemptContextReactive.java:477)
	at com.couchbase.transactions.AttemptContextReactive.lambda$null$83(AttemptContextReactive.java:1590)
	at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44)
	... 16 more

Other K/V operations on the non-default collections work fine.
The user accessing the server is Admin with full access.

Hi @zoltan.zvara. That’s a scenario we test, so this surprises me. There was a bug with using the non-default collection, but it was fixed in the 1.1.2 release.
Could you possibly upload the full transaction logs? https://docs.couchbase.com/java-sdk/current/howtos/distributed-acid-transactions-from-the-sdk.html#logging

1 Like

@graham.pople, thanks, that helped!

DEBUG logs show that the collection did not exist. I missed that scope must be selected first, before the collection.

Few questions:

  • Should we run Transaction API in general in DEBUG logging to identify such bummers?
  • Why is the default scope and collection name starts with an underscore?

Thanks!

Hi @zoltan.zvara. Ah yes, if you do bucket.collection(collectionName) you’ll get that collection on the default scope.

The transactions library always stores all logs for each transaction in an in-memory buffer, that’s returned in the TransactionResult (and the TransactionFailed/TransactionCommitAmbiguous exceptions if the transaction fails). So I recommend a strategy of always writing out all the logs for any failed transactions, so if you see an occasional failure you’ll have the diagnostics to debug it. Perhaps to a secondary logfile as the logging is quite verbose.

The default scope and collection start with an underscore to indicate that they’re system-created. You should never need to write “_default” anywhere though, you can use the convenience methods e.g. defaultCollection().

2 Likes