Improve vehicle reference caching during ingest
This commit is contained in:
parent
2e6e1aa5c6
commit
bd3620b9af
|
|
@ -4,7 +4,10 @@ import at.procon.eventhub.dto.DriverCardRefDto;
|
|||
import at.procon.eventhub.dto.DriverRefDto;
|
||||
import at.procon.eventhub.dto.EventHubEventDto;
|
||||
import at.procon.eventhub.dto.SourcePackageRefDto;
|
||||
import at.procon.eventhub.dto.VehicleRefDto;
|
||||
import at.procon.eventhub.dto.VehicleRegistrationRefDto;
|
||||
import at.procon.eventhub.persistence.VehicleIdentityRepository.ResolvedVehicleReference;
|
||||
import at.procon.eventhub.persistence.VehicleIdentityRepository.ResolvedVehicleReferenceResolution;
|
||||
import at.procon.eventhub.service.EventAcquisitionRecordKeyService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
|
@ -51,10 +54,11 @@ public class EventRepository {
|
|||
*/
|
||||
public int batchInsert(UUID packageId, String tenantKey, int eventSourceId, List<EventHubEventDto> events) {
|
||||
Map<String, UUID> entityIdCache = new HashMap<>();
|
||||
Map<String, List<VehicleRefCacheEntry>> vehicleRefCache = new HashMap<>();
|
||||
List<ResolvedEventImportRow> rows = new ArrayList<>(events.size());
|
||||
|
||||
for (EventHubEventDto event : events) {
|
||||
ResolvedEntityRefs refs = resolveEntityRefs(tenantKey, eventSourceId, event, entityIdCache);
|
||||
ResolvedEntityRefs refs = resolveEntityRefs(tenantKey, eventSourceId, event, entityIdCache, vehicleRefCache);
|
||||
rows.add(resolveEventImportRow(packageId, eventSourceId, event, refs));
|
||||
}
|
||||
|
||||
|
|
@ -350,10 +354,11 @@ public class EventRepository {
|
|||
String tenantKey,
|
||||
int eventSourceId,
|
||||
EventHubEventDto event,
|
||||
Map<String, UUID> entityIdCache
|
||||
Map<String, UUID> entityIdCache,
|
||||
Map<String, List<VehicleRefCacheEntry>> vehicleRefCache
|
||||
) {
|
||||
UUID driverEntityId = resolveDriverEntityId(tenantKey, eventSourceId, event, entityIdCache);
|
||||
ResolvedVehicleReference vehicleRef = resolveVehicleReference(tenantKey, eventSourceId, event);
|
||||
ResolvedVehicleReference vehicleRef = resolveVehicleReference(tenantKey, eventSourceId, event, vehicleRefCache);
|
||||
UUID sourcePackageEntityId = resolveSourcePackageEntityId(tenantKey, eventSourceId, event, entityIdCache);
|
||||
return new ResolvedEntityRefs(driverEntityId, vehicleRef.vehicleId(), vehicleRef.vehicleRegistrationId(), sourcePackageEntityId);
|
||||
}
|
||||
|
|
@ -399,14 +404,32 @@ public class EventRepository {
|
|||
private ResolvedVehicleReference resolveVehicleReference(
|
||||
String tenantKey,
|
||||
int eventSourceId,
|
||||
EventHubEventDto event
|
||||
EventHubEventDto event,
|
||||
Map<String, List<VehicleRefCacheEntry>> vehicleRefCache
|
||||
) {
|
||||
return vehicleIdentityRepository.resolveOrCreateVehicleReference(
|
||||
String cacheKey = vehicleRefCacheKey(event.vehicleRef());
|
||||
if (cacheKey != null) {
|
||||
ResolvedVehicleReference cached = findCachedVehicleReference(
|
||||
vehicleRefCache.get(cacheKey),
|
||||
event.occurredAt()
|
||||
);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
||||
ResolvedVehicleReferenceResolution resolved = vehicleIdentityRepository.resolveOrCreateVehicleReference(
|
||||
tenantKey,
|
||||
eventSourceId,
|
||||
event.vehicleRef(),
|
||||
event.occurredAt()
|
||||
);
|
||||
if (cacheKey != null) {
|
||||
vehicleRefCache
|
||||
.computeIfAbsent(cacheKey, ignored -> new ArrayList<>())
|
||||
.add(buildVehicleRefCacheEntry(event.vehicleRef(), event.occurredAt(), resolved));
|
||||
}
|
||||
return resolved.reference();
|
||||
}
|
||||
|
||||
private UUID resolveSourcePackageEntityId(
|
||||
|
|
@ -469,7 +492,7 @@ public class EventRepository {
|
|||
return null;
|
||||
}
|
||||
|
||||
String cacheKey = entityType + "|" + normalizedSourceEntityId + "|" + normalizeNullable(sourceExternalKey);
|
||||
String cacheKey = entityType + "|" + normalizedSourceEntityId;
|
||||
UUID cached = entityIdCache.get(cacheKey);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
|
|
@ -503,6 +526,70 @@ public class EventRepository {
|
|||
}
|
||||
}
|
||||
|
||||
private String vehicleRefCacheKey(VehicleRefDto vehicleRef) {
|
||||
if (vehicleRef == null || !vehicleRef.hasAnyReference()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String sourceVehicleEntityId = normalizeNullable(vehicleRef.sourceVehicleEntityId());
|
||||
String vin = normalizeNullable(vehicleRef.vin());
|
||||
String sourceRegistrationEntityId = normalizeNullable(vehicleRef.sourceRegistrationEntityId());
|
||||
VehicleRegistrationRefDto registration = vehicleRef.vehicleRegistration();
|
||||
String registrationNation = registration == null ? null : normalizeNullable(registration.nation());
|
||||
String registrationNumber = registration == null ? null : normalizeNullable(registration.number());
|
||||
|
||||
return String.join("|",
|
||||
sourceVehicleEntityId == null ? "" : sourceVehicleEntityId,
|
||||
vin == null ? "" : vin,
|
||||
sourceRegistrationEntityId == null ? "" : sourceRegistrationEntityId,
|
||||
registrationNation == null ? "" : registrationNation,
|
||||
registrationNumber == null ? "" : registrationNumber
|
||||
);
|
||||
}
|
||||
|
||||
private ResolvedVehicleReference findCachedVehicleReference(
|
||||
List<VehicleRefCacheEntry> cacheEntries,
|
||||
OffsetDateTime occurredAt
|
||||
) {
|
||||
if (cacheEntries == null || cacheEntries.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
for (VehicleRefCacheEntry cacheEntry : cacheEntries) {
|
||||
if (cacheEntry.matches(occurredAt)) {
|
||||
return cacheEntry.reference();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private VehicleRefCacheEntry buildVehicleRefCacheEntry(
|
||||
VehicleRefDto vehicleRef,
|
||||
OffsetDateTime occurredAt,
|
||||
ResolvedVehicleReferenceResolution resolved
|
||||
) {
|
||||
boolean hasDirectVehicleIdentity = hasDirectVehicleIdentity(vehicleRef);
|
||||
if (hasDirectVehicleIdentity) {
|
||||
return new VehicleRefCacheEntry(resolved.reference(), null, null, null);
|
||||
}
|
||||
if (resolved.resolvedFromAssignment()) {
|
||||
return new VehicleRefCacheEntry(
|
||||
resolved.reference(),
|
||||
resolved.assignmentValidFrom(),
|
||||
resolved.assignmentValidTo(),
|
||||
null
|
||||
);
|
||||
}
|
||||
return new VehicleRefCacheEntry(resolved.reference(), null, null, occurredAt);
|
||||
}
|
||||
|
||||
private boolean hasDirectVehicleIdentity(VehicleRefDto vehicleRef) {
|
||||
if (vehicleRef == null) {
|
||||
return false;
|
||||
}
|
||||
return normalizeNullable(vehicleRef.sourceVehicleEntityId()) != null
|
||||
|| normalizeNullable(vehicleRef.vin()) != null;
|
||||
}
|
||||
|
||||
private String toJson(JsonNode value) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(value == null ? objectMapper.createObjectNode() : value);
|
||||
|
|
@ -560,4 +647,23 @@ public class EventRepository {
|
|||
String attributesJson
|
||||
) {
|
||||
}
|
||||
|
||||
private record VehicleRefCacheEntry(
|
||||
ResolvedVehicleReference reference,
|
||||
OffsetDateTime validFrom,
|
||||
OffsetDateTime validTo,
|
||||
OffsetDateTime exactOccurredAt
|
||||
) {
|
||||
private boolean matches(OffsetDateTime occurredAt) {
|
||||
if (exactOccurredAt != null) {
|
||||
return exactOccurredAt.equals(occurredAt);
|
||||
}
|
||||
if (validFrom == null && validTo == null) {
|
||||
return true;
|
||||
}
|
||||
boolean fromMatches = validFrom == null || occurredAt == null || !occurredAt.isBefore(validFrom);
|
||||
boolean toMatches = validTo == null || occurredAt == null || occurredAt.isBefore(validTo);
|
||||
return fromMatches && toMatches;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ public class VehicleIdentityRepository {
|
|||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
public ResolvedVehicleReference resolveOrCreateVehicleReference(
|
||||
public ResolvedVehicleReferenceResolution resolveOrCreateVehicleReference(
|
||||
String tenantKey,
|
||||
int eventSourceId,
|
||||
VehicleRefDto vehicleRef,
|
||||
OffsetDateTime occurredAt
|
||||
) {
|
||||
if (vehicleRef == null || !vehicleRef.hasAnyReference()) {
|
||||
return ResolvedVehicleReference.empty();
|
||||
return ResolvedVehicleReferenceResolution.empty();
|
||||
}
|
||||
|
||||
String normalizedTenantKey = normalizeRequired(tenantKey, "tenantKey");
|
||||
|
|
@ -68,8 +68,10 @@ public class VehicleIdentityRepository {
|
|||
sourceVehicleEntityId,
|
||||
vin
|
||||
);
|
||||
AssignedVehicleReference assignedVehicle = null;
|
||||
if (vehicleId == null && registrationId != null) {
|
||||
vehicleId = resolveAssignedVehicleId(registrationId, occurredAt);
|
||||
assignedVehicle = resolveAssignedVehicleReference(registrationId, occurredAt);
|
||||
vehicleId = assignedVehicle == null ? null : assignedVehicle.vehicleId();
|
||||
}
|
||||
if (vehicleId == null && (sourceVehicleEntityId != null || vin != null)) {
|
||||
vehicleId = createVehicle(
|
||||
|
|
@ -86,7 +88,12 @@ public class VehicleIdentityRepository {
|
|||
if (registrationId != null) {
|
||||
touchRegistration(registrationId, sourceRegistrationEntityId, registrationNation, registrationNumber);
|
||||
}
|
||||
return new ResolvedVehicleReference(vehicleId, registrationId);
|
||||
return new ResolvedVehicleReferenceResolution(
|
||||
new ResolvedVehicleReference(vehicleId, registrationId),
|
||||
assignedVehicle != null,
|
||||
assignedVehicle == null ? null : assignedVehicle.validFrom(),
|
||||
assignedVehicle == null ? null : assignedVehicle.validTo()
|
||||
);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
|
|
@ -431,10 +438,10 @@ public class VehicleIdentityRepository {
|
|||
);
|
||||
}
|
||||
|
||||
private UUID resolveAssignedVehicleId(UUID registrationId, OffsetDateTime occurredAt) {
|
||||
private AssignedVehicleReference resolveAssignedVehicleReference(UUID registrationId, OffsetDateTime occurredAt) {
|
||||
return jdbcTemplate.query(
|
||||
"""
|
||||
select vehicle_id
|
||||
select vehicle_id, valid_from, valid_to
|
||||
from eventhub.vehicle_registration_assignment
|
||||
where vehicle_registration_id = ?
|
||||
and (? is null or valid_from is null or valid_from <= ?)
|
||||
|
|
@ -442,7 +449,13 @@ public class VehicleIdentityRepository {
|
|||
order by valid_from desc nulls last, updated_at desc
|
||||
limit 1
|
||||
""",
|
||||
rs -> rs.next() ? (UUID) rs.getObject("vehicle_id") : null,
|
||||
rs -> rs.next()
|
||||
? new AssignedVehicleReference(
|
||||
(UUID) rs.getObject("vehicle_id"),
|
||||
rs.getObject("valid_from", OffsetDateTime.class),
|
||||
rs.getObject("valid_to", OffsetDateTime.class)
|
||||
)
|
||||
: null,
|
||||
registrationId,
|
||||
occurredAt,
|
||||
occurredAt,
|
||||
|
|
@ -676,4 +689,22 @@ public class VehicleIdentityRepository {
|
|||
}
|
||||
}
|
||||
|
||||
public record ResolvedVehicleReferenceResolution(
|
||||
ResolvedVehicleReference reference,
|
||||
boolean resolvedFromAssignment,
|
||||
OffsetDateTime assignmentValidFrom,
|
||||
OffsetDateTime assignmentValidTo
|
||||
) {
|
||||
public static ResolvedVehicleReferenceResolution empty() {
|
||||
return new ResolvedVehicleReferenceResolution(ResolvedVehicleReference.empty(), false, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private record AssignedVehicleReference(
|
||||
UUID vehicleId,
|
||||
OffsetDateTime validFrom,
|
||||
OffsetDateTime validTo
|
||||
) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue