spring-projects / spring-data-mongodb

Provides support to increase developer productivity in Java when using MongoDB. Uses familiar Spring concepts such as a template classes for core API usage and lightweight repository style data access.
https://spring.io/projects/spring-data-mongodb/
Apache License 2.0
1.62k stars 1.09k forks source link

TTL index creation/verification fails on spring data mongodb 4.4.0 when expression result in 0 seconds #4844

Open Alcarmor opened 5 days ago

Alcarmor commented 5 days ago

with spring data mongodb 4.4.0 the annotation Indexed has deprecated the expireAfterSeconds in favor of expireAfter https://docs.spring.io/spring-data/data-mongodb/docs/current/api/org/springframework/data/mongodb/core/index/Indexed.html#expireAfter()

so, according to documentation an entity mapped like this

@Indexed(expireAfterSeconds = 0) private LocalDateTime retentionDate;

should be changed in this

@Indexed(expireAfter = "0s") private LocalDateTime retentionDate;

here comes the problem: the new attribute seems ignore any value that result in 0 seconds

generating a non valid ttl index and in case of an existing index a relative validation failure

Caused by: com.mongodb.MongoCommandException: Command failed with error 85 (IndexOptionsConflict): 'An equivalent index already exists with the same name but different options. Requested index: { v: 2, key: { retentionDate: 1 }, name: "retentionDate" }, existing index: { v: 2, key: { retentionDate: 1 }, name: "retentionDate", expireAfterSeconds: 0 }' on server localhost:27017. The full response is {"ok": 0.0, "errmsg": "An equivalent index already exists with the same name but different options. Requested index: { v: 2, key: { retentionDate: 1 }, name: \"retentionDate\" }, existing index: { v: 2, key: { retentionDate: 1 }, name: \"retentionDate\", expireAfterSeconds: 0 }", "code": 85, "codeName": "IndexOptionsConflict", "$clusterTime": {"clusterTime": {"$timestamp": {"t": 1732554648, "i": 1}}, "signature": {"hash": {"$binary": {"base64": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "subType": "00"}}, "keyId": 0}}, "operationTime": {"$timestamp": {"t": 1732554648, "i": 1}}} at com.mongodb.internal.connection.ProtocolHelper.getCommandFailureException(ProtocolHelper.java:210) at com.mongodb.internal.connection.InternalStreamConnection.receiveCommandMessageResponse(InternalStreamConnection.java:520) at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceiveInternal(InternalStreamConnection.java:448) at com.mongodb.internal.connection.InternalStreamConnection.lambda$sendAndReceive$0(InternalStreamConnection.java:375) at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:378) at com.mongodb.internal.connection.UsageTrackingInternalConnection.sendAndReceive(UsageTrackingInternalConnection.java:111) at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection.sendAndReceive(DefaultConnectionPool.java:748) at com.mongodb.internal.connection.CommandProtocolImpl.execute(CommandProtocolImpl.java:68) at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:208) at com.mongodb.internal.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:113) at com.mongodb.internal.connection.DefaultServerConnection.command(DefaultServerConnection.java:82) at com.mongodb.internal.connection.DefaultServerConnection.command(DefaultServerConnection.java:73) at com.mongodb.internal.connection.DefaultServer$OperationCountTrackingConnection.command(DefaultServer.java:298) at com.mongodb.internal.operation.SyncOperationHelper.lambda$executeCommand$5(SyncOperationHelper.java:209) at com.mongodb.internal.operation.SyncOperationHelper.lambda$withSourceAndConnection$0(SyncOperationHelper.java:131) at com.mongodb.internal.operation.SyncOperationHelper.withSuppliedResource(SyncOperationHelper.java:156) at com.mongodb.internal.operation.SyncOperationHelper.lambda$withSourceAndConnection$1(SyncOperationHelper.java:130) at com.mongodb.internal.operation.SyncOperationHelper.withSuppliedResource(SyncOperationHelper.java:156) at com.mongodb.internal.operation.SyncOperationHelper.withSourceAndConnection(SyncOperationHelper.java:129) at com.mongodb.internal.operation.SyncOperationHelper.executeCommand(SyncOperationHelper.java:207) at com.mongodb.internal.operation.CreateIndexesOperation.execute(CreateIndexesOperation.java:105) at com.mongodb.internal.operation.CreateIndexesOperation.execute(CreateIndexesOperation.java:60) at com.mongodb.client.internal.MongoClusterImpl$OperationExecutorImpl.execute(MongoClusterImpl.java:379) at com.mongodb.client.internal.MongoCollectionImpl.executeCreateIndexes(MongoCollectionImpl.java:941) at com.mongodb.client.internal.MongoCollectionImpl.createIndexes(MongoCollectionImpl.java:923) at com.mongodb.client.internal.MongoCollectionImpl.createIndexes(MongoCollectionImpl.java:918) at com.mongodb.client.internal.MongoCollectionImpl.createIndex(MongoCollectionImpl.java:903) at org.springframework.data.mongodb.core.DefaultIndexOperations.lambda$ensureIndex$0(DefaultIndexOperations.java:131) at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:603) at org.springframework.data.mongodb.core.DefaultIndexOperations.execute(DefaultIndexOperations.java:217) at org.springframework.data.mongodb.core.DefaultIndexOperations.ensureIndex(DefaultIndexOperations.java:121) at org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator.createIndex(MongoPersistentEntityIndexCreator.java:152)

ttl indexes are generted succesfully only if the expression result in more than 0 secods, but this is a big problem for existing indexes

christophstrobl commented 4 days ago

thanks for bringing this to our attention. It would be great if you could provide a complete minimal sample (something that we can unzip or git clone, build, and deploy) that reproduces the problem.

Alcarmor commented 2 days ago

demo.zip in this spring boot sample project i have prepared 2 entities MyEntityWithDeprecatedTTL and MyEntityWithNewTTL with the same attributes as indexes, one with the new ttl annotation attribute and the other with the deprecated one.

Executing "mvn test" the DemoApplicationTests class will compare the generated indexes. (the project uses test containers, so requires docker) You will see that the test wich compare the 1 second expression ttl indexes will pass, but the test with the 0 seconds expression will fail.

Or it can be deployed to see the differences between the two entities directly on mongodb