From ca6ca7e317ea3a6bb5fb3bc48abff2d33f507f7e Mon Sep 17 00:00:00 2001
From: Rich Megginson <rmeggins@redhat.com>
Date: Fri, 17 Feb 2012 15:13:39 -0700
Subject: [PATCH] Ticket #301 - implement transaction support using thread local storage

https://fedorahosted.org/389/ticket/301
Resolves: Ticket #301
Bug Description: implement transaction support using thread local storage
Reviewed by: ???
Branch: master
Fix Description: Create a new thread local storage key in dblayer.c
thread_private_txn_stack.  This is used to hold the stack of transactions
in the current thread.  A stack is used because there may be nested
transactions.  The transaction at the head of the stack is the currently
executing one and the parent to use for new nested transactions.  When a
new txn is started, it is pushed on the stack to be the new head.  When a
txn is commited or aborted, it is popped off the head of the list, and the
old parent transaction is restored in the pblock.  Any db backend top level
function must call dblayer_txn_init() to set its txn handle to the current
transaction, and set SLAPI_TXN in the pblock.  The txn functions also allow
the txn to be passed in via the pblock.  This approach gives us the
flexibility to support LDAP transactions in the future.
Platforms tested: RHEL6 x86_64
Flag Day: no
Doc impact: no
---
 ldap/servers/slapd/back-ldbm/dblayer.c      |  165 +++++++++++++++++++++++++--
 ldap/servers/slapd/back-ldbm/ldbm_add.c     |   13 +-
 ldap/servers/slapd/back-ldbm/ldbm_bind.c    |    5 +
 ldap/servers/slapd/back-ldbm/ldbm_compare.c |    5 +
 ldap/servers/slapd/back-ldbm/ldbm_delete.c  |   10 +-
 ldap/servers/slapd/back-ldbm/ldbm_modify.c  |   16 ++-
 ldap/servers/slapd/back-ldbm/ldbm_modrdn.c  |   12 +-
 ldap/servers/slapd/back-ldbm/ldbm_search.c  |   10 ++
 8 files changed, 204 insertions(+), 32 deletions(-)

diff --git a/ldap/servers/slapd/back-ldbm/dblayer.c b/ldap/servers/slapd/back-ldbm/dblayer.c
index 3e41e89..c6f3863 100644
--- a/ldap/servers/slapd/back-ldbm/dblayer.c
+++ b/ldap/servers/slapd/back-ldbm/dblayer.c
@@ -218,6 +218,10 @@ static int trans_batch_count=1;
 static int trans_batch_limit=0;
 static PRBool log_flush_thread=PR_FALSE;
 static int dblayer_db_remove_ex(dblayer_private_env *env, char const path[], char const dbName[], PRBool use_lock);
+static void dblayer_init_pvt_txn();
+static void dblayer_push_pvt_txn(back_txn *txn);
+static back_txn *dblayer_get_pvt_txn();
+static void dblayer_pop_pvt_txn();
 
 #define MEGABYTE (1024 * 1024)
 #define GIGABYTE (1024 * MEGABYTE)
@@ -1563,6 +1567,7 @@ dblayer_start(struct ldbminfo *li, int dbmode)
             if (!(dbmode & DBLAYER_NO_DBTHREADS_MODE))
                 dbmode = DBLAYER_NORMAL_MODE; /* to restart helper threads */
         }
+        dblayer_init_pvt_txn();
     }
 
     if (priv->dblayer_private_mem) {
@@ -3359,16 +3364,21 @@ int dblayer_erase_index_file(backend *be, struct attrinfo *a, int no_force_chkpt
  * know the transaction mechanism underneath (because the caller is
  * typically a few calls up the stack from any DB stuff).
  * Sadly, in slapd there was no handy structure associated with
- * an LDAP operation, and passed around evberywhere, so we had
+ * an LDAP operation, and passed around everywhere, so we had
  * to invent the back_txn structure.
  * The lower levels of the back-end look into this structure, and
  * take out the DB_TXN they need.
  */
 int dblayer_txn_init(struct ldbminfo *li, back_txn *txn)
 {
+    back_txn *cur_txn = dblayer_get_pvt_txn();
     PR_ASSERT(NULL != txn);
 
-    txn->back_txn_txn = NULL;
+    if (cur_txn && txn) {
+        txn->back_txn_txn = cur_txn->back_txn_txn;
+    } else if (txn) {
+        txn->back_txn_txn = NULL;
+    }
     return 0;
 }
 
@@ -3377,7 +3387,7 @@ int dblayer_txn_begin_ext(struct ldbminfo *li, back_txnid parent_txn, back_txn *
 {
     int return_value = -1;
     dblayer_private *priv = NULL;
-    PR_ASSERT(NULL != txn);
+    back_txn new_txn = {NULL};
     PR_ASSERT(NULL != li);
     /*
      * When server is shutting down, some components need to
@@ -3391,18 +3401,38 @@ int dblayer_txn_begin_ext(struct ldbminfo *li, back_txnid parent_txn, back_txn *
     priv = (dblayer_private*)li->li_dblayer_private;
     PR_ASSERT(NULL != priv);
 
+    if (txn) {
+        txn->back_txn_txn = NULL;
+    }
+
     if (priv->dblayer_enable_transactions)
     {
         dblayer_private_env *pEnv = priv->dblayer_env;
         if(use_lock) slapi_rwlock_rdlock(pEnv->dblayer_env_lock);
+        if (!parent_txn)
+        {
+            /* see if we have a stored parent txn */
+            back_txn *par_txn_txn = dblayer_get_pvt_txn();
+            if (par_txn_txn) {
+                parent_txn = par_txn_txn->back_txn_txn;
+            }
+        }
         return_value = TXN_BEGIN(pEnv->dblayer_DB_ENV,
                                  (DB_TXN*)parent_txn,
-                                 &txn->back_txn_txn,
+                                 &new_txn.back_txn_txn,
                                  0);
         if (0 != return_value) 
         {
             if(use_lock) slapi_rwlock_unlock(priv->dblayer_env->dblayer_env_lock);
-            txn->back_txn_txn = NULL;
+        }
+        else
+        {
+            /* this txn is now our current transaction for current operations
+               and new parent for any nested transactions created */
+            dblayer_push_pvt_txn(&new_txn);
+            if (txn) {
+                txn->back_txn_txn = new_txn.back_txn_txn;
+            }
         }
     } else
     {
@@ -3430,21 +3460,42 @@ int dblayer_txn_commit_ext(struct ldbminfo *li, back_txn *txn, PRBool use_lock)
 {
     int return_value = -1;
     dblayer_private *priv = NULL;
-    DB_TXN *db_txn;
+    DB_TXN *db_txn = NULL;
+    back_txn *cur_txn = NULL;
 
-    PR_ASSERT(NULL != txn);
     PR_ASSERT(NULL != li);
 
     priv = (dblayer_private*)li->li_dblayer_private;
     PR_ASSERT(NULL != priv);
 
-    db_txn = txn->back_txn_txn;
+    /* use the transaction we are given - if none, see if there
+       is a transaction in progress */
+    if (txn) {
+        db_txn = txn->back_txn_txn;
+    }
+    cur_txn = dblayer_get_pvt_txn();
+    if (!db_txn) {
+        if (cur_txn) {
+            db_txn = cur_txn->back_txn_txn;
+        }
+    }
     if (NULL != db_txn &&
         1 != priv->dblayer_stop_threads &&
         priv->dblayer_env &&
         priv->dblayer_enable_transactions)
     {
         return_value = TXN_COMMIT(db_txn, 0);
+        /* if we were given a transaction, and it is the same as the
+           current transaction in progress, pop it off the stack
+           or, if no transaction was given, we must be using the
+           current one - must pop it */
+        if (!txn || (cur_txn && (cur_txn->back_txn_txn == db_txn))) {
+            dblayer_pop_pvt_txn();
+        }
+        if (txn) {
+            /* this handle is no longer value - set it to NULL */
+            txn->back_txn_txn = NULL;
+        }
         if ((priv->dblayer_durable_transactions) && use_lock ) {
             if(trans_batch_limit > 0) {        
                 if(trans_batch_count % trans_batch_limit) {
@@ -3487,20 +3538,41 @@ int dblayer_txn_abort_ext(struct ldbminfo *li, back_txn *txn, PRBool use_lock)
 {
     int return_value = -1;
     dblayer_private *priv = NULL;
-    DB_TXN *db_txn;
+    DB_TXN *db_txn = NULL;
+    back_txn *cur_txn = NULL;
 
-    PR_ASSERT(NULL != txn);
     PR_ASSERT(NULL != li);
 
     priv = (dblayer_private*)li->li_dblayer_private;
     PR_ASSERT(NULL != priv);
 
-    db_txn = txn->back_txn_txn;
+    /* use the transaction we are given - if none, see if there
+       is a transaction in progress */
+    if (txn) {
+        db_txn = txn->back_txn_txn;
+    }
+    cur_txn = dblayer_get_pvt_txn();
+    if (!db_txn) {
+        if (cur_txn) {
+            db_txn = cur_txn->back_txn_txn;
+        }
+    }
     if (NULL != db_txn &&
         priv->dblayer_env &&
         priv->dblayer_enable_transactions)
     {
         return_value = TXN_ABORT(db_txn);
+        /* if we were given a transaction, and it is the same as the
+           current transaction in progress, pop it off the stack
+           or, if no transaction was given, we must be using the
+           current one - must pop it */
+        if (!txn || (cur_txn && (cur_txn->back_txn_txn == db_txn))) {
+            dblayer_pop_pvt_txn();
+        }
+        if (txn) {
+            /* this handle is no longer value - set it to NULL */
+            txn->back_txn_txn = NULL;
+        }
         if(use_lock) slapi_rwlock_unlock(priv->dblayer_env->dblayer_env_lock);
     } else
     {
@@ -6599,3 +6671,74 @@ ldbm_back_ctrl_info(Slapi_Backend *be, int cmd, void *info)
 
     return rc;
 }
+
+#include <prthread.h>
+#include <prclist.h>
+
+static PRUintn thread_private_txn_stack;
+
+typedef struct dblayer_txn_stack {
+    PRCList list;
+    back_txn txn;
+} dblayer_txn_stack;
+
+static void
+dblayer_cleanup_txn_stack(void *arg)
+{
+    dblayer_txn_stack *txn_stack =  (dblayer_txn_stack *)arg;
+    while (txn_stack && !PR_CLIST_IS_EMPTY(&txn_stack->list)) {
+        dblayer_txn_stack *elem = (dblayer_txn_stack *)PR_LIST_HEAD(&txn_stack->list);
+        PR_REMOVE_LINK(&elem->list);
+        slapi_ch_free((void **)&elem);
+    }
+    if (txn_stack) {
+        slapi_ch_free((void **)&txn_stack);
+    }
+    PR_SetThreadPrivate(thread_private_txn_stack, NULL);
+    return;
+}
+
+static void
+dblayer_init_pvt_txn()
+{
+    PR_NewThreadPrivateIndex(&thread_private_txn_stack, dblayer_cleanup_txn_stack);
+}
+
+static void
+dblayer_push_pvt_txn(back_txn *txn)
+{
+    dblayer_txn_stack *new_elem = NULL;
+    dblayer_txn_stack *txn_stack = PR_GetThreadPrivate(thread_private_txn_stack);
+    if (!txn_stack) {
+        txn_stack = (dblayer_txn_stack *)slapi_ch_calloc(1, sizeof(dblayer_txn_stack));
+        PR_INIT_CLIST(&txn_stack->list);
+        PR_SetThreadPrivate(thread_private_txn_stack, txn_stack);
+    }
+    new_elem = (dblayer_txn_stack *)slapi_ch_calloc(1, sizeof(dblayer_txn_stack));
+    new_elem->txn = *txn; /* copy contents */
+    PR_APPEND_LINK(&new_elem->list, &txn_stack->list);
+}
+
+static back_txn *
+dblayer_get_pvt_txn()
+{
+    back_txn *txn = NULL;
+    dblayer_txn_stack *txn_stack = PR_GetThreadPrivate(thread_private_txn_stack);
+    if (txn_stack && !PR_CLIST_IS_EMPTY(&txn_stack->list)) {
+        txn = &((dblayer_txn_stack *)PR_LIST_TAIL(&txn_stack->list))->txn;
+    }
+    return txn;
+}
+
+static void
+dblayer_pop_pvt_txn()
+{
+    dblayer_txn_stack *elem = NULL;
+    dblayer_txn_stack *txn_stack = PR_GetThreadPrivate(thread_private_txn_stack);
+    if (txn_stack && !PR_CLIST_IS_EMPTY(&txn_stack->list)) {
+        elem = (dblayer_txn_stack *)PR_LIST_TAIL(&txn_stack->list);
+        PR_REMOVE_LINK(&elem->list);
+        slapi_ch_free((void **)&elem);
+    }
+    return;
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c
index 8cecada..e38456d 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_add.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c
@@ -141,9 +141,14 @@ ldbm_back_add( Slapi_PBlock *pb )
 	slapi_entry_delete_values( e, numsubordinates, NULL );
 
 	dblayer_txn_init(li,&txn);
-	/* the calls to get_copy_of_entry require the parent txn if any
+	/* the calls to perform searches require the parent txn if any
 	   so set txn to the parent_txn until we begin the child transaction */
-	txn.back_txn_txn = parent_txn;
+	if (parent_txn) {
+		txn.back_txn_txn = parent_txn;
+	} else {
+		parent_txn = txn.back_txn_txn;
+		slapi_pblock_set( pb, SLAPI_TXN, parent_txn );
+	}
 
 	/* The dblock serializes writes to the database,
 	 * which reduces deadlocking in the db code,
@@ -669,8 +674,6 @@ ldbm_back_add( Slapi_PBlock *pb )
 	for (retry_count = 0; retry_count < RETRY_TIMES; retry_count++) {
 		if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) {
 			dblayer_txn_abort(li,&txn);
-			/* txn is no longer valid - reset slapi_txn to the parent */
-			txn.back_txn_txn = NULL;
 			slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
 			/* We're re-trying */
 			LDAPDebug( LDAP_DEBUG_TRACE, "Add Retrying Transaction\n", 0, 0, 0 );
@@ -930,7 +933,6 @@ ldbm_back_add( Slapi_PBlock *pb )
 
 	retval = dblayer_txn_commit(li,&txn);
 	/* after commit - txn is no longer valid - replace SLAPI_TXN with parent */
-	txn.back_txn_txn = NULL;
 	slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
 	if (0 != retval)
 	{
@@ -979,7 +981,6 @@ diskfull_return:
 		if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) {
 			dblayer_txn_abort(li,&txn); /* abort crashes in case disk full */
 			/* txn is no longer valid - reset the txn pointer to the parent */
-			txn.back_txn_txn = NULL;
 			slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
 		}
 		rc= SLAPI_FAIL_GENERAL;
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_bind.c b/ldap/servers/slapd/back-ldbm/ldbm_bind.c
index 49b2cca..24c0b4f 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_bind.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_bind.c
@@ -219,6 +219,11 @@ ldbm_back_bind( Slapi_PBlock *pb )
 	slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method );
 	slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &cred );
 	slapi_pblock_get( pb, SLAPI_TXN, &txn.back_txn_txn );
+
+	if ( !txn.back_txn_txn ) {
+		dblayer_txn_init( li, &txn );
+		slapi_pblock_set( pb, SLAPI_TXN, txn.back_txn_txn );
+	}
 	
 	inst = (ldbm_instance *) be->be_instance_info;
 
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_compare.c b/ldap/servers/slapd/back-ldbm/ldbm_compare.c
index b510a08..e9761ec 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_compare.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_compare.c
@@ -69,6 +69,11 @@ ldbm_back_compare( Slapi_PBlock *pb )
 	slapi_pblock_get( pb, SLAPI_COMPARE_VALUE, &bval );
 	slapi_pblock_get( pb, SLAPI_TXN, &txn.back_txn_txn );
 	
+	if ( !txn.back_txn_txn ) {
+		dblayer_txn_init( li, &txn );
+		slapi_pblock_set( pb, SLAPI_TXN, txn.back_txn_txn );
+	}
+
 	inst = (ldbm_instance *) be->be_instance_info;
 	/* get the namespace dn */
 	namespace_dn = (Slapi_DN*)slapi_be_getsuffix(be, 0);
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
index 2625506..375b5f3 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
@@ -112,7 +112,12 @@ ldbm_back_delete( Slapi_PBlock *pb )
 	dblayer_txn_init(li,&txn);
 	/* the calls to perform searches require the parent txn if any
 	   so set txn to the parent_txn until we begin the child transaction */
-	txn.back_txn_txn = parent_txn;
+	if (parent_txn) {
+		txn.back_txn_txn = parent_txn;
+	} else {
+		parent_txn = txn.back_txn_txn;
+		slapi_pblock_set( pb, SLAPI_TXN, parent_txn );
+	}
 
 	if (pb->pb_conn)
 	{
@@ -462,6 +467,7 @@ ldbm_back_delete( Slapi_PBlock *pb )
 	for (retry_count = 0; retry_count < RETRY_TIMES; retry_count++) {
 		if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) {
 			dblayer_txn_abort(li,&txn);
+			slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
 			/* We're re-trying */
 			LDAPDebug( LDAP_DEBUG_TRACE, "Delete Retrying Transaction\n", 0, 0, 0 );
 #ifndef LDBM_NO_BACKOFF_DELAY
@@ -924,7 +930,6 @@ ldbm_back_delete( Slapi_PBlock *pb )
 
 	retval = dblayer_txn_commit(li,&txn);
 	/* after commit - txn is no longer valid - replace SLAPI_TXN with parent */
-	txn.back_txn_txn = NULL;
 	slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
 	if (0 != retval)
 	{
@@ -990,7 +995,6 @@ error_return:
 	if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) {
 		dblayer_txn_abort(li,&txn); /* abort crashes in case disk full */
 		/* txn is no longer valid - reset the txn pointer to the parent */
-		txn.back_txn_txn = NULL;
 		slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
 	}
 	
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
index 7d16d0c..817fe16 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
@@ -229,6 +229,15 @@ ldbm_back_modify( Slapi_PBlock *pb )
 
 	slapi_pblock_get( pb, SLAPI_OPERATION, &operation );
 	dblayer_txn_init(li,&txn); /* must do this before first goto error_return */
+	/* the calls to perform searches require the parent txn if any
+	   so set txn to the parent_txn until we begin the child transaction */
+	if (parent_txn) {
+		txn.back_txn_txn = parent_txn;
+	} else {
+		parent_txn = txn.back_txn_txn;
+		slapi_pblock_set( pb, SLAPI_TXN, parent_txn );
+	}
+
 	if (NULL == operation)
 	{
 		ldap_result_code = LDAP_OPERATIONS_ERROR;
@@ -244,9 +253,6 @@ ldbm_back_modify( Slapi_PBlock *pb )
 		goto error_return;
 	}
 
-	/* the calls to search for entries require the parent txn if any
-	   so set txn to the parent_txn until we begin the child transaction */
-	txn.back_txn_txn = parent_txn;
 	/* no need to check the dn syntax as this is a replicated op */
 	if(!repl_op){
 		ldap_result_code = slapi_dn_syntax_check(pb, slapi_sdn_get_dn(addr->sdn), 1);
@@ -416,8 +422,6 @@ ldbm_back_modify( Slapi_PBlock *pb )
 
 		if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) {
 			dblayer_txn_abort(li,&txn);
-			/* txn is no longer valid - reset slapi_txn to the parent */
-			txn.back_txn_txn = NULL;
 			slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
 			LDAPDebug( LDAP_DEBUG_TRACE, "Modify Retrying Transaction\n", 0, 0, 0 );
 #ifndef LDBM_NO_BACKOFF_DELAY
@@ -574,7 +578,6 @@ ldbm_back_modify( Slapi_PBlock *pb )
 
 	retval = dblayer_txn_commit(li,&txn);
 	/* after commit - txn is no longer valid - replace SLAPI_TXN with parent */
-	txn.back_txn_txn = NULL;
 	slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
 	if (0 != retval) {
 		if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
@@ -619,7 +622,6 @@ error_return:
 			/* It is safer not to abort when the transaction is not started. */
 			dblayer_txn_abort(li,&txn); /* abort crashes in case disk full */
 			/* txn is no longer valid - reset the txn pointer to the parent */
-			txn.back_txn_txn = NULL;
 			slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
 		}
 	    rc= SLAPI_FAIL_GENERAL;
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
index 2a974ea..360179d 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
@@ -135,9 +135,14 @@ ldbm_back_modrdn( Slapi_PBlock *pb )
 
     /* dblayer_txn_init needs to be called before "goto error_return" */
     dblayer_txn_init(li,&txn);
-    /* the calls to search for entries require the parent txn if any
+    /* the calls to perform searches require the parent txn if any
        so set txn to the parent_txn until we begin the child transaction */
-    txn.back_txn_txn = parent_txn;
+    if (parent_txn) {
+        txn.back_txn_txn = parent_txn;
+    } else {
+        parent_txn = txn.back_txn_txn;
+        slapi_pblock_set( pb, SLAPI_TXN, parent_txn );
+    }
 
     if (pb->pb_conn)
     {
@@ -705,7 +710,6 @@ ldbm_back_modrdn( Slapi_PBlock *pb )
         {
             dblayer_txn_abort(li,&txn);
             /* txn is no longer valid - reset slapi_txn to the parent */
-            txn.back_txn_txn = NULL;
             slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
             /* We're re-trying */
             LDAPDebug( LDAP_DEBUG_TRACE, "Modrdn Retrying Transaction\n", 0, 0, 0 );
@@ -941,7 +945,6 @@ ldbm_back_modrdn( Slapi_PBlock *pb )
 
     retval = dblayer_txn_commit(li,&txn);
     /* after commit - txn is no longer valid - replace SLAPI_TXN with parent */
-    txn.back_txn_txn = NULL;
     slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
     if (0 != retval)
     {
@@ -1076,7 +1079,6 @@ error_return:
         {
             dblayer_txn_abort(li,&txn); /* abort crashes in case disk full */
             /* txn is no longer valid - reset the txn pointer to the parent */
-            txn.back_txn_txn = NULL;
             slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
         }
         retval= SLAPI_FAIL_GENERAL;
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_search.c b/ldap/servers/slapd/back-ldbm/ldbm_search.c
index 46309eb..7d897c7 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_search.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_search.c
@@ -353,6 +353,11 @@ ldbm_back_search( Slapi_PBlock *pb )
     slapi_pblock_get( pb, SLAPI_BACKEND_COUNT, &backend_count );
     slapi_pblock_get( pb, SLAPI_TXN, &txn.back_txn_txn );
 
+    if ( !txn.back_txn_txn ) {
+        dblayer_txn_init( li, &txn );
+		slapi_pblock_set( pb, SLAPI_TXN, txn.back_txn_txn );
+	}
+
     inst = (ldbm_instance *) be->be_instance_info;
 
     if (NULL == basesdn) {
@@ -1350,6 +1355,11 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
     slapi_pblock_get( pb, SLAPI_SEARCH_RESULT_SET, &sr );
     slapi_pblock_get( pb, SLAPI_TXN, &txn.back_txn_txn );
 
+    if ( !txn.back_txn_txn ) {
+        dblayer_txn_init( li, &txn );
+        slapi_pblock_set( pb, SLAPI_TXN, txn.back_txn_txn );
+    }
+
     if (NULL == sr) {
         goto bail;
     }
-- 
1.7.1

