diff --git a/bindings/java/src-completable/main/com/apple/foundationdb/tuple/Tuple.java b/bindings/java/src-completable/main/com/apple/foundationdb/tuple/Tuple.java index 5196d2cd10..aa2b38cb4f 100644 --- a/bindings/java/src-completable/main/com/apple/foundationdb/tuple/Tuple.java +++ b/bindings/java/src-completable/main/com/apple/foundationdb/tuple/Tuple.java @@ -294,12 +294,12 @@ public class Tuple implements Comparable, Iterable { } /** - * Get an encoded representation of this {@code Tuple} for use with - * {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY MutationType.SET_VERSIONSTAMPED_KEY}. - * This works the same as the {@link #packWithVersionstamp(byte[]) one-paramter version of this method}, - * but it does not add any prefix to the array. + * Get an encoded representation of this {@code Tuple}. Each element is encoded to + * {@code byte}s and concatenated, and then the prefix supplied is prepended to + * the array. * - * @return a serialized representation of this {@code Tuple} for use with versionstamp ops. + * @param prefix additional byte-array prefix to prepend to serialized bytes. + * @return a serialized representation of this {@code Tuple} prepended by the {@code prefix}. */ public byte[] pack(byte[] prefix) { TupleUtil.EncodeResult encoded = TupleUtil.pack(elements, prefix); @@ -710,9 +710,9 @@ public class Tuple implements Comparable, Iterable { /** * Determines if there is a {@link Versionstamp} included in this {@code Tuple} that has - * not had it's transaction version set. It will search through nested {@code Tuple}s - * contained within this {@code Tuple}. It will not attempt to throw an error if it - * finds multiple incomplete {@code Versionstamp} instances. + * not had its transaction version set. It will search through nested {@code Tuple}s + * contained within this {@code Tuple}. It will not throw an error if it finds multiple + * incomplete {@code Versionstamp} instances. * * @return whether there is at least one incomplete {@link Versionstamp} included in this * {@code Tuple} diff --git a/bindings/java/src-completable/main/com/apple/foundationdb/tuple/Versionstamp.java b/bindings/java/src-completable/main/com/apple/foundationdb/tuple/Versionstamp.java index dddaa18a25..3c45d8d804 100644 --- a/bindings/java/src-completable/main/com/apple/foundationdb/tuple/Versionstamp.java +++ b/bindings/java/src-completable/main/com/apple/foundationdb/tuple/Versionstamp.java @@ -24,28 +24,81 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; + /** - * Used to represent values written by versionstamp operations within a {@link Tuple}. - * This wraps a single array which should contain 12 bytes. The first 10 bytes - * are used by the transaction resolver to impose a globally ordered version. The first - * eight of those bytes are used to specify the transaction version, and the next two - * are used to specify the version of the transaction within a commit batch. - * The final two bytes are to be set by the user and represent a way for - * users to specify an order within a single transaction. + * Used to represent values written by versionstamp operations with a {@link Tuple}. + * This wraps a single array which should contain twelve bytes. The first ten bytes + * are the "transaction" version, and they are usually assigned by the database + * in such a way that all transactions receive a different version that is consistent + * with a serialization order of the transactions within the database. (One can + * use the {@link com.apple.foundationdb.Transaction#getVersionstamp() Transaction.getVersionstamp()} + * method to retrieve this version from a {@code Transaction}.) The final two bytes are the + * "user" version and should be set by the client. This allows the user to use this class to + * impose a total order of items across multiple transactions in the database in a consistent + * and conflict-free way. The user can elect to ignore this parameter by instantiating the + * class with the paramaterless {@link #incomplete() incomplete()} and one-parameter + * {@link #complete(byte[]) complete} static initializers. + * + *

+ * All {@code Versionstamp}s can exist in one of two states: "incomplete" and "complete". + * An "incomplete" {@code Versionstamp} is a {@code Versionstamp} that has not been + * initialized with a meaningful transaction version. For example, this might be used + * with a {@code Versionstamp} that one wants to fill in with the current transaction's + * version information. A "complete" {@code Versionstamp}, in contradistinction, is one + * that has been assigned a meaningful transaction version. This is usually the + * case if one is reading back a {@code Versionstamp} from the database. + *

+ * + *

+ * Example usage might be to do something like the following: + *

+ * + *
+ * 
+ *  {@code CompletableFuture} trVersionFuture = db.run((Transaction tr) -> {
+ *       // The incomplete Versionstamp will be overwritten with tr's version information when committed.
+ *       Tuple t = Tuple.from("prefix", Versionstamp.incomplete());
+ *       tr.mutate(MutationType.SET_VERSIONSTAMPED_KEY, t.packWithVersionstamp(), new byte[0]);
+ *       return tr.getVersionstamp();
+ *   });
+ *
+ *   byte[] trVersion = trVersionFuture.get();
+ *
+ *   Versionstamp v = db.run((Transaction tr) -> {
+ *       Subspace subspace = new Subspace(Tuple.from("prefix"));
+ *       byte[] serialized = tr.getRange(subspace.range(), 1).iterator().next().getKey();
+ *       Tuple t = subspace.unpack(serialized);
+ *       return t.getVersionstamp(0);
+ *   });
+ *
+ *   assert v.equals(Versionstamp.complete(trVersion));
+ * 
+ * 
+ * + *

+ * Here, an incomplete {@code Versionstamp} is packed and written to the database with + * the {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY SET_VERSIONSTAMPED_KEY} + * {@code MutationType}. After committing, we then attempt to read back the same key that + * we just wrote. Then we verify the invariant that the deserialized {@link Versionstamp} is + * the same as a complete {@code Versionstamp} instance created from the first transaction's + * version information. + *

*/ public class Versionstamp implements Comparable { + /** + * Length of a serialized {@code Versionstamp} instance when converted into a byte array. + */ public static final int LENGTH = 12; - protected static final byte[] UNSET_GLOBAL_VERSION = {(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff}; + private static final byte[] UNSET_TRANSACTION_VERSION = {(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff}; private boolean complete; - public byte[] versionBytes; + private byte[] versionBytes; /** * From a byte array, unpack the user version starting at the given position. - * This assumes the bytes are serialized in the array in the same way in - * the given array as this class would serialize them, i.e., in big-endian - * order as an unsigned short. + * This assumes that the bytes are stored in big-endian order as an unsigned + * short, which is the way the user version is serialized in packed {@code Versionstamp}s. * * @param bytes byte array including user version * @param pos starting position of user version @@ -59,8 +112,8 @@ public class Versionstamp implements Comparable { * Creates a {@code Versionstamp} instance based on the given byte array * representation. This follows the same format as that used by * the main constructor, but the completeness of the {@code Versionstamp} - * is instead automatically determined by comparing its global version - * with the value used to indicate an unset global version. + * is instead automatically determined by comparing its transaction version + * with the value used to indicate an unset transaction version. * * @param versionBytes byte array representation of {@code Versionstamp} * @return equivalent instantiated {@code Versionstamp} object @@ -70,8 +123,8 @@ public class Versionstamp implements Comparable { throw new IllegalArgumentException("Versionstamp bytes must have length " + LENGTH); } boolean complete = false; - for(int i = 0; i < UNSET_GLOBAL_VERSION.length; i++) { - if(versionBytes[i] != UNSET_GLOBAL_VERSION[i]) { + for(int i = 0; i < UNSET_TRANSACTION_VERSION.length; i++) { + if(versionBytes[i] != UNSET_TRANSACTION_VERSION[i]) { complete = true; } } @@ -82,7 +135,8 @@ public class Versionstamp implements Comparable { * Creates an incomplete {@code Versionstamp} instance with the given * user version. The provided user version must fit within an unsigned * short. When converted into a byte array, the bytes for the transaction - * version will be filled in with the + * version will be filled in with dummy bytes to be later filled + * in at transaction commit time. * * @param userVersion intra-transaction portion of version (set by user code) * @return an incomplete {@code Versionstamp} with the given user version @@ -92,11 +146,26 @@ public class Versionstamp implements Comparable { throw new IllegalArgumentException("Local version must fit in unsigned short"); } ByteBuffer bb = ByteBuffer.allocate(LENGTH).order(ByteOrder.BIG_ENDIAN); - bb.put(UNSET_GLOBAL_VERSION); + bb.put(UNSET_TRANSACTION_VERSION); bb.putShort((short)userVersion); return new Versionstamp(false, bb.array()); } + /** + * Creates an incomplete {@code Versionstamp} instance with the default user + * version. When converted into a byte array, the bytes for the transaction + * version will be filled in with dummy bytes to be later filled in at + * transaction commit time. If multiple keys are created using the returned + * {@code Versionstamp} within the same transaction, then all of those + * keys will have the same version, but it will provide an ordering between + * different transactions if that is all that is required. + * + * @return an incomplete {@code Versionstamp} with the default user version + */ + public static Versionstamp incomplete() { + return incomplete(0); + } + /** * Creates a complete {@code Versionstamp} instance with the given * transaction and user versions. The provided transaction version must have @@ -108,7 +177,7 @@ public class Versionstamp implements Comparable { * @return a complete {@code Versionstamp} assembled from the given parts */ public static Versionstamp complete(byte[] trVersion, int userVersion) { - if(trVersion.length != UNSET_GLOBAL_VERSION.length) { + if(trVersion.length != UNSET_TRANSACTION_VERSION.length) { throw new IllegalArgumentException("Global version has invalid length " + trVersion.length); } if(userVersion < 0 || userVersion > 0xffff) { @@ -121,22 +190,19 @@ public class Versionstamp implements Comparable { } /** - * Create a Versionstamp instance from a byte array. - * The byte array should have length {@value LENGTH}, the first - * 10 of which represent the transaction version of - * an operation and the last two of which are set per transaction - * to impose a global ordering on all versionstamps. + * Creates a complete {@code Versionstamp} instance with the given + * transaction and default user versions. The provided transaction version + * must have exactly 10 bytes. * - * If the transaction version is not yet known (because the commit has not - * been performed), set complete to false - * and the first 10 bytes of versionBytes - * to any value. Otherwise, complete should be set to - * true. - * - * @param complete whether the transaction version has been set - * @param versionBytes byte array representing this Versionstamp information + * @param trVersion inter-transaction portion of version (set by resolver) + * @return a complete {@code Versionstamp} assembled from the given transaction + * version and the default user version */ - public Versionstamp(boolean complete, byte[] versionBytes) { + public static Versionstamp complete(byte[] trVersion) { + return complete(trVersion, 0); + } + + private Versionstamp(boolean complete, byte[] versionBytes) { if (versionBytes.length != LENGTH) { throw new IllegalArgumentException("Versionstamp bytes must have length " + LENGTH); } @@ -145,17 +211,14 @@ public class Versionstamp implements Comparable { } /** - * Whether this Versionstamp's transaction version is - * meaningful. The transaction resolver will assign each transaction - * a different transaction version. This Versionstamp is - * considered complete if some version has been previously received - * from the transaction resolver and used to construct this - * object. If the commit is still yet to occur, the Versionstamp - * is considered incomplete. If one uses this class with - * our {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY SET_VERSIONSTAMPED_KEY} - * or {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_VALUE SET_VERSIONSTAMPED_VALUE} - * mutations, then the appropriate bytes should be filled in within - * the database during the commit. + * Whether this {@code Versionstamp}'s transaction version is + * meaningful. The database will assign each transaction a different transaction + * version. A {@code Versionstamp} is considered to be "complete" if its + * transaction version is one of those database-assigned versions rather than + * just dummy bytes. If one uses this class with our + * {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY SET_VERSIONSTAMPED_KEY} + * mutation, then the appropriate bytes should be filled in within the database at + * commit time. * * @return whether the transaction version has been set */ @@ -164,18 +227,16 @@ public class Versionstamp implements Comparable { } /** - * Retrieve a byte-array representation of this Versionstamp. + * Retrieve a byte-array representation of this {@code Versionstamp}. * This representation can then be serialized and added to the database. - * If this Versionstamp is not complete, the first - * 10 bytes (representing the transaction version) will - * not be meaningful and one should probably use either the + * If this {@code Versionstamp} is not complete, the first 10 bytes (representing the + * transaction version) will not be meaningful and one should probably use with the * {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY SET_VERSIONSTAMPED_KEY} - * or {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_VALUE SET_VERSIONSTAMPED_VALUE} - * mutations. + * mutation. * * Warning: For performance reasons, this method does not create a copy of * its underlying data array. As a result, it is dangerous to modify the - * return value of this function and should not be done in most circumstances. + * return value of this function. * * @return byte representation of this Versionstamp */ @@ -185,8 +246,7 @@ public class Versionstamp implements Comparable { /** * Retrieve the portion of this Versionstamp that is set by - * the transaction resolver. These 10 bytes are what provide an ordering - * between different commits. + * the database. These 10 bytes are what provide an ordering between different commits. * * @return transaction version of this Versionstamp */ @@ -199,7 +259,7 @@ public class Versionstamp implements Comparable { /** * Retrieve the portion of this Versionstamp that is set * by the user. This integer is what provides an ordering within - * one commit. + * a single commit. * * @return user version of this Versionstamp */ @@ -207,10 +267,18 @@ public class Versionstamp implements Comparable { return unpackUserVersion(versionBytes, LENGTH - 2); } + /** + * Generate a human-readable representation of this {@code Versionstamp}. It contains + * information as to whether this {@code Versionstamp} is incomplete or not, what + * its transaction version is (if the {@code Versionstamp} is complete), and what its + * user version is. + * + * @return a human-readable representation of this {@code Versionstamp} + */ @Override public String toString() { if(complete) { - return "Versionstamp(" + ByteArrayUtil.printable(versionBytes) + ")"; + return "Versionstamp(" + ByteArrayUtil.printable(getTransactionVersion()) + " " + getUserVersion() + ")"; } else { return "Versionstamp( " + getUserVersion() + ")"; } diff --git a/bindings/java/src-completable/test/com/apple/foundationdb/test/VersionstampSmokeTest.java b/bindings/java/src-completable/test/com/apple/foundationdb/test/VersionstampSmokeTest.java new file mode 100644 index 0000000000..50d8f7986a --- /dev/null +++ b/bindings/java/src-completable/test/com/apple/foundationdb/test/VersionstampSmokeTest.java @@ -0,0 +1,63 @@ +/* + * VersionstampSmokeTest.java + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2018 Apple Inc. and the FoundationDB project 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 com.apple.foundationdb.test; + +import com.apple.foundationdb.Database; +import com.apple.foundationdb.FDB; +import com.apple.foundationdb.MutationType; +import com.apple.foundationdb.Transaction; +import com.apple.foundationdb.subspace.Subspace; +import com.apple.foundationdb.tuple.Tuple; +import com.apple.foundationdb.tuple.Versionstamp; + +import java.util.concurrent.CompletableFuture; + +public class VersionstampSmokeTest { + public static void main(String[] args) { + FDB fdb = FDB.selectAPIVersion(500); + Database db = fdb.open(); + + db.run(tr -> { + tr.clear(Tuple.from("prefix").range()); + return null; + }); + + CompletableFuture trVersionFuture = db.run((Transaction tr) -> { + // The incomplete Versionstamp will have tr's version information when committed. + Tuple t = Tuple.from("prefix", Versionstamp.incomplete()); + tr.mutate(MutationType.SET_VERSIONSTAMPED_KEY, t.packWithVersionstamp(), new byte[0]); + return tr.getVersionstamp(); + }); + + byte[] trVersion = trVersionFuture.join(); + + Versionstamp v = db.run((Transaction tr) -> { + Subspace subspace = new Subspace(Tuple.from("prefix")); + byte[] serialized = tr.getRange(subspace.range(), 1).iterator().next().getKey(); + Tuple t = subspace.unpack(serialized); + return t.getVersionstamp(0); + }); + + System.out.println(v); + System.out.println(Versionstamp.complete(trVersion)); + assert v.equals(Versionstamp.complete(trVersion)); + } +} diff --git a/bindings/java/src/main/com/apple/foundationdb/tuple/Tuple.java b/bindings/java/src/main/com/apple/foundationdb/tuple/Tuple.java index 0999ccb488..1929fc0dc5 100644 --- a/bindings/java/src/main/com/apple/foundationdb/tuple/Tuple.java +++ b/bindings/java/src/main/com/apple/foundationdb/tuple/Tuple.java @@ -698,9 +698,9 @@ public class Tuple implements Comparable, Iterable { /** * Determines if there is a {@link Versionstamp} included in this {@code Tuple} that has - * not had it's transaction version set. It will search through nested {@code Tuple}s - * contained within this {@code Tuple}. It will not attempt to throw an error if it - * finds multiple incomplete {@code Versionstamp} instances. + * not had its transaction version set. It will search through nested {@code Tuple}s + * contained within this {@code Tuple}. It will not throw an error if it finds multiple + * incomplete {@code Versionstamp} instances. * * @return whether there is at least one incomplete {@link Versionstamp} included in this * {@code Tuple} diff --git a/bindings/java/src/main/com/apple/foundationdb/tuple/Versionstamp.java b/bindings/java/src/main/com/apple/foundationdb/tuple/Versionstamp.java index 056ccd300a..b60a79aded 100644 --- a/bindings/java/src/main/com/apple/foundationdb/tuple/Versionstamp.java +++ b/bindings/java/src/main/com/apple/foundationdb/tuple/Versionstamp.java @@ -24,28 +24,87 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; + /** - * Used to represent values written by versionstamp operations within a {@link Tuple}. - * This wraps a single array which should contain 12 bytes. The first 10 bytes - * are used by the transaction resolver to impose a globally ordered version. The first - * eight of those bytes are used to specify the transaction version, and the next two - * are used to specify the version of the transaction within a commit batch. - * The final two bytes are to be set by the user and represent a way for - * users to specify an order within a single transaction. + * Used to represent values written by versionstamp operations with a {@link Tuple}. + * This wraps a single array which should contain twelve bytes. The first ten bytes + * are the "transaction" version, and they are usually assigned by the database + * in such a way that all transactions receive a different version that is consistent + * with a serialization order of the transactions within the database. (One can + * use the {@link com.apple.foundationdb.Transaction#getVersionstamp() Transaction.getVersionstamp()} + * method to retrieve this version from a {@code Transaction}.) The final two bytes are the + * "user" version and should be set by the client. This allows the user to use this class to + * impose a total order of items across multiple transactions in the database in a consistent + * and conflict-free way. The user can elect to ignore this parameter by instantiating the + * class with the paramaterless {@link #incomplete() incomplete()} and one-parameter + * {@link #complete(byte[]) complete} static initializers. + * + *

+ * All {@code Versionstamp}s can exist in one of two states: "incomplete" and "complete". + * An "incomplete" {@code Versionstamp} is a {@code Versionstamp} that has not been + * initialized with a meaningful transaction version. For example, this might be used + * with a {@code Versionstamp} that one wants to fill in with the current transaction's + * version information. A "complete" {@code Versionstamp}, in contradistinction, is one + * that has been assigned a meaningful transaction version. This is usually the + * case if one is reading back a {@code Versionstamp} from the database. + *

+ * + *

+ * Example usage might be to do something like the following: + *

+ * + *
+ * 
+ *  {@code Future} trVersionFuture = db.run(new{@code Function>}() {
+ *    {@literal @}Override
+ *     public {@code Future} apply(Transaction tr) {
+ *       // The incomplete Versionstamp will be overwritten with tr's version information when committed.
+ *       Tuple t = Tuple.from("prefix", Versionstamp.incomplete());
+ *       tr.mutate(MutationType.SET_VERSIONSTAMPED_KEY, t.packWithVersionstamp(), new byte[0]);
+ *       return tr.getVersionstamp();
+ *     }
+ *   });
+ *
+ *   byte[] trVersion = trVersionFuture.get();
+ *
+ *   Versionstamp v = db.run(new{@code Function}() {
+ *    {@literal @}Override
+ *     public Versionstamp apply(Transaction tr) {
+ *       Subspace subspace = new Subspace(Tuple.from("prefix"));
+ *       byte[] serialized = tr.getRange(subspace.range(), 1).iterator().next().getKey();
+ *       Tuple t = subspace.unpack(serialized);
+ *       return t.getVersionstamp(0);
+ *     }
+ *   });
+ *
+ *   assert v.equals(Versionstamp.complete(trVersion));
+ * 
+ * 
+ * + *

+ * Here, an incomplete {@code Versionstamp} is packed and written to the database with + * the {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY SET_VERSIONSTAMPED_KEY} + * {@code MutationType}. After committing, we then attempt to read back the same key that + * we just wrote. Then we verify the invariant that the deserialized {@link Versionstamp} is + * the same as a complete {@code Versionstamp} instance created from the first transaction's + * version information. + *

*/ public class Versionstamp implements Comparable { + /** + * Length of a serialized {@code Versionstamp} instance when converted into a byte array. + */ public static final int LENGTH = 12; - protected static final byte[] UNSET_GLOBAL_VERSION = {(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff}; + private static final byte[] UNSET_TRANSACTION_VERSION = {(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff}; private boolean complete; - public byte[] versionBytes; + private byte[] versionBytes; /** * From a byte array, unpack the user version starting at the given position. - * This assumes the bytes are serialized in the array in the same way in - * the given array as this class would serialize them, i.e., in big-endian - * order as an unsigned short. + * This assumes that the bytes are stored in big-endian order as an unsigned + * short, which is the way the user version is serialized in packed {@code Versionstamp}s. * * @param bytes byte array including user version * @param pos starting position of user version @@ -59,8 +118,8 @@ public class Versionstamp implements Comparable { * Creates a {@code Versionstamp} instance based on the given byte array * representation. This follows the same format as that used by * the main constructor, but the completeness of the {@code Versionstamp} - * is instead automatically determined by comparing its global version - * with the value used to indicate an unset global version. + * is instead automatically determined by comparing its transaction version + * with the value used to indicate an unset transaction version. * * @param versionBytes byte array representation of {@code Versionstamp} * @return equivalent instantiated {@code Versionstamp} object @@ -70,8 +129,8 @@ public class Versionstamp implements Comparable { throw new IllegalArgumentException("Versionstamp bytes must have length " + LENGTH); } boolean complete = false; - for(int i = 0; i < UNSET_GLOBAL_VERSION.length; i++) { - if(versionBytes[i] != UNSET_GLOBAL_VERSION[i]) { + for(int i = 0; i < UNSET_TRANSACTION_VERSION.length; i++) { + if(versionBytes[i] != UNSET_TRANSACTION_VERSION[i]) { complete = true; } } @@ -82,7 +141,8 @@ public class Versionstamp implements Comparable { * Creates an incomplete {@code Versionstamp} instance with the given * user version. The provided user version must fit within an unsigned * short. When converted into a byte array, the bytes for the transaction - * version will be filled in with the + * version will be filled in with dummy bytes to be later filled + * in at transaction commit time. * * @param userVersion intra-transaction portion of version (set by user code) * @return an incomplete {@code Versionstamp} with the given user version @@ -92,11 +152,26 @@ public class Versionstamp implements Comparable { throw new IllegalArgumentException("Local version must fit in unsigned short"); } ByteBuffer bb = ByteBuffer.allocate(LENGTH).order(ByteOrder.BIG_ENDIAN); - bb.put(UNSET_GLOBAL_VERSION); + bb.put(UNSET_TRANSACTION_VERSION); bb.putShort((short)userVersion); return new Versionstamp(false, bb.array()); } + /** + * Creates an incomplete {@code Versionstamp} instance with the default user + * version. When converted into a byte array, the bytes for the transaction + * version will be filled in with dummy bytes to be later filled in at + * transaction commit time. If multiple keys are created using the returned + * {@code Versionstamp} within the same transaction, then all of those + * keys will have the same version, but it will provide an ordering between + * different transactions if that is all that is required. + * + * @return an incomplete {@code Versionstamp} with the default user version + */ + public static Versionstamp incomplete() { + return incomplete(0); + } + /** * Creates a complete {@code Versionstamp} instance with the given * transaction and user versions. The provided transaction version must have @@ -108,7 +183,7 @@ public class Versionstamp implements Comparable { * @return a complete {@code Versionstamp} assembled from the given parts */ public static Versionstamp complete(byte[] trVersion, int userVersion) { - if(trVersion.length != UNSET_GLOBAL_VERSION.length) { + if(trVersion.length != UNSET_TRANSACTION_VERSION.length) { throw new IllegalArgumentException("Global version has invalid length " + trVersion.length); } if(userVersion < 0 || userVersion > 0xffff) { @@ -121,22 +196,19 @@ public class Versionstamp implements Comparable { } /** - * Create a Versionstamp instance from a byte array. - * The byte array should have length {@value LENGTH}, the first - * 10 of which represent the transaction version of - * an operation and the last two of which are set per transaction - * to impose a global ordering on all versionstamps. + * Creates a complete {@code Versionstamp} instance with the given + * transaction and default user versions. The provided transaction version + * must have exactly 10 bytes. * - * If the transaction version is not yet known (because the commit has not - * been performed), set complete to false - * and the first 10 bytes of versionBytes - * to any value. Otherwise, complete should be set to - * true. - * - * @param complete whether the transaction version has been set - * @param versionBytes byte array representing this Versionstamp information + * @param trVersion inter-transaction portion of version (set by resolver) + * @return a complete {@code Versionstamp} assembled from the given transaction + * version and the default user version */ - public Versionstamp(boolean complete, byte[] versionBytes) { + public static Versionstamp complete(byte[] trVersion) { + return complete(trVersion, 0); + } + + private Versionstamp(boolean complete, byte[] versionBytes) { if (versionBytes.length != LENGTH) { throw new IllegalArgumentException("Versionstamp bytes must have length " + LENGTH); } @@ -145,17 +217,14 @@ public class Versionstamp implements Comparable { } /** - * Whether this Versionstamp's transaction version is - * meaningful. The transaction resolver will assign each transaction - * a different transaction version. This Versionstamp is - * considered complete if some version has been previously received - * from the transaction resolver and used to construct this - * object. If the commit is still yet to occur, the Versionstamp - * is considered incomplete. If one uses this class with - * our {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY SET_VERSIONSTAMPED_KEY} - * or {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_VALUE SET_VERSIONSTAMPED_VALUE} - * mutations, then the appropriate bytes should be filled in within - * the database during the commit. + * Whether this {@code Versionstamp}'s transaction version is + * meaningful. The database will assign each transaction a different transaction + * version. A {@code Versionstamp} is considered to be "complete" if its + * transaction version is one of those database-assigned versions rather than + * just dummy bytes. If one uses this class with our + * {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY SET_VERSIONSTAMPED_KEY} + * mutation, then the appropriate bytes should be filled in within the database at + * commit time. * * @return whether the transaction version has been set */ @@ -164,18 +233,16 @@ public class Versionstamp implements Comparable { } /** - * Retrieve a byte-array representation of this Versionstamp. + * Retrieve a byte-array representation of this {@code Versionstamp}. * This representation can then be serialized and added to the database. - * If this Versionstamp is not complete, the first - * 10 bytes (representing the transaction version) will - * not be meaningful and one should probably use either the + * If this {@code Versionstamp} is not complete, the first 10 bytes (representing the + * transaction version) will not be meaningful and one should probably use with the * {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY SET_VERSIONSTAMPED_KEY} - * or {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_VALUE SET_VERSIONSTAMPED_VALUE} - * mutations. + * mutation. * * Warning: For performance reasons, this method does not create a copy of * its underlying data array. As a result, it is dangerous to modify the - * return value of this function and should not be done in most circumstances. + * return value of this function. * * @return byte representation of this Versionstamp */ @@ -185,8 +252,7 @@ public class Versionstamp implements Comparable { /** * Retrieve the portion of this Versionstamp that is set by - * the transaction resolver. These 10 bytes are what provide an ordering - * between different commits. + * the database. These 10 bytes are what provide an ordering between different commits. * * @return transaction version of this Versionstamp */ @@ -199,7 +265,7 @@ public class Versionstamp implements Comparable { /** * Retrieve the portion of this Versionstamp that is set * by the user. This integer is what provides an ordering within - * one commit. + * a single commit. * * @return user version of this Versionstamp */ @@ -207,10 +273,18 @@ public class Versionstamp implements Comparable { return unpackUserVersion(versionBytes, LENGTH - 2); } + /** + * Generate a human-readable representation of this {@code Versionstamp}. It contains + * information as to whether this {@code Versionstamp} is incomplete or not, what + * its transaction version is (if the {@code Versionstamp} is complete), and what its + * user version is. + * + * @return a human-readable representation of this {@code Versionstamp} + */ @Override public String toString() { if(complete) { - return "Versionstamp(" + ByteArrayUtil.printable(versionBytes) + ")"; + return "Versionstamp(" + ByteArrayUtil.printable(getTransactionVersion()) + " " + getUserVersion() + ")"; } else { return "Versionstamp( " + getUserVersion() + ")"; } @@ -228,7 +302,7 @@ public class Versionstamp implements Comparable { * *
  • Two incomplete {@code Versionstamp}s will sort based on their user versions.
  • * - * + * * @param other {@code Versionstamp} instance to compare against * @return -1 if this {@code Versionstamp} is smaller than {@code other}, 1 if it is bigger, and * 0 if it is equal