org.elasticsearch.cluster.SnapshotDeletionsInProgress Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :server
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.cluster;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.ClusterState.Custom;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.repositories.RepositoryOperation;
import org.elasticsearch.snapshots.Snapshot;
import org.elasticsearch.snapshots.SnapshotId;
import org.elasticsearch.snapshots.SnapshotsService;
import org.elasticsearch.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* A class that represents the snapshot deletions that are in progress in the cluster.
*/
public class SnapshotDeletionsInProgress extends AbstractNamedDiffable implements Custom {
public static final SnapshotDeletionsInProgress EMPTY = new SnapshotDeletionsInProgress(Collections.emptyList());
public static final String TYPE = "snapshot_deletions";
// the list of snapshot deletion request entries
private final List entries;
private SnapshotDeletionsInProgress(List entries) {
this.entries = entries;
assert entries.size() == entries.stream().map(Entry::uuid).distinct().count() : "Found duplicate UUIDs in entries " + entries;
assert assertNoConcurrentDeletionsForSameRepository(entries);
}
public static SnapshotDeletionsInProgress of(List entries) {
if (entries.isEmpty()) {
return EMPTY;
}
return new SnapshotDeletionsInProgress(Collections.unmodifiableList(entries));
}
public SnapshotDeletionsInProgress(StreamInput in) throws IOException {
this(in.readList(Entry::new));
}
private static boolean assertNoConcurrentDeletionsForSameRepository(List entries) {
final Set activeRepositories = new HashSet<>();
for (Entry entry : entries) {
if (entry.state() == State.STARTED) {
final boolean added = activeRepositories.add(entry.repository());
assert added : "Found multiple running deletes for a single repository in " + entries;
}
}
return true;
}
/**
* Returns a new instance of {@link SnapshotDeletionsInProgress} which adds
* the given {@link Entry} to the invoking instance.
*/
public SnapshotDeletionsInProgress withAddedEntry(Entry entry) {
return SnapshotDeletionsInProgress.of(CollectionUtils.appendToCopy(getEntries(), entry));
}
/**
* Returns a new instance of {@link SnapshotDeletionsInProgress} that has the entry with the given {@code deleteUUID} removed from its
* entries.
*/
public SnapshotDeletionsInProgress withRemovedEntry(String deleteUUID) {
List updatedEntries = new ArrayList<>(entries.size() - 1);
boolean removed = false;
for (Entry entry : entries) {
if (entry.uuid().equals(deleteUUID)) {
removed = true;
} else {
updatedEntries.add(entry);
}
}
return removed ? SnapshotDeletionsInProgress.of(updatedEntries) : this;
}
/**
* Returns an unmodifiable list of snapshot deletion entries.
*/
public List getEntries() {
return entries;
}
/**
* Checks if there is an actively executing delete operation for the given repository
*
* @param repository repository name
*/
public boolean hasExecutingDeletion(String repository) {
for (Entry entry : entries) {
if (entry.state() == State.STARTED && entry.repository().equals(repository)) {
return true;
}
}
return false;
}
/**
* Returns {@code true} if there are snapshot deletions in progress in the cluster,
* returns {@code false} otherwise.
*/
public boolean hasDeletionsInProgress() {
return entries.isEmpty() == false;
}
@Override
public String getWriteableName() {
return TYPE;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SnapshotDeletionsInProgress that = (SnapshotDeletionsInProgress) o;
return entries.equals(that.entries);
}
@Override
public int hashCode() {
return 31 + entries.hashCode();
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeList(entries);
}
public static NamedDiff readDiffFrom(StreamInput in) throws IOException {
return readDiffFrom(Custom.class, TYPE, in);
}
@Override
public Version getMinimalSupportedVersion() {
return Version.CURRENT.minimumCompatibilityVersion();
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startArray(TYPE);
for (Entry entry : entries) {
builder.startObject();
{
builder.field("repository", entry.repository());
builder.startArray("snapshots");
for (SnapshotId snapshot : entry.snapshots) {
builder.value(snapshot.getName());
}
builder.endArray();
builder.timeField("start_time_millis", "start_time", entry.startTime);
builder.field("repository_state_id", entry.repositoryStateId);
builder.field("state", entry.state);
}
builder.endObject();
}
builder.endArray();
return builder;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("SnapshotDeletionsInProgress[");
for (int i = 0; i < entries.size(); i++) {
builder.append(entries.get(i).getSnapshots());
if (i + 1 < entries.size()) {
builder.append(",");
}
}
return builder.append("]").toString();
}
/**
* A class representing a snapshot deletion request entry in the cluster state.
*/
public static final class Entry implements Writeable, RepositoryOperation {
private final List snapshots;
private final String repoName;
private final State state;
private final long startTime;
private final long repositoryStateId;
private final String uuid;
public Entry(List snapshots, String repoName, long startTime, long repositoryStateId, State state) {
this(snapshots, repoName, startTime, repositoryStateId, state, UUIDs.randomBase64UUID());
}
private Entry(List snapshots, String repoName, long startTime, long repositoryStateId, State state, String uuid) {
this.snapshots = snapshots;
assert snapshots.size() == new HashSet<>(snapshots).size() : "Duplicate snapshot ids in " + snapshots;
this.repoName = repoName;
this.startTime = startTime;
this.repositoryStateId = repositoryStateId;
this.state = state;
this.uuid = uuid;
}
public Entry(StreamInput in) throws IOException {
if (in.getVersion().onOrAfter(SnapshotsService.MULTI_DELETE_VERSION)) {
this.repoName = in.readString();
this.snapshots = in.readList(SnapshotId::new);
} else {
final Snapshot snapshot = new Snapshot(in);
this.snapshots = Collections.singletonList(snapshot.getSnapshotId());
this.repoName = snapshot.getRepository();
}
this.startTime = in.readVLong();
this.repositoryStateId = in.readLong();
if (in.getVersion().onOrAfter(SnapshotsService.FULL_CONCURRENCY_VERSION)) {
this.state = State.readFrom(in);
this.uuid = in.readString();
} else {
this.state = State.STARTED;
this.uuid = IndexMetadata.INDEX_UUID_NA_VALUE;
}
}
public Entry started() {
assert state == State.WAITING;
return new Entry(snapshots, repository(), startTime, repositoryStateId, State.STARTED, uuid);
}
public Entry withAddedSnapshots(Collection newSnapshots) {
assert state == State.WAITING;
final Collection updatedSnapshots = new HashSet<>(snapshots);
if (updatedSnapshots.addAll(newSnapshots) == false) {
return this;
}
return new Entry(
Collections.unmodifiableList(new ArrayList<>(updatedSnapshots)),
repository(),
startTime,
repositoryStateId,
State.WAITING,
uuid
);
}
public Entry withSnapshots(Collection snapshots) {
return new Entry(
Collections.unmodifiableList(new ArrayList<>(snapshots)),
repository(),
startTime,
repositoryStateId,
state,
uuid
);
}
public Entry withRepoGen(long repoGen) {
return new Entry(snapshots, repository(), startTime, repoGen, state, uuid);
}
public State state() {
return state;
}
public String uuid() {
return uuid;
}
public List getSnapshots() {
return snapshots;
}
/**
* The start time in milliseconds for deleting the snapshots.
*/
public long getStartTime() {
return startTime;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Entry that = (Entry) o;
return repoName.equals(that.repoName)
&& snapshots.equals(that.snapshots)
&& startTime == that.startTime
&& repositoryStateId == that.repositoryStateId
&& state == that.state
&& uuid.equals(that.uuid);
}
@Override
public int hashCode() {
return Objects.hash(snapshots, repoName, startTime, repositoryStateId, state, uuid);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
if (out.getVersion().onOrAfter(SnapshotsService.MULTI_DELETE_VERSION)) {
out.writeString(repoName);
out.writeCollection(snapshots);
} else {
assert snapshots.size() == 1
: "Only single deletion allowed in mixed version cluster containing [" + out.getVersion() + "] but saw " + snapshots;
new Snapshot(repoName, snapshots.get(0)).writeTo(out);
}
out.writeVLong(startTime);
out.writeLong(repositoryStateId);
if (out.getVersion().onOrAfter(SnapshotsService.FULL_CONCURRENCY_VERSION)) {
state.writeTo(out);
out.writeString(uuid);
}
}
@Override
public String repository() {
return repoName;
}
@Override
public long repositoryStateId() {
return repositoryStateId;
}
@Override
public String toString() {
return "SnapshotDeletionsInProgress.Entry[[" + uuid + "][" + state + "]" + snapshots + "]";
}
}
public enum State implements Writeable {
/**
* Delete is waiting to execute because there are snapshots and or a delete operation that has to complete before this delete may
* run.
*/
WAITING((byte) 0),
/**
* Delete is physically executing on the repository.
*/
STARTED((byte) 1);
private final byte value;
State(byte value) {
this.value = value;
}
public static State readFrom(StreamInput in) throws IOException {
final byte value = in.readByte();
switch (value) {
case 0:
return WAITING;
case 1:
return STARTED;
default:
throw new IllegalArgumentException("No snapshot delete state for value [" + value + "]");
}
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeByte(value);
}
}
}