/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch 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.elasticsearch.index.translog; import java.io.Closeable; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import org.apache.lucene.index.Term; import org.apache.lucene.index.TwoPhaseCommit; import org.apache.lucene.util.Accountable; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.lease.Releasable; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.Callback; import org.elasticsearch.index.VersionType; import org.elasticsearch.index.engine.Engine; /** * Dummy translog implementation * @author vroyer * */ public class Translog implements Closeable, TwoPhaseCommit { public static final Translog DUMMY_TRANSLOG = new Translog(null); public static final String TRANSLOG_GENERATION_KEY = "translog_generation"; public static final String TRANSLOG_UUID_KEY = "translog_uuid"; public static final String TRANSLOG_FILE_PREFIX = "translog-"; public static final String TRANSLOG_FILE_SUFFIX = ".tlog"; public static final String CHECKPOINT_SUFFIX = ".ckp"; public static final String CHECKPOINT_FILE_NAME = "translog" + CHECKPOINT_SUFFIX; private TranslogWriter current; public Translog(TranslogConfig config) { } /** * Adds a created / delete / index operations to the transaction log. * * @see org.elasticsearch.index.translog.Translog.Operation * @see org.elasticsearch.index.translog.Translog.Create * @see org.elasticsearch.index.translog.Translog.Index * @see org.elasticsearch.index.translog.Translog.Delete */ public Location add(Operation operation) throws IOException { return DUMMY_LOCATION; } public boolean isOpen() { return false; } /** If this {@code Translog} was closed as a side-effect of a tragic exception, * e.g. disk full while flushing a new segment, this returns the root cause exception. * Otherwise (no tragic exception has occurred) it returns null. */ public Throwable getTragicException() { return null; } /** * Reads a list of operations written with {@link #writeOperations(StreamOutput, List)} */ public static List<Operation> readOperations(StreamInput input) throws IOException { return Collections.EMPTY_LIST; } /** * Writes all operations in the given iterable to the given output stream including the size of the array * use {@link #readOperations(StreamInput)} to read it back. */ public static void writeOperations(StreamOutput outStream, List<Operation> toWrite) throws IOException { } public static void upgradeLegacyTranslog(ESLogger logger, TranslogConfig config) throws IOException { } public boolean ensureSynced(Location location) throws IOException { return true; } public Translog.Operation read(Location location) { return null; } public void updateBuffer(ByteSizeValue bufferSize) { } public int totalOperations() { return 0; } public static final TranslogStats DUMMY_TRANSLOG_STATS= new TranslogStats(0,0); public TranslogStats stats() { return DUMMY_TRANSLOG_STATS; } public enum Durabilty { /** * Async durability - translogs are synced based on a time interval. */ ASYNC, /** * Request durability - translogs are synced for each high levle request (bulk, index, delete) */ REQUEST; } public static final class View implements Closeable { public static final Translog.View EMPTY_VIEW = new View(Collections.EMPTY_LIST, null); View(List<TranslogReader> orderedTranslogs, Callback<View> onClose) { } @Override public void close() { } public long minTranslogGeneration() { return 0; } /** * The total number of operations in the view. */ public int totalOperations() { return 0; } /** create a snapshot from this view */ public Snapshot snapshot() { return createSnapshot(new TranslogReader[0]); } } /** * Returns a view into the current translog that is guaranteed to retain all current operations * while receiving future ones as well */ public Translog.View newView() { return View.EMPTY_VIEW; } public static class Source { public final BytesReference source; public final String routing; public final String parent; public final long timestamp; public final long ttl; public Source(BytesReference source, String routing, String parent, long timestamp, long ttl) { this.source = source; this.routing = routing; this.parent = parent; this.timestamp = timestamp; this.ttl = ttl; } } /** * A generic interface representing an operation performed on the transaction log. * Each is associated with a type. */ public interface Operation extends Streamable { enum Type { CREATE((byte) 1), SAVE((byte) 2), DELETE((byte) 3), DELETE_BY_QUERY((byte) 4); private final byte id; private Type(byte id) { this.id = id; } public byte id() { return this.id; } public static Type fromId(byte id) { switch (id) { case 1: return CREATE; case 2: return SAVE; case 3: return DELETE; case 4: return DELETE_BY_QUERY; default: throw new IllegalArgumentException("No type mapped for [" + id + "]"); } } } Type opType(); long estimateSize(); Source getSource(); } public static class Create implements Operation { public static final int SERIALIZATION_FORMAT = 6; private String id; private String type; private BytesReference source; private String routing; private String parent; private long timestamp; private long ttl; private long version = Versions.MATCH_ANY; private VersionType versionType = VersionType.INTERNAL; public Create() { } public Create(Engine.Create create) { this.id = create.id(); this.type = create.type(); this.source = create.source(); this.routing = create.routing(); this.parent = create.parent(); this.timestamp = create.timestamp(); this.ttl = create.ttl(); this.version = create.version(); this.versionType = create.versionType(); } public Create(String type, String id, byte[] source) { this.id = id; this.type = type; this.source = new BytesArray(source); } @Override public Type opType() { return Type.CREATE; } @Override public long estimateSize() { return ((id.length() + type.length()) * 2) + source.length() + 12; } public String id() { return this.id; } public BytesReference source() { return this.source; } public String type() { return this.type; } public String routing() { return this.routing; } public String parent() { return this.parent; } public long timestamp() { return this.timestamp; } public long ttl() { return this.ttl; } public long version() { return this.version; } public VersionType versionType() { return versionType; } @Override public Source getSource() { return new Source(source, routing, parent, timestamp, ttl); } @Override public void readFrom(StreamInput in) throws IOException { int version = in.readVInt(); // version id = in.readString(); type = in.readString(); source = in.readBytesReference(); if (version >= 1) { if (in.readBoolean()) { routing = in.readString(); } } if (version >= 2) { if (in.readBoolean()) { parent = in.readString(); } } if (version >= 3) { this.version = in.readLong(); } if (version >= 4) { this.timestamp = in.readLong(); } if (version >= 5) { this.ttl = in.readLong(); } if (version >= 6) { this.versionType = VersionType.fromValue(in.readByte()); } assert versionType.validateVersionForWrites(version); } @Override public void writeTo(StreamOutput out) throws IOException { out.writeVInt(SERIALIZATION_FORMAT); out.writeString(id); out.writeString(type); out.writeBytesReference(source); if (routing == null) { out.writeBoolean(false); } else { out.writeBoolean(true); out.writeString(routing); } if (parent == null) { out.writeBoolean(false); } else { out.writeBoolean(true); out.writeString(parent); } out.writeLong(version); out.writeLong(timestamp); out.writeLong(ttl); out.writeByte(versionType.getValue()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Create create = (Create) o; if (timestamp != create.timestamp || ttl != create.ttl || version != create.version || id.equals(create.id) == false || type.equals(create.type) == false || source.equals(create.source) == false) { return false; } if (routing != null ? !routing.equals(create.routing) : create.routing != null) { return false; } if (parent != null ? !parent.equals(create.parent) : create.parent != null) { return false; } return versionType == create.versionType; } @Override public int hashCode() { int result = id.hashCode(); result = 31 * result + type.hashCode(); result = 31 * result + source.hashCode(); result = 31 * result + (routing != null ? routing.hashCode() : 0); result = 31 * result + (parent != null ? parent.hashCode() : 0); result = 31 * result + (int) (timestamp ^ (timestamp >>> 32)); result = 31 * result + (int) (ttl ^ (ttl >>> 32)); result = 31 * result + (int) (version ^ (version >>> 32)); result = 31 * result + versionType.hashCode(); return result; } @Override public String toString() { return "Create{" + "id='" + id + '\'' + ", type='" + type + '\'' + '}'; } } public static class Index implements Operation { public static final int SERIALIZATION_FORMAT = 6; private String id; private String type; private long version = Versions.MATCH_ANY; private VersionType versionType = VersionType.INTERNAL; private BytesReference source; private String routing; private String parent; private long timestamp; private long ttl; public Index() { } public Index(Engine.Index index) { this.id = index.id(); this.type = index.type(); this.source = index.source(); this.routing = index.routing(); this.parent = index.parent(); this.version = index.version(); this.timestamp = index.timestamp(); this.ttl = index.ttl(); this.versionType = index.versionType(); } public Index(String type, String id, byte[] source) { this.type = type; this.id = id; this.source = new BytesArray(source); } @Override public Type opType() { return Type.SAVE; } @Override public long estimateSize() { return ((id.length() + type.length()) * 2) + source.length() + 12; } public String type() { return this.type; } public String id() { return this.id; } public String routing() { return this.routing; } public String parent() { return this.parent; } public long timestamp() { return this.timestamp; } public long ttl() { return this.ttl; } public BytesReference source() { return this.source; } public long version() { return this.version; } public VersionType versionType() { return versionType; } @Override public Source getSource() { return new Source(source, routing, parent, timestamp, ttl); } @Override public void readFrom(StreamInput in) throws IOException { int version = in.readVInt(); // version id = in.readString(); type = in.readString(); source = in.readBytesReference(); try { if (version >= 1) { if (in.readBoolean()) { routing = in.readString(); } } if (version >= 2) { if (in.readBoolean()) { parent = in.readString(); } } if (version >= 3) { this.version = in.readLong(); } if (version >= 4) { this.timestamp = in.readLong(); } if (version >= 5) { this.ttl = in.readLong(); } if (version >= 6) { this.versionType = VersionType.fromValue(in.readByte()); } } catch (Exception e) { throw new ElasticsearchException("failed to read [" + type + "][" + id + "]", e); } assert versionType.validateVersionForWrites(version); } @Override public void writeTo(StreamOutput out) throws IOException { out.writeVInt(SERIALIZATION_FORMAT); out.writeString(id); out.writeString(type); out.writeBytesReference(source); if (routing == null) { out.writeBoolean(false); } else { out.writeBoolean(true); out.writeString(routing); } if (parent == null) { out.writeBoolean(false); } else { out.writeBoolean(true); out.writeString(parent); } out.writeLong(version); out.writeLong(timestamp); out.writeLong(ttl); out.writeByte(versionType.getValue()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Index index = (Index) o; if (version != index.version || timestamp != index.timestamp || ttl != index.ttl || id.equals(index.id) == false || type.equals(index.type) == false || versionType != index.versionType || source.equals(index.source) == false) { return false; } if (routing != null ? !routing.equals(index.routing) : index.routing != null) { return false; } return !(parent != null ? !parent.equals(index.parent) : index.parent != null); } @Override public int hashCode() { int result = id.hashCode(); result = 31 * result + type.hashCode(); result = 31 * result + (int) (version ^ (version >>> 32)); result = 31 * result + versionType.hashCode(); result = 31 * result + source.hashCode(); result = 31 * result + (routing != null ? routing.hashCode() : 0); result = 31 * result + (parent != null ? parent.hashCode() : 0); result = 31 * result + (int) (timestamp ^ (timestamp >>> 32)); result = 31 * result + (int) (ttl ^ (ttl >>> 32)); return result; } @Override public String toString() { return "Index{" + "id='" + id + '\'' + ", type='" + type + '\'' + '}'; } } public static class Delete implements Operation { public static final int SERIALIZATION_FORMAT = 2; private Term uid; private long version = Versions.MATCH_ANY; private VersionType versionType = VersionType.INTERNAL; public Delete() { } public Delete(Engine.Delete delete) { this(delete.uid()); this.version = delete.version(); this.versionType = delete.versionType(); } public Delete(Term uid) { this.uid = uid; } public Delete(Term uid, long version, VersionType versionType) { this.uid = uid; this.version = version; this.versionType = versionType; } @Override public Type opType() { return Type.DELETE; } @Override public long estimateSize() { return ((uid.field().length() + uid.text().length()) * 2) + 20; } public Term uid() { return this.uid; } public long version() { return this.version; } public VersionType versionType() { return this.versionType; } @Override public Source getSource() { throw new IllegalStateException("trying to read doc source from delete operation"); } @Override public void readFrom(StreamInput in) throws IOException { int version = in.readVInt(); // version uid = new Term(in.readString(), in.readString()); if (version >= 1) { this.version = in.readLong(); } if (version >= 2) { this.versionType = VersionType.fromValue(in.readByte()); } assert versionType.validateVersionForWrites(version); } @Override public void writeTo(StreamOutput out) throws IOException { out.writeVInt(SERIALIZATION_FORMAT); out.writeString(uid.field()); out.writeString(uid.text()); out.writeLong(version); out.writeByte(versionType.getValue()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Delete delete = (Delete) o; return version == delete.version && uid.equals(delete.uid) && versionType == delete.versionType; } @Override public int hashCode() { int result = uid.hashCode(); result = 31 * result + (int) (version ^ (version >>> 32)); result = 31 * result + versionType.hashCode(); return result; } @Override public String toString() { return "Delete{" + "uid=" + uid + '}'; } } /** @deprecated Delete-by-query is removed in 2.0, but we keep this so translog can replay on upgrade. */ @Deprecated public static class DeleteByQuery implements Operation { public static final int SERIALIZATION_FORMAT = 2; private BytesReference source; @Nullable private String[] filteringAliases; private String[] types = Strings.EMPTY_ARRAY; public DeleteByQuery() { } public DeleteByQuery(Engine.DeleteByQuery deleteByQuery) { this(deleteByQuery.source(), deleteByQuery.filteringAliases(), deleteByQuery.types()); } public DeleteByQuery(BytesReference source, String[] filteringAliases, String... types) { this.source = source; this.types = types == null ? Strings.EMPTY_ARRAY : types; this.filteringAliases = filteringAliases; } @Override public Type opType() { return Type.DELETE_BY_QUERY; } @Override public long estimateSize() { return source.length() + 8; } public BytesReference source() { return this.source; } public String[] filteringAliases() { return filteringAliases; } public String[] types() { return this.types; } @Override public Source getSource() { throw new IllegalStateException("trying to read doc source from delete_by_query operation"); } @Override public void readFrom(StreamInput in) throws IOException { int version = in.readVInt(); // version source = in.readBytesReference(); if (version < 2) { // for query_parser_name, which was removed if (in.readBoolean()) { in.readString(); } } int typesSize = in.readVInt(); if (typesSize > 0) { types = new String[typesSize]; for (int i = 0; i < typesSize; i++) { types[i] = in.readString(); } } if (version >= 1) { int aliasesSize = in.readVInt(); if (aliasesSize > 0) { filteringAliases = new String[aliasesSize]; for (int i = 0; i < aliasesSize; i++) { filteringAliases[i] = in.readString(); } } } } @Override public void writeTo(StreamOutput out) throws IOException { out.writeVInt(SERIALIZATION_FORMAT); out.writeBytesReference(source); out.writeVInt(types.length); for (String type : types) { out.writeString(type); } if (filteringAliases != null) { out.writeVInt(filteringAliases.length); for (String alias : filteringAliases) { out.writeString(alias); } } else { out.writeVInt(0); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DeleteByQuery that = (DeleteByQuery) o; if (!Arrays.equals(filteringAliases, that.filteringAliases)) { return false; } if (!Arrays.equals(types, that.types)) { return false; } return source.equals(that.source); } @Override public int hashCode() { int result = source.hashCode(); result = 31 * result + (filteringAliases != null ? Arrays.hashCode(filteringAliases) : 0); result = 31 * result + Arrays.hashCode(types); return result; } @Override public String toString() { return "DeleteByQuery{" + "types=" + Arrays.toString(types) + '}'; } } /** * References a transaction log generation */ public final static TranslogGeneration DUMMY_GENERATION = new TranslogGeneration("",0L); public final static class TranslogGeneration { public final String translogUUID; public final long translogFileGeneration; public TranslogGeneration(String translogUUID, long translogFileGeneration) { this.translogUUID = translogUUID; this.translogFileGeneration = translogFileGeneration; } } public final static Location DUMMY_LOCATION = new Location(); public static class Location implements Accountable, Comparable<Location> { @Override public long ramBytesUsed() { return 0; } @Override public int compareTo(Location o) { return 0; } @Override public Collection<Accountable> getChildResources() { return Collections.EMPTY_LIST; } } /** * A snapshot of the transaction log, allows to iterate over all the transaction log operations. */ public interface Snapshot extends Releasable { /** * The total number of operations in the translog. */ int estimatedTotalOperations(); /** * Returns the next operation in the snapshot or <code>null</code> if we reached the end. */ Translog.Operation next() throws IOException; } /** * Snapshots the current transaction log allowing to safely iterate over the snapshot. * Snapshots are fixed in time and will not be updated with future operations. */ public Snapshot newSnapshot() { return createSnapshot(new TranslogReader[0]); } private static Snapshot createSnapshot(TranslogReader... translogs) { return new MultiSnapshot(new Snapshot[0]); } @Override public void prepareCommit() throws IOException { } @Override public void commit() throws IOException { } public boolean syncNeeded() { return true; } @Override public void rollback() throws IOException { } @Override public void close() throws IOException { } public TranslogGeneration getGeneration() { return DUMMY_GENERATION; } public long currentFileGeneration() { return 0; } /** * Returns <code>true</code> iff the given generation is the current gbeneration of this translog * @param generation * @return */ public boolean isCurrent(TranslogGeneration generation) { return true; } }