So I was recently trying to switch my Discord bots database over from using Kmongo (which is now deprecated), to the official kotlin driver. Turns out the kotlin driver was not ready for production and caused several problems. However, once reverting back to Kmongo, there were many multiple records in my database which are of a different type to what they should be. (Discord snowflakes saved as strings rather than 64 bit integers). This has lead to roughly 21k errors per day and I'm scared to fix it. The error below is what we get whenever someone tries to access a part of the database.
27-12-2023 17:16:37 | DefaultDispatcher-worker-11 | ERROR | c.k.k.e.c.application.slash.SlashCommand | Error during execution of view slash command (dev.kord.core.event.interaction.GuildChatInputCommandInteractionCreateEvent@6ed9deae)
org.bson.BsonInvalidOperationException: readInt64 can only be called when CurrentBSONType is INT64, not when CurrentBSONType is STRING.
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 com.github.jershell.kbson.FlexibleDecoder.decodeLong(BsonFlexibleDecoder.kt:123)
at dev.kord.common.entity.Snowflake$Serializer.deserialize(Snowflake.kt:226)
at dev.kord.common.entity.Snowflake$Serializer.deserialize(Snowflake.kt:222)
at kotlinx.serialization.encoding.Decoder$DefaultImpls.decodeSerializableValue(Decoding.kt:257)
at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:16)
at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
at kotlinx.serialization.encoding.AbstractDecoder.decodeNullableSerializableElement(AbstractDecoder.kt:78)
at org.hyacinthbots.lilybot.database.entities.ModerationConfigData$$serializer.deserialize(Config.kt:49)
at org.hyacinthbots.lilybot.database.entities.ModerationConfigData$$serializer.deserialize(Config.kt:49)
at kotlinx.serialization.encoding.Decoder$DefaultImpls.decodeSerializableValue(Decoding.kt:257)
at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:16)
at org.litote.kmongo.serialization.SerializationCodec.decode(SerializationCodec.kt:66)
at com.mongodb.internal.operation.CommandResultArrayCodec.decode(CommandResultArrayCodec.java:52)
at com.mongodb.internal.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:60)
at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:87)
at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:42)
at org.bson.internal.LazyCodec.decode(LazyCodec.java:53)
at org.bson.codecs.BsonDocumentCodec.readValue(BsonDocumentCodec.java:104)
at com.mongodb.internal.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:63)
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:129)
at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:284)
at java.base/sun.nio.ch.WindowsAsynchronousSocketChannelImpl$ReadTask.completed(WindowsAsynchronousSocketChannelImpl.java:586)
at java.base/sun.nio.ch.Iocp$EventHandlerTask.run(Iocp.java:387)
at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:113)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:840)
I've attempted to write my own codec to read these properly:
object SnowflakeCodec : Codec<Snowflake> {
override fun encode(writer: BsonWriter, value: Snowflake, encoderContext: EncoderContext) {
writer.writeInt64(value.value.toLong())
}
override fun decode(reader: BsonReader, decoderContext: DecoderContext): Snowflake = try {
println("We try")
Snowflake(reader.readString())
} catch (_: BsonInvalidOperationException) {
Snowflake(reader.readInt64())
}
override fun getEncoderClass(): Class<Snowflake> = Snowflake::class.java
}
Then once I created my codec registry I started getting collections using said Codec
internal val collection = configDb.configDatabase.getCollection<ModerationConfigData>().withCodecRegistry(codecRegistry)
When getting the collection with the codec registry, I recieve the following error
27-12-2023 17:20:41 | DefaultDispatcher-worker-10 | ERROR | c.k.k.e.c.application.slash.SlashCommand | Error during execution of view slash command (dev.kord.core.event.interaction.GuildChatInputCommandInteractionCreateEvent@35891a8b)
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for CodecCacheKey{clazz=class org.hyacinthbots.lilybot.database.entities.ModerationConfigData, types=null}.
at org.bson.internal.ProvidersCodecRegistry.lambda$get$0(ProvidersCodecRegistry.java:87)
at java.base/java.util.Optional.orElseGet(Optional.java:364)
at org.bson.internal.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:80)
at org.bson.internal.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:50)
at com.mongodb.internal.operation.Operations.createFindOperation(Operations.java:188)
at com.mongodb.internal.operation.Operations.findFirst(Operations.java:173)
at com.mongodb.internal.operation.AsyncOperations.findFirst(AsyncOperations.java:119)
at com.mongodb.reactivestreams.client.internal.FindPublisherImpl.asAsyncFirstReadOperation(FindPublisherImpl.java:227)
at com.mongodb.reactivestreams.client.internal.MongoOperationPublisher.createReadOperationMono(MongoOperationPublisher.java:441)
at com.mongodb.reactivestreams.client.internal.MongoOperationPublisher.createReadOperationMono(MongoOperationPublisher.java:434)
at com.mongodb.reactivestreams.client.internal.BatchCursorPublisher.batchCursor(BatchCursorPublisher.java:133)
at com.mongodb.reactivestreams.client.internal.BatchCursorPublisher.first(BatchCursorPublisher.java:105)
at org.litote.kmongo.coroutine.CoroutineFindPublisher.first(CoroutineFindPublisher.kt:49)
at org.litote.kmongo.coroutine.CoroutineCollection.findOne(CoroutineCollection.kt:1107)
at org.hyacinthbots.lilybot.extensions.config.Config$setup$2$10$2.invokeSuspend(Config.kt:862)
at org.hyacinthbots.lilybot.extensions.config.Config$setup$2$10$2.invoke(Config.kt)
at org.hyacinthbots.lilybot.extensions.config.Config$setup$2$10$2.invoke(Config.kt)
at com.kotlindiscord.kord.extensions.commands.application.slash.EphemeralSlashCommand.run(EphemeralSlashCommand.kt:155)
at com.kotlindiscord.kord.extensions.commands.application.slash.EphemeralSlashCommand$run$1.invokeSuspend(EphemeralSlashCommand.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
I looked around at how to fix this and discovered many recommendations to use a POJO codec registry. When i attempt to use a POJO Codec registry the following error occurs
27-12-2023 17:22:22 | DefaultDispatcher-worker-3 | ERROR | c.k.k.e.c.application.slash.SlashCommand | Error during execution of view slash command (dev.kord.core.event.interaction.GuildChatInputCommandInteractionCreateEvent@35a42a11)
org.bson.codecs.configuration.CodecConfigurationException: An exception occurred when decoding using the AutomaticPojoCodec.
Decoding into a 'ModerationConfigData' failed with the following exception:
Cannot find a public constructor for 'ModerationConfigData'. Please ensure the class has a public, empty constructor with no arguments, or else a constructor with a BsonCreator annotation
A custom Codec or PojoCodec may need to be explicitly configured and registered to handle this type.
at org.bson.codecs.pojo.AutomaticPojoCodec.decode(AutomaticPojoCodec.java:40)
at com.mongodb.internal.operation.CommandResultArrayCodec.decode(CommandResultArrayCodec.java:52)
at com.mongodb.internal.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:60)
at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:87)
at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:42)
at org.bson.internal.LazyCodec.decode(LazyCodec.java:53)
at org.bson.codecs.BsonDocumentCodec.readValue(BsonDocumentCodec.java:104)
at com.mongodb.internal.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:63)
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:129)
at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:284)
at java.base/sun.nio.ch.WindowsAsynchronousSocketChannelImpl$ReadTask.completed(WindowsAsynchronousSocketChannelImpl.java:586)
at java.base/sun.nio.ch.Iocp$EventHandlerTask.run(Iocp.java:387)
at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:113)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: org.bson.codecs.configuration.CodecConfigurationException: Cannot find a public constructor for 'ModerationConfigData'. Please ensure the class has a public, empty constructor with no arguments, or else a constructor with a BsonCreator annotation
at org.bson.codecs.pojo.CreatorExecutable.checkHasAnExecutable(CreatorExecutable.java:140)
at org.bson.codecs.pojo.CreatorExecutable.getInstance(CreatorExecutable.java:107)
at org.bson.codecs.pojo.InstanceCreatorImpl.<init>(InstanceCreatorImpl.java:40)
at org.bson.codecs.pojo.InstanceCreatorFactoryImpl.create(InstanceCreatorFactoryImpl.java:28)
at org.bson.codecs.pojo.ClassModel.getInstanceCreator(ClassModel.java:76)
at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:99)
at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:104)
at org.bson.codecs.pojo.AutomaticPojoCodec.decode(AutomaticPojoCodec.java:37)
... 26 common frames omitted
I don't fully understand what this error means or how to fix it. ModerationConfigData can be found below
@Serializable
data class ModerationConfigData(
val guildId: Snowflake,
val enabled: Boolean,
val channel: Snowflake?,
val role: Snowflake?,
val quickTimeoutLength: DateTimePeriod?,
val autoPunishOnWarn: Boolean?,
val publicLogging: Boolean?,
val banDmMessage: String?,
)