Skip to content

Commit

Permalink
[fixup] PR review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
emerkle826 committed Jul 8, 2021
1 parent 5a3ce81 commit cdf6b0d
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,13 @@ private Response addRepairSchedule(
boolean force) {

Optional<RepairSchedule> conflictingRepairSchedule
= repairScheduleService.conflictingRepairSchedule(cluster, unitBuilder);
= repairScheduleService.identicalRepairUnit(cluster, unitBuilder);

if (conflictingRepairSchedule.isPresent()) {
return Response.noContent().location(buildRepairScheduleUri(uriInfo, conflictingRepairSchedule.get())).build();
}

conflictingRepairSchedule = repairScheduleService.conflictingRepairSchedule(cluster, unitBuilder);

if (conflictingRepairSchedule.isPresent()) {
RepairSchedule existingSchedule = conflictingRepairSchedule.get();
Expand All @@ -272,7 +278,7 @@ private Response addRepairSchedule(
}
}

RepairUnit unit = repairUnitService.getOrCreateRepairUnit(cluster, unitBuilder);
RepairUnit unit = repairUnitService.getOrCreateRepairUnit(cluster, unitBuilder, force);

Preconditions
.checkState(unit.getIncrementalRepair() == incremental, "%s!=%s", unit.getIncrementalRepair(), incremental);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,24 @@ public Optional<RepairSchedule> conflictingRepairSchedule(Cluster cluster, Repai
return Optional.empty();
}

public Optional<RepairSchedule> identicalRepairUnit(Cluster cluster, RepairUnit.Builder repairUnit) {

Collection<RepairSchedule> repairSchedules = context.storage
.getRepairSchedulesForClusterAndKeyspace(repairUnit.clusterName, repairUnit.keyspaceName);

for (RepairSchedule sched : repairSchedules) {
RepairUnit repairUnitForSched = context.storage.getRepairUnit(sched.getRepairUnitId());
Preconditions.checkState(repairUnitForSched.getClusterName().equals(repairUnit.clusterName));
Preconditions.checkState(repairUnitForSched.getKeyspaceName().equals(repairUnit.keyspaceName));

// if the schedule is identical, return immediately
if (repairUnitService.identicalUnits(cluster, repairUnitForSched, repairUnit)) {
return Optional.of(sched);
}
}
return Optional.empty();
}

/**
* Instantiates a RepairSchedule and stores it in the storage backend.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
import io.cassandrareaper.AppContext;
import io.cassandrareaper.ReaperException;
import io.cassandrareaper.core.Cluster;
import io.cassandrareaper.core.Node;
import io.cassandrareaper.core.RepairSchedule;
import io.cassandrareaper.core.RepairUnit;
import io.cassandrareaper.core.Table;
import io.cassandrareaper.jmx.ClusterFacade;

import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -58,6 +60,10 @@ public static RepairUnitService create(AppContext context) {
}

public RepairUnit getOrCreateRepairUnit(Cluster cluster, RepairUnit.Builder params) {
return getOrCreateRepairUnit(cluster, params, false);
}

public RepairUnit getOrCreateRepairUnit(Cluster cluster, RepairUnit.Builder params, boolean force) {
if (params.incrementalRepair) {
try {
String version = ClusterFacade.create(context).getCassandraVersion(cluster);
Expand All @@ -69,7 +75,7 @@ public RepairUnit getOrCreateRepairUnit(Cluster cluster, RepairUnit.Builder para
}
}
Optional<RepairUnit> repairUnit = context.storage.getRepairUnit(params);
return repairUnit.isPresent() ? repairUnit.get() : createRepairUnit(cluster, params);
return repairUnit.isPresent() ? repairUnit.get() : createRepairUnit(cluster, params, force);
}

/**
Expand Down Expand Up @@ -130,9 +136,9 @@ private static boolean isBlackListedCompactionStrategy(Table table) {
.anyMatch(s -> table.getCompactionStrategy().toLowerCase().contains(s.toLowerCase()));
}

private RepairUnit createRepairUnit(Cluster cluster, RepairUnit.Builder builder) {
private RepairUnit createRepairUnit(Cluster cluster, RepairUnit.Builder builder, boolean force) {
Preconditions.checkArgument(
!unitConflicts(cluster, builder),
force || !unitConflicts(cluster, builder),
"unit conflicts with existing in " + builder.clusterName + ":" + builder.keyspaceName);

return context.storage.addRepairUnit(builder);
Expand Down Expand Up @@ -170,6 +176,51 @@ boolean conflictingUnits(Cluster cluster, RepairUnit unit, RepairUnit.Builder bu
return !Sets.intersection(listRepairTables(unit.with(), tables), listRepairTables(builder, tables)).isEmpty();
}

boolean identicalUnits(Cluster cluster, RepairUnit unit, RepairUnit.Builder builder) {
// if the Builders are equal, everything is the same
if (unit.with().equals(builder)) {
return true;
}

// if incremental repair is not the same, the units are not identical
if (unit.getIncrementalRepair() != builder.incrementalRepair.booleanValue()) {
// incremental reapir is not the same
return false;
}

// check the set of tables to be repaired
Preconditions.checkState(unit.getKeyspaceName().equals(builder.keyspaceName));

Set<String> tables = unit.getColumnFamilies().isEmpty() || builder.columnFamilies.isEmpty()
? getTableNamesForKeyspace(cluster, unit.getKeyspaceName())
: Collections.emptySet();

// if the set of tables to repair is not the same, the units are not identical
if (!Objects.equals(listRepairTables(unit.with(), tables), listRepairTables(builder, tables))) {
// repair tables not the same
return false;
}

// if the set of nodes isn't the same, the units are not identical
Set<String> unitNodes = getRepairUnitNodes(cluster, unit.with());
Set<String> builderNodes = getRepairUnitNodes(cluster, builder);
if (!Objects.equals(unitNodes, builderNodes)) {
// repair unit nodes not the same
return false;
}

// if the set of datacenetrrs isn't the same, the units are not identical
Set<String> unitDatacenters = getRepairUnitDatacenters(cluster, unit.with(), unitNodes);
Set<String> builderDatacenters = getRepairUnitDatacenters(cluster, builder, builderNodes);
if (!Objects.equals(unitDatacenters, builderDatacenters)) {
// repair datacenters not the same
return false;
}

// units are effectively identical
return true;
}

public Set<String> getTableNamesForKeyspace(Cluster cluster, String keyspace) {
try {
return ClusterFacade
Expand All @@ -184,6 +235,38 @@ public Set<String> getTableNamesForKeyspace(Cluster cluster, String keyspace) {
}
}

private Set<String> getRepairUnitNodes(Cluster cluster, RepairUnit.Builder builder) {
if (!builder.nodes.isEmpty()) {
return builder.nodes;
}
try {
return ClusterFacade
.create(context)
.getLiveNodes(cluster)
.stream()
.collect(Collectors.toSet());
} catch (ReaperException e) {
LOG.warn("Unable to get list of live nodes for cluster {}", cluster.getName());
return Collections.emptySet();
}
}

private Set<String> getRepairUnitDatacenters(Cluster cluster, RepairUnit.Builder builder, Set<String> nodes) {
if (!builder.datacenters.isEmpty()) {
return builder.datacenters;
}
Set<String> datacenters = Sets.newHashSet();
try {
ClusterFacade facade = ClusterFacade.create(context);
for (String node : nodes) {
datacenters.add(facade.getDatacenter(Node.builder().withHostname(node).build()));
}
} catch (ReaperException | InterruptedException e) {
LOG.warn("Unable to get the list of datacenters for cluster {}", cluster.getName(), e);
}
return datacenters;
}

private static Set<String> listRepairTables(RepairUnit.Builder builder, Set<String> allTables) {
// subtract blacklisted tables from all tables (or those explicitly listed)
Set<String> tables = Sets.newHashSet(builder.columnFamilies.isEmpty() ? allTables : builder.columnFamilies);
Expand Down
12 changes: 6 additions & 6 deletions src/ui/app/jsx/repair-form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const repairForm = CreateReactClass({
obs.subscribe(
r => this.setState({
addRepairResultMsg: null,
addRepairResultStatus: r.status,
addRepairResultStatus: null,
showModal: false,
force: "false",
}),
Expand Down Expand Up @@ -399,10 +399,7 @@ const repairForm = CreateReactClass({

let addMsg = null;
if(this.state.addRepairResultMsg) {
if(this.state.addRepairResultStatus != 409) {
addMsg = <div className="alert alert-danger" role="alert">{this.state.addRepairResultMsg}</div>
}
else {
if(this.state.addRepairResultStatus && this.state.addRepairResultStatus === 409) {
addMsg = (
<span>
<Modal show={this.state.showModal} onHide={this._onClose}>
Expand All @@ -415,13 +412,16 @@ const repairForm = CreateReactClass({
<p>For Cassandra 4.0 and later, you can force creating this schedule by clicking the "Force" button below.</p>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this._onClose}>Close</Button>
<Button variant="secondary" onClick={this._onClose}>Cancel</Button>
<Button variant="primary" onClick={this._onForce}>Force</Button>
</Modal.Footer>
</Modal>
</span>
);
}
else {
addMsg = <div className="alert alert-danger" role="alert">{this.state.addRepairResultMsg}</div>
}
}

const clusterNameOptions = this.state.clusterNames.sort().map(name => {
Expand Down

0 comments on commit cdf6b0d

Please sign in to comment.