Skip to content

Shard key not honored in ReferenceLookupDelegate when DocumentReference resolves to a empty collection #4612

Closed
@stefanbildl

Description

@stefanbildl

Shard key not honored in ReferenceLookupDelegate

I have identified an issue in org.springframework.data.mongodb.core.convert.ReferenceLookupDelegate when using sharded mongodb collections.

The shard key of our "Task" collection includes the fields "datacenter" and "flowId":

@Getter
@Setter
@Document(collection = "task", language = "none")
@AllArgsConstructor
@Sharded(shardKey = {"datacenter", "flowId"}, immutableKey = true)
public class Task {
....
}
  • We use a String field named "datacenter" that specifies where the data is stored (can be in one of our datacenters around the world)
  • e.g. If data is in Mexico, datacenter is "MEX", if data is in Shanghai, datacenter is "SHA", etc.
  • If we omit the datacenter field in any query, the query is run globally on every location (which is very slow)
  • If a location is not reachable global queries fail. This leads to extended downtime in our case!
@Getter
@Setter
@Document(collection = "task", language = "none")
@AllArgsConstructor
@Sharded(shardKey = {"datacenter", "flowId"}, immutableKey = true)
public class Task {
...
	@DocumentReference(lookup = "{'flowId': ?#{flowId}, 'datacenter': ?#{datacenter}}")
	@Indexed
	private Set<Task> dependsOn = new LinkedHashSet<>();
}

If "dependsOn" is a empty set, the ReferenceLookupDelegate method computeFilter
returns

ListDocumentReferenceQuery(NO_RESULTS_PREDICATE,...)

which - in our case - causes a global query. If a datacenter is currently unreachable, the query fails!

	DocumentReferenceQuery computeFilter(MongoPersistentProperty property, Object source, SpELContext spELContext) {
...
			if (objects.isEmpty()) {
				return new ListDocumentReferenceQuery(NO_RESULTS_PREDICATE, sort); // here is the Problem NO_RESULTS_PREDICATE is a query without datacenter field
			}
...

I don't understand, why this NO_RESULTS_PREDICATE is needed here. Is this just a way to say: query for a empty list?

Proposition

Instead of handling the empty list case in computeFilter, just add the following lines to readReference:

	@Nullable
	public Object readReference(MongoPersistentProperty property, Object source, LookupFunction lookupFunction,
			MongoEntityReader entityReader) {

		Object value = source instanceof DocumentReferenceSource documentReferenceSource ? documentReferenceSource.getTargetSource()
				: source;
 
                 // THIS IS NEW
		 if (property.isCollectionLike() && value instanceof Collection && ((Collection)value).isEmpty()) {
                      return new ArrayList<>();
                 }
                 // END OF NEW STUFF

                DocumentReferenceQuery filter = computeFilter(property, source, spELContext);

.....

Thank you very much!

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions