[JAVA-2963] UnsupportedOperationException with retryable writes Created: 07/Sep/18  Updated: 28/Oct/23  Resolved: 18/Sep/18

Status: Closed
Project: Java Driver
Component/s: Write Operations
Affects Version/s: 3.6.0
Fix Version/s: 3.8.2

Type: Bug Priority: Minor - P4
Reporter: Lukáš Křečan Assignee: Jeffrey Yemin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified


 Description   

I am getting UnsupportedOperationException when using retryable writes with col.findOneAndUpdate(find, update, new FindOneAndUpdateOptions().returnDocument(AFTER)).

java.lang.UnsupportedOperationException: null
	at com.mongodb.internal.connection.NoOpSessionContext.advanceTransactionNumber(NoOpSessionContext.java:63)
	at com.mongodb.operation.BaseFindAndModifyOperation.addTxnNumberToCommand(BaseFindAndModifyOperation.java:129)
	at com.mongodb.operation.FindAndUpdateOperation$1.create(FindAndUpdateOperation.java:365)
	at com.mongodb.operation.CommandOperationHelper$4.call(CommandOperationHelper.java:451)
	at com.mongodb.operation.OperationHelper.withReleasableConnection(OperationHelper.java:419)
	at com.mongodb.operation.CommandOperationHelper.executeRetryableCommand(CommandOperationHelper.java:445)
	at com.mongodb.operation.BaseFindAndModifyOperation.execute(BaseFindAndModifyOperation.java:69)
	at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.execute(MongoClientDelegate.java:193)
	at com.mongodb.client.internal.MongoCollectionImpl.executeFindOneAndUpdate(MongoCollectionImpl.java:743)
	at com.mongodb.client.internal.MongoCollectionImpl.findOneAndUpdate(MongoCollectionImpl.java:723) 



 Comments   
Comment by Githook User [ 18/Sep/18 ]

Author:

{'name': 'Jeff Yemin', 'email': 'jeff.yemin@10gen.com', 'username': 'jyemin'}

Message: Disable retryable writes when no session is available for any reason.

The only known reason that this covers is when the driver is configured
with multiple credentials (which is deprecated), in which case the
server supports retryable writes but sessions are still unsupported due
to the multiple credentials.

JAVA-2963
Branch: 3.8.x
https://github.com/mongodb/mongo-java-driver/commit/874fe5d0e98a184ab16bf71852b32c2e4970e9ab

Comment by Githook User [ 18/Sep/18 ]

Author:

{'name': 'Jeff Yemin', 'email': 'jeff.yemin@10gen.com', 'username': 'jyemin'}

Message: Disable retryable writes when no session is available for any reason.

The only known reason that this covers is when the driver is configured
with multiple credentials (which is deprecated), in which case the
server supports retryable writes but sessions are still unsupported due
to the multiple credentials.

JAVA-2963
Branch: master
https://github.com/mongodb/mongo-java-driver/commit/9e4bb705cde420ac82c3e7a0661ee3a1fe6c816f

Comment by Jeffrey Yemin [ 17/Sep/18 ]

The current retryable writes and sessions specifications require that writes are not retried when there is no active session, implicit or otherwise. To comply with the specifications, the driver should (silently) not retry writes in this situation. So that's the change we'll make in scope of this issue.

We'll also consider a specification change to require drivers to report an error on MongoClient construction in this case, but we'll handle that driver change in a separate issue if and when the specification change is approved.

Thanks again for the report.

Comment by Jeffrey Yemin [ 17/Sep/18 ]

I agree.  I offered the workaround to unblock you, but I still consider this a bug and we will address it in a future release.

Comment by Lukáš Křečan [ 17/Sep/18 ]

Just a last parting thought. Given that it's an unsupported combination and it's currently broken anyway, I think you can throw an exception directly from the constructor. It would prevent some unpleasant surprises for people with legacy configuration like us.

Comment by Lukáš Křečan [ 17/Sep/18 ]

After removing multiple credentials, I stopped getting the exception.

Comment by Lukáš Křečan [ 17/Sep/18 ]

Thanks a lot, I will try it today and confirm if it indeed was the case. As a user I would prefer at least a warning, when an unsupported combination is encountered. Thanks again.

Comment by Jeffrey Yemin [ 16/Sep/18 ]

I suspect that the issue is with the multiple databases list. MongoDB sessions, on which retryable writes depend, do not support connections on which more than one user is authenticated. The driver should detect this and disable (silently) retryable writes, but it appears that it's not doing so.

Since retryable writes are not supported with multiple credentials, the workaround for this bug is to either

  • not enable retryable writes in MongoClientOptions
  • authenticate a single credential on the MongoClient
Comment by Lukáš Křečan [ 16/Sep/18 ]

There are 3 elements in serverAddresses list and 3 elements in databases.

Comment by Jeffrey Yemin [ 14/Sep/18 ]

Hi lukas.krecan@liftago.com

  • How many elements are in the serverAddresses list? Is it one or more than one?
  • How many elements are in the databases list? Is it one or more than one?
Comment by Jeffrey Yemin [ 07/Sep/18 ]

For reference: https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst#requirements-for-retryable-writes

Comment by Jeffrey Yemin [ 07/Sep/18 ]

Not explicitly.  The driver will create an implicit session assuming that the server version is >= 3.6 and the cluster is either a replica set or sharded and not a standalone.

Comment by Lukáš Křečan [ 07/Sep/18 ]

Actually, the code looks like it requires clientSession for retryable writes to work, but I can not find any mention of it in the documentation.

Comment by Lukáš Křečan [ 07/Sep/18 ]

Constructor:

            List<MongoCredential> credentials = new ArrayList<>();
            for (String database : databases) {
                credentials.add(MongoCredential.createCredential(username, database, password));
            }
            CodecRegistry pojoCodecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(),
                fromProviders(PojoCodecProvider.builder().automatic(true).build(),
                    new GeoJsonCodecProvider()));
            mongo = new MongoClient(serverAddresses, credentials,
                new MongoClientOptions.Builder()
                    .codecRegistry(pojoCodecRegistry)
                    .retryWrites(true)
                    .connectionsPerHost(connectionsPerHost).build()
            );

Server version: 3.6.6

Comment by Jeffrey Yemin [ 07/Sep/18 ]

Hi lukas.krecan@liftago.com

So we can diagnose this faster, please provide

  • The MongoClient constructor invocation the application is using, in particular the connection string
  • The MongoDB server version the application to which the application is connecting

 

 

Generated at Thu Feb 08 08:58:28 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.