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.DriverRefDto;
|
||||||
import at.procon.eventhub.dto.EventHubEventDto;
|
import at.procon.eventhub.dto.EventHubEventDto;
|
||||||
import at.procon.eventhub.dto.SourcePackageRefDto;
|
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.ResolvedVehicleReference;
|
||||||
|
import at.procon.eventhub.persistence.VehicleIdentityRepository.ResolvedVehicleReferenceResolution;
|
||||||
import at.procon.eventhub.service.EventAcquisitionRecordKeyService;
|
import at.procon.eventhub.service.EventAcquisitionRecordKeyService;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
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) {
|
public int batchInsert(UUID packageId, String tenantKey, int eventSourceId, List<EventHubEventDto> events) {
|
||||||
Map<String, UUID> entityIdCache = new HashMap<>();
|
Map<String, UUID> entityIdCache = new HashMap<>();
|
||||||
|
Map<String, List<VehicleRefCacheEntry>> vehicleRefCache = new HashMap<>();
|
||||||
List<ResolvedEventImportRow> rows = new ArrayList<>(events.size());
|
List<ResolvedEventImportRow> rows = new ArrayList<>(events.size());
|
||||||
|
|
||||||
for (EventHubEventDto event : events) {
|
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));
|
rows.add(resolveEventImportRow(packageId, eventSourceId, event, refs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -350,10 +354,11 @@ public class EventRepository {
|
||||||
String tenantKey,
|
String tenantKey,
|
||||||
int eventSourceId,
|
int eventSourceId,
|
||||||
EventHubEventDto event,
|
EventHubEventDto event,
|
||||||
Map<String, UUID> entityIdCache
|
Map<String, UUID> entityIdCache,
|
||||||
|
Map<String, List<VehicleRefCacheEntry>> vehicleRefCache
|
||||||
) {
|
) {
|
||||||
UUID driverEntityId = resolveDriverEntityId(tenantKey, eventSourceId, event, entityIdCache);
|
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);
|
UUID sourcePackageEntityId = resolveSourcePackageEntityId(tenantKey, eventSourceId, event, entityIdCache);
|
||||||
return new ResolvedEntityRefs(driverEntityId, vehicleRef.vehicleId(), vehicleRef.vehicleRegistrationId(), sourcePackageEntityId);
|
return new ResolvedEntityRefs(driverEntityId, vehicleRef.vehicleId(), vehicleRef.vehicleRegistrationId(), sourcePackageEntityId);
|
||||||
}
|
}
|
||||||
|
|
@ -399,14 +404,32 @@ public class EventRepository {
|
||||||
private ResolvedVehicleReference resolveVehicleReference(
|
private ResolvedVehicleReference resolveVehicleReference(
|
||||||
String tenantKey,
|
String tenantKey,
|
||||||
int eventSourceId,
|
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,
|
tenantKey,
|
||||||
eventSourceId,
|
eventSourceId,
|
||||||
event.vehicleRef(),
|
event.vehicleRef(),
|
||||||
event.occurredAt()
|
event.occurredAt()
|
||||||
);
|
);
|
||||||
|
if (cacheKey != null) {
|
||||||
|
vehicleRefCache
|
||||||
|
.computeIfAbsent(cacheKey, ignored -> new ArrayList<>())
|
||||||
|
.add(buildVehicleRefCacheEntry(event.vehicleRef(), event.occurredAt(), resolved));
|
||||||
|
}
|
||||||
|
return resolved.reference();
|
||||||
}
|
}
|
||||||
|
|
||||||
private UUID resolveSourcePackageEntityId(
|
private UUID resolveSourcePackageEntityId(
|
||||||
|
|
@ -469,7 +492,7 @@ public class EventRepository {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String cacheKey = entityType + "|" + normalizedSourceEntityId + "|" + normalizeNullable(sourceExternalKey);
|
String cacheKey = entityType + "|" + normalizedSourceEntityId;
|
||||||
UUID cached = entityIdCache.get(cacheKey);
|
UUID cached = entityIdCache.get(cacheKey);
|
||||||
if (cached != null) {
|
if (cached != null) {
|
||||||
return cached;
|
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) {
|
private String toJson(JsonNode value) {
|
||||||
try {
|
try {
|
||||||
return objectMapper.writeValueAsString(value == null ? objectMapper.createObjectNode() : value);
|
return objectMapper.writeValueAsString(value == null ? objectMapper.createObjectNode() : value);
|
||||||
|
|
@ -560,4 +647,23 @@ public class EventRepository {
|
||||||
String attributesJson
|
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;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResolvedVehicleReference resolveOrCreateVehicleReference(
|
public ResolvedVehicleReferenceResolution resolveOrCreateVehicleReference(
|
||||||
String tenantKey,
|
String tenantKey,
|
||||||
int eventSourceId,
|
int eventSourceId,
|
||||||
VehicleRefDto vehicleRef,
|
VehicleRefDto vehicleRef,
|
||||||
OffsetDateTime occurredAt
|
OffsetDateTime occurredAt
|
||||||
) {
|
) {
|
||||||
if (vehicleRef == null || !vehicleRef.hasAnyReference()) {
|
if (vehicleRef == null || !vehicleRef.hasAnyReference()) {
|
||||||
return ResolvedVehicleReference.empty();
|
return ResolvedVehicleReferenceResolution.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
String normalizedTenantKey = normalizeRequired(tenantKey, "tenantKey");
|
String normalizedTenantKey = normalizeRequired(tenantKey, "tenantKey");
|
||||||
|
|
@ -68,8 +68,10 @@ public class VehicleIdentityRepository {
|
||||||
sourceVehicleEntityId,
|
sourceVehicleEntityId,
|
||||||
vin
|
vin
|
||||||
);
|
);
|
||||||
|
AssignedVehicleReference assignedVehicle = null;
|
||||||
if (vehicleId == null && registrationId != 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)) {
|
if (vehicleId == null && (sourceVehicleEntityId != null || vin != null)) {
|
||||||
vehicleId = createVehicle(
|
vehicleId = createVehicle(
|
||||||
|
|
@ -86,7 +88,12 @@ public class VehicleIdentityRepository {
|
||||||
if (registrationId != null) {
|
if (registrationId != null) {
|
||||||
touchRegistration(registrationId, sourceRegistrationEntityId, registrationNation, registrationNumber);
|
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
|
@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(
|
return jdbcTemplate.query(
|
||||||
"""
|
"""
|
||||||
select vehicle_id
|
select vehicle_id, valid_from, valid_to
|
||||||
from eventhub.vehicle_registration_assignment
|
from eventhub.vehicle_registration_assignment
|
||||||
where vehicle_registration_id = ?
|
where vehicle_registration_id = ?
|
||||||
and (? is null or valid_from is null or valid_from <= ?)
|
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
|
order by valid_from desc nulls last, updated_at desc
|
||||||
limit 1
|
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,
|
registrationId,
|
||||||
occurredAt,
|
occurredAt,
|
||||||
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