perf: cache mysql vehicle identity lookups
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
lingniu
2026-06-29 17:00:54 +08:00
parent 114e707a5b
commit f00d851f2a
4 changed files with 139 additions and 46 deletions

View File

@@ -14,6 +14,7 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
private final DataSource dataSource;
private final String table;
private final InMemoryVehicleIdentityService index = new InMemoryVehicleIdentityService();
public MySqlVehicleIdentityService(DataSource dataSource, String table) {
if (dataSource == null) {
@@ -22,6 +23,7 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
this.dataSource = dataSource;
this.table = sanitizeTable(table);
initializeSchema();
loadIndex();
}
@Override
@@ -32,6 +34,7 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
upsert(binding.protocol(), "PHONE", binding.phone(), binding.vin());
upsert(binding.protocol(), "DEVICE_ID", binding.deviceId(), binding.vin());
upsert(binding.protocol(), "PLATE", binding.plate(), binding.vin());
index.bind(binding);
}
@Override
@@ -39,25 +42,7 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
if (lookup == null) {
return new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN);
}
if (!lookup.vin().isBlank()) {
return new VehicleIdentity(lookup.vin(), true, VehicleIdentitySource.EXPLICIT_VIN);
}
VehicleIdentity phone = resolveBound(lookup.protocol(), "PHONE", lookup.phone(), VehicleIdentitySource.BOUND_PHONE);
if (phone != null) return phone;
VehicleIdentity device = resolveBound(lookup.protocol(), "DEVICE_ID", lookup.deviceId(), VehicleIdentitySource.BOUND_DEVICE_ID);
if (device != null) return device;
VehicleIdentity plate = resolveBound(lookup.protocol(), "PLATE", lookup.plate(), VehicleIdentitySource.BOUND_PLATE);
if (plate != null) return plate;
if (!lookup.deviceId().isBlank()) {
return new VehicleIdentity(lookup.deviceId(), false, VehicleIdentitySource.FALLBACK_DEVICE_ID);
}
if (!lookup.phone().isBlank()) {
return new VehicleIdentity(lookup.phone(), false, VehicleIdentitySource.FALLBACK_PHONE);
}
if (!lookup.plate().isBlank()) {
return new VehicleIdentity(lookup.plate(), false, VehicleIdentitySource.FALLBACK_PLATE);
}
return new VehicleIdentity("unknown", false, VehicleIdentitySource.UNKNOWN);
return index.resolve(lookup);
}
private void initializeSchema() {
@@ -81,6 +66,39 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
}
}
private void loadIndex() {
String sql = "SELECT protocol, identifier_type, identifier_value, vin FROM " + table;
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
bindLoadedRow(
protocol(resultSet.getString("protocol")),
resultSet.getString("identifier_type"),
resultSet.getString("identifier_value"),
resultSet.getString("vin"));
}
} catch (SQLException e) {
throw new IllegalStateException("vehicle identity mysql index load failed: " + table, e);
}
}
private void bindLoadedRow(ProtocolId protocol, String identifierType, String identifierValue, String vin) {
if (vin == null || vin.isBlank()) {
return;
}
String type = identifierType == null ? "" : identifierType.trim().toUpperCase(Locale.ROOT);
VehicleIdentityBinding binding = switch (type) {
case "PHONE" -> new VehicleIdentityBinding(protocol, vin, identifierValue, "", "");
case "DEVICE_ID" -> new VehicleIdentityBinding(protocol, vin, "", identifierValue, "");
case "PLATE" -> new VehicleIdentityBinding(protocol, vin, "", "", identifierValue);
default -> null;
};
if (binding != null) {
index.bind(binding);
}
}
private void upsert(ProtocolId protocol, String identifierType, String identifierValue, String vin) {
String normalizedIdentifier = normalize(identifierValue);
if (normalizedIdentifier.isBlank()) {
@@ -101,32 +119,6 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
}
}
private VehicleIdentity resolveBound(ProtocolId protocol,
String identifierType,
String identifierValue,
VehicleIdentitySource source) {
String normalizedIdentifier = normalize(identifierValue);
if (normalizedIdentifier.isBlank()) {
return null;
}
String sql = "SELECT vin FROM " + table
+ " WHERE protocol = ? AND identifier_type = ? AND identifier_value = ? LIMIT 1";
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setString(1, protocolName(protocol));
statement.setString(2, identifierType);
statement.setString(3, normalizedIdentifier);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
return new VehicleIdentity(resultSet.getString(1), true, source);
}
}
} catch (SQLException e) {
throw new IllegalStateException("vehicle identity mysql resolve failed: " + table, e);
}
return null;
}
private static String sanitizeTable(String table) {
String value = table == null || table.isBlank() ? "vehicle_identity_binding" : table.trim();
if (!value.matches("[A-Za-z0-9_]+")) {
@@ -139,6 +131,17 @@ public final class MySqlVehicleIdentityService implements VehicleIdentityResolve
return protocol == null ? "UNKNOWN" : protocol.name();
}
private static ProtocolId protocol(String value) {
if (value == null || value.isBlank()) {
return ProtocolId.UNKNOWN;
}
try {
return ProtocolId.valueOf(value.trim().toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException ignored) {
return ProtocolId.UNKNOWN;
}
}
private static String normalize(String value) {
return value == null ? "" : value.trim().toUpperCase(Locale.ROOT);
}