diff --git a/CHANGES.md b/CHANGES.md index 0ddec8d9a..c1e21103a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ ## Version 5.0.0 (Not yet released) +* Make priority granularity configurable - Issue #599 * Bump springboot from 2.7.12 to 2.7.17 - Issue #604 * Bump io.micrometer from 1.9.2 to 1.9.16 - Issue #604 * Bump io.dropwizard.metrics from 4.2.10 to 4.2.21 - Issue #604 diff --git a/application/src/main/java/com/ericsson/bss/cassandra/ecchronos/application/config/repair/Priority.java b/application/src/main/java/com/ericsson/bss/cassandra/ecchronos/application/config/repair/Priority.java new file mode 100644 index 000000000..0c6931ea9 --- /dev/null +++ b/application/src/main/java/com/ericsson/bss/cassandra/ecchronos/application/config/repair/Priority.java @@ -0,0 +1,68 @@ +/* + * Copyright 2023 Telefonaktiebolaget LM Ericsson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ericsson.bss.cassandra.ecchronos.application.config.repair; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.EnumSet; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +public class Priority +{ + + private static final Set ALLOWED_UNITS = EnumSet.of(TimeUnit.HOURS, TimeUnit.MINUTES, TimeUnit.SECONDS); + private static final String ALLOWED_VALUES_STR = String.join(", ", ALLOWED_UNITS.stream() + .map(TimeUnit::name) + .collect(Collectors.toList())); + + private TimeUnit myGranularityUnit = TimeUnit.HOURS; + + public Priority() + { + // Default constructor for jackson + } + + @JsonCreator + public Priority(@JsonProperty("granularity_unit") final TimeUnit granularityUnit) + { + myGranularityUnit = granularityUnit; + } + + public final TimeUnit getPriorityGranularityUnit() + { + return myGranularityUnit; + } + + @JsonProperty ("granularity_unit") + public final void setPriorityGranularityUnit(final TimeUnit granularityUnit) + { + Optional.ofNullable(granularityUnit) + .orElseThrow(() -> new IllegalArgumentException(String.format( + "Granularity unit cannot be null. Allowed values are: %s.", ALLOWED_VALUES_STR))); + + if (!ALLOWED_UNITS.contains(granularityUnit)) + { + throw new IllegalArgumentException(String.format( + "Invalid granularity unit '%s'. Allowed values are: %s.", + granularityUnit.name(), ALLOWED_VALUES_STR)); + } + + myGranularityUnit = granularityUnit; + } +} diff --git a/application/src/main/java/com/ericsson/bss/cassandra/ecchronos/application/config/repair/RepairConfig.java b/application/src/main/java/com/ericsson/bss/cassandra/ecchronos/application/config/repair/RepairConfig.java index 9efad2d9a..b2c8dea7d 100644 --- a/application/src/main/java/com/ericsson/bss/cassandra/ecchronos/application/config/repair/RepairConfig.java +++ b/application/src/main/java/com/ericsson/bss/cassandra/ecchronos/application/config/repair/RepairConfig.java @@ -39,6 +39,19 @@ public class RepairConfig private boolean myIgnoreTwcsTables = false; private RepairOptions.RepairType myRepairType = RepairOptions.RepairType.VNODE; + private Priority myPriority = new Priority(); + + public final Priority getPriority() + { + return myPriority; + } + + @JsonProperty("priority") + public final void setPriority(final Priority priority) + { + myPriority = priority; + } + @JsonProperty("interval") public final void setRepairInterval(final Interval repairInterval) { @@ -126,7 +139,7 @@ public final void validate(final String repairConfigType) if (warningIntervalSeconds >= errorIntervalSeconds) { throw new IllegalArgumentException(String.format("%s warning interval must be shorter than error interval." - + " Current warning interval: %d seconds, error interval: %d seconds", repairConfigType, + + " Current warning interval: %d seconds, error interval: %d seconds", repairConfigType, warningIntervalSeconds, errorIntervalSeconds)); } } @@ -149,6 +162,7 @@ public RepairConfiguration asRepairConfiguration() .withTargetRepairSizeInBytes(mySizeTarget) .withBackoff(myBackoff.getInterval(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS) .withRepairType(myRepairType) + .withPriorityGranularityUnit(myPriority.getPriorityGranularityUnit()) .build(); } } diff --git a/application/src/main/resources/ecc.yml b/application/src/main/resources/ecc.yml index d16288bec..958906c83 100644 --- a/application/src/main/resources/ecc.yml +++ b/application/src/main/resources/ecc.yml @@ -86,6 +86,17 @@ repair: time: 7 unit: days ## + ## The unit of time granularity for priority calculation, can be HOURS, MINUTES, or SECONDS. + ## This unit is used in the calculation of priority. + ## Default is HOURS for backward compatibility. + ## Ensure to pause repair operations prior to changing the granularity. + ## Not doing so may lead to inconsistencies as some ecchronos instances + ## could have different priorities compared to others for the same repair. + ## Possible values are HOURS, MINUTES, or SECONDS. + ## + priority: + granularity_unit: HOURS + ## ## Specifies the type of lock to use for repairs. ## "vnode" will lock each node involved in a repair individually and increase the number of ## parallel repairs that can run in a single data center. diff --git a/application/src/test/java/com/ericsson/bss/cassandra/ecchronos/application/config/TestConfig.java b/application/src/test/java/com/ericsson/bss/cassandra/ecchronos/application/config/TestConfig.java index f942b2536..504f75a8e 100644 --- a/application/src/test/java/com/ericsson/bss/cassandra/ecchronos/application/config/TestConfig.java +++ b/application/src/test/java/com/ericsson/bss/cassandra/ecchronos/application/config/TestConfig.java @@ -108,6 +108,7 @@ public void testAllValues() throws Exception .withIgnoreTWCSTables(true) .withBackoff(13, TimeUnit.SECONDS) .withTargetRepairSizeInBytes(UnitConverter.toBytes("5m")) + .withPriorityGranularityUnit(TimeUnit.MINUTES) .build(); GlobalRepairConfig repairConfig = config.getRepairConfig(); @@ -122,6 +123,7 @@ public void testAllValues() throws Exception assertThat(repairConfig.getAlarm().getFaultReporterClass()).isEqualTo(TestFaultReporter.class); assertThat(repairConfig.getIgnoreTWCSTables()).isTrue(); assertThat(repairConfig.getBackoff().getInterval(TimeUnit.SECONDS)).isEqualTo(13); + assertThat(repairConfig.getPriority().getPriorityGranularityUnit()).isEqualTo(TimeUnit.MINUTES); StatisticsConfig statisticsConfig = config.getStatisticsConfig(); assertThat(statisticsConfig.isEnabled()).isFalse(); @@ -217,6 +219,7 @@ public void testWithDefaultFile() throws Exception assertThat(repairConfig.getAlarm().getFaultReporterClass()).isEqualTo(LoggingFaultReporter.class); assertThat(repairConfig.getIgnoreTWCSTables()).isFalse(); assertThat(repairConfig.getBackoff().getInterval(TimeUnit.MINUTES)).isEqualTo(30); + assertThat(repairConfig.getPriority().getPriorityGranularityUnit()).isEqualTo(TimeUnit.HOURS); StatisticsConfig statisticsConfig = config.getStatisticsConfig(); assertThat(statisticsConfig.isEnabled()).isTrue(); diff --git a/application/src/test/java/com/ericsson/bss/cassandra/ecchronos/application/config/repair/TestPriority.java b/application/src/test/java/com/ericsson/bss/cassandra/ecchronos/application/config/repair/TestPriority.java new file mode 100644 index 000000000..8d67bb92d --- /dev/null +++ b/application/src/test/java/com/ericsson/bss/cassandra/ecchronos/application/config/repair/TestPriority.java @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Telefonaktiebolaget LM Ericsson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.ericsson.bss.cassandra.ecchronos.application.config.repair; + +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +public class TestPriority +{ + private Priority priority; + + @Before + public void setUp() { + priority = new Priority(); + } + + @Test + public void testDefaultConstructorSetsHours() + { + assertThat(priority.getPriorityGranularityUnit()).isEqualTo(TimeUnit.HOURS); + } + + @Test + public void testSetValidGranularityUnit() + { + priority.setPriorityGranularityUnit(TimeUnit.MINUTES); + assertThat(priority.getPriorityGranularityUnit()).isEqualTo(TimeUnit.MINUTES); + } + + @Test(expected = IllegalArgumentException.class) + public void testSetInvalidGranularityUnit() + { + priority.setPriorityGranularityUnit(TimeUnit.DAYS); + } + + @Test(expected = IllegalArgumentException.class) + public void testSetNullGranularityUnit() + { + priority.setPriorityGranularityUnit(null); + } +} diff --git a/application/src/test/resources/all_set.yml b/application/src/test/resources/all_set.yml index db2e66c80..f72fb74ff 100644 --- a/application/src/test/resources/all_set.yml +++ b/application/src/test/resources/all_set.yml @@ -55,6 +55,8 @@ repair: backoff: time: 13 unit: seconds + priority: + granularity_unit: MINUTES statistics: enabled: false diff --git a/core/src/main/java/com/ericsson/bss/cassandra/ecchronos/core/repair/RepairConfiguration.java b/core/src/main/java/com/ericsson/bss/cassandra/ecchronos/core/repair/RepairConfiguration.java index 38eb03efb..f58f1ff1b 100644 --- a/core/src/main/java/com/ericsson/bss/cassandra/ecchronos/core/repair/RepairConfiguration.java +++ b/core/src/main/java/com/ericsson/bss/cassandra/ecchronos/core/repair/RepairConfiguration.java @@ -14,6 +14,8 @@ */ package com.ericsson.bss.cassandra.ecchronos.core.repair; +import static com.ericsson.bss.cassandra.ecchronos.core.repair.RepairOptions.RepairParallelism; + import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -29,8 +31,7 @@ public class RepairConfiguration private static final long DEFAULT_REPAIR_INTERVAL_IN_MS = TimeUnit.DAYS.toMillis(7); private static final long DEFAULT_REPAIR_WARNING_TIME_IN_MS = TimeUnit.DAYS.toMillis(8); private static final long DEFAULT_REPAIR_ERROR_TIME_IN_MS = TimeUnit.DAYS.toMillis(10); - private static final RepairOptions.RepairParallelism DEFAULT_REPAIR_PARALLELISM - = RepairOptions.RepairParallelism.PARALLEL; + private static final RepairParallelism DEFAULT_REPAIR_PARALLELISM = RepairParallelism.PARALLEL; private static final RepairOptions.RepairType DEFAULT_REPAIR_TYPE = RepairOptions.RepairType.VNODE; private static final double DEFAULT_UNWIND_RATIO = NO_UNWIND; private static final long DEFAULT_TARGET_REPAIR_SIZE_IN_BYTES = FULL_REPAIR_SIZE; @@ -39,8 +40,9 @@ public class RepairConfiguration private static final boolean DEFAULT_IGNORE_TWCS_TABLES = false; public static final RepairConfiguration DEFAULT = newBuilder().build(); - public static final RepairConfiguration DISABLED - = newBuilder().withRepairInterval(0, TimeUnit.MILLISECONDS).build(); + public static final RepairConfiguration DISABLED = newBuilder() + .withRepairInterval(0, TimeUnit.MILLISECONDS) + .build(); private final RepairOptions.RepairParallelism myRepairParallelism; private final long myRepairIntervalInMs; @@ -50,6 +52,7 @@ public class RepairConfiguration private final long myTargetRepairSizeInBytes; private final boolean myIgnoreTWCSTables; private final long myBackoffInMs; + private final TimeUnit myPriorityGranularityUnit; private final RepairOptions.RepairType myRepairType; @@ -64,6 +67,12 @@ private RepairConfiguration(final Builder builder) myIgnoreTWCSTables = builder.myIgnoreTWCSTables; myBackoffInMs = builder.myBackoffInMs; myRepairType = builder.myRepairType; + myPriorityGranularityUnit = builder.myPriorityGranularityUnit; + } + + public TimeUnit getPriorityGranularityUnit() + { + return myPriorityGranularityUnit; } public RepairOptions.RepairParallelism getRepairParallelism() @@ -126,9 +135,9 @@ public String toString() { return String.format( "RepairConfiguration(interval=%dms,warning=%dms,error=%dms,parallelism=%s,unwindRatio=%.2f" - + ",ignoreTWCS=%b,backoff=%dms,repairType=%s)", + + ",ignoreTWCS=%b,backoff=%dms,repairType=%s)", myRepairIntervalInMs, myRepairWarningTimeInMs, myRepairErrorTimeInMs, myRepairParallelism, - myRepairUnwindRatio, myIgnoreTWCSTables, myBackoffInMs, myRepairType); + myRepairUnwindRatio, myIgnoreTWCSTables, myBackoffInMs, myRepairType, myPriorityGranularityUnit); } @Override @@ -151,7 +160,8 @@ public boolean equals(final Object o) && myRepairParallelism == that.myRepairParallelism && myIgnoreTWCSTables == that.myIgnoreTWCSTables && myBackoffInMs == that.myBackoffInMs - && myRepairType == that.myRepairType; + && myRepairType == that.myRepairType + && myPriorityGranularityUnit == that.myPriorityGranularityUnit; } @Override @@ -159,7 +169,7 @@ public int hashCode() { return Objects.hash(myRepairParallelism, myRepairIntervalInMs, myRepairWarningTimeInMs, myRepairErrorTimeInMs, myRepairUnwindRatio, myTargetRepairSizeInBytes, myIgnoreTWCSTables, - myBackoffInMs, myRepairType); + myBackoffInMs, myRepairType, myPriorityGranularityUnit); } public static class Builder @@ -173,6 +183,7 @@ public static class Builder private long myTargetRepairSizeInBytes = DEFAULT_TARGET_REPAIR_SIZE_IN_BYTES; private long myBackoffInMs = DEFAULT_BACKOFF_IN_MS; private boolean myIgnoreTWCSTables = DEFAULT_IGNORE_TWCS_TABLES; + private TimeUnit myPriorityGranularityUnit = TimeUnit.HOURS; /** * Constructor. @@ -196,6 +207,7 @@ public Builder(final RepairConfiguration from) myRepairErrorTimeInMs = from.getRepairErrorTimeInMs(); myRepairUnwindRatio = from.getRepairUnwindRatio(); myBackoffInMs = from.getBackoffInMs(); + myPriorityGranularityUnit = from.getPriorityGranularityUnit(); } /** @@ -324,6 +336,18 @@ public Builder withBackoff(final long backoff, final TimeUnit timeUnit) return this; } + /** + * Build with Priority Granularity Unit for the scheduling job. + * + * @param unit The Priority Granularity Unit. + * @return Builder + */ + public Builder withPriorityGranularityUnit(final TimeUnit unit) + { + myPriorityGranularityUnit = unit; + return this; + } + /** * Build repair configuration. * diff --git a/core/src/main/java/com/ericsson/bss/cassandra/ecchronos/core/repair/RepairSchedulerImpl.java b/core/src/main/java/com/ericsson/bss/cassandra/ecchronos/core/repair/RepairSchedulerImpl.java index 9ae3ad535..a1439fd2b 100644 --- a/core/src/main/java/com/ericsson/bss/cassandra/ecchronos/core/repair/RepairSchedulerImpl.java +++ b/core/src/main/java/com/ericsson/bss/cassandra/ecchronos/core/repair/RepairSchedulerImpl.java @@ -244,6 +244,7 @@ private ScheduledRepairJob createScheduledRepairJob(final TableReference tableRe .withPriority(ScheduledJob.Priority.LOW) .withRunInterval(repairConfiguration.getRepairIntervalInMs(), TimeUnit.MILLISECONDS) .withBackoff(repairConfiguration.getBackoffInMs(), TimeUnit.MILLISECONDS) + .withPriorityGranularity(repairConfiguration.getPriorityGranularityUnit()) .build(); ScheduledRepairJob job; if (repairConfiguration.getRepairType().equals(RepairOptions.RepairType.INCREMENTAL)) diff --git a/core/src/main/java/com/ericsson/bss/cassandra/ecchronos/core/scheduling/ScheduledJob.java b/core/src/main/java/com/ericsson/bss/cassandra/ecchronos/core/scheduling/ScheduledJob.java index d80369025..29884ef78 100644 --- a/core/src/main/java/com/ericsson/bss/cassandra/ecchronos/core/scheduling/ScheduledJob.java +++ b/core/src/main/java/com/ericsson/bss/cassandra/ecchronos/core/scheduling/ScheduledJob.java @@ -24,7 +24,6 @@ @SuppressWarnings("VisibilityModifier") public abstract class ScheduledJob implements Iterable { - private static final int ONE_HOUR_IN_MILLIS = 3600000; private static final long DEFAULT_BACKOFF_IN_MINUTES = 30; private final Priority myPriority; @@ -35,6 +34,7 @@ public abstract class ScheduledJob implements Iterable private volatile long myNextRunTime = -1; private volatile long myRunOffset = 0; private final UUID myId; + private final TimeUnit myPriorityGranularity; public ScheduledJob(final Configuration configuration) { @@ -48,6 +48,7 @@ public ScheduledJob(final Configuration configuration, final UUID id) myRunIntervalInMs = configuration.runIntervalInMs; myBackoffInMs = configuration.backoffInMs; myLastSuccessfulRun = System.currentTimeMillis() - myRunIntervalInMs; + myPriorityGranularity = configuration.priorityGranularity; } /** @@ -172,9 +173,16 @@ public final int getRealPriority(final long lastSuccessfulRun) return -1; } - int hours = (int) (diff / ONE_HOUR_IN_MILLIS) + 1; + long granularityInMs = myPriorityGranularity.toMillis(1); + long unitsPassed = diff / granularityInMs + 1; - return hours * myPriority.getValue(); + // Overflow protection + if (unitsPassed > Integer.MAX_VALUE / myPriority.getValue()) + { + return Integer.MAX_VALUE; + } + + return (int) unitsPassed * myPriority.getValue(); } /** @@ -214,7 +222,8 @@ public boolean equals(final Object o) && myNextRunTime == that.myNextRunTime && myRunOffset == that.myRunOffset && myPriority == that.myPriority - && Objects.equals(myId, that.myId); + && Objects.equals(myId, that.myId) + && Objects.equals(myPriorityGranularity, that.myPriorityGranularity); } /** @@ -224,7 +233,7 @@ public boolean equals(final Object o) public int hashCode() { return Objects.hash(myPriority, myBackoffInMs, myRunIntervalInMs, myLastSuccessfulRun, - myNextRunTime, myRunOffset, myId); + myNextRunTime, myRunOffset, myId, myPriorityGranularity); } /** @@ -313,11 +322,17 @@ public static class Configuration */ public final long backoffInMs; + /** + * The unit of time granularity used for priority calculation in scheduling jobs. + */ + public final TimeUnit priorityGranularity; + Configuration(final ConfigurationBuilder builder) { priority = builder.priority; runIntervalInMs = builder.runIntervalInMs; backoffInMs = builder.backoffInMs; + priorityGranularity = builder.granularityUnit; } } @@ -329,6 +344,13 @@ public static class ConfigurationBuilder private Priority priority = Priority.LOW; private long runIntervalInMs = TimeUnit.DAYS.toMillis(1); private long backoffInMs = TimeUnit.MINUTES.toMillis(DEFAULT_BACKOFF_IN_MINUTES); + private TimeUnit granularityUnit = TimeUnit.HOURS; + + public final ConfigurationBuilder withPriorityGranularity(final TimeUnit granularityTimeUnit) + { + this.granularityUnit = granularityTimeUnit; + return this; + } public final ConfigurationBuilder withPriority(final Priority aPriority) { diff --git a/core/src/test/java/com/ericsson/bss/cassandra/ecchronos/core/repair/TestTableRepairJob.java b/core/src/test/java/com/ericsson/bss/cassandra/ecchronos/core/repair/TestTableRepairJob.java index 0363ec1a6..db0043dc3 100644 --- a/core/src/test/java/com/ericsson/bss/cassandra/ecchronos/core/repair/TestTableRepairJob.java +++ b/core/src/test/java/com/ericsson/bss/cassandra/ecchronos/core/repair/TestTableRepairJob.java @@ -664,6 +664,90 @@ public void testRunnableWithOffset() { assertThat(myRepairJob.runnable()).isFalse(); } + @Test + public void testGetRealPriorityWithMinuteGranularity() + { + ScheduledJob.Configuration configuration = new ScheduledJob.ConfigurationBuilder() + .withPriority(ScheduledJob.Priority.LOW) + .withRunInterval(RUN_INTERVAL_IN_DAYS, TimeUnit.DAYS) + .withPriorityGranularity(TimeUnit.MINUTES) + .build(); + + myRepairJob = new TableRepairJob.Builder() + .withConfiguration(configuration) + .withTableReference(myTableReference) + .withJmxProxyFactory(myJmxProxyFactory) + .withRepairState(myRepairState) + .withTableRepairMetrics(myTableRepairMetrics) + .withRepairConfiguration(myRepairConfiguration) + .withRepairLockType(RepairLockType.VNODE) + .withTableStorageStates(myTableStorageStates) + .withRepairHistory(myRepairHistory) + .build(); + + long lastRepaired = System.currentTimeMillis(); + doReturn(lastRepaired).when(myRepairStateSnapshot).lastCompletedAt(); + doReturn(false).when(myRepairStateSnapshot).canRepair(); + mockRepairGroup(lastRepaired); + assertThat(myRepairJob.getRealPriority()).isEqualTo(-1); + + lastRepaired = System.currentTimeMillis() - (TimeUnit.DAYS.toMillis(RUN_INTERVAL_IN_DAYS) - TimeUnit.MINUTES.toMillis(1)); + doReturn(lastRepaired).when(myRepairStateSnapshot).lastCompletedAt(); + doReturn(true).when(myRepairStateSnapshot).canRepair(); + mockRepairGroup(lastRepaired); + assertThat(myRepairJob.getRealPriority()).isEqualTo(-1); + + lastRepaired = System.currentTimeMillis() - (TimeUnit.DAYS.toMillis(RUN_INTERVAL_IN_DAYS)); + doReturn(lastRepaired).when(myRepairStateSnapshot).lastCompletedAt(); + doReturn(true).when(myRepairStateSnapshot).canRepair(); + mockRepairGroup(lastRepaired); + assertThat(myRepairJob.getRealPriority()).isEqualTo(1); + + lastRepaired = System.currentTimeMillis() - (TimeUnit.DAYS.toMillis(RUN_INTERVAL_IN_DAYS) + TimeUnit.MINUTES.toMillis(1)); + doReturn(lastRepaired).when(myRepairStateSnapshot).lastCompletedAt(); + doReturn(true).when(myRepairStateSnapshot).canRepair(); + mockRepairGroup(lastRepaired); + assertThat(myRepairJob.getRealPriority()).isEqualTo(2); + } + @Test + public void testGetRealPriorityOverflow() + { + ScheduledJob.Configuration configuration = new ScheduledJob.ConfigurationBuilder() + .withPriority(ScheduledJob.Priority.HIGHEST) + .withPriorityGranularity(TimeUnit.MILLISECONDS) + .build(); + + myRepairJob = new TableRepairJob.Builder() + .withConfiguration(configuration) + .withTableReference(myTableReference) + .withJmxProxyFactory(myJmxProxyFactory) + .withRepairState(myRepairState) + .withTableRepairMetrics(myTableRepairMetrics) + .withRepairConfiguration(myRepairConfiguration) + .withRepairLockType(RepairLockType.VNODE) + .withTableStorageStates(myTableStorageStates) + .withRepairHistory(myRepairHistory) + .build(); + + long diffLargeEnoughForOverflow = (long) (Integer.MAX_VALUE / myRepairJob.getPriority().getValue()) + * myRepairJob + .getRepairConfiguration() + .getPriorityGranularityUnit() + .toMillis(1) + 1; + + long currentTimeMillis = System.currentTimeMillis(); + long lastRepaired = currentTimeMillis - diffLargeEnoughForOverflow - myRepairJob + .getRepairConfiguration() + .getRepairIntervalInMs(); + + doReturn(lastRepaired).when(myRepairStateSnapshot).lastCompletedAt(); + doReturn(true).when(myRepairStateSnapshot).canRepair(); + mockRepairGroup(lastRepaired); + + assertThat(myRepairJob.getRealPriority()).isEqualTo(Integer.MAX_VALUE); + } + + @Test public void testGetRealPriority() { diff --git a/docs/UPGRADE.md b/docs/UPGRADE.md index ae7fe5265..8b572429d 100644 --- a/docs/UPGRADE.md +++ b/docs/UPGRADE.md @@ -33,6 +33,16 @@ directly within the `ecc.yaml` file. It’s important to note that while making these changes, the default behavior remains unchanged. +## Priority Calculation + +The unit of time granularity used for priority calculation. +Possible values are HOURS, MINUTES, or SECONDS. +This unit influences how quickly the priority of a job increases. +Default is set to HOURS for backward compatibility. +IMPORTANT: Ensure to pause repair operations prior to changing the granularity. +Not doing so may lead to inconsistencies as some ecchronos instances could have +different priorities compared to others for the same repair. + # Upgrade to 4.x ## Metrics