spring-data-mongodb icon indicating copy to clipboard operation
spring-data-mongodb copied to clipboard

With HASH sharding strategy the filter which participate in update operation is incorrect

Open tigrantovmasyan opened this issue 3 years ago • 2 comments

In MongoTemplate when executing collectionToUse.replaceOne(filter, replacement, new ReplaceOptions().upsert(true)); after applying ShardKey the filter looks like this { _id=15003b6e6b8a41afa458dd5696cced6b, customer=hash } and I am getting this exception

Write operation error on server . Write error: WriteError{code=11000, message='Failed to update document's shard key field. There is either an orphan for this document or _id for this collection is not globally unique. :: caused by :: E11000 duplicate key error collection: mydb.mycollection index: id dup key: { _id: "15003b6e6b8a41afa458dd5696cced6b" }', details={}}.

My annotation on the entity looks like this @Sharded(shardKey = {"customer", "_id"}, shardingStrategy = ShardingStrategy.HASH)

tigrantovmasyan avatar Jul 25 '22 12:07 tigrantovmasyan

Thanks for reporting. Which version of spring-data are you using? Also please share a bit more information about the domain types involved and the calling code. A complete minimal sample (something that we can unzip or git clone, build, and deploy) that reproduces the problem would be nice.

christophstrobl avatar Jul 27 '22 12:07 christophstrobl

I am using spring-boot-starter-data-mongodb - 2.6.6 where spring-data-mongodb's version is 3.3.3.

I have a Task entity

@Sharded(shardKey = {"_id", "customer"})
@Document(collection = "task")
public class Task {

	@Id
	private String id;

	private String name;

	private String description;

	@Indexed
	private String customer;
}

which I am trying to update calling taskRepository.save(task);

TaskRepository looks like this

public interface TaskRepository extends MongoRepository<Task, String> {

}

In documentation it is said that Spring Data adds the shard key to filter queries used for replaceOne operations triggered by save operations

But in MongoTemplate when executing collectionToUse.replaceOne(filter, replacement, new ReplaceOptions().upsert(true)); after applying ShardKey the filter looks like this

{
    _id=15003b6e6b8a41afa458dd5696cced6b,
   customer=hash
}

customer value is lost.

tigrantovmasyan avatar Jul 28 '22 05:07 tigrantovmasyan

Thanks for providing more information. I might be missing something but it looks as if everything behaves within defined boundaries. Please take the time to provide a complete minimal sample (something that we can unzip or git clone, build, and deploy) that reproduces the problem. Maybe it's just setting ShardKey#immutableKey to true that needs to be done.

christophstrobl avatar Aug 22 '22 06:08 christophstrobl

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

spring-projects-issues avatar Aug 29 '22 07:08 spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

spring-projects-issues avatar Sep 05 '22 07:09 spring-projects-issues

Hi, I have the same problem like @tigrantovmasyan. My version is Spring Boot 2.7.2, Spring data mongo 3.4.2

I try to explane my scenario (my cloud application is multi-tenancy and I use the tenantId as shard key):

This is the document entity:

@Document("products")
@Sharded(shardKey = { "tenantId"}, shardingStrategy = ShardingStrategy.HASH)
public class Product implements Serializable {

    @Id
    private ObjectId id;

    private String tenantId;

   private String sku;

   private String productId;

    /* and other properties*/
}

My code is very simple, I read the product from MongoDB (version 6.0) with find

private Product findProduct(String productId, String tenantId) {
        Query query = new Query();

        query.addCriteria(Criteria.where("tenantId").is(tenantId));
        query.addCriteria(Criteria.where("productId").is(productId));

        return mongoTemplate.find(query, Product .class);
}

Find the product, set sku and save

Product  p = findProduct("123", "tenantX");
p.setSku("123456");
mongoTemplate.save(p);  //here thows an Exception WriteError{code=11000, message='E11000 duplicate key error collection

I investigated inside source code and seems the issue is in MongoTemplate.java inside method saveDocument

if (updateContext.requiresShardKey(filter, entity)) {

					if (entity.getShardKey().isImmutable()) {
						filter = updateContext.applyShardKey(entity, filter, null);
					} else {
						filter = updateContext.applyShardKey(entity, filter,
								collection.find(filter, Document.class).projection(updateContext.getMappedShardKey(entity)).first());
					}
				}

the filter when the shardKey in not Immutable is modified, after "updateContext.applyShardKey"

{
"id": ObjectId(id_oj_row)
"tenantId": "shard"  //I suppose this is the issue, the value of tenantId filter is not tenantX in my scenario
}

Thanks

luirzy avatar Oct 20 '22 08:10 luirzy