[JAVA-5303] Unable to deserialize small values to Long when using kotlinx.serialization Created: 28/Jan/24  Updated: 06/Feb/24

Status: Backlog
Project: Java Driver
Component/s: Kotlin
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Gareth Coles Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Assigned Teams:
Java Drivers

 Description   

Kotlin: 1.9.22
MongoDB Driver: 4.11.1 (mongodb-driver-kotlin-coroutine)
BSON Kotlinx Library: 4.11.1 (bson-kotlinx)


When following [this guide to auto-incrementing fields|https://www.mongodb.com/docs/v2.2/tutorial/create-an-auto-incrementing-field/,] let's assume we have a simple data structure like this:

 

@Serializable
data class Counter(
    @SerialName("_id")
    val name: String,
    val count: Long = -1,
) 

 

We might create a document by using findOneAndUpdate as follows:

 

val collection = db.getCollection<Counter>("counters")
 
collection.findOneAndUpdate(
    Filters.eq("_id", name),
    Updates.inc("count", 1),
 
    FindOneAndUpdateOptions().upsert(true).returnDocument(ReturnDocument.AFTER)
)!!.count 

 

I would expect this to create a document in the current collection, and then return the count field of the resulting document. Instead, an exception is thrown:

 

org.bson.BsonInvalidOperationException: Reading field 'count' failed expected INT64 type but found: INT32.	at org.bson.codecs.kotlinx.DefaultBsonDecoder.decodeLong(BsonDecoder.kt:295)	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Assembly trace from producer [reactor.core.publisher.MonoCreate] :	reactor.core.publisher.Mono.create(Mono.java:202)	com.mongodb.reactivestreams.client.internal.OperationExecutorImpl.lambda$execute$12(OperationExecutorImpl.java:122)Error has been observed at the following site(s):	*_____Mono.create ? at com.mongodb.reactivestreams.client.internal.OperationExecutorImpl.lambda$execute$12(OperationExecutorImpl.java:122)	|_ Mono.doOnError ? at com.mongodb.reactivestreams.client.internal.OperationExecutorImpl.lambda$execute$12(OperationExecutorImpl.java:128)	*____Mono.flatMap ? at com.mongodb.reactivestreams.client.internal.OperationExecutorImpl.lambda$execute$13(OperationExecutorImpl.java:121)	*_______Mono.from ? at com.mongodb.reactivestreams.client.internal.OperationExecutorImpl.execute(OperationExecutorImpl.java:115)Original Stack Trace:		at org.bson.codecs.kotlinx.DefaultBsonDecoder.decodeLong(BsonDecoder.kt:295)		at kotlinx.serialization.encoding.AbstractDecoder.decodeLongElement(AbstractDecoder.kt:54)		at org.comect.auditing.storage.nosql.entities.Counter$$serializer.deserialize(Counter.kt:7)		at org.comect.auditing.storage.nosql.entities.Counter$$serializer.deserialize(Counter.kt:7)		at org.bson.codecs.kotlinx.KotlinSerializerCodec.decode(KotlinSerializerCodec.kt:182)		at com.mongodb.internal.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:58)		at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:87)		at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:42)		at com.mongodb.internal.connection.ReplyMessage.<init>(ReplyMessage.java:48)		at com.mongodb.internal.connection.InternalStreamConnection.getCommandResult(InternalStreamConnection.java:567)		at com.mongodb.internal.connection.InternalStreamConnection.lambda$sendCommandMessageAsync$0(InternalStreamConnection.java:554)		at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:849)		at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:812)		at com.mongodb.internal.connection.InternalStreamConnection$3.completed(InternalStreamConnection.java:671)		at com.mongodb.internal.connection.InternalStreamConnection$3.completed(InternalStreamConnection.java:668)		at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:252)		at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:235)		at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)		at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:282)		at java.base/sun.nio.ch.WindowsAsynchronousSocketChannelImpl$ReadTask.completed(WindowsAsynchronousSocketChannelImpl.java:581)		at java.base/sun.nio.ch.Iocp$EventHandlerTask.run(Iocp.java:387)		at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)		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:829)Caused by: org.bson.BsonInvalidOperationException: readInt64 can only be called when CurrentBSONType is INT64, not when CurrentBSONType is INT32.	at org.bson.AbstractBsonReader.verifyBSONType(AbstractBsonReader.java:689)	at org.bson.AbstractBsonReader.checkPreconditions(AbstractBsonReader.java:721)	at org.bson.AbstractBsonReader.readInt64(AbstractBsonReader.java:372)	at org.bson.codecs.kotlinx.DefaultBsonDecoder.decodeLong(BsonDecoder.kt:164)	at kotlinx.serialization.encoding.AbstractDecoder.decodeLongElement(AbstractDecoder.kt:54)	at org.comect.auditing.storage.nosql.entities.Counter$$serializer.deserialize(Counter.kt:7)	at org.comect.auditing.storage.nosql.entities.Counter$$serializer.deserialize(Counter.kt:7)	at org.bson.codecs.kotlinx.KotlinSerializerCodec.decode(KotlinSerializerCodec.kt:182)	at com.mongodb.internal.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:58)	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:87)	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:42)	at com.mongodb.internal.connection.ReplyMessage.<init>(ReplyMessage.java:48)	at com.mongodb.internal.connection.InternalStreamConnection.getCommandResult(InternalStreamConnection.java:567)	at com.mongodb.internal.connection.InternalStreamConnection.lambda$sendCommandMessageAsync$0(InternalStreamConnection.java:554)	at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:849)	at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:812)Caused by: org.bson.BsonInvalidOperationException: readInt64 can only be called when CurrentBSONType is INT64, not when CurrentBSONType is INT32.
	at com.mongodb.internal.connection.InternalStreamConnection$3.completed(InternalStreamConnection.java:671)	at com.mongodb.internal.connection.InternalStreamConnection$3.completed(InternalStreamConnection.java:668)	at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:252)	at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:235)	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:282)	at java.base/sun.nio.ch.WindowsAsynchronousSocketChannelImpl$ReadTask.completed(WindowsAsynchronousSocketChannelImpl.java:581)	at java.base/sun.nio.ch.Iocp$EventHandlerTask.run(Iocp.java:387)	at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)	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:829) 

My understanding of this exception is that the BSON library is assuming that all short numbers must map to Int objects, and this assumption doesn't match the Long type it's being asked to deserialize the value to.

I've searched both this Jira and the Internet in general, and I have been able to find pretty much no information, but this does seem like a bug and a breakage of expected behaviour.

 



 Comments   
Comment by Slav Babanin [ 06/Feb/24 ]

Thank you for bringing this to our attention gdude2002@gmail.com.

The behavior you've encountered is related to how Updates.inc generates a BSON document. In your case, you're using an integer as an increment, which can lead to a situation where, if a document is initially created, it stores the value of a 'count' field as an Integer. Upon retrieval, the KotlinSerializerCodec expects a Long, causing the discrepancy you observed. 

To address this issue for the time being, please consider specifying the increment as a Long (e.g., 1L):

Updates.inc("count", 1L)

We will revisit this ticket for further progress at a later time. Please look for further updates on this ticket.

Comment by Gareth Coles [ 28/Jan/24 ]

My apologies for the description formatting, I appear to be unable to edit it to fix that.

Generated at Thu Feb 08 09:04:14 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.