Browse Source

Move LTS version range check into LtsVersion

pull/15925/head
Viacheslav Klimov 7 days ago
parent
commit
8ec2a949e6
Failed to extract signature
  1. 31
      application/src/main/java/org/thingsboard/server/service/install/lts/LtsMigrationService.java
  2. 8
      application/src/main/java/org/thingsboard/server/service/install/lts/LtsVersion.java
  3. 20
      application/src/test/java/org/thingsboard/server/service/install/lts/LtsMigrationServiceTest.java

31
application/src/main/java/org/thingsboard/server/service/install/lts/LtsMigrationService.java

@ -114,28 +114,23 @@ public class LtsMigrationService {
private List<VersionedMigration> select(String fromVersion, String toVersion) {
LtsVersion from = LtsVersion.parse(fromVersion);
LtsVersion to = LtsVersion.parse(toVersion);
// Select every migration in the (from, to] range, regardless of family. On a cross-family offline
// upgrade (e.g. 4.3.x -> 4.4) this is what makes the real in-range older-family beans run: it picks the
// 4.3.1.x schema/data changes the source has not yet passed AND the new target-family beans, each exactly
// once -- the half-open (from, to] range skips anything the source already applied. One logical migration
// is thus authored once (one bean + one lts/<version>/schema_update.sql) and reused by both the offline
// and no-downtime paths; nothing is reproduced into a newer family.
//
// Load-bearing invariant: no two beans may reproduce the same change within a single supported upgrade
// range. A reproduction-duplicate bean (one that re-does an older bean's work on a newer-family branch)
// must sit STRICTLY BELOW the minimum supported upgrade source, so it can never be selected together with
// the bean it duplicates. The only such pair today is 4.2.2.3 <-> 4.3.1.3, and the supported-source floor
// is 4.3.0.0 (SUPPORTED_VERSIONS_FOR_UPGRADE), well above 4.2.2.3. LtsMigrationServiceTest guards this.
return migrations.stream()
.filter(vm -> isInRange(vm.version(), from, to))
.filter(vm -> vm.version().isInRange(from, to))
.toList();
}
// Select every migration in the (from, to] range, regardless of family. On a cross-family offline
// upgrade (e.g. 4.3.x -> 4.4) this is what makes the real in-range older-family beans run: it picks the
// 4.3.1.x schema/data changes the source has not yet passed AND the new target-family beans, each exactly
// once -- the half-open (from, to] range skips anything the source already applied. One logical migration
// is thus authored once (one bean + one lts/<version>/schema_update.sql) and reused by both the offline
// and no-downtime paths; nothing is reproduced into a newer family.
//
// Load-bearing invariant: no two beans may reproduce the same change within a single supported upgrade
// range. A reproduction-duplicate bean (one that re-does an older bean's work on a newer-family branch)
// must sit STRICTLY BELOW the minimum supported upgrade source, so it can never be selected together with
// the bean it duplicates. The only such pair today is 4.2.2.3 <-> 4.3.1.3, and the supported-source floor
// is 4.3.0.0 (SUPPORTED_VERSIONS_FOR_UPGRADE), well above 4.2.2.3. LtsMigrationServiceTest guards this.
static boolean isInRange(LtsVersion version, LtsVersion from, LtsVersion to) {
return version.compareTo(from) > 0
&& version.compareTo(to) <= 0;
}
private void runSchemaUpdate(String version) {
Path sqlFile = Paths.get(installScripts.getDataDir(), "upgrade", "lts", version, SCHEMA_UPDATE_SQL);
if (!Files.exists(sqlFile)) {

8
application/src/main/java/org/thingsboard/server/service/install/lts/LtsVersion.java

@ -37,6 +37,14 @@ public record LtsVersion(int major, int minor, int maintenance, int patch) imple
return major == other.major && minor == other.minor;
}
/**
* Half-open range check: {@code from < this <= to}. The lower bound is exclusive (a source already at
* {@code from} has applied that version) and the upper bound inclusive (the target version's own migration runs).
*/
public boolean isInRange(LtsVersion from, LtsVersion to) {
return compareTo(from) > 0 && compareTo(to) <= 0;
}
@Override
public int compareTo(LtsVersion o) {
int c = Integer.compare(major, o.major);

20
application/src/test/java/org/thingsboard/server/service/install/lts/LtsMigrationServiceTest.java

@ -179,28 +179,28 @@ class LtsMigrationServiceTest {
LtsVersion from = LtsVersion.parse("4.3.1.1");
LtsVersion to = LtsVersion.parse("4.3.1.3");
// within-family in range
assertTrue(LtsMigrationService.isInRange(LtsVersion.parse("4.3.1.2"), from, to));
assertTrue(LtsVersion.parse("4.3.1.2").isInRange(from, to));
// the target itself (upper boundary, inclusive)
assertTrue(LtsMigrationService.isInRange(to, from, to));
assertTrue(to.isInRange(from, to));
// at from (lower boundary, exclusive)
assertFalse(LtsMigrationService.isInRange(from, from, to));
assertFalse(from.isInRange(from, to));
// below from
assertFalse(LtsMigrationService.isInRange(LtsVersion.parse("4.3.1.0"), from, to));
assertFalse(LtsVersion.parse("4.3.1.0").isInRange(from, to));
// above to
assertFalse(LtsMigrationService.isInRange(LtsVersion.parse("4.3.1.4"), from, to));
assertFalse(LtsVersion.parse("4.3.1.4").isInRange(from, to));
// Cross-family: an in-range older-family bean IS now selected (the whole point of the loosened filter),
// as is the target-family baseline; only a bean below `from` stays out of range.
LtsVersion crossFrom = LtsVersion.parse("4.3.0.0");
LtsVersion crossTo = LtsVersion.parse("4.4.0.0");
assertTrue(LtsMigrationService.isInRange(LtsVersion.parse("4.3.1.3"), crossFrom, crossTo));
assertTrue(LtsMigrationService.isInRange(crossTo, crossFrom, crossTo));
assertFalse(LtsMigrationService.isInRange(LtsVersion.parse("4.2.2.3"), crossFrom, crossTo));
assertTrue(LtsVersion.parse("4.3.1.3").isInRange(crossFrom, crossTo));
assertTrue(crossTo.isInRange(crossFrom, crossTo));
assertFalse(LtsVersion.parse("4.2.2.3").isInRange(crossFrom, crossTo));
}
@Test
void reproductionDuplicateBeanSitsBelowSupportedSourceFloor() {
// Load-bearing invariant (see LtsMigrationService.isInRange): the only reproduction-duplicate pair is
// Load-bearing invariant (see LtsMigrationService.select): the only reproduction-duplicate pair is
// 4.2.2.3 <-> 4.3.1.3 (both do the solution-template / widget-bundle work). For the loosened (from, to]
// filter to never select both in a single supported upgrade, the duplicate 4.2.2.3 must sit strictly
// below the minimum supported upgrade source. That floor is 4.3.0.0 (SUPPORTED_VERSIONS_FOR_UPGRADE).
@ -208,7 +208,7 @@ class LtsMigrationServiceTest {
LtsVersion supportedSourceFloor = LtsVersion.parse("4.3.0.0");
assertTrue(duplicate.compareTo(supportedSourceFloor) < 0);
// So on any supported cross-family upgrade (from >= floor), the duplicate is out of range.
assertFalse(LtsMigrationService.isInRange(duplicate, supportedSourceFloor, LtsVersion.parse("4.4.0.0")));
assertFalse(duplicate.isInRange(supportedSourceFloor, LtsVersion.parse("4.4.0.0")));
}
@Test

Loading…
Cancel
Save