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(-)
New commits: commit a0488ff111761f85049d67363aafb0c37f9ae2d0 Author: Rich Megginson rmeggins@redhat.com Date: Fri Feb 17 15:13:39 2012 -0700
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: nhosoi (Thanks!) 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
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 5dbd89a..d7765bb 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_add.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c @@ -142,9 +142,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, @@ -674,8 +679,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);
backentry_free(&addingentry); @@ -944,7 +947,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) { @@ -993,7 +995,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 77165fc..82acee6 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c @@ -113,7 +113,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) { @@ -468,6 +473,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);
backentry_free(&e); slapi_pblock_set( pb, SLAPI_DELETE_EXISTING_ENTRY, original_entry->ep_entry ); @@ -938,7 +944,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) { @@ -1004,7 +1009,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 b74e718..082802f 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c @@ -230,6 +230,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; @@ -245,9 +254,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); @@ -428,8 +434,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); /* * Since be_txn_preop functions could have modified the entry/mods, @@ -605,7 +609,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; @@ -650,7 +653,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 796b421..39e965f 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c @@ -144,9 +144,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) { @@ -738,7 +743,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);
slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &newrdn); @@ -1016,7 +1020,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) { @@ -1151,7 +1154,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; }
389-commits@lists.fedoraproject.org