From 7d3abf9a115df061777c95776c57777a92ad6df8 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Tue, 10 Jan 2023 16:18:30 +1100 Subject: [PATCH 01/32] Migration to add secondary index on repair runs table. --- .../resources/db/cassandra/add_2i_status.cql | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/server/src/main/resources/db/cassandra/add_2i_status.cql diff --git a/src/server/src/main/resources/db/cassandra/add_2i_status.cql b/src/server/src/main/resources/db/cassandra/add_2i_status.cql new file mode 100644 index 000000000..18e61a8a2 --- /dev/null +++ b/src/server/src/main/resources/db/cassandra/add_2i_status.cql @@ -0,0 +1,18 @@ +-- +-- Copyright 2021-2021 Datastax inc. +-- +-- 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. +-- +-- Add a secondary index on `state` to the `repair_run` table. + +CREATE INDEX state2i ON repair_run_by_cluster_v2 (repair_run_state); From 37d6eaf9a684be7c89309860dea0f02dbcd54e3b Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Tue, 10 Jan 2023 16:19:36 +1100 Subject: [PATCH 02/32] RepairRunResource should prioritise RUNNING repairs when a limit is passed. --- .../java/io/cassandrareaper/resources/RepairRunResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java index 7ca4b1687..bdaf0ec00 100644 --- a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java +++ b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java @@ -608,7 +608,7 @@ public Response getRepairRunsForCluster( @QueryParam("limit") Optional limit) { LOG.debug("get repair run for cluster called with: cluster_name = {}", clusterName); - final Collection repairRuns = context.storage.getRepairRunsForCluster(clusterName, limit); + final Collection repairRuns = context.storage.getRepairRunsForClusterPrioritiseRunning(clusterName, limit); final Collection repairRunViews = new ArrayList<>(); for (final RepairRun repairRun : repairRuns) { repairRunViews.add(getRepairRunStatus(repairRun)); From b06d3579af932755d6534f64d0b927dba83a1414 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Tue, 10 Jan 2023 16:20:39 +1100 Subject: [PATCH 03/32] Stub out the bones of queries required to use new secondary index. --- .../storage/CassandraStorage.java | 47 +++++++++++++++++++ .../io/cassandrareaper/storage/IStorage.java | 2 + .../storage/MemoryStorage.java | 2 + 3 files changed, 51 insertions(+) diff --git a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java index fd0c1763a..30b29fd9e 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java @@ -157,6 +157,7 @@ public RepairUnit load(UUID repairUnitId) throws Exception { private PreparedStatement insertRepairRunUnitIndexPrepStmt; private PreparedStatement getRepairRunPrepStmt; private PreparedStatement getRepairRunForClusterPrepStmt; + private PreparedStatement getRepairRunForClusterWhereStatusPrepStmt; private PreparedStatement getRepairRunForUnitPrepStmt; private PreparedStatement deleteRepairRunPrepStmt; private PreparedStatement deleteRepairRunByClusterPrepStmt; @@ -422,6 +423,8 @@ private void prepareStatements() { .setConsistencyLevel(ConsistencyLevel.QUORUM); getRepairRunForClusterPrepStmt = session.prepare( "SELECT * FROM repair_run_by_cluster_v2 WHERE cluster_name = ? limit ?"); + getRepairRunForClusterWhereStatusPrepStmt = session.prepare( + "SELECT * FROM repair_run_by_cluster_v2 WHERE cluster_name = ? AND state = ? limit ?"); getRepairRunForUnitPrepStmt = session.prepare("SELECT * FROM repair_run_by_unit WHERE repair_unit_id = ?"); deleteRepairRunPrepStmt = session.prepare("DELETE FROM repair_run WHERE id = ?"); deleteRepairRunByClusterPrepStmt @@ -957,6 +960,50 @@ public Collection getRepairRunsForCluster(String clusterName, Optiona return getRepairRunsAsync(repairRunFutures); } + @Override + public Collection getRepairRunsForClusterPrioritiseRunning(String clusterName, Optional limit) { + List repairRunFutures = Lists.newArrayList(); + // Grab all RUNNING repair_runs for the given cluster name + repairRunFutures.add( + session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "RUNNING", limit.orElse( + MAX_RETURNED_REPAIR_RUNS))) + ); + // Grab all NOT_STARTED repair_runs for the given cluster name + repairRunFutures.add( + session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "NOT_STARTED", limit.orElse( + MAX_RETURNED_REPAIR_RUNS))) + ); + // Other possible statuses are: + // ERROR, + // PAUSED, + // ABORTED, + // DELETED, + // DONE + // We add these to the result set according to how interesting this result is. + repairRunFutures.add( + session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "ERROR", limit.orElse( + MAX_RETURNED_REPAIR_RUNS))) + ); + repairRunFutures.add( + session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "PAUSED", limit.orElse( + MAX_RETURNED_REPAIR_RUNS))) + ); + repairRunFutures.add( + session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "ABORTED", limit.orElse( + MAX_RETURNED_REPAIR_RUNS))) + ); + repairRunFutures.add( + session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "DELETED", limit.orElse( + MAX_RETURNED_REPAIR_RUNS))) + ); + repairRunFutures.add( + session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "DONE", limit.orElse( + MAX_RETURNED_REPAIR_RUNS))) + ); + + return getRepairRunsAsync(repairRunFutures); + } + @Override public Collection getRepairRunsForUnit(UUID repairUnitId) { List repairRunFutures = Lists.newArrayList(); diff --git a/src/server/src/main/java/io/cassandrareaper/storage/IStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/IStorage.java index b4dd97ca7..4a4a73001 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/IStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/IStorage.java @@ -71,6 +71,8 @@ public interface IStorage extends Managed { /** return all the repair runs in a cluster, in reverse chronological order, with default limit is 1000 */ Collection getRepairRunsForCluster(String clusterName, Optional limit); + Collection getRepairRunsForClusterPrioritiseRunning(String clusterName, Optional limit); + Collection getRepairRunsForUnit(UUID repairUnitId); Collection getRepairRunsWithState(RepairRun.RunState runState); diff --git a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java index 75dead325..fd5d1ccfc 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java @@ -192,6 +192,8 @@ public List getRepairRunsForCluster(String clusterName, Optional getRepairRunsForClusterPrioritiseRunning(String clusterName, Optional limit); @Override public Collection getRepairRunsForUnit(UUID repairUnitId) { From 21e3e7aab8c531fa6da4586d2082bff312153a51 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Wed, 11 Jan 2023 15:52:22 +1100 Subject: [PATCH 04/32] Shift around order or RepairRun.RunState so it reflects our desired order. --- .../src/main/java/io/cassandrareaper/core/RepairRun.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java index e55a9361c..1d3dcace2 100644 --- a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java +++ b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java @@ -181,13 +181,13 @@ public String toString() { } public enum RunState { - NOT_STARTED, RUNNING, + NOT_STARTED, ERROR, - DONE, PAUSED, ABORTED, - DELETED; + DELETED, + DONE; public boolean isActive() { return this == RUNNING || this == PAUSED; From 8af069e5873ae74175288d9d7c0ee270fa9e9c6f Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Wed, 11 Jan 2023 15:56:11 +1100 Subject: [PATCH 05/32] Implement getRepairRunsForClusterPrioritiseRunning for MemoryStorage. --- .../io/cassandrareaper/storage/MemoryStorage.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java index fd5d1ccfc..220a0854b 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java @@ -31,6 +31,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -48,6 +49,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import org.apache.cassandra.tools.nodetool.Repair; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.slf4j.Logger; @@ -193,7 +195,17 @@ public List getRepairRunsForCluster(String clusterName, Optional getRepairRunsForClusterPrioritiseRunning(String clusterName, Optional limit); + public List getRepairRunsForClusterPrioritiseRunning(String clusterName, Optional limit) { + List foundRepairRuns = repairRuns.values().stream().filter(row -> row.getClusterName() == clusterName).collect(Collectors.toList()); + Comparator comparator = new Comparator() { + @Override + public int compare(RepairRun o1, RepairRun o2) { + return o1.getRunState().compareTo(o2.getRunState()); + } + }; + Collections.sort(foundRepairRuns, comparator); + return foundRepairRuns; + } @Override public Collection getRepairRunsForUnit(UUID repairUnitId) { From 12046fe7a7436579bc0a5d563f556c403d15020f Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Wed, 11 Jan 2023 15:56:32 +1100 Subject: [PATCH 06/32] Additional import for CassandraStorage. --- .../cassandrareaper/storage/CassandraStorage.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java index 30b29fd9e..4454e7383 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java @@ -47,6 +47,7 @@ import java.io.IOException; import java.math.BigInteger; import java.time.LocalDate; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -961,7 +962,7 @@ public Collection getRepairRunsForCluster(String clusterName, Optiona } @Override - public Collection getRepairRunsForClusterPrioritiseRunning(String clusterName, Optional limit) { + public List getRepairRunsForClusterPrioritiseRunning(String clusterName, Optional limit) { List repairRunFutures = Lists.newArrayList(); // Grab all RUNNING repair_runs for the given cluster name repairRunFutures.add( @@ -1001,7 +1002,15 @@ public Collection getRepairRunsForClusterPrioritiseRunning(String clu MAX_RETURNED_REPAIR_RUNS))) ); - return getRepairRunsAsync(repairRunFutures); + List flattenedRows = Lists.newArrayList(); + repairRunFutures + .stream() + .forEach( + resSet -> resSet.getUninterruptibly().forEach( + row -> flattenedRows.add(buildRepairRunFromRow(row, row.getUUID("id"))) + ) + ); + return flattenedRows.subList(0, limit.orElse(MAX_RETURNED_REPAIR_RUNS)); } @Override From e31d419a7f268e6597209f358d6e114e72b5abb6 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Wed, 11 Jan 2023 16:08:52 +1100 Subject: [PATCH 07/32] Checkstyle, rename migration. --- .../io/cassandrareaper/resources/RepairRunResource.java | 4 +++- .../io/cassandrareaper/storage/CassandraStorage.java | 3 +-- .../java/io/cassandrareaper/storage/MemoryStorage.java | 9 +++++++-- .../{add_2i_status.cql => 032_add_2i_status.cql} | 0 4 files changed, 11 insertions(+), 5 deletions(-) rename src/server/src/main/resources/db/cassandra/{add_2i_status.cql => 032_add_2i_status.cql} (100%) diff --git a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java index bdaf0ec00..2860cfd79 100644 --- a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java +++ b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java @@ -608,7 +608,9 @@ public Response getRepairRunsForCluster( @QueryParam("limit") Optional limit) { LOG.debug("get repair run for cluster called with: cluster_name = {}", clusterName); - final Collection repairRuns = context.storage.getRepairRunsForClusterPrioritiseRunning(clusterName, limit); + final Collection repairRuns = context + .storage + .getRepairRunsForClusterPrioritiseRunning(clusterName, limit); final Collection repairRunViews = new ArrayList<>(); for (final RepairRun repairRun : repairRuns) { repairRunViews.add(getRepairRunStatus(repairRun)); diff --git a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java index 4454e7383..be1764c9d 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java @@ -47,7 +47,6 @@ import java.io.IOException; import java.math.BigInteger; import java.time.LocalDate; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -1009,7 +1008,7 @@ public List getRepairRunsForClusterPrioritiseRunning(String clusterNa resSet -> resSet.getUninterruptibly().forEach( row -> flattenedRows.add(buildRepairRunFromRow(row, row.getUUID("id"))) ) - ); + ); return flattenedRows.subList(0, limit.orElse(MAX_RETURNED_REPAIR_RUNS)); } diff --git a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java index 220a0854b..c98ba8b11 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java @@ -49,7 +49,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import org.apache.cassandra.tools.nodetool.Repair; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.slf4j.Logger; @@ -194,9 +193,15 @@ public List getRepairRunsForCluster(String clusterName, Optional getRepairRunsForClusterPrioritiseRunning(String clusterName, Optional limit) { - List foundRepairRuns = repairRuns.values().stream().filter(row -> row.getClusterName() == clusterName).collect(Collectors.toList()); + List foundRepairRuns = repairRuns + .values() + .stream() + .filter( + row -> row.getClusterName() == clusterName).collect(Collectors.toList() + ); Comparator comparator = new Comparator() { @Override public int compare(RepairRun o1, RepairRun o2) { diff --git a/src/server/src/main/resources/db/cassandra/add_2i_status.cql b/src/server/src/main/resources/db/cassandra/032_add_2i_status.cql similarity index 100% rename from src/server/src/main/resources/db/cassandra/add_2i_status.cql rename to src/server/src/main/resources/db/cassandra/032_add_2i_status.cql From bced1f11a31c9190888b899a9f488b905ce4798d Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Wed, 11 Jan 2023 16:39:05 +1100 Subject: [PATCH 08/32] A more elegant way to iterate over the RunStates and query for them. --- .../io/cassandrareaper/core/RepairRun.java | 3 +- .../storage/CassandraStorage.java | 49 +++++-------------- 2 files changed, 13 insertions(+), 39 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java index 1d3dcace2..d41581fda 100644 --- a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java +++ b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java @@ -179,7 +179,8 @@ public int hashCode() { public String toString() { return String.format("%s[%s] for %s", getClass().getSimpleName(), id.toString(), clusterName); } - + // The values in this enum are declared in order of "interestingness", + // this is used to order RepairRuns in the UI so that e.g. RUNNING runs come first. public enum RunState { RUNNING, NOT_STARTED, diff --git a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java index be1764c9d..aa87eb8f3 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java @@ -963,44 +963,17 @@ public Collection getRepairRunsForCluster(String clusterName, Optiona @Override public List getRepairRunsForClusterPrioritiseRunning(String clusterName, Optional limit) { List repairRunFutures = Lists.newArrayList(); - // Grab all RUNNING repair_runs for the given cluster name - repairRunFutures.add( - session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "RUNNING", limit.orElse( - MAX_RETURNED_REPAIR_RUNS))) - ); - // Grab all NOT_STARTED repair_runs for the given cluster name - repairRunFutures.add( - session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "NOT_STARTED", limit.orElse( - MAX_RETURNED_REPAIR_RUNS))) - ); - // Other possible statuses are: - // ERROR, - // PAUSED, - // ABORTED, - // DELETED, - // DONE - // We add these to the result set according to how interesting this result is. - repairRunFutures.add( - session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "ERROR", limit.orElse( - MAX_RETURNED_REPAIR_RUNS))) - ); - repairRunFutures.add( - session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "PAUSED", limit.orElse( - MAX_RETURNED_REPAIR_RUNS))) - ); - repairRunFutures.add( - session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "ABORTED", limit.orElse( - MAX_RETURNED_REPAIR_RUNS))) - ); - repairRunFutures.add( - session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "DELETED", limit.orElse( - MAX_RETURNED_REPAIR_RUNS))) - ); - repairRunFutures.add( - session.executeAsync(getRepairRunForClusterWhereStatusPrepStmt.bind(clusterName, "DONE", limit.orElse( - MAX_RETURNED_REPAIR_RUNS))) - ); - + // We've set up the RunState enum so that values are declared in order of "interestingness", + // we iterate over the table via the secondary index according to that ordering. + for (RunState state : RepairRun.RunState.values()) { + repairRunFutures.add( + session + .executeAsync(getRepairRunForClusterWhereStatusPrepStmt + .bind(clusterName, state.toString(), limit.orElse(MAX_RETURNED_REPAIR_RUNS) + ) + ) + ); + } List flattenedRows = Lists.newArrayList(); repairRunFutures .stream() From e0d0bf9267a343112edbb2c63f0eba2ae047e772 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Wed, 11 Jan 2023 17:36:07 +1100 Subject: [PATCH 09/32] Checkstyle, make sure MemoryStorage returns only the first rows in getRepairRunsForClusterPrioritiseRunning. --- src/server/src/main/java/io/cassandrareaper/core/RepairRun.java | 1 + .../src/main/java/io/cassandrareaper/storage/MemoryStorage.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java index d41581fda..aec6dcef9 100644 --- a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java +++ b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java @@ -179,6 +179,7 @@ public int hashCode() { public String toString() { return String.format("%s[%s] for %s", getClass().getSimpleName(), id.toString(), clusterName); } + // The values in this enum are declared in order of "interestingness", // this is used to order RepairRuns in the UI so that e.g. RUNNING runs come first. public enum RunState { diff --git a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java index c98ba8b11..c664f8ba2 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java @@ -209,7 +209,7 @@ public int compare(RepairRun o1, RepairRun o2) { } }; Collections.sort(foundRepairRuns, comparator); - return foundRepairRuns; + return foundRepairRuns.subList(0, limit.orElse(1000)); } @Override From d61b8d6c7aa450d37e923aa1618013eae7a6a97b Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Thu, 12 Jan 2023 13:18:59 +1100 Subject: [PATCH 10/32] Fix use of incorrectly named column in `getRepairRunForClusterWhereStatusPrepStmt`. --- .../main/java/io/cassandrareaper/storage/CassandraStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java index aa87eb8f3..ec3fb3621 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java @@ -424,7 +424,7 @@ private void prepareStatements() { getRepairRunForClusterPrepStmt = session.prepare( "SELECT * FROM repair_run_by_cluster_v2 WHERE cluster_name = ? limit ?"); getRepairRunForClusterWhereStatusPrepStmt = session.prepare( - "SELECT * FROM repair_run_by_cluster_v2 WHERE cluster_name = ? AND state = ? limit ?"); + "SELECT * FROM repair_run_by_cluster_v2 WHERE cluster_name = ? AND repair_run_state = ? limit ?"); getRepairRunForUnitPrepStmt = session.prepare("SELECT * FROM repair_run_by_unit WHERE repair_unit_id = ?"); deleteRepairRunPrepStmt = session.prepare("DELETE FROM repair_run WHERE id = ?"); deleteRepairRunByClusterPrepStmt From def27998e8cbbc6821221c366af26b394937ecc6 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Fri, 13 Jan 2023 15:49:12 +1100 Subject: [PATCH 11/32] Use index table `repair_run_by_cluster_v2` to obtain UUIDs for cluster and state and then query the main `repair_runs` table using the found UUIDs. --- .../storage/CassandraStorage.java | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java index ec3fb3621..17a7b71b2 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java @@ -424,7 +424,7 @@ private void prepareStatements() { getRepairRunForClusterPrepStmt = session.prepare( "SELECT * FROM repair_run_by_cluster_v2 WHERE cluster_name = ? limit ?"); getRepairRunForClusterWhereStatusPrepStmt = session.prepare( - "SELECT * FROM repair_run_by_cluster_v2 WHERE cluster_name = ? AND repair_run_state = ? limit ?"); + "SELECT id FROM repair_run_by_cluster_v2 WHERE cluster_name = ? AND repair_run_state = ? limit ?"); getRepairRunForUnitPrepStmt = session.prepare("SELECT * FROM repair_run_by_unit WHERE repair_unit_id = ?"); deleteRepairRunPrepStmt = session.prepare("DELETE FROM repair_run WHERE id = ?"); deleteRepairRunByClusterPrepStmt @@ -962,11 +962,13 @@ public Collection getRepairRunsForCluster(String clusterName, Optiona @Override public List getRepairRunsForClusterPrioritiseRunning(String clusterName, Optional limit) { - List repairRunFutures = Lists.newArrayList(); + List repairUuidFuturesByState = Lists.newArrayList(); // We've set up the RunState enum so that values are declared in order of "interestingness", // we iterate over the table via the secondary index according to that ordering. for (RunState state : RepairRun.RunState.values()) { - repairRunFutures.add( + repairUuidFuturesByState.add( + // repairUUIDFutures will be a List of resultSetFutures, each of which contains a ResultSet of + // UUIDs for one status. session .executeAsync(getRepairRunForClusterWhereStatusPrepStmt .bind(clusterName, state.toString(), limit.orElse(MAX_RETURNED_REPAIR_RUNS) @@ -974,17 +976,38 @@ public List getRepairRunsForClusterPrioritiseRunning(String clusterNa ) ); } - List flattenedRows = Lists.newArrayList(); - repairRunFutures + List flattenedUuids = Lists.newArrayList(); + // Flatten the UUIDs from each status down into a single array and trim. + for (ResultSetFuture idResSetFuture : repairUuidFuturesByState) { + idResSetFuture + .getUninterruptibly() + .forEach( + row -> flattenedUuids.add(row.getUUID("id")) + ); + } + flattenedUuids.subList(0, limit.orElse(MAX_RETURNED_REPAIR_RUNS)); + // Run an async query on each UUID in the flattened list, against the main repair_run table with + // all columns required as an input to `buildRepairRunFromRow`. + List repairRunFutures = Lists.newArrayList(); + flattenedUuids.forEach(uuid -> + repairRunFutures.add( + session + .executeAsync(getRepairRunPrepStmt.bind(uuid) + ) + ) + ); + // Defuture the repair_run rows and build the strongly typed RepairRun objects from the contents. + return repairRunFutures .stream() - .forEach( - resSet -> resSet.getUninterruptibly().forEach( - row -> flattenedRows.add(buildRepairRunFromRow(row, row.getUUID("id"))) - ) - ); - return flattenedRows.subList(0, limit.orElse(MAX_RETURNED_REPAIR_RUNS)); + .map( + row -> { + Row extractedRow = row.getUninterruptibly().one(); + return buildRepairRunFromRow(extractedRow, extractedRow.getUUID("id")); + } + ).collect(Collectors.toList()); } + @Override public Collection getRepairRunsForUnit(UUID repairUnitId) { List repairRunFutures = Lists.newArrayList(); From b5c09890d629cb1cfe260e2a4a8fe9f38fa61df7 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Fri, 13 Jan 2023 17:34:12 +1100 Subject: [PATCH 12/32] Avoid out of bounds list indices by ensuring that we always use min(list.size(), limit.orElse(...)) --- .../java/io/cassandrareaper/storage/CassandraStorage.java | 4 +++- .../main/java/io/cassandrareaper/storage/MemoryStorage.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java index 17a7b71b2..32a73b92d 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java @@ -114,6 +114,8 @@ import systems.composable.dropwizard.cassandra.pooling.PoolingOptionsFactory; import systems.composable.dropwizard.cassandra.retry.RetryPolicyFactory; +import static java.lang.Math.min; + public final class CassandraStorage implements IStorage, IDistributedStorage { private static final int METRICS_PARTITIONING_TIME_MINS = 10; @@ -985,7 +987,7 @@ public List getRepairRunsForClusterPrioritiseRunning(String clusterNa row -> flattenedUuids.add(row.getUUID("id")) ); } - flattenedUuids.subList(0, limit.orElse(MAX_RETURNED_REPAIR_RUNS)); + flattenedUuids.subList(0, min(flattenedUuids.size(), limit.orElse(MAX_RETURNED_REPAIR_RUNS)) + 1); // Run an async query on each UUID in the flattened list, against the main repair_run table with // all columns required as an input to `buildRepairRunFromRow`. List repairRunFutures = Lists.newArrayList(); diff --git a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java index c664f8ba2..4ce34843d 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java @@ -54,6 +54,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static java.lang.Math.min; + /** * Implements the StorageAPI using transient Java classes. */ @@ -209,7 +211,7 @@ public int compare(RepairRun o1, RepairRun o2) { } }; Collections.sort(foundRepairRuns, comparator); - return foundRepairRuns.subList(0, limit.orElse(1000)); + return foundRepairRuns.subList(0, min(foundRepairRuns.size(), limit.orElse(1000)) + 1); } @Override From 2ee5be84df38f3c2f741d231989064871b7de184 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Fri, 13 Jan 2023 20:00:30 +1100 Subject: [PATCH 13/32] Integration test definition. --- .../integration_reaper_functionality.feature | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/server/src/test/resources/io.cassandrareaper.acceptance/integration_reaper_functionality.feature b/src/server/src/test/resources/io.cassandrareaper.acceptance/integration_reaper_functionality.feature index 2a31085b3..c82b11695 100644 --- a/src/server/src/test/resources/io.cassandrareaper.acceptance/integration_reaper_functionality.feature +++ b/src/server/src/test/resources/io.cassandrareaper.acceptance/integration_reaper_functionality.feature @@ -414,3 +414,17 @@ Feature: Using Reaper When the last added cluster is force deleted Then reaper has no longer the last added cluster in storage ${cucumber.upgrade-versions} + +@sidecar + Scenario Outline: Verify that ongoing repairs are prioritized over finished ones when listing the runs + Given that reaper is running + And reaper has no cluster in storage + When an add-cluster request is made to reaper with authentication + Then reaper has the last added cluster in storage + And a new repair is added for "test" and keyspace "test_keyspace" + And I add and abort 10 repairs for "test" and keyspace "test_keyspace2" + Then when I list the last 10 repairs, I can see 1 repairs at "NOT_STARTED" state + And when I list the last 10 repairs, I can see 9 repairs at "ABORTED" state + When the last added cluster is deleted + Then reaper has no longer the last added cluster in storage + ${cucumber.upgrade-versions} \ No newline at end of file From 094c190b24e316ccb0cdb9679731801159407d99 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Fri, 13 Jan 2023 20:01:04 +1100 Subject: [PATCH 14/32] Re-order the RunState enum according to clarified requirements. --- .../src/main/java/io/cassandrareaper/core/RepairRun.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java index aec6dcef9..f2d77fd8f 100644 --- a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java +++ b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java @@ -184,12 +184,13 @@ public String toString() { // this is used to order RepairRuns in the UI so that e.g. RUNNING runs come first. public enum RunState { RUNNING, + PAUSED, NOT_STARTED, + ERROR, - PAUSED, + DONE, ABORTED, - DELETED, - DONE; + DELETED; public boolean isActive() { return this == RUNNING || this == PAUSED; From 1bb531f7e66941af77bd49a40f6a3f1ff08a4c8f Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Mon, 16 Jan 2023 16:39:24 +1100 Subject: [PATCH 15/32] Checkstyle... --- .../acceptance/BasicSteps.java | 87 ++++++++++++++++++- .../integration_reaper_functionality.feature | 2 +- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java index 2d09bae39..7406373bc 100644 --- a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java +++ b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java @@ -45,7 +45,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; - import javax.ws.rs.client.Client; import javax.ws.rs.core.Response; import javax.ws.rs.sse.SseEventSource; @@ -57,6 +56,7 @@ import com.datastax.driver.core.VersionNumber; import com.datastax.driver.core.exceptions.AlreadyExistsException; import com.datastax.driver.core.utils.UUIDs; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -2895,4 +2895,89 @@ public void startFakeRepairSchedule() { Response.Status.NOT_FOUND); }); } + + @And("^I add 11 and abort the most recent 10 repairs for cluster \"([^\"]*)\" and keyspace \"([^\"]*)\"$") + public void addAndAbortRepairs(Integer repairsAdded, String clusterName, String keyspace) throws Throwable { + synchronized (BasicSteps.class) { + testContext.TEST_CLUSTER = (String) clusterName; + Set tables = Sets.newHashSet(); + testContext.addClusterInfo(clusterName, keyspace, tables); + HashMap params = Maps.newHashMap(); + params.put("clusterName", clusterName); + params.put("keyspace", keyspace); + params.put("owner", "test_user"); + + RUNNERS.parallelStream().forEach(runner -> { + Integer iter = 1; + while (iter <= 11) { + Response response = runner.callReaper("POST", "/repair_run", Optional.of(params)); + String responseData = response.readEntity(String.class); + Assertions + .assertThat(response.getStatus()) + .isEqualTo(Response.Status.CREATED.getStatusCode()) + .withFailMessage(responseData); + String id = ""; + try { + id = new ObjectMapper().readValue(responseData, String.class); + testContext.addCurrentRepairId(UUID.fromString(id)); + } catch (Throwable e) { + Assertions.fail("response deserialisation failed"); + } + response = runner.callReaper( + "PUT", + String.format("/%s/state/%s", id, "ABORTED"), + Optional.empty()); + Assertions + .assertThat(response.getStatus()) + .isEqualTo(Response.Status.OK.getStatusCode()) + .withFailMessage(responseData); + iter = iter ++; + } + }); + } + + } + + @Then("^when I list the last (\\d+) repairs, I can see (\\d+) repairs at (\\d+) state$") + public void listRepairs(Integer limit, Integer expectedRepairsCount, String expectedState) { + synchronized (BasicSteps.class) { + RUNNERS.parallelStream().forEach(runner -> { + HashMap params = Maps.newHashMap(); + params.put("limit", limit.toString()); + Response resp = runner.callReaper( + "GET", + "/repair_run/cluster/" + TestContext.TEST_CLUSTER, + Optional.of(params) + ); + String responseData = resp.readEntity(String.class); + Assertions + .assertThat(resp.getStatus()) + .isEqualTo(Response.Status.OK.getStatusCode()) + .withFailMessage(responseData); + Assertions + .assertThat(responseData).isNotBlank(); + + List runs = SimpleReaperClient.parseRepairRunStatusListJSON(responseData) + .stream() + .filter(r -> RepairRun.RunState.RUNNING == r.getState() || RepairRun.RunState.DONE == r.getState()) + .filter(r -> r.getCause().contains(testContext.getCurrentScheduleId().toString())) + .collect(Collectors.toList()); + Integer countInState = runs.stream() + .filter(run -> run.getState() == RepairRun.RunState.valueOf(expectedState)) + .collect(Collectors.toList()) + .size(); + Assertions + .assertThat(countInState) + .isEqualTo(expectedRepairsCount) + .withFailMessage( + "actual number %i of repairs in state %s did not match expected number %i", + countInState, + expectedState, + expectedRepairsCount); + }); + + } + } + } + diff --git a/src/server/src/test/resources/io.cassandrareaper.acceptance/integration_reaper_functionality.feature b/src/server/src/test/resources/io.cassandrareaper.acceptance/integration_reaper_functionality.feature index c82b11695..8af3588f8 100644 --- a/src/server/src/test/resources/io.cassandrareaper.acceptance/integration_reaper_functionality.feature +++ b/src/server/src/test/resources/io.cassandrareaper.acceptance/integration_reaper_functionality.feature @@ -422,7 +422,7 @@ Feature: Using Reaper When an add-cluster request is made to reaper with authentication Then reaper has the last added cluster in storage And a new repair is added for "test" and keyspace "test_keyspace" - And I add and abort 10 repairs for "test" and keyspace "test_keyspace2" + And I add 11 and abort the most recent 10 repairs for cluster "test" and keyspace "test_keyspace2" Then when I list the last 10 repairs, I can see 1 repairs at "NOT_STARTED" state And when I list the last 10 repairs, I can see 9 repairs at "ABORTED" state When the last added cluster is deleted From 5e126e659651c13ac0558bd33f8233dbfda0bb3f Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Mon, 16 Jan 2023 19:28:38 +1100 Subject: [PATCH 16/32] Rework the storage layer queries so that they only use 2i for high priority statuses. Fix issue in MemoryStorage and add test for it. --- .../storage/CassandraStorage.java | 29 +++++++++++++++++-- .../storage/MemoryStorage.java | 5 ++-- .../acceptance/BasicSteps.java | 1 - .../resources/RepairRunResourceTest.java | 1 + 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java index 32a73b92d..cb408dc8c 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java @@ -47,6 +47,7 @@ import java.io.IOException; import java.math.BigInteger; import java.time.LocalDate; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -967,7 +968,14 @@ public List getRepairRunsForClusterPrioritiseRunning(String clusterNa List repairUuidFuturesByState = Lists.newArrayList(); // We've set up the RunState enum so that values are declared in order of "interestingness", // we iterate over the table via the secondary index according to that ordering. - for (RunState state : RepairRun.RunState.values()) { + for (RunState state : + Arrays + .stream(RunState.values()) + .filter(v -> + Arrays.asList("RUNNING", "PAUSED", "NOT_STARTED") + .contains(v.toString())) + .collect(Collectors.toList()) + ) { repairUuidFuturesByState.add( // repairUUIDFutures will be a List of resultSetFutures, each of which contains a ResultSet of // UUIDs for one status. @@ -978,8 +986,14 @@ public List getRepairRunsForClusterPrioritiseRunning(String clusterNa ) ); } + ResultSetFuture repairUuidFuturesNoState = session + .executeAsync(getRepairRunForClusterPrepStmt + .bind(clusterName, limit.orElse(MAX_RETURNED_REPAIR_RUNS) + ) + ); + List flattenedUuids = Lists.newArrayList(); - // Flatten the UUIDs from each status down into a single array and trim. + // Flatten the UUIDs from each status down into a single array. for (ResultSetFuture idResSetFuture : repairUuidFuturesByState) { idResSetFuture .getUninterruptibly() @@ -987,7 +1001,16 @@ public List getRepairRunsForClusterPrioritiseRunning(String clusterNa row -> flattenedUuids.add(row.getUUID("id")) ); } - flattenedUuids.subList(0, min(flattenedUuids.size(), limit.orElse(MAX_RETURNED_REPAIR_RUNS)) + 1); + // Merge the two lists and trim. + repairUuidFuturesNoState.getUninterruptibly().forEach(row -> { + UUID uuid = row.getUUID("id"); + if (!flattenedUuids.contains(uuid)) { + flattenedUuids.add(uuid); + } + } + ); + flattenedUuids.subList(0, min(flattenedUuids.size(), limit.orElse(MAX_RETURNED_REPAIR_RUNS))); + // Run an async query on each UUID in the flattened list, against the main repair_run table with // all columns required as an input to `buildRepairRunFromRow`. List repairRunFutures = Lists.newArrayList(); diff --git a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java index 4ce34843d..69607248c 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java @@ -34,6 +34,7 @@ import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; @@ -202,7 +203,7 @@ public List getRepairRunsForClusterPrioritiseRunning(String clusterNa .values() .stream() .filter( - row -> row.getClusterName() == clusterName).collect(Collectors.toList() + row -> row.getClusterName().equals(clusterName.toLowerCase(Locale.ROOT))).collect(Collectors.toList() ); Comparator comparator = new Comparator() { @Override @@ -211,7 +212,7 @@ public int compare(RepairRun o1, RepairRun o2) { } }; Collections.sort(foundRepairRuns, comparator); - return foundRepairRuns.subList(0, min(foundRepairRuns.size(), limit.orElse(1000)) + 1); + return foundRepairRuns.subList(0, min(foundRepairRuns.size(), limit.orElse(1000))); } @Override diff --git a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java index 7406373bc..8516ce493 100644 --- a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java +++ b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java @@ -2935,7 +2935,6 @@ public void addAndAbortRepairs(Integer repairsAdded, String clusterName, String } }); } - } @Then("^when I list the last (\\d+) repairs, I can see (\\d+) repairs at (\\d+) state$") diff --git a/src/server/src/test/java/io/cassandrareaper/resources/RepairRunResourceTest.java b/src/server/src/test/java/io/cassandrareaper/resources/RepairRunResourceTest.java index 1154a1cf7..d571e88eb 100644 --- a/src/server/src/test/java/io/cassandrareaper/resources/RepairRunResourceTest.java +++ b/src/server/src/test/java/io/cassandrareaper/resources/RepairRunResourceTest.java @@ -259,6 +259,7 @@ public void testAddRepairRun() throws Exception { assertEquals(1, context.storage.getClusters().size()); assertEquals(1, context.storage.getRepairRunsForCluster(clustername, Optional.of(2)).size()); + assertEquals(1, context.storage.getRepairRunsForClusterPrioritiseRunning(clustername, Optional.of(2)).size()); assertEquals(1, context.storage.getRepairRunIdsForCluster(clustername, Optional.empty()).size()); UUID runId = context.storage.getRepairRunIdsForCluster(clustername, Optional.empty()).iterator().next(); RepairRun run = context.storage.getRepairRun(runId).get(); From 6c6028fc9e4e14721a9710d18dc9db60fa2728d3 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Mon, 16 Jan 2023 21:01:58 +1100 Subject: [PATCH 17/32] Fix acceptance test arity issue. --- .../src/test/java/io/cassandrareaper/acceptance/BasicSteps.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java index 8516ce493..e80f45572 100644 --- a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java +++ b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java @@ -2897,7 +2897,7 @@ public void startFakeRepairSchedule() { } @And("^I add 11 and abort the most recent 10 repairs for cluster \"([^\"]*)\" and keyspace \"([^\"]*)\"$") - public void addAndAbortRepairs(Integer repairsAdded, String clusterName, String keyspace) throws Throwable { + public void addAndAbortRepairs(String clusterName, String keyspace) throws Throwable { synchronized (BasicSteps.class) { testContext.TEST_CLUSTER = (String) clusterName; Set tables = Sets.newHashSet(); From 330b0bb95d75b1f22943c36a1415ae1de45c36c9 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Tue, 17 Jan 2023 10:46:39 +1100 Subject: [PATCH 18/32] Alex's suggestions. --- .../acceptance/BasicSteps.java | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java index e80f45572..05ab311e9 100644 --- a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java +++ b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java @@ -2907,33 +2907,33 @@ public void addAndAbortRepairs(String clusterName, String keyspace) throws Throw params.put("keyspace", keyspace); params.put("owner", "test_user"); - RUNNERS.parallelStream().forEach(runner -> { - Integer iter = 1; - while (iter <= 11) { - Response response = runner.callReaper("POST", "/repair_run", Optional.of(params)); - String responseData = response.readEntity(String.class); - Assertions - .assertThat(response.getStatus()) - .isEqualTo(Response.Status.CREATED.getStatusCode()) - .withFailMessage(responseData); - String id = ""; - try { - id = new ObjectMapper().readValue(responseData, String.class); - testContext.addCurrentRepairId(UUID.fromString(id)); - } catch (Throwable e) { - Assertions.fail("response deserialisation failed"); - } - response = runner.callReaper( - "PUT", - String.format("/%s/state/%s", id, "ABORTED"), - Optional.empty()); - Assertions - .assertThat(response.getStatus()) - .isEqualTo(Response.Status.OK.getStatusCode()) - .withFailMessage(responseData); - iter = iter ++; + Integer iter = 1; + while (iter <= 11) { + Response response = RUNNERS.get(0).callReaper("POST", "/repair_run", Optional.of(params)); + String responseData = response.readEntity(String.class); + Assertions + .assertThat(response.getStatus()) + .isEqualTo(Response.Status.CREATED.getStatusCode()) + .withFailMessage(responseData); + String id = ""; + try { + id = new ObjectMapper().readValue(responseData, String.class); + testContext.addCurrentRepairId(UUID.fromString(id)); + } catch (Throwable e) { + LOG.error("response deserialisation failed", e); + LOG.error("Response data was: {}", responseData); + Assertions.fail("response deserialisation failed"); } - }); + response = RUNNERS.get(0).callReaper( + "PUT", + String.format("/%s/state/%s", id, "ABORTED"), + Optional.empty()); + Assertions + .assertThat(response.getStatus()) + .isEqualTo(Response.Status.OK.getStatusCode()) + .withFailMessage(responseData); + iter = iter ++; + }; } } From d2e65beb00c7a18cea5e4248a9efba1a1ef26434 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Tue, 17 Jan 2023 17:36:48 +1100 Subject: [PATCH 19/32] Fix deserialisation problem when getting RepairRun back from API. --- .../java/io/cassandrareaper/acceptance/BasicSteps.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java index 05ab311e9..c3f07ee81 100644 --- a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java +++ b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java @@ -2915,10 +2915,11 @@ public void addAndAbortRepairs(String clusterName, String keyspace) throws Throw .assertThat(response.getStatus()) .isEqualTo(Response.Status.CREATED.getStatusCode()) .withFailMessage(responseData); - String id = ""; + UUID id = UUID.randomUUID(); try { - id = new ObjectMapper().readValue(responseData, String.class); - testContext.addCurrentRepairId(UUID.fromString(id)); + RepairRun repairRun = new ObjectMapper().readValue(responseData, RepairRun.class); + id = repairRun.getId(); + testContext.addCurrentRepairId(id); } catch (Throwable e) { LOG.error("response deserialisation failed", e); LOG.error("Response data was: {}", responseData); @@ -2926,7 +2927,7 @@ public void addAndAbortRepairs(String clusterName, String keyspace) throws Throw } response = RUNNERS.get(0).callReaper( "PUT", - String.format("/%s/state/%s", id, "ABORTED"), + String.format("/%s/state/%s", id.toString(), "ABORTED"), Optional.empty()); Assertions .assertThat(response.getStatus()) From e8d061e51994cb8e8f9bc07f21888313b08e9207 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Tue, 17 Jan 2023 17:49:42 +1100 Subject: [PATCH 20/32] Fix test logic so that repair runs > 1 are aborted and repair run 1 is paused. --- .../acceptance/BasicSteps.java | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java index c3f07ee81..f10b1193d 100644 --- a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java +++ b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java @@ -2917,7 +2917,7 @@ public void addAndAbortRepairs(String clusterName, String keyspace) throws Throw .withFailMessage(responseData); UUID id = UUID.randomUUID(); try { - RepairRun repairRun = new ObjectMapper().readValue(responseData, RepairRun.class); + RepairRunStatus repairRun = new ObjectMapper().readValue(responseData, RepairRunStatus.class); id = repairRun.getId(); testContext.addCurrentRepairId(id); } catch (Throwable e) { @@ -2925,14 +2925,25 @@ public void addAndAbortRepairs(String clusterName, String keyspace) throws Throw LOG.error("Response data was: {}", responseData); Assertions.fail("response deserialisation failed"); } - response = RUNNERS.get(0).callReaper( - "PUT", - String.format("/%s/state/%s", id.toString(), "ABORTED"), - Optional.empty()); - Assertions - .assertThat(response.getStatus()) - .isEqualTo(Response.Status.OK.getStatusCode()) - .withFailMessage(responseData); + if (iter > 1) { + response = RUNNERS.get(0).callReaper( + "PUT", + String.format("repair_run/%s/state/%s", id.toString(), "ABORTED"), + Optional.empty()); + Assertions + .assertThat(response.getStatus()) + .isEqualTo(Response.Status.OK.getStatusCode()) + .withFailMessage(responseData); + } else { + response = RUNNERS.get(0).callReaper( + "PUT", + String.format("repair_run/%s/state/%s", id.toString(), "PAUSED"), + Optional.empty()); + Assertions + .assertThat(response.getStatus()) + .isEqualTo(Response.Status.OK.getStatusCode()) + .withFailMessage(responseData); + } iter = iter ++; }; } From 23c06d1a1431f7ab194f7c8d9b3a384871a94daa Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Wed, 18 Jan 2023 15:28:13 +1100 Subject: [PATCH 21/32] Need to set repair run to RUNNING before pausing/aborting. --- .../java/io/cassandrareaper/acceptance/BasicSteps.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java index f10b1193d..bb5c864fb 100644 --- a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java +++ b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java @@ -2925,6 +2925,14 @@ public void addAndAbortRepairs(String clusterName, String keyspace) throws Throw LOG.error("Response data was: {}", responseData); Assertions.fail("response deserialisation failed"); } + response = RUNNERS.get(0).callReaper( + "PUT", + String.format("repair_run/%s/state/%s", id.toString(), "RUNNING"), + Optional.empty()); + Assertions + .assertThat(response.getStatus()) + .isEqualTo(Response.Status.OK.getStatusCode()) + .withFailMessage(responseData); if (iter > 1) { response = RUNNERS.get(0).callReaper( "PUT", From 4cf7a8081f67a00a2594e67cce84697d0c7349e3 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Wed, 18 Jan 2023 16:57:13 +1100 Subject: [PATCH 22/32] While loop not terminating in tests, let's try a for loop. --- .../test/java/io/cassandrareaper/acceptance/BasicSteps.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java index bb5c864fb..08f4d2779 100644 --- a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java +++ b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java @@ -2907,8 +2907,7 @@ public void addAndAbortRepairs(String clusterName, String keyspace) throws Throw params.put("keyspace", keyspace); params.put("owner", "test_user"); - Integer iter = 1; - while (iter <= 11) { + for (int iter = 1; iter < 12; iter++) { Response response = RUNNERS.get(0).callReaper("POST", "/repair_run", Optional.of(params)); String responseData = response.readEntity(String.class); Assertions @@ -2952,7 +2951,6 @@ public void addAndAbortRepairs(String clusterName, String keyspace) throws Throw .isEqualTo(Response.Status.OK.getStatusCode()) .withFailMessage(responseData); } - iter = iter ++; }; } } From e38cc83ffebf561adc1fa23482c123dc21438ce9 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Thu, 19 Jan 2023 13:02:36 +1100 Subject: [PATCH 23/32] Make number of repairs added and aborted configurable. --- .../io/cassandrareaper/acceptance/BasicSteps.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java index 08f4d2779..17ab2fc2e 100644 --- a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java +++ b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java @@ -2896,8 +2896,11 @@ public void startFakeRepairSchedule() { }); } - @And("^I add 11 and abort the most recent 10 repairs for cluster \"([^\"]*)\" and keyspace \"([^\"]*)\"$") - public void addAndAbortRepairs(String clusterName, String keyspace) throws Throwable { + @And("^I add (\\d+) and abort the most recent (\\d+) repairs for cluster \"([^\"]*)\" and keyspace \"([^\"]*)\"$") + public void addAndAbortRepairs(Integer repairsAdded, + Integer repairsAborted, + String clusterName, + String keyspace) throws Throwable { synchronized (BasicSteps.class) { testContext.TEST_CLUSTER = (String) clusterName; Set tables = Sets.newHashSet(); @@ -2907,7 +2910,7 @@ public void addAndAbortRepairs(String clusterName, String keyspace) throws Throw params.put("keyspace", keyspace); params.put("owner", "test_user"); - for (int iter = 1; iter < 12; iter++) { + for (int iter = 1; iter <= repairsAdded; iter++) { Response response = RUNNERS.get(0).callReaper("POST", "/repair_run", Optional.of(params)); String responseData = response.readEntity(String.class); Assertions @@ -2932,7 +2935,7 @@ public void addAndAbortRepairs(String clusterName, String keyspace) throws Throw .assertThat(response.getStatus()) .isEqualTo(Response.Status.OK.getStatusCode()) .withFailMessage(responseData); - if (iter > 1) { + if (iter > (repairsAdded - repairsAborted)) { response = RUNNERS.get(0).callReaper( "PUT", String.format("repair_run/%s/state/%s", id.toString(), "ABORTED"), From 86d3a5a61d882cc2cf5aaef4ed748dd43530a7f0 Mon Sep 17 00:00:00 2001 From: Miles Garnsey <11435896+Miles-Garnsey@users.noreply.github.com> Date: Tue, 14 Feb 2023 14:49:05 +1100 Subject: [PATCH 24/32] Update src/server/src/main/resources/db/cassandra/032_add_2i_status.cql Co-authored-by: Alexander Dejanovski --- .../src/main/resources/db/cassandra/032_add_2i_status.cql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/src/main/resources/db/cassandra/032_add_2i_status.cql b/src/server/src/main/resources/db/cassandra/032_add_2i_status.cql index 18e61a8a2..a4fc5986d 100644 --- a/src/server/src/main/resources/db/cassandra/032_add_2i_status.cql +++ b/src/server/src/main/resources/db/cassandra/032_add_2i_status.cql @@ -15,4 +15,4 @@ -- -- Add a secondary index on `state` to the `repair_run` table. -CREATE INDEX state2i ON repair_run_by_cluster_v2 (repair_run_state); +CREATE INDEX IF NOT EXISTS state2i ON repair_run_by_cluster_v2 (repair_run_state); From 3d9e2f0651242085689a75a62c9291e2de3ff5f7 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Wed, 15 Feb 2023 17:03:05 +1100 Subject: [PATCH 25/32] Add additional test to check RepairRunStatus ordering against `/repair_runs/` as well as `/repair_runs/cluster` --- .../acceptance/BasicSteps.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java index 17ab2fc2e..d9300ad6e 100644 --- a/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java +++ b/src/server/src/test/java/io/cassandrareaper/acceptance/BasicSteps.java @@ -2964,6 +2964,7 @@ public void listRepairs(Integer limit, Integer expectedRepairsCount, String expe RUNNERS.parallelStream().forEach(runner -> { HashMap params = Maps.newHashMap(); params.put("limit", limit.toString()); + // Run query against /repair_run/cluster/ Response resp = runner.callReaper( "GET", "/repair_run/cluster/" + TestContext.TEST_CLUSTER, @@ -2995,7 +2996,41 @@ public void listRepairs(Integer limit, Integer expectedRepairsCount, String expe expectedState, expectedRepairsCount); }); + RUNNERS.parallelStream().forEach(runner -> { + HashMap params = Maps.newHashMap(); + params.put("limit", limit.toString()); + // Run query against /repair_run + Response resp = runner.callReaper( + "GET", + "/repair_run", + Optional.of(params) + ); + String responseData = resp.readEntity(String.class); + Assertions + .assertThat(resp.getStatus()) + .isEqualTo(Response.Status.OK.getStatusCode()) + .withFailMessage(responseData); + Assertions + .assertThat(responseData).isNotBlank(); + List runs = SimpleReaperClient.parseRepairRunStatusListJSON(responseData) + .stream() + .filter(r -> RepairRun.RunState.RUNNING == r.getState() || RepairRun.RunState.DONE == r.getState()) + .filter(r -> r.getCause().contains(testContext.getCurrentScheduleId().toString())) + .collect(Collectors.toList()); + Integer countInState = runs.stream() + .filter(run -> run.getState() == RepairRun.RunState.valueOf(expectedState)) + .collect(Collectors.toList()) + .size(); + Assertions + .assertThat(countInState) + .isEqualTo(expectedRepairsCount) + .withFailMessage( + "actual number %i of repairs in state %s did not match expected number %i", + countInState, + expectedState, + expectedRepairsCount); + }); } } From aa8fb9c025306934b84018072587a813cc136f51 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Wed, 15 Feb 2023 17:10:37 +1100 Subject: [PATCH 26/32] New ordering function for Lists of RepairRuns, using `isTerminated()` and the timeUUID to determine ordering. --- .../io/cassandrareaper/core/RepairRun.java | 22 +++++++++++++++++++ .../resources/RepairRunResource.java | 20 ++++++++--------- .../storage/MemoryStorage.java | 8 +------ 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java index f2d77fd8f..9cec369cb 100644 --- a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java +++ b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java @@ -17,7 +17,11 @@ package io.cassandrareaper.core; +import java.util.Collection; import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.UUID; @@ -27,6 +31,8 @@ import org.joda.time.DateTime; import org.joda.time.DateTimeComparator; +import static java.lang.Math.min; + public final class RepairRun implements Comparable { private final UUID id; @@ -344,4 +350,20 @@ public RepairRun build(UUID id) { return new RepairRun(this, id); } } + + public static void SortByRunState (List repairRunCollection) { + Comparator comparator = new Comparator() { + @Override + public int compare(RepairRun o1, RepairRun o2) { + if ((!o1.getRunState().isTerminated()) && o2.getRunState().isTerminated()) { + return -1; // o1 appears first. + } else if (o1.getRunState().isTerminated() && !o2.getRunState().isTerminated()) { + return 1; // o2 appears first. + } else { // Both RunStates have equal isFinished() values; compare on time instead. + return o1.getId().compareTo(o2.getId()); + } + } + }; + Collections.sort(repairRunCollection, comparator); + } } diff --git a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java index 2860cfd79..dbcc90828 100644 --- a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java +++ b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java @@ -663,17 +663,15 @@ public Response listRepairRuns( : context.storage.getClusters(); List runStatuses = Lists.newArrayList(); - for (final Cluster clstr : clusters) { - Collection runs = context.storage.getRepairRunsForCluster(clstr.getName(), limit); - - runStatuses.addAll( - (List) getRunStatuses(runs, desiredStates) - .stream() - .filter((run) -> !keyspace.isPresent() - || ((RepairRunStatus)run).getKeyspaceName().equals(keyspace.get())) - .collect(Collectors.toList())); - } - + List repairRuns = Lists.newArrayList(); + clusters.forEach(clstr -> repairRuns.addAll(context.storage.getRepairRunsForCluster(clstr.getName(), limit))); + RepairRun.SortByRunState(repairRuns); + runStatuses.addAll( + (List) getRunStatuses(repairRuns, desiredStates) + .stream() + .filter((run) -> !keyspace.isPresent() + || ((RepairRunStatus)run).getKeyspaceName().equals(keyspace.get())) + .collect(Collectors.toList())); return Response.ok().entity(runStatuses).build(); } catch (IllegalArgumentException e) { return Response.serverError().entity("Failed find cluster " + cluster.get()).build(); diff --git a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java index 69607248c..8012ca32d 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java @@ -205,13 +205,7 @@ public List getRepairRunsForClusterPrioritiseRunning(String clusterNa .filter( row -> row.getClusterName().equals(clusterName.toLowerCase(Locale.ROOT))).collect(Collectors.toList() ); - Comparator comparator = new Comparator() { - @Override - public int compare(RepairRun o1, RepairRun o2) { - return o1.getRunState().compareTo(o2.getRunState()); - } - }; - Collections.sort(foundRepairRuns, comparator); + RepairRun.SortByRunState(foundRepairRuns); return foundRepairRuns.subList(0, min(foundRepairRuns.size(), limit.orElse(1000))); } From 093633c1ea77ac71ddae94aadaa30c5306ea7277 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Wed, 15 Feb 2023 18:07:29 +1100 Subject: [PATCH 27/32] Checkstyle... --- .../src/main/java/io/cassandrareaper/core/RepairRun.java | 8 ++------ .../io/cassandrareaper/resources/RepairRunResource.java | 7 +++++-- .../java/io/cassandrareaper/storage/MemoryStorage.java | 3 +-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java index 9cec369cb..19741362c 100644 --- a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java +++ b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java @@ -17,10 +17,8 @@ package io.cassandrareaper.core; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.Set; @@ -31,8 +29,6 @@ import org.joda.time.DateTime; import org.joda.time.DateTimeComparator; -import static java.lang.Math.min; - public final class RepairRun implements Comparable { private final UUID id; @@ -351,11 +347,11 @@ public RepairRun build(UUID id) { } } - public static void SortByRunState (List repairRunCollection) { + public static void sortByRunState(List repairRunCollection) { Comparator comparator = new Comparator() { @Override public int compare(RepairRun o1, RepairRun o2) { - if ((!o1.getRunState().isTerminated()) && o2.getRunState().isTerminated()) { + if (!o1.getRunState().isTerminated() && o2.getRunState().isTerminated()) { return -1; // o1 appears first. } else if (o1.getRunState().isTerminated() && !o2.getRunState().isTerminated()) { return 1; // o2 appears first. diff --git a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java index dbcc90828..7b8b3c878 100644 --- a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java +++ b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java @@ -63,6 +63,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static java.lang.Math.min; + @Path("/repair_run") @Produces(MediaType.APPLICATION_JSON) @@ -665,9 +667,10 @@ public Response listRepairRuns( List runStatuses = Lists.newArrayList(); List repairRuns = Lists.newArrayList(); clusters.forEach(clstr -> repairRuns.addAll(context.storage.getRepairRunsForCluster(clstr.getName(), limit))); - RepairRun.SortByRunState(repairRuns); + RepairRun.sortByRunState(repairRuns); runStatuses.addAll( - (List) getRunStatuses(repairRuns, desiredStates) + (List) getRunStatuses( + repairRuns.subList(0, min(repairRuns.size(), limit.orElse(1000))), desiredStates) .stream() .filter((run) -> !keyspace.isPresent() || ((RepairRunStatus)run).getKeyspaceName().equals(keyspace.get())) diff --git a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java index 8012ca32d..f91632166 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java @@ -31,7 +31,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; @@ -205,7 +204,7 @@ public List getRepairRunsForClusterPrioritiseRunning(String clusterNa .filter( row -> row.getClusterName().equals(clusterName.toLowerCase(Locale.ROOT))).collect(Collectors.toList() ); - RepairRun.SortByRunState(foundRepairRuns); + RepairRun.sortByRunState(foundRepairRuns); return foundRepairRuns.subList(0, min(foundRepairRuns.size(), limit.orElse(1000))); } From e935f54077e0a5756eeec8cf13e60cc535b66cfa Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Fri, 17 Feb 2023 12:33:17 +1100 Subject: [PATCH 28/32] Move `SortByRunState()` into `RepairRunService`. --- .../java/io/cassandrareaper/core/RepairRun.java | 17 ----------------- .../resources/RepairRunResource.java | 2 +- .../service/RepairRunService.java | 17 +++++++++++++++++ .../cassandrareaper/storage/MemoryStorage.java | 3 ++- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java index 19741362c..04e4a0fd3 100644 --- a/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java +++ b/src/server/src/main/java/io/cassandrareaper/core/RepairRun.java @@ -18,8 +18,6 @@ package io.cassandrareaper.core; import java.util.Collections; -import java.util.Comparator; -import java.util.List; import java.util.Objects; import java.util.Set; import java.util.UUID; @@ -347,19 +345,4 @@ public RepairRun build(UUID id) { } } - public static void sortByRunState(List repairRunCollection) { - Comparator comparator = new Comparator() { - @Override - public int compare(RepairRun o1, RepairRun o2) { - if (!o1.getRunState().isTerminated() && o2.getRunState().isTerminated()) { - return -1; // o1 appears first. - } else if (o1.getRunState().isTerminated() && !o2.getRunState().isTerminated()) { - return 1; // o2 appears first. - } else { // Both RunStates have equal isFinished() values; compare on time instead. - return o1.getId().compareTo(o2.getId()); - } - } - }; - Collections.sort(repairRunCollection, comparator); - } } diff --git a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java index 7b8b3c878..2c77dff79 100644 --- a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java +++ b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java @@ -667,7 +667,7 @@ public Response listRepairRuns( List runStatuses = Lists.newArrayList(); List repairRuns = Lists.newArrayList(); clusters.forEach(clstr -> repairRuns.addAll(context.storage.getRepairRunsForCluster(clstr.getName(), limit))); - RepairRun.sortByRunState(repairRuns); + RepairRunService.sortByRunState(repairRuns); runStatuses.addAll( (List) getRunStatuses( repairRuns.subList(0, min(repairRuns.size(), limit.orElse(1000))), desiredStates) diff --git a/src/server/src/main/java/io/cassandrareaper/service/RepairRunService.java b/src/server/src/main/java/io/cassandrareaper/service/RepairRunService.java index c51bd744f..b68dd03fa 100644 --- a/src/server/src/main/java/io/cassandrareaper/service/RepairRunService.java +++ b/src/server/src/main/java/io/cassandrareaper/service/RepairRunService.java @@ -35,6 +35,7 @@ import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -85,6 +86,22 @@ public static RepairRunService create(AppContext context) { return new RepairRunService(context, () -> ClusterFacade.create(context)); } + public static void sortByRunState(List repairRunCollection) { + Comparator comparator = new Comparator() { + @Override + public int compare(RepairRun o1, RepairRun o2) { + if (!o1.getRunState().isTerminated() && o2.getRunState().isTerminated()) { + return 1; // o2 appears first. + } else if (o1.getRunState().isTerminated() && !o2.getRunState().isTerminated()) { + return -1; // o1 appears first. + } else { // Both RunStates have equal isFinished() values; compare on time instead. + return o1.getId().compareTo(o2.getId()); + } + } + }; + Collections.sort(repairRunCollection, comparator); + } + /** * Creates a repair run but does not start it immediately. * diff --git a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java index f91632166..e1c685167 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java @@ -49,6 +49,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import io.cassandrareaper.service.RepairRunService; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.slf4j.Logger; @@ -204,7 +205,7 @@ public List getRepairRunsForClusterPrioritiseRunning(String clusterNa .filter( row -> row.getClusterName().equals(clusterName.toLowerCase(Locale.ROOT))).collect(Collectors.toList() ); - RepairRun.sortByRunState(foundRepairRuns); + RepairRunService.sortByRunState(foundRepairRuns); return foundRepairRuns.subList(0, min(foundRepairRuns.size(), limit.orElse(1000))); } From 106043f8be316022d480648c4931121f56cb895c Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Fri, 17 Feb 2023 12:38:56 +1100 Subject: [PATCH 29/32] Use getRepairRunsForCluster for the repair_runs/ endpoint. --- .../java/io/cassandrareaper/resources/RepairRunResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java index 2c77dff79..faeb62864 100644 --- a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java +++ b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java @@ -666,7 +666,7 @@ public Response listRepairRuns( List runStatuses = Lists.newArrayList(); List repairRuns = Lists.newArrayList(); - clusters.forEach(clstr -> repairRuns.addAll(context.storage.getRepairRunsForCluster(clstr.getName(), limit))); + clusters.forEach(clstr -> repairRuns.addAll(context.storage.getRepairRunsForClusterPrioritiseRunning(clstr.getName(), limit))); RepairRunService.sortByRunState(repairRuns); runStatuses.addAll( (List) getRunStatuses( From ccc1f3ebe28fba9e239e3f976da43acbcf4b9e9f Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Fri, 17 Feb 2023 13:55:31 +1100 Subject: [PATCH 30/32] Checkstyle... --- .../java/io/cassandrareaper/resources/RepairRunResource.java | 4 +++- .../main/java/io/cassandrareaper/storage/MemoryStorage.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java index faeb62864..778d45b12 100644 --- a/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java +++ b/src/server/src/main/java/io/cassandrareaper/resources/RepairRunResource.java @@ -666,7 +666,9 @@ public Response listRepairRuns( List runStatuses = Lists.newArrayList(); List repairRuns = Lists.newArrayList(); - clusters.forEach(clstr -> repairRuns.addAll(context.storage.getRepairRunsForClusterPrioritiseRunning(clstr.getName(), limit))); + clusters.forEach(clstr -> repairRuns.addAll( + context.storage.getRepairRunsForClusterPrioritiseRunning(clstr.getName(), limit)) + ); RepairRunService.sortByRunState(repairRuns); runStatuses.addAll( (List) getRunStatuses( diff --git a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java index e1c685167..3e4d2bb6d 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/MemoryStorage.java @@ -27,6 +27,7 @@ import io.cassandrareaper.core.Snapshot; import io.cassandrareaper.resources.view.RepairRunStatus; import io.cassandrareaper.resources.view.RepairScheduleStatus; +import io.cassandrareaper.service.RepairRunService; import java.util.ArrayList; import java.util.Collection; @@ -49,7 +50,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import io.cassandrareaper.service.RepairRunService; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.slf4j.Logger; From b9320a4a35aad4bee04fcb502275227de04d9d75 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Fri, 17 Feb 2023 15:03:52 +1100 Subject: [PATCH 31/32] Flip the ordering in RunState comparator. --- .../java/io/cassandrareaper/service/RepairRunService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/service/RepairRunService.java b/src/server/src/main/java/io/cassandrareaper/service/RepairRunService.java index b68dd03fa..8a4f25472 100644 --- a/src/server/src/main/java/io/cassandrareaper/service/RepairRunService.java +++ b/src/server/src/main/java/io/cassandrareaper/service/RepairRunService.java @@ -91,9 +91,9 @@ public static void sortByRunState(List repairRunCollection) { @Override public int compare(RepairRun o1, RepairRun o2) { if (!o1.getRunState().isTerminated() && o2.getRunState().isTerminated()) { - return 1; // o2 appears first. - } else if (o1.getRunState().isTerminated() && !o2.getRunState().isTerminated()) { return -1; // o1 appears first. + } else if (o1.getRunState().isTerminated() && !o2.getRunState().isTerminated()) { + return 1; // o2 appears first. } else { // Both RunStates have equal isFinished() values; compare on time instead. return o1.getId().compareTo(o2.getId()); } From b23b40e1e01d81ca5f1a0cc0be904579e6d6ce55 Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Mon, 20 Feb 2023 12:23:38 +1100 Subject: [PATCH 32/32] Simplify filtering logic for runstates in CassandraStorage. --- .../io/cassandrareaper/storage/CassandraStorage.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java index cb408dc8c..f689598cf 100644 --- a/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java +++ b/src/server/src/main/java/io/cassandrareaper/storage/CassandraStorage.java @@ -968,14 +968,7 @@ public List getRepairRunsForClusterPrioritiseRunning(String clusterNa List repairUuidFuturesByState = Lists.newArrayList(); // We've set up the RunState enum so that values are declared in order of "interestingness", // we iterate over the table via the secondary index according to that ordering. - for (RunState state : - Arrays - .stream(RunState.values()) - .filter(v -> - Arrays.asList("RUNNING", "PAUSED", "NOT_STARTED") - .contains(v.toString())) - .collect(Collectors.toList()) - ) { + for (String state:Arrays.asList("RUNNING", "PAUSED", "NOT_STARTED")) { repairUuidFuturesByState.add( // repairUUIDFutures will be a List of resultSetFutures, each of which contains a ResultSet of // UUIDs for one status.