Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -663,37 +663,75 @@ Optional<String> hasOverlappingSiblings(long catalogId, String checkLocation) {

var locationIdentifier = identifierFromLocationString(checkLocation);
var locationIndexKey = locationIdentifier.toIndexKey();
// TODO VALIDATE THE CHECKS HERE !

// Check for children and exact matches first using forward iteration (preserves
// existing test expectations on which conflicting location is reported).
// Also iterate fully in case early entries are filtered out.
var iter = locationsIndex.iterator(locationIndexKey, null, false);
if (!iter.hasNext()) {
return Optional.empty();
while (iter.hasNext()) {
var elem = iter.next();
var elemKey = elem.key();
var elemIdentifier = indexKeyToIdentifier(elemKey);
if (!elemIdentifier.startsWith(locationIdentifier)) {
break; // No more matches due to ordering
}

var conflicting =
elem.value().entityIds().stream()
.map(IndexKey::key)
.map(byId::get)
.filter(Objects::nonNull)
.map(byName::get)
.filter(Objects::nonNull)
.map(objRef -> persistence.fetch(objRef, ContentObj.class))
.filter(Objects::nonNull)
.map(
contentObj -> {
var conflictingBaseLocation =
contentObj.properties().get(ENTITY_BASE_LOCATION);
return conflictingBaseLocation != null
? conflictingBaseLocation
: String.join("/", elemIdentifier.elements());
})
.findFirst();
if (conflicting.isPresent()) {
return conflicting;
}
}

var elem = iter.next();
var elemKey = elem.key();
var elemIdentifier = indexKeyToIdentifier(elemKey);
if (!elemIdentifier.startsWith(locationIdentifier)) {
return Optional.empty();
// Check for parent (prefix) overlaps. These have shorter keys and are missed by
// forward iteration starting at the full target key.
for (int i = 1; i <= locationIdentifier.length(); i++) {
var prefixElements = locationIdentifier.elements().subList(0, i);
var prefix = ContentIdentifier.identifier(prefixElements);
var prefixKey = prefix.toIndexKey();
var entry = locationsIndex.get(prefixKey);
if (entry != null) {
var conflicting =
entry.entityIds().stream()
.map(IndexKey::key)
.map(byId::get)
.filter(Objects::nonNull)
.map(byName::get)
.filter(Objects::nonNull)
.map(objRef -> persistence.fetch(objRef, ContentObj.class))
.filter(Objects::nonNull)
.map(
contentObj -> {
var conflictingBaseLocation =
contentObj.properties().get(ENTITY_BASE_LOCATION);
return conflictingBaseLocation != null
? conflictingBaseLocation
: String.join("/", prefix.elements());
})
.findFirst();
if (conflicting.isPresent()) {
return conflicting;
}
}
}

return elem.value().entityIds().stream()
.map(IndexKey::key)
.map(byId::get)
.filter(Objects::nonNull)
.map(byName::get)
.filter(Objects::nonNull)
.map(objRef -> persistence.fetch(objRef, ContentObj.class))
.filter(Objects::nonNull)
.map(
contentObj -> {
// Check if conflict is the parent namespace - TODO recurse??
var conflictingBaseLocation =
contentObj.properties().get(ENTITY_BASE_LOCATION);
return conflictingBaseLocation != null
? conflictingBaseLocation
: String.join("/", elemIdentifier.elements());
})
.findFirst();
return Optional.empty();
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,19 @@ public void overlappingLocations() {
"ns2",
Map.of(ENTITY_BASE_LOCATION, "s3://bucket/foo/"));
assertThat(nsFoo).extracting(EntityResult::isSuccess, BOOLEAN).isTrue();

// Test parent namespace overlap detection for a new child location.
// This case was not detected by the original NoSQL implementation.
soft.assertThat(
metaStore.hasOverlappingSiblings(
callContext,
new NamespaceEntity.Builder(Namespace.of("x"))
.setCatalogId(catalog.getId())
.setBaseLocation("s3://bucket/foo/newchild/")
.build()))
.isPresent()
.contains(Optional.of("s3://bucket/foo/"));

var nsBar =
createEntity(
List.of(catalog),
Expand Down Expand Up @@ -242,6 +255,10 @@ public void overlappingLocations() {

// Drop one of the entities with the duplicate base location
metaStore.dropEntityIfExists(callContext, List.of(catalog), nsFoobar2.getEntity(), null, false);

// Drop the parent too so the final "no overlap" check for a bar location is clean.
metaStore.dropEntityIfExists(callContext, List.of(catalog), nsFoo.getEntity(), null, false);

// No more overlaps
soft.assertThat(
metaStore.hasOverlappingSiblings(
Expand Down
Loading