Java tutorial
// Copyright 2017 JanusGraph Authors // // 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. package org.janusgraph.graphdb.database.log; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import org.janusgraph.core.log.Change; import org.janusgraph.diskstorage.ReadBuffer; import org.janusgraph.diskstorage.StaticBuffer; import org.janusgraph.diskstorage.util.BufferUtil; import org.janusgraph.diskstorage.util.HashingUtil; import org.janusgraph.graphdb.database.idhandling.VariableLong; import org.janusgraph.graphdb.database.serialize.DataOutput; import org.janusgraph.graphdb.database.serialize.Serializer; import org.janusgraph.graphdb.internal.InternalRelation; import org.janusgraph.graphdb.log.StandardTransactionId; import org.janusgraph.graphdb.transaction.StandardJanusGraphTx; import org.janusgraph.graphdb.transaction.TransactionConfiguration; import org.janusgraph.diskstorage.util.time.TimestampProvider; import org.apache.commons.lang.StringUtils; import java.time.Instant; import java.util.*; /** * @author Matthias Broecheler (me@matthiasb.com) */ public class TransactionLogHeader { private final long transactionId; private final Instant txTimestamp; private TimestampProvider times; private final StaticBuffer logKey; public TransactionLogHeader(long transactionId, Instant txTimestamp, TimestampProvider times) { this.transactionId = transactionId; this.txTimestamp = txTimestamp; this.times = times; Preconditions.checkArgument(this.transactionId > 0); Preconditions.checkNotNull(this.txTimestamp); logKey = HashingUtil.hashPrefixKey(HashingUtil.HashLength.SHORT, BufferUtil.getLongBuffer(transactionId)); } public long getId() { return transactionId; } public Instant getTimestamp() { return txTimestamp; } public StaticBuffer getLogKey() { return logKey; } public StaticBuffer serializeModifications(Serializer serializer, LogTxStatus status, StandardJanusGraphTx tx, final Collection<InternalRelation> addedRelations, final Collection<InternalRelation> deletedRelations) { Preconditions.checkArgument(status == LogTxStatus.PRECOMMIT || status == LogTxStatus.USER_LOG); DataOutput out = serializeHeader(serializer, 256 + (addedRelations.size() + deletedRelations.size()) * 40, status, status == LogTxStatus.PRECOMMIT ? tx.getConfiguration() : null); logRelations(out, addedRelations, tx); logRelations(out, deletedRelations, tx); return out.getStaticBuffer(); } private static void logRelations(DataOutput out, final Collection<InternalRelation> relations, StandardJanusGraphTx tx) { VariableLong.writePositive(out, relations.size()); for (InternalRelation rel : relations) { VariableLong.writePositive(out, rel.getVertex(0).longId()); org.janusgraph.diskstorage.Entry entry = tx.getEdgeSerializer().writeRelation(rel, 0, tx); BufferUtil.writeEntry(out, entry); } } public StaticBuffer serializeUserLog(Serializer serializer, Entry sourceTxEntry, StandardTransactionId sourceTxId) { Preconditions.checkArgument(sourceTxEntry != null && sourceTxEntry.status == LogTxStatus.PRECOMMIT && sourceTxEntry.header.transactionId == sourceTxId.getTransactionId()); StaticBuffer sourceContent = sourceTxEntry.content; Preconditions.checkArgument(sourceContent != null && sourceContent.length() > 0); EnumMap<LogTxMeta, Object> meta = new EnumMap<LogTxMeta, Object>(LogTxMeta.class); meta.put(LogTxMeta.SOURCE_TRANSACTION, sourceTxId); DataOutput out = serializeHeader(serializer, 50 + sourceContent.length(), LogTxStatus.USER_LOG, meta); out.putBytes(sourceContent); return out.getStaticBuffer(); } public StaticBuffer serializePrimary(Serializer serializer, LogTxStatus status) { Preconditions .checkArgument(status == LogTxStatus.PRIMARY_SUCCESS || status == LogTxStatus.COMPLETE_SUCCESS); DataOutput out = serializeHeader(serializer, 30, status); return out.getStaticBuffer(); } public StaticBuffer serializeSecondary(Serializer serializer, LogTxStatus status, Map<String, Throwable> indexFailures, boolean userLogSuccess) { Preconditions .checkArgument(status == LogTxStatus.SECONDARY_SUCCESS || status == LogTxStatus.SECONDARY_FAILURE); DataOutput out = serializeHeader(serializer, 30, status); if (status == LogTxStatus.SECONDARY_FAILURE) { out.putBoolean(userLogSuccess); out.putInt(indexFailures.size()); for (String index : indexFailures.keySet()) { assert StringUtils.isNotBlank(index); out.writeObjectNotNull(index); } } else assert userLogSuccess && indexFailures.isEmpty(); return out.getStaticBuffer(); } private DataOutput serializeHeader(Serializer serializer, int capacity, LogTxStatus status) { return serializeHeader(serializer, capacity, status, new EnumMap<LogTxMeta, Object>(LogTxMeta.class)); } private DataOutput serializeHeader(Serializer serializer, int capacity, LogTxStatus status, TransactionConfiguration txConfig) { EnumMap<LogTxMeta, Object> metaMap = new EnumMap<LogTxMeta, Object>(LogTxMeta.class); if (txConfig != null) { for (LogTxMeta meta : LogTxMeta.values()) { Object value = meta.getValue(txConfig); if (value != null) { metaMap.put(meta, value); } } } return serializeHeader(serializer, capacity, status, metaMap); } private DataOutput serializeHeader(Serializer serializer, int capacity, LogTxStatus status, EnumMap<LogTxMeta, Object> meta) { Preconditions.checkArgument(status != null && meta != null, "Invalid status or meta"); DataOutput out = serializer.getDataOutput(capacity); out.putLong(times.getTime(txTimestamp)); VariableLong.writePositive(out, transactionId); out.writeObjectNotNull(status); Preconditions.checkArgument(meta.size() < Byte.MAX_VALUE, "Too much meta data: %s", meta.size()); out.putByte(VariableLong.unsignedByte(meta.size())); for (Map.Entry<LogTxMeta, Object> metaentry : meta.entrySet()) { assert metaentry.getValue() != null; out.putByte(VariableLong.unsignedByte(metaentry.getKey().ordinal())); out.writeObjectNotNull(metaentry.getValue()); } return out; } public static Entry parse(StaticBuffer buffer, Serializer serializer, TimestampProvider times) { ReadBuffer read = buffer.asReadBuffer(); Instant txTimestamp = times.getTime(read.getLong()); TransactionLogHeader header = new TransactionLogHeader(VariableLong.readPositive(read), txTimestamp, times); LogTxStatus status = serializer.readObjectNotNull(read, LogTxStatus.class); EnumMap<LogTxMeta, Object> metadata = new EnumMap<LogTxMeta, Object>(LogTxMeta.class); int metaSize = VariableLong.unsignedByte(read.getByte()); for (int i = 0; i < metaSize; i++) { LogTxMeta meta = LogTxMeta.values()[VariableLong.unsignedByte(read.getByte())]; metadata.put(meta, serializer.readObjectNotNull(read, meta.dataType())); } if (read.hasRemaining()) { StaticBuffer content = read.subrange(read.getPosition(), read.length() - read.getPosition()); return new Entry(header, content, status, metadata); } else { return new Entry(header, null, status, metadata); } } public static class Entry { private final TransactionLogHeader header; private final StaticBuffer content; private final LogTxStatus status; private final EnumMap<LogTxMeta, Object> metadata; public Entry(TransactionLogHeader header, StaticBuffer content, LogTxStatus status, EnumMap<LogTxMeta, Object> metadata) { Preconditions.checkArgument(status != null && metadata != null); Preconditions.checkArgument(header != null); Preconditions.checkArgument(content == null || content.length() > 0); this.header = header; this.content = content; this.status = status; this.metadata = metadata; } public TransactionLogHeader getHeader() { return header; } public boolean hasContent() { return content != null; } public LogTxStatus getStatus() { return status; } public EnumMap<LogTxMeta, Object> getMetadata() { return metadata; } public StaticBuffer getContent() { Preconditions.checkState(hasContent(), "Does not have any content"); return content; } public SecondaryFailures getContentAsSecondaryFailures(Serializer serializer) { Preconditions.checkArgument(status == LogTxStatus.SECONDARY_FAILURE); return new SecondaryFailures(content, serializer); } public Collection<Modification> getContentAsModifications(Serializer serializer) { Preconditions.checkArgument(status == LogTxStatus.PRECOMMIT || status == LogTxStatus.USER_LOG); List<Modification> mods = Lists.newArrayList(); ReadBuffer in = content.asReadBuffer(); mods.addAll(readModifications(Change.ADDED, in, serializer)); mods.addAll(readModifications(Change.REMOVED, in, serializer)); return mods; } private static Collection<Modification> readModifications(Change state, ReadBuffer in, Serializer serializer) { List<Modification> mods = Lists.newArrayList(); long size = VariableLong.readPositive(in); for (int i = 0; i < size; i++) { long vid = VariableLong.readPositive(in); org.janusgraph.diskstorage.Entry entry = BufferUtil.readEntry(in, serializer); mods.add(new Modification(state, vid, entry)); } return mods; } } public static class SecondaryFailures { public final boolean userLogFailure; public final Set<String> failedIndexes; private SecondaryFailures(StaticBuffer content, Serializer serializer) { ReadBuffer in = content.asReadBuffer(); this.userLogFailure = !in.getBoolean(); int size = in.getInt(); ImmutableSet.Builder<String> builder = ImmutableSet.builder(); for (int i = 0; i < size; i++) { builder.add(serializer.readObjectNotNull(in, String.class)); } this.failedIndexes = builder.build(); } } public static class Modification { public final Change state; public final long outVertexId; public final org.janusgraph.diskstorage.Entry relationEntry; private Modification(Change state, long outVertexId, org.janusgraph.diskstorage.Entry relationEntry) { this.state = state; this.outVertexId = outVertexId; this.relationEntry = relationEntry; } } }