TEPHRA-287 ActionChange.getChangeKey() not implemented correctly
diff --git a/tephra-core/src/main/java/org/apache/tephra/AbstractTransactionAwareTable.java b/tephra-core/src/main/java/org/apache/tephra/AbstractTransactionAwareTable.java
index 22cfaad..1cc1404 100644
--- a/tephra-core/src/main/java/org/apache/tephra/AbstractTransactionAwareTable.java
+++ b/tephra-core/src/main/java/org/apache/tephra/AbstractTransactionAwareTable.java
@@ -24,6 +24,8 @@
 import com.google.common.primitives.Bytes;
 import com.google.common.primitives.UnsignedBytes;
 
+import org.apache.hadoop.io.WritableUtils;
+
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,15 +44,18 @@
   // map of write pointers to change set associated with each
   protected final Map<Long, Set<ActionChange>> changeSets;
   protected final TxConstants.ConflictDetection conflictLevel;
+  protected final boolean pre014ChangeSetKey;
   protected Transaction tx;
   protected boolean allowNonTransactional;
   protected static final byte[] SEPARATOR_BYTE_ARRAY = new byte[] {0};
 
-  public AbstractTransactionAwareTable(TxConstants.ConflictDetection conflictLevel, boolean allowNonTransactional) {
+  public AbstractTransactionAwareTable(TxConstants.ConflictDetection conflictLevel,
+      boolean allowNonTransactional, boolean pre014ChangeSetKey) {
     this.conflictLevel = conflictLevel;
     this.allowNonTransactional = allowNonTransactional;
     this.txCodec = new TransactionCodec();
     this.changeSets = Maps.newHashMap();
+    this.pre014ChangeSetKey = pre014ChangeSetKey;
   }
 
   /**
@@ -88,13 +93,98 @@
     Collection<byte[]> txChanges = new TreeSet<byte[]>(UnsignedBytes.lexicographicalComparator());
     for (Set<ActionChange> changeSet : changeSets.values()) {
       for (ActionChange change : changeSet) {
-        txChanges.add(getChangeKey(change.getRow(), change.getFamily(), change.getQualifier()));
+        byte[] row = change.getRow();
+        byte[] fam = change.getFamily();
+        byte[] qual = change.getQualifier();
+        txChanges.add(getChangeKey(row, fam, qual));
+        if (pre014ChangeSetKey) {
+          txChanges.add(getChangeKeyWithoutSeparators(row, fam, qual));
+        }
       }
     }
     return txChanges;
   }
 
+  /**
+   * @param vint long to make a vint of.
+   * @return long in vint byte array representation
+   * We could alternatively make this abstract and
+   * implement this method as Bytes.vintToBytes(long) in
+   * every compat module. 
+   */
+  protected byte [] getVIntBytes(final long vint) {
+    long i = vint;
+    int size = WritableUtils.getVIntSize(i);
+    byte [] result = new byte[size];
+    int offset = 0;
+    if (i >= -112 && i <= 127) {
+      result[offset] = (byte) i;
+      return result;
+    }
+
+    int len = -112;
+    if (i < 0) {
+      i ^= -1L; // take one's complement'
+      len = -120;
+    }
+
+    long tmp = i;
+    while (tmp != 0) {
+      tmp = tmp >> 8;
+    len--;
+    }
+
+    result[offset++] = (byte) len;
+
+    len = (len < -120) ? -(len + 120) : -(len + 112);
+
+    for (int idx = len; idx != 0; idx--) {
+      int shiftbits = (idx - 1) * 8;
+      long mask = 0xFFL << shiftbits;
+      result[offset++] = (byte) ((i & mask) >> shiftbits);
+    }
+    return result;
+  }
+
+  /**
+   * The unique bytes identifying what is changing. We use the
+   * following structure:
+   * ROW conflict level: <table_name><0 byte separator><row key>
+   * since we know that table_name cannot contain a zero byte.
+   * COLUMN conflict level: <table_name><0 byte separator>
+   *     <length of family as vint><family>
+   *     <length of qualifier as vint><qualifier><row key>
+   * The last part of the change key does not need the length to be part
+   * of the key since there's nothing after it that may overlap with it.
+   * @param row
+   * @param family
+   * @param qualifier 
+   * @return unique change key
+   */
   public byte[] getChangeKey(byte[] row, byte[] family, byte[] qualifier) {
+    return getChangeKeyWithSeparators(row, family, qualifier);
+  }
+  
+  private byte[] getChangeKeyWithSeparators(byte[] row, byte[] family, byte[] qualifier) {
+    byte[] key;
+    byte[] tableKey = getTableKey();
+    switch (conflictLevel) {
+    case ROW:
+      key = Bytes.concat(tableKey, SEPARATOR_BYTE_ARRAY, row);
+      break;
+    case COLUMN:
+      key = Bytes.concat(tableKey, SEPARATOR_BYTE_ARRAY, getVIntBytes(family.length), family,
+          getVIntBytes(qualifier.length), qualifier, row);
+      break;
+    case NONE:
+      throw new IllegalStateException("NONE conflict detection does not support change keys");
+    default:
+      throw new IllegalStateException("Unknown conflict detection level: " + conflictLevel);
+    }
+    return key;
+  }
+
+  private byte[] getChangeKeyWithoutSeparators(byte[] row, byte[] family, byte[] qualifier) {
     byte[] key;
     byte[] tableKey = getTableKey();
     switch (conflictLevel) {
diff --git a/tephra-core/src/main/java/org/apache/tephra/TxConstants.java b/tephra-core/src/main/java/org/apache/tephra/TxConstants.java
index bb4b139..1e50888 100644
--- a/tephra-core/src/main/java/org/apache/tephra/TxConstants.java
+++ b/tephra-core/src/main/java/org/apache/tephra/TxConstants.java
@@ -124,6 +124,13 @@
    */
   public static final String CLIENT_ID = "tephra.client.id";
 
+  /** Whether or not to put separators in change set row keys  (see TEPHRA-287).
+   * Currently defaults to true so that a mix of old and new tx clients will
+   * consistenly use the old logic. Once all clients are know to be upgraded
+   * the default should be change to false */
+  public static final String TX_PRE_014_CHANGESET_KEY = "data.tx.pre.014.changeset.key";
+  public static final boolean DEFAULT_TX_PRE_014_CHANGESET_KEY = true;
+
   /**
    * TransactionManager configuration.
    */
@@ -196,7 +203,7 @@
     public static final long DEFAULT_TX_CHANGESET_SIZE_LIMIT = Long.MAX_VALUE;
     /** The default warning threshold for the total size in bytes of a change set is unlimited. */
     public static final long DEFAULT_TX_CHANGESET_SIZE_WARN_THRESHOLD = Long.MAX_VALUE;
-
+ 
     /** Whether and how long to retain the client id of a transaction. Valid values are:
      * <ul>
      *   <li>OFF - do not retain the client id at all</li>
diff --git a/tephra-core/src/test/java/org/apache/tephra/AbstractTransactionAwareTableTest.java b/tephra-core/src/test/java/org/apache/tephra/AbstractTransactionAwareTableTest.java
index 5f9d482..a23d7fa 100644
--- a/tephra-core/src/test/java/org/apache/tephra/AbstractTransactionAwareTableTest.java
+++ b/tephra-core/src/test/java/org/apache/tephra/AbstractTransactionAwareTableTest.java
@@ -18,6 +18,8 @@
 
 package org.apache.tephra;
 
+import com.google.common.primitives.Bytes;
+
 import org.apache.tephra.TxConstants.ConflictDetection;
 import org.junit.Test;
 
@@ -26,19 +28,22 @@
 import java.util.Set;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 public class AbstractTransactionAwareTableTest {
   public static final byte[] TABLE_NAME = "a".getBytes();
-  public static final String ROW1 = "r1";
-  public static final String ROW2 = "r2";
+  public static final String ROW_PREFIX = "r";
+  public static final String ROW1 = ROW_PREFIX + "1";
+  public static final String ROW2 = ROW_PREFIX + "2";
+  public static final String ROW3 = "1";
   public static final String FAM1 = "f1";
 
   private static class ConcreteTransactionAwareTable extends AbstractTransactionAwareTable {
     private final byte[] tableKey;
 
     public ConcreteTransactionAwareTable(ConflictDetection conflictLevel,
-        boolean allowNonTransactional, byte[] tableKey) {
-      super(conflictLevel, allowNonTransactional);
+        boolean allowNonTransactional, byte[] tableKey, boolean pre014ChangeSetKey) {
+      super(conflictLevel, allowNonTransactional, pre014ChangeSetKey);
       this.tableKey = tableKey;
     }
 
@@ -69,9 +74,9 @@
     long wp = System.currentTimeMillis();
     long rp = wp - 100;
     ConcreteTransactionAwareTable table1 = 
-        new ConcreteTransactionAwareTable(ConflictDetection.ROW, false, TABLE_NAME);
+        new ConcreteTransactionAwareTable(ConflictDetection.ROW, false, TABLE_NAME, true);
     ConcreteTransactionAwareTable table2 = 
-        new ConcreteTransactionAwareTable(ConflictDetection.ROW, false, TABLE_NAME);
+        new ConcreteTransactionAwareTable(ConflictDetection.ROW, false, TABLE_NAME, true);
     Transaction tx = new Transaction(rp, wp, new long[] {}, new long[] {}, wp);
     table1.startTx(tx);
     table2.startTx(tx);
@@ -84,4 +89,41 @@
     table2.addToChangeSet(ROW2.getBytes(), FAM1.getBytes(), null);
     assertEquals(table1.getChangeSet(), table2.getChangeSet());
   }
+
+  @Test
+  public void testActionChangeOverlapWithSeparators() {
+    testActionChangeOverlap(false);
+  }
+
+  @Test
+  public void testActionChangeOverlapWithoutSeparators() {
+    testActionChangeOverlap(true);
+  }
+
+  /**
+   * Demonstrates false positives on conflicts due to concatenation of
+   * keys for change sets (see TEPHRA-287).
+   * @param pre014ChangeSetKey
+   */
+  private void testActionChangeOverlap(boolean pre014ChangeSetKey) {
+    long wp = System.currentTimeMillis();
+    long rp = wp - 100;
+    ConcreteTransactionAwareTable table1 = 
+        new ConcreteTransactionAwareTable(ConflictDetection.ROW, false, TABLE_NAME, pre014ChangeSetKey);
+    ConcreteTransactionAwareTable table2 = 
+        new ConcreteTransactionAwareTable(ConflictDetection.ROW, false, 
+            Bytes.concat(TABLE_NAME, ROW_PREFIX.getBytes()), pre014ChangeSetKey);
+    Transaction tx = new Transaction(rp, wp, new long[] {}, new long[] {}, wp);
+    table1.startTx(tx);
+    table2.startTx(tx);
+    table1.addToChangeSet(ROW1.getBytes(), FAM1.getBytes(), null);
+    table2.addToChangeSet(ROW3.getBytes(), FAM1.getBytes(), null);
+    assertNotEquals(table1.getChangeSet(), table2.getChangeSet());
+    ConcreteTransactionAwareTable table3 = 
+        new ConcreteTransactionAwareTable(ConflictDetection.COLUMN, false, TABLE_NAME, pre014ChangeSetKey);
+    table3.startTx(tx);
+    table3.addToChangeSet("e".getBytes(), "bc".getBytes(), "d".getBytes());
+    table3.addToChangeSet("e".getBytes(), "b".getBytes(), "cd".getBytes());
+    assertEquals(pre014ChangeSetKey ? 3 : 2, table3.getTxChanges().size());
+  }
 }
diff --git a/tephra-hbase-compat-0.96/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java b/tephra-hbase-compat-0.96/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
index e1e5d7d..5127680 100644
--- a/tephra-hbase-compat-0.96/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
+++ b/tephra-hbase-compat-0.96/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
@@ -106,7 +106,10 @@
    */
   public TransactionAwareHTable(HTableInterface hTable, TxConstants.ConflictDetection conflictLevel,
                                 boolean allowNonTransactional) {
-    super(conflictLevel, allowNonTransactional);
+    super(conflictLevel, allowNonTransactional, 
+        hTable.getConfiguration().getBoolean(
+            TxConstants.TX_PRE_014_CHANGESET_KEY,
+            TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY));
     this.hTable = hTable;
   }
 
diff --git a/tephra-hbase-compat-0.96/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java b/tephra-hbase-compat-0.96/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
index 9c5dca2..dc7c2a9 100644
--- a/tephra-hbase-compat-0.96/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
+++ b/tephra-hbase-compat-0.96/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
@@ -931,7 +931,10 @@
 
     Collection<byte[]> changeSet = txTable1.getTxChanges();
     assertNotNull(changeSet);
-    assertEquals(2, changeSet.size());
+    boolean pre014ChangeSetKeys = txTable1.getConfiguration().getBoolean(
+        TxConstants.TX_PRE_014_CHANGESET_KEY,
+        TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY);
+    assertEquals(pre014ChangeSetKeys ? 4 : 2, changeSet.size());
     assertTrue(changeSet.contains(txTable1.getChangeKey(row1, null, null)));
     assertTrue(changeSet.contains(txTable1.getChangeKey(row2, null, null)));
     txContext1.finish();
diff --git a/tephra-hbase-compat-0.98/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java b/tephra-hbase-compat-0.98/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
index 7ad9835..62f1e99 100644
--- a/tephra-hbase-compat-0.98/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
+++ b/tephra-hbase-compat-0.98/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
@@ -110,7 +110,10 @@
    */
   public TransactionAwareHTable(HTableInterface hTable, TxConstants.ConflictDetection conflictLevel,
                                 boolean allowNonTransactional) {
-    super(conflictLevel, allowNonTransactional);
+    super(conflictLevel, allowNonTransactional, 
+        hTable.getConfiguration().getBoolean(
+            TxConstants.TX_PRE_014_CHANGESET_KEY,
+            TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY));
     this.hTable = hTable;
   }
 
diff --git a/tephra-hbase-compat-0.98/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java b/tephra-hbase-compat-0.98/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
index 68324c9..e3ccd27 100644
--- a/tephra-hbase-compat-0.98/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
+++ b/tephra-hbase-compat-0.98/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
@@ -931,7 +931,10 @@
 
     Collection<byte[]> changeSet = txTable1.getTxChanges();
     assertNotNull(changeSet);
-    assertEquals(2, changeSet.size());
+    boolean pre014ChangeSetKeys = txTable1.getConfiguration().getBoolean(
+        TxConstants.TX_PRE_014_CHANGESET_KEY,
+        TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY);
+    assertEquals(pre014ChangeSetKeys ? 4 : 2, changeSet.size());
     assertTrue(changeSet.contains(txTable1.getChangeKey(row1, null, null)));
     assertTrue(changeSet.contains(txTable1.getChangeKey(row2, null, null)));
     txContext1.finish();
diff --git a/tephra-hbase-compat-1.0-cdh/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java b/tephra-hbase-compat-1.0-cdh/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
index 4965914..fa6a018 100644
--- a/tephra-hbase-compat-1.0-cdh/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
+++ b/tephra-hbase-compat-1.0-cdh/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
@@ -110,7 +110,10 @@
    */
   public TransactionAwareHTable(HTableInterface hTable, TxConstants.ConflictDetection conflictLevel,
                                 boolean allowNonTransactional) {
-    super(conflictLevel, allowNonTransactional);
+    super(conflictLevel, allowNonTransactional, 
+        hTable.getConfiguration().getBoolean(
+            TxConstants.TX_PRE_014_CHANGESET_KEY,
+            TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY));
     this.hTable = hTable;
   }
 
diff --git a/tephra-hbase-compat-1.0-cdh/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java b/tephra-hbase-compat-1.0-cdh/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
index defad80..7f77fb8 100644
--- a/tephra-hbase-compat-1.0-cdh/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
+++ b/tephra-hbase-compat-1.0-cdh/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
@@ -931,7 +931,10 @@
 
     Collection<byte[]> changeSet = txTable1.getTxChanges();
     assertNotNull(changeSet);
-    assertEquals(2, changeSet.size());
+    boolean pre014ChangeSetKeys = txTable1.getConfiguration().getBoolean(
+        TxConstants.TX_PRE_014_CHANGESET_KEY,
+        TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY);
+    assertEquals(pre014ChangeSetKeys ? 4 : 2, changeSet.size());
     assertTrue(changeSet.contains(txTable1.getChangeKey(row1, null, null)));
     assertTrue(changeSet.contains(txTable1.getChangeKey(row2, null, null)));
     txContext1.finish();
diff --git a/tephra-hbase-compat-1.0/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java b/tephra-hbase-compat-1.0/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
index 6b266a2..faf2a99 100644
--- a/tephra-hbase-compat-1.0/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
+++ b/tephra-hbase-compat-1.0/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
@@ -110,7 +110,10 @@
    */
   public TransactionAwareHTable(HTableInterface hTable, TxConstants.ConflictDetection conflictLevel,
                                 boolean allowNonTransactional) {
-    super(conflictLevel, allowNonTransactional);
+    super(conflictLevel, allowNonTransactional, 
+        hTable.getConfiguration().getBoolean(
+            TxConstants.TX_PRE_014_CHANGESET_KEY,
+            TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY));
     this.hTable = hTable;
   }
 
diff --git a/tephra-hbase-compat-1.0/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java b/tephra-hbase-compat-1.0/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
index 30fcd8a..097cf14 100644
--- a/tephra-hbase-compat-1.0/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
+++ b/tephra-hbase-compat-1.0/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
@@ -931,7 +931,10 @@
 
     Collection<byte[]> changeSet = txTable1.getTxChanges();
     assertNotNull(changeSet);
-    assertEquals(2, changeSet.size());
+    boolean pre014ChangeSetKeys = txTable1.getConfiguration().getBoolean(
+        TxConstants.TX_PRE_014_CHANGESET_KEY,
+        TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY);
+    assertEquals(pre014ChangeSetKeys ? 4 : 2, changeSet.size());
     assertTrue(changeSet.contains(txTable1.getChangeKey(row1, null, null)));
     assertTrue(changeSet.contains(txTable1.getChangeKey(row2, null, null)));
     txContext1.finish();
diff --git a/tephra-hbase-compat-1.1-base/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java b/tephra-hbase-compat-1.1-base/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
index b28737f..3543671 100644
--- a/tephra-hbase-compat-1.1-base/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
+++ b/tephra-hbase-compat-1.1-base/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
@@ -110,7 +110,10 @@
    */
   public TransactionAwareHTable(Table hTable, TxConstants.ConflictDetection conflictLevel,
                                 boolean allowNonTransactional) {
-    super(conflictLevel, allowNonTransactional);
+    super(conflictLevel, allowNonTransactional, 
+        hTable.getConfiguration().getBoolean(
+            TxConstants.TX_PRE_014_CHANGESET_KEY,
+            TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY));
     this.hTable = hTable;
   }
 
diff --git a/tephra-hbase-compat-1.1-base/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java b/tephra-hbase-compat-1.1-base/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
index e48a158..7ee725b 100644
--- a/tephra-hbase-compat-1.1-base/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
+++ b/tephra-hbase-compat-1.1-base/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
@@ -943,7 +943,10 @@
 
     Collection<byte[]> changeSet = txTable1.getTxChanges();
     assertNotNull(changeSet);
-    assertEquals(2, changeSet.size());
+    boolean pre014ChangeSetKeys = txTable1.getConfiguration().getBoolean(
+        TxConstants.TX_PRE_014_CHANGESET_KEY,
+        TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY);
+    assertEquals(pre014ChangeSetKeys ? 4 : 2, changeSet.size());
     assertTrue(changeSet.contains(txTable1.getChangeKey(row1, null, null)));
     assertTrue(changeSet.contains(txTable1.getChangeKey(row2, null, null)));
     txContext1.finish();
diff --git a/tephra-hbase-compat-1.3/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java b/tephra-hbase-compat-1.3/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
index ed1dbb1..af4b350 100644
--- a/tephra-hbase-compat-1.3/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
+++ b/tephra-hbase-compat-1.3/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
@@ -110,7 +110,10 @@
    */
   public TransactionAwareHTable(HTableInterface hTable, TxConstants.ConflictDetection conflictLevel,
                                 boolean allowNonTransactional) {
-    super(conflictLevel, allowNonTransactional);
+    super(conflictLevel, allowNonTransactional, 
+        hTable.getConfiguration().getBoolean(
+            TxConstants.TX_PRE_014_CHANGESET_KEY,
+            TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY));
     this.hTable = hTable;
   }
 
diff --git a/tephra-hbase-compat-1.3/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java b/tephra-hbase-compat-1.3/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
index 11ffd1a..73f9d45 100644
--- a/tephra-hbase-compat-1.3/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
+++ b/tephra-hbase-compat-1.3/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
@@ -937,7 +937,10 @@
 
     Collection<byte[]> changeSet = txTable1.getTxChanges();
     assertNotNull(changeSet);
-    assertEquals(2, changeSet.size());
+    boolean pre014ChangeSetKeys = txTable1.getConfiguration().getBoolean(
+        TxConstants.TX_PRE_014_CHANGESET_KEY,
+        TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY);
+    assertEquals(pre014ChangeSetKeys ? 4 : 2, changeSet.size());
     assertTrue(changeSet.contains(txTable1.getChangeKey(row1, null, null)));
     assertTrue(changeSet.contains(txTable1.getChangeKey(row2, null, null)));
     txContext1.finish();
diff --git a/tephra-hbase-compat-1.4/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java b/tephra-hbase-compat-1.4/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
index f47d93f..d6eb30e 100644
--- a/tephra-hbase-compat-1.4/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
+++ b/tephra-hbase-compat-1.4/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
@@ -110,7 +110,10 @@
    */
   public TransactionAwareHTable(HTableInterface hTable, TxConstants.ConflictDetection conflictLevel,
                                 boolean allowNonTransactional) {
-    super(conflictLevel, allowNonTransactional);
+    super(conflictLevel, allowNonTransactional, 
+        hTable.getConfiguration().getBoolean(
+            TxConstants.TX_PRE_014_CHANGESET_KEY,
+            TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY));
     this.hTable = hTable;
   }
 
diff --git a/tephra-hbase-compat-1.4/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java b/tephra-hbase-compat-1.4/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
index 11ffd1a..73f9d45 100644
--- a/tephra-hbase-compat-1.4/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
+++ b/tephra-hbase-compat-1.4/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
@@ -937,7 +937,10 @@
 
     Collection<byte[]> changeSet = txTable1.getTxChanges();
     assertNotNull(changeSet);
-    assertEquals(2, changeSet.size());
+    boolean pre014ChangeSetKeys = txTable1.getConfiguration().getBoolean(
+        TxConstants.TX_PRE_014_CHANGESET_KEY,
+        TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY);
+    assertEquals(pre014ChangeSetKeys ? 4 : 2, changeSet.size());
     assertTrue(changeSet.contains(txTable1.getChangeKey(row1, null, null)));
     assertTrue(changeSet.contains(txTable1.getChangeKey(row2, null, null)));
     txContext1.finish();
diff --git a/tephra-hbase-compat-2.0/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java b/tephra-hbase-compat-2.0/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
index 1f59ea0..b35c8aa 100644
--- a/tephra-hbase-compat-2.0/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
+++ b/tephra-hbase-compat-2.0/src/main/java/org/apache/tephra/hbase/TransactionAwareHTable.java
@@ -112,7 +112,10 @@
    */
   public TransactionAwareHTable(Table hTable, TxConstants.ConflictDetection conflictLevel,
       boolean allowNonTransactional) {
-    super(conflictLevel, allowNonTransactional);
+    super(conflictLevel, allowNonTransactional, 
+        hTable.getConfiguration().getBoolean(
+            TxConstants.TX_PRE_014_CHANGESET_KEY,
+            TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY));
     this.hTable = hTable;
   }
 
diff --git a/tephra-hbase-compat-2.0/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java b/tephra-hbase-compat-2.0/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
index ccffa20..59bec8d 100644
--- a/tephra-hbase-compat-2.0/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
+++ b/tephra-hbase-compat-2.0/src/test/java/org/apache/tephra/hbase/TransactionAwareHTableTest.java
@@ -977,7 +977,10 @@
 
     Collection<byte[]> changeSet = txTable1.getTxChanges();
     assertNotNull(changeSet);
-    assertEquals(2, changeSet.size());
+    boolean pre014ChangeSetKeys = txTable1.getConfiguration().getBoolean(
+        TxConstants.TX_PRE_014_CHANGESET_KEY,
+        TxConstants.DEFAULT_TX_PRE_014_CHANGESET_KEY);
+    assertEquals(pre014ChangeSetKeys ? 4 : 2, changeSet.size());
     assertTrue(changeSet.contains(txTable1.getChangeKey(row1, null, null)));
     assertTrue(changeSet.contains(txTable1.getChangeKey(row2, null, null)));
     txContext1.finish();