All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.cassandra.repair.consistent.RepairedState Maven / Gradle / Ivy

Go to download

The Apache Cassandra Project develops a highly scalable second-generation distributed database, bringing together Dynamo's fully distributed design and Bigtable's ColumnFamily-based data model.

There is a newer version: 5.0.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.cassandra.repair.consistent;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;

import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;

import static org.apache.cassandra.service.ActiveRepairService.UNREPAIRED_SSTABLE;

/**
 * Tracks the repaired state of token ranges per table, and is effectively an
 * in memory representation of the on-disk local incremental repair state.
 *
 * The main purpose of this class is to provide metrics via nodetool repair_admin. To make sure those metrics
 * are accurate, it also determines when a completed IR session can be deleted, which is explained in a bit
 * more detail in LocalSessions#cleanup, by the call to isSuperseded.
 */
public class RepairedState
{
    static class Level
    {
        final List> ranges;
        final long repairedAt;

        private static final Comparator timeComparator = Comparator.comparingLong(l -> -l.repairedAt);

        Level(Collection> ranges, long repairedAt)
        {
            this.ranges = Range.normalize(ranges);
            this.repairedAt = repairedAt;
        }

        Level subtract(Collection> ranges)
        {
            if (ranges.isEmpty())
                return this;

            Set> difference = Range.subtract(this.ranges, ranges);
            if (difference.isEmpty())
                return null;

            return new Level(difference, repairedAt);
        }

        public boolean equals(Object o)
        {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Level level = (Level) o;
            return repairedAt == level.repairedAt &&
                   Objects.equals(ranges, level.ranges);
        }

        public int hashCode()
        {
            return Objects.hash(ranges, repairedAt);
        }

        @Override
        public String toString()
        {
            return "Level{" +
                   "ranges=" + ranges +
                   ", repairedAt=" + repairedAt +
                   '}';
        }
    }

    public static class Section
    {
        public final Range range;
        public final long repairedAt;
        private static final Comparator
tokenComparator = (l, r) -> l.range.left.compareTo(r.range.left); Section(Range range, long repairedAt) { this.range = range; this.repairedAt = repairedAt; } Section makeSubsection(Range subrange) { Preconditions.checkArgument(range.contains(subrange)); return new Section(subrange, repairedAt); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Section section = (Section) o; return repairedAt == section.repairedAt && Objects.equals(range, section.range); } public int hashCode() { return Objects.hash(range, repairedAt); } @Override public String toString() { return "Section{" + "range=" + range + ", repairedAt=" + repairedAt + '}'; } } public static class Stats { public static final Stats EMPTY = new Stats(UNREPAIRED_SSTABLE, UNREPAIRED_SSTABLE, Collections.emptyList()); public final long minRepaired; public final long maxRepaired; public final List
sections; public Stats(long minRepaired, long maxRepaired, List
sections) { this.minRepaired = minRepaired; this.maxRepaired = maxRepaired; this.sections = sections; } } static class State { final ImmutableList levels; final ImmutableList> covered; final ImmutableList
sections; State(List levels, List> covered, List
sections) { this.levels = ImmutableList.copyOf(levels); this.covered = ImmutableList.copyOf(covered); this.sections = ImmutableList.copyOf(sections); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; State state = (State) o; return Objects.equals(levels, state.levels) && Objects.equals(covered, state.covered); } public int hashCode() { return Objects.hash(levels, covered); } @Override public String toString() { return "State{" + "levels=" + levels + ", covered=" + covered + '}'; } } private volatile State state = new State(Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); State state() { return state; } public synchronized void add(Collection> ranges, long repairedAt) { addAll(Collections.singletonList(new Level(ranges, repairedAt))); } public void addAll(List newLevels) { State lastState = state; List tmp = new ArrayList<>(lastState.levels.size() + newLevels.size()); tmp.addAll(lastState.levels); tmp.addAll(newLevels); tmp.sort(Level.timeComparator); List levels = new ArrayList<>(tmp.size()); List> covered = new ArrayList<>(); for (Level level : tmp) { Level subtracted = level.subtract(covered); if (subtracted == null) continue; levels.add(subtracted); covered.addAll(subtracted.ranges); covered = Range.normalize(covered); } List
sections = new ArrayList<>(); for (Level level : levels) { for (Range range : level.ranges) { sections.add(new Section(range, level.repairedAt)); } } sections.sort(Section.tokenComparator); state = new State(levels, covered, sections); } public long minRepairedAt(Collection> ranges) { State current = state; Set> remainingRanges = new HashSet<>(ranges); long minTime = Long.MAX_VALUE; for (Section section : current.sections) { if (section.range.intersects(remainingRanges)) { minTime = Math.min(minTime, section.repairedAt); remainingRanges = Range.subtract(remainingRanges, Collections.singleton(section.range)); } if (remainingRanges.isEmpty()) break; } // if there are still ranges we don't have data for, part of the requested ranges is unrepaired return remainingRanges.isEmpty() ? minTime : UNREPAIRED_SSTABLE; } static List
getRepairedStats(List
sections, Collection> ranges) { if (ranges.isEmpty()) return Collections.emptyList(); Set> remaining = Sets.newHashSet(Range.normalize(ranges)); List
results = new ArrayList<>(); for (Section section : sections) { if (remaining.isEmpty()) break; Set> sectionRanges = Range.rangeSet(section.range); for (Range range : remaining) { if (sectionRanges.isEmpty()) break; Set> intersection = new HashSet<>(); sectionRanges.forEach(r -> intersection.addAll(r.intersectionWith(range))); if (intersection.isEmpty()) continue; intersection.forEach(r -> results.add(section.makeSubsection(r))); sectionRanges = Range.subtract(sectionRanges, intersection); } remaining = Range.subtract(remaining, Collections.singleton(section.range)); } remaining.forEach(r -> results.add(new Section(r, UNREPAIRED_SSTABLE))); results.sort(Section.tokenComparator); return results; } public Stats getRepairedStats(Collection> ranges) { List
sections = getRepairedStats(state.sections, ranges); if (sections.isEmpty()) return new Stats(UNREPAIRED_SSTABLE, UNREPAIRED_SSTABLE, Collections.emptyList()); long minTime = Long.MAX_VALUE; long maxTime = Long.MIN_VALUE; for (Section section : sections) { minTime = Math.min(minTime, section.repairedAt); maxTime = Math.max(maxTime, section.repairedAt); } return new Stats(minTime, maxTime, sections); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy