From 2af7525dc754ba78d7dc21d059d7c0b30b97ece9 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Tue, 27 Oct 2015 11:12:28 +0000
Subject: [PATCH] OPENDJ-2335: prevent JE index corruption due to phantom reads

---
 opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java |   31 ++++++++++++++++++++-----------
 1 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java
index 2ad53f2..30e4cc7 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java
@@ -464,24 +464,33 @@
     {
       try
       {
-        Database tree = getOrOpenTree(treeName);
-        DatabaseEntry dbKey = db(key);
-        DatabaseEntry dbValue = new DatabaseEntry();
-
-        boolean isDefined = tree.get(txn, dbKey, dbValue, RMW) == SUCCESS;
-        final ByteSequence oldValue = valueToBytes(dbValue, isDefined);
-        final ByteSequence newValue = f.computeNewValue(oldValue);
-        if (!Objects.equals(newValue, oldValue))
+        final Database tree = getOrOpenTree(treeName);
+        final DatabaseEntry dbKey = db(key);
+        final DatabaseEntry dbValue = new DatabaseEntry();
+        for (;;)
         {
+          final boolean isDefined = tree.get(txn, dbKey, dbValue, RMW) == SUCCESS;
+          final ByteSequence oldValue = valueToBytes(dbValue, isDefined);
+          final ByteSequence newValue = f.computeNewValue(oldValue);
+          if (Objects.equals(newValue, oldValue))
+          {
+            return false;
+          }
           if (newValue == null)
           {
             return tree.delete(txn, dbKey) == SUCCESS;
           }
-
           setData(dbValue, newValue);
-          return tree.put(txn, dbKey, dbValue) == SUCCESS;
+          if (isDefined)
+          {
+            return tree.put(txn, dbKey, dbValue) == SUCCESS;
+          }
+          else if (tree.putNoOverwrite(txn, dbKey, dbValue) == SUCCESS)
+          {
+            return true;
+          }
+          // else retry due to phantom read: another thread inserted a record
         }
-        return false;
       }
       catch (DatabaseException e)
       {

--
Gitblit v1.10.0