Skip to content

Commit 30835e2

Browse files
rakhiagrRakhi Agrawal
and
Rakhi Agrawal
authored
Adding shadowDao for read requests (#541)
* Adding shadowDao for read requests * Adding shadowDao for read requests * Fix keys * Added logs --------- Co-authored-by: Rakhi Agrawal <rakagrawal@linkedin.com>
1 parent b94ec80 commit 30835e2

File tree

2 files changed

+114
-12
lines changed

2 files changed

+114
-12
lines changed

restli-resources/src/main/java/com/linkedin/metadata/restli/BaseAspectRoutingResource.java

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -567,11 +567,53 @@ private List<INTERNAL_ASPECT_UNION> getInternalAspectsFromLocalDao(URN urn, Set<
567567
.map(aspectClass -> new AspectKey<>(aspectClass, urn, LATEST_VERSION))
568568
.collect(Collectors.toSet());
569569

570-
return getLocalDAO().get(keys)
571-
.values()
572-
.stream()
573-
.filter(java.util.Optional::isPresent)
574-
.map(aspect -> ModelUtils.newAspectUnion(_internalAspectUnionClass, aspect.get()))
570+
if (getShadowReadLocalDAO() == null) {
571+
return getLocalDAO().get(keys)
572+
.values()
573+
.stream()
574+
.filter(java.util.Optional::isPresent)
575+
.map(aspect -> ModelUtils.newAspectUnion(_internalAspectUnionClass, aspect.get()))
576+
.collect(Collectors.toList());
577+
}
578+
return getInternalAspectsWithShadowComparison(keys);
579+
}
580+
581+
/**
582+
* Get internal aspect values from shadow DAO for specified aspect keys.
583+
* This method is used to retrieve aspects from shadow DAO when the local DAO does not have the shadow read capability.
584+
* @param keys Aspect keys to be retrieved from shadow DAO
585+
* @return A list of internal aspects.
586+
*/
587+
private List<INTERNAL_ASPECT_UNION> getInternalAspectsWithShadowComparison(Set<AspectKey<URN, ? extends RecordTemplate>> keys) {
588+
589+
Map<AspectKey<URN, ? extends RecordTemplate>, java.util.Optional<? extends RecordTemplate>> localResults =
590+
getLocalDAO().get(keys);
591+
Map<AspectKey<URN, ? extends RecordTemplate>, java.util.Optional<? extends RecordTemplate>> shadowResults =
592+
getShadowReadLocalDAO().get(keys);
593+
594+
return keys.stream()
595+
.map(key -> {
596+
java.util.Optional<? extends RecordTemplate> local = localResults.getOrDefault(key, java.util.Optional.empty());
597+
java.util.Optional<? extends RecordTemplate> shadow = shadowResults.getOrDefault(key, java.util.Optional.empty());
598+
599+
RecordTemplate valueToUse = null;
600+
if (shadow.isPresent() && local.isPresent() && !Objects.equals(local.get(), shadow.get())) {
601+
log.warn("Aspect mismatch for URN {} and aspect {}: local = {}, shadow = {}",
602+
key.getUrn(), key.getAspectClass().getSimpleName(), local.get(), shadow.get());
603+
valueToUse = local.get();
604+
} else if (shadow.isPresent()) {
605+
log.warn("Only shadow value present for URN {} and aspect {}", key.getUrn(), key.getAspectClass().getSimpleName());
606+
valueToUse = shadow.get();
607+
} else if (local.isPresent()) {
608+
log.info("Only local value present for URN {} and aspect {}. Using local.", key.getUrn(), key.getAspectClass().getSimpleName());
609+
valueToUse = local.get();
610+
}
611+
612+
return valueToUse != null
613+
? ModelUtils.newAspectUnion(_internalAspectUnionClass, valueToUse)
614+
: null;
615+
})
616+
.filter(Objects::nonNull)
575617
.collect(Collectors.toList());
576618
}
577619

restli-resources/src/main/java/com/linkedin/metadata/restli/BaseEntityResource.java

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,13 @@
4848
import java.util.LinkedHashMap;
4949
import java.util.List;
5050
import java.util.Map;
51+
import java.util.Objects;
5152
import java.util.Set;
5253
import java.util.function.Function;
5354
import java.util.stream.Collectors;
5455
import javax.annotation.Nonnull;
5556
import javax.annotation.Nullable;
57+
import lombok.extern.slf4j.Slf4j;
5658

5759
import static com.linkedin.metadata.dao.BaseReadDAO.*;
5860
import static com.linkedin.metadata.dao.utils.IngestionUtils.*;
@@ -73,6 +75,7 @@
7375
* @param <INTERNAL_ASPECT_UNION> must be a valid internal aspect union type supported by the internal snapshot
7476
* @param <ASSET> must be a valid asset type defined in com.linkedin.metadata.asset
7577
*/
78+
@Slf4j
7679
public abstract class BaseEntityResource<
7780
// @formatter:off
7881
KEY,
@@ -172,6 +175,11 @@ protected BaseLocalDAO<INTERNAL_ASPECT_UNION, URN> getShadowLocalDAO() {
172175
return null; // override in resource class only if needed
173176
}
174177

178+
@Nullable
179+
protected BaseLocalDAO<INTERNAL_ASPECT_UNION, URN> getShadowReadLocalDAO() {
180+
return null; // override in resource class only if needed
181+
}
182+
175183
/**
176184
* Creates an URN from its string representation.
177185
*/
@@ -1142,18 +1150,70 @@ private Map<URN, List<UnionTemplate>> getUrnAspectMap(@Nonnull Collection<URN> u
11421150
final Map<URN, List<UnionTemplate>> urnAspectsMap =
11431151
urns.stream().collect(Collectors.toMap(Function.identity(), urn -> new ArrayList<>()));
11441152

1145-
if (isInternalModelsEnabled) {
1146-
getLocalDAO().get(keys)
1147-
.forEach((key, aspect) -> aspect.ifPresent(metadata -> urnAspectsMap.get(key.getUrn())
1148-
.add(ModelUtils.newAspectUnion(_internalAspectUnionClass, metadata))));
1153+
if (getShadowReadLocalDAO() == null) {
1154+
if (isInternalModelsEnabled) {
1155+
getLocalDAO().get(keys)
1156+
.forEach((key, aspect) -> aspect.ifPresent(metadata -> urnAspectsMap.get(key.getUrn())
1157+
.add(ModelUtils.newAspectUnion(_internalAspectUnionClass, metadata))));
1158+
} else {
1159+
getLocalDAO().get(keys)
1160+
.forEach((key, aspect) -> aspect.ifPresent(metadata -> urnAspectsMap.get(key.getUrn())
1161+
.add(ModelUtils.newAspectUnion(_aspectUnionClass, metadata))));
1162+
}
1163+
return urnAspectsMap;
11491164
} else {
1150-
getLocalDAO().get(keys)
1151-
.forEach((key, aspect) -> aspect.ifPresent(
1152-
metadata -> urnAspectsMap.get(key.getUrn()).add(ModelUtils.newAspectUnion(_aspectUnionClass, metadata))));
1165+
return getUrnAspectMapFromShadowDao(urns, keys, isInternalModelsEnabled);
11531166
}
1167+
}
1168+
1169+
@Nonnull
1170+
private Map<URN, List<UnionTemplate>> getUrnAspectMapFromShadowDao(
1171+
@Nonnull Collection<URN> urns,
1172+
@Nonnull Set<AspectKey<URN, ? extends RecordTemplate>> keys,
1173+
boolean isInternalModelsEnabled) {
1174+
1175+
Map<AspectKey<URN, ? extends RecordTemplate>, java.util.Optional<? extends RecordTemplate>> localResults =
1176+
getLocalDAO().get(keys);
1177+
1178+
BaseLocalDAO<INTERNAL_ASPECT_UNION, URN> shadowDao = getShadowReadLocalDAO();
1179+
Map<AspectKey<URN, ? extends RecordTemplate>, java.util.Optional<? extends RecordTemplate>> shadowResults =
1180+
shadowDao.get(keys);
1181+
1182+
final Map<URN, List<UnionTemplate>> urnAspectsMap =
1183+
urns.stream().collect(Collectors.toMap(Function.identity(), urn -> new ArrayList<>()));
1184+
1185+
keys.forEach(key -> {
1186+
java.util.Optional<? extends RecordTemplate> localValue = localResults.getOrDefault(key, java.util.Optional.empty());
1187+
java.util.Optional<? extends RecordTemplate> shadowValue = shadowResults.getOrDefault(key, java.util.Optional.empty());
1188+
1189+
RecordTemplate valueToUse = null;
1190+
1191+
if (localValue.isPresent() && shadowValue.isPresent()) {
1192+
if (!Objects.equals(localValue.get(), shadowValue.get())) {
1193+
log.warn("Aspect mismatch for URN {} and aspect {}: local = {}, shadow = {}",
1194+
key.getUrn(), key.getAspectClass().getSimpleName(),
1195+
localValue.get(), shadowValue.get());
1196+
valueToUse = localValue.get(); // fallback to local if there's mismatch
1197+
} else {
1198+
valueToUse = shadowValue.get(); // match → use shadow
1199+
}
1200+
} else if (shadowValue.isPresent()) {
1201+
valueToUse = shadowValue.get();
1202+
} else if (localValue.isPresent()) {
1203+
valueToUse = localValue.get();
1204+
}
1205+
1206+
if (valueToUse != null) {
1207+
urnAspectsMap.get(key.getUrn()).add(ModelUtils.newAspectUnion(
1208+
(Class<? extends ASPECT_UNION>) (isInternalModelsEnabled ? _internalAspectUnionClass : _aspectUnionClass),
1209+
valueToUse));
1210+
}
1211+
});
1212+
11541213
return urnAspectsMap;
11551214
}
11561215

1216+
11571217
@Nonnull
11581218
private SNAPSHOT newSnapshot(@Nonnull URN urn, @Nonnull List<UnionTemplate> aspects) {
11591219
return ModelUtils.newSnapshot(_snapshotClass, urn, aspects);

0 commit comments

Comments
 (0)