add.c bind.c compare.c delete.c modify.c modrdn.c search.c \
extended.c passwd.c referral.c attribute.c group.c \
attr.c index.c key.c dbcache.c filterindex.c \
- dn2entry.c dn2id.c error.c id2entry.c idl.c nextid.c
+ dn2entry.c dn2id.c error.c id2entry.c idl.c nextid.c cache.c
OBJS = init.lo tools.lo config.lo \
add.lo bind.lo compare.lo delete.lo modify.lo modrdn.lo search.lo \
extended.lo passwd.lo referral.lo attribute.lo group.lo \
attr.lo index.lo key.lo dbcache.lo filterindex.lo \
- dn2entry.lo dn2id.lo error.lo id2entry.lo idl.lo nextid.lo
+ dn2entry.lo dn2id.lo error.lo id2entry.lo idl.lo nextid.lo cache.lo
LDAP_INCDIR= ../../../include
LDAP_LIBDIR= ../../../libraries
}
if( 0 ) {
- /* transaction retry */
-retry: rc = txn_abort( ltid );
+retry: /* transaction retry */
+ rc = txn_abort( ltid );
ltid = NULL;
op->o_private = NULL;
if( rc != 0 ) {
#endif
/* get parent */
- rc = bdb_dn2entry( be, ltid, &pdn, &p, &matched, 0 );
+ rc = bdb_dn2entry_r( be, ltid, &pdn, &p, &matched, 0 );
switch( rc ) {
case 0:
refs = is_entry_referral( matched )
? get_entry_referrals( be, conn, op, matched )
: NULL;
- bdb_entry_return( be, matched );
+ bdb_cache_return_entry_r(&bdb->bi_cache, matched);
matched = NULL;
} else {
switch( opinfo.boi_err ) {
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
- /* free parent and writer lock */
- bdb_entry_return( be, p );
+ /* free parent and reader lock */
+ bdb_cache_return_entry_r( &bdb->bi_cache, p );
p = NULL;
goto retry;
}
if ( is_entry_referral( p ) ) {
/* parent is a referral, don't allow add */
- char *matched_dn = ch_strdup( p->e_dn );
- BerVarray refs = is_entry_referral( p )
- ? get_entry_referrals( be, conn, op, p )
- : NULL;
+ char *matched_dn = p->e_dn;
+ BerVarray refs = get_entry_referrals( be, conn, op, p );
Debug( LDAP_DEBUG_TRACE, "bdb_add: parent is referral\n",
0, 0, 0 );
matched_dn, NULL, refs, NULL );
ber_bvarray_free( refs );
- free( matched_dn );
+ bdb_cache_return_entry_r( be, p );
+ p = NULL;
goto done;
}
/* parent must be an administrative point of the required kind */
}
- /* free parent and writer lock */
- bdb_entry_return( be, p );
+ /* free parent and reader lock */
+ bdb_cache_return_entry_r( be, p );
p = NULL;
} else {
text = "commit failed";
} else {
+ /* add the entry to the entry cache */
+ /* we should add to cache only upon free of txn-abort */
+ if (bdb_cache_add_entry_rw(&bdb->bi_cache, e, CACHE_WRITE_LOCK) != 0) {
+ text = "cache add failed";
+ goto return_results;
+ }
+
Debug( LDAP_DEBUG_TRACE,
"bdb_add: added id=%08lx dn=\"%s\"\n",
e->e_id, e->e_dn, 0 );
text = NULL;
}
+ bdb_cache_entry_commit (e);
+
return_results:
send_ldap_result( conn, op, rc,
NULL, text, NULL, NULL );
}
done:
- if (p != NULL) {
- /* free parent and writer lock */
- bdb_entry_return( be, p );
- }
if( ltid != NULL ) {
txn_abort( ltid );
AttributeDescription *entry_at,
BerVarray *vals )
{
- struct bdbinfo *li = (struct bdbinfo *) be->be_private;
+ struct bdb_info *bdb = (struct bdb_info *) be->be_private;
struct bdb_op_info *boi = (struct bdb_op_info *) op->o_private;
DB_TXN *txn = NULL;
Entry *e;
} else {
/* can we find entry */
- rc = bdb_dn2entry( be, txn, entry_ndn, &e, NULL, 0 );
+ rc = bdb_dn2entry_r( be, NULL, entry_ndn, &e, NULL, 0 );
switch( rc ) {
case DB_NOTFOUND:
case 0:
return_results:
if( target != e ) {
/* free entry */
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_r(&bdb->bi_cache, e);
}
#ifdef NEW_LOGGING
#endif
#endif
+#define DEFAULT_CACHE_SIZE 1000
+
+/* for the in-core cache of entries */
+typedef struct bdb_cache {
+ int c_maxsize;
+ int c_cursize;
+ Avlnode *c_dntree;
+ Avlnode *c_idtree;
+ Entry *c_lruhead; /* lru - add accessed entries here */
+ Entry *c_lrutail; /* lru - rem lru entries from here */
+ ldap_pvt_thread_mutex_t c_mutex;
+} Cache;
+
+#define CACHE_READ_LOCK 0
+#define CACHE_WRITE_LOCK 1
+
#define BDB_INDICES 128
struct bdb_db_info {
int bi_db_opflags; /* db-specific flags */
slap_mask_t bi_defaultmask;
+ Cache bi_cache;
Avlnode *bi_attrs;
#ifdef BDB_HIER
Avlnode *bi_tree;
Debug( LDAP_DEBUG_ARGS, "==> bdb_bind: dn: %s\n", dn->bv_val, 0, 0);
/* get entry */
- rc = bdb_dn2entry( be, NULL, ndn, &e, &matched, 0 );
+ rc = bdb_dn2entry_r( be, NULL, ndn, &e, &matched, 0 );
switch(rc) {
case DB_NOTFOUND:
? get_entry_referrals( be, conn, op, matched )
: NULL;
- bdb_entry_return( be, matched );
+ bdb_cache_return_entry_r( &bdb->bi_cache, matched );
matched = NULL;
} else {
done:
/* free entry and reader lock */
if( e != NULL ) {
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_r( &bdb->bi_cache, e );
}
/* front end with send result on success (rc==0) */
--- /dev/null
+/* cache.c - routines to maintain an in-core cache of entries */
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+#include "back-bdb.h"
+
+/* BDB backend specific entry info -- visible only to the cache */
+typedef struct bdb_entry_info {
+ ldap_pvt_thread_rdwr_t bei_rdwr; /* reader/writer lock */
+
+ /*
+ * remaining fields require backend cache lock to access
+ * These items are specific to the LDBM backend and should
+ * be hidden.
+ */
+ int bei_state; /* for the cache */
+#define CACHE_ENTRY_UNDEFINED 0
+#define CACHE_ENTRY_CREATING 1
+#define CACHE_ENTRY_READY 2
+#define CACHE_ENTRY_DELETED 3
+#define CACHE_ENTRY_COMMITTED 4
+
+ int bei_refcnt; /* # threads ref'ing this entry */
+ Entry *bei_lrunext; /* for cache lru list */
+ Entry *bei_lruprev;
+} EntryInfo;
+#undef BEI
+#define BEI(e) ((EntryInfo *) ((e)->e_private))
+
+static int bdb_cache_delete_entry_internal(Cache *cache, Entry *e);
+#ifdef LDAP_DEBUG
+static void lru_print(Cache *cache);
+#endif
+
+static int
+bdb_cache_entry_rdwr_lock(Entry *e, int rw)
+{
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_ENTRY,
+ "bdb_cache_entry_rdwr_lock: %s lock on ID %ld\n",
+ rw ? "w" : "r", e->e_id ));
+#else
+ Debug( LDAP_DEBUG_ARGS, "entry_rdwr_%slock: ID: %ld\n",
+ rw ? "w" : "r", e->e_id, 0);
+#endif
+
+ if (rw)
+ return ldap_pvt_thread_rdwr_wlock(&BEI(e)->bei_rdwr);
+ else
+ return ldap_pvt_thread_rdwr_rlock(&BEI(e)->bei_rdwr);
+}
+
+static int
+bdb_cache_entry_rdwr_trylock(Entry *e, int rw)
+{
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_ENTRY,
+ "bdb_cache_entry_rdwr_trylock: try %s lock on ID: %ld.\n",
+ rw ? "w" : "r", e->e_id ));
+#else
+ Debug( LDAP_DEBUG_ARGS, "entry_rdwr_%strylock: ID: %ld\n",
+ rw ? "w" : "r", e->e_id, 0);
+#endif
+
+
+ if (rw)
+ return ldap_pvt_thread_rdwr_wtrylock(&BEI(e)->bei_rdwr);
+ else
+ return ldap_pvt_thread_rdwr_rtrylock(&BEI(e)->bei_rdwr);
+}
+
+static int
+bdb_cache_entry_rdwr_unlock(Entry *e, int rw)
+{
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_ENTRY,
+ "bdb_cache_entry_rdwr_unlock: remove %s lock on ID %ld.\n",
+ rw ? "w" : "r", e->e_id ));
+#else
+ Debug( LDAP_DEBUG_ARGS, "entry_rdwr_%sunlock: ID: %ld\n",
+ rw ? "w" : "r", e->e_id, 0);
+#endif
+
+
+ if (rw)
+ return ldap_pvt_thread_rdwr_wunlock(&BEI(e)->bei_rdwr);
+ else
+ return ldap_pvt_thread_rdwr_runlock(&BEI(e)->bei_rdwr);
+}
+
+static int
+bdb_cache_entry_rdwr_init(Entry *e)
+{
+ return ldap_pvt_thread_rdwr_init( &BEI(e)->bei_rdwr );
+}
+
+static int
+bdb_cache_entry_rdwr_destroy(Entry *e)
+{
+ return ldap_pvt_thread_rdwr_destroy( &BEI(e)->bei_rdwr );
+}
+
+static int
+bdb_cache_entry_private_init( Entry *e )
+{
+ assert( e->e_private == NULL );
+
+ if( e->e_private != NULL ) {
+ /* this should never happen */
+ return 1;
+ }
+
+ e->e_private = ch_calloc(1, sizeof(struct bdb_entry_info));
+
+ if( bdb_cache_entry_rdwr_init( e ) != 0 ) {
+ free( BEI(e) );
+ e->e_private = NULL;
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * marks an entry in CREATING state as committed, so it is really returned
+ * to the cache. Otherwise an entry in CREATING state is removed.
+ * Makes e_private be destroyed at the following cache_return_entry_w,
+ * but lets the entry untouched (owned by someone else)
+ */
+void
+bdb_cache_entry_commit( Entry *e )
+{
+ assert( e );
+ assert( e->e_private );
+ assert( BEI(e)->bei_state == CACHE_ENTRY_CREATING );
+ /* assert( BEI(e)->bei_refcnt == 1 ); */
+
+ BEI(e)->bei_state = CACHE_ENTRY_COMMITTED;
+}
+
+static int
+bdb_cache_entry_private_destroy( Entry *e )
+{
+ assert( e->e_private );
+
+ bdb_cache_entry_rdwr_destroy( e );
+
+ free( e->e_private );
+ e->e_private = NULL;
+ return 0;
+}
+
+void
+bdb_cache_return_entry_rw( Cache *cache, Entry *e, int rw )
+{
+ ID id;
+ int refcnt, freeit = 1;
+
+ /* set cache mutex */
+ ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+ assert( e->e_private );
+
+ bdb_cache_entry_rdwr_unlock(e, rw);
+
+ id = e->e_id;
+ refcnt = --BEI(e)->bei_refcnt;
+
+ /*
+ * if the entry is returned when in CREATING state, it is deleted
+ * but not freed because it may belong to someone else (do_add,
+ * for instance)
+ */
+ if ( BEI(e)->bei_state == CACHE_ENTRY_CREATING ) {
+ bdb_cache_delete_entry_internal( cache, e );
+ freeit = 0;
+ /* now the entry is in DELETED state */
+ }
+
+ if ( BEI(e)->bei_state == CACHE_ENTRY_COMMITTED ) {
+ BEI(e)->bei_state = CACHE_ENTRY_READY;
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
+ "bdb_cache_return_entry_rw: return (%ld):%s, refcnt=%d\n",
+ id, rw ? "w" : "r", refcnt ));
+#else
+ Debug( LDAP_DEBUG_TRACE,
+ "====> bdb_cache_return_entry_%s( %ld ): created (%d)\n",
+ rw ? "w" : "r", id, refcnt );
+#endif
+
+
+ } else if ( BEI(e)->bei_state == CACHE_ENTRY_DELETED ) {
+ if( refcnt > 0 ) {
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
+ "bdb_cache_return_entry_rw: %ld, delete pending (%d).\n",
+ id, refcnt ));
+#else
+ Debug( LDAP_DEBUG_TRACE,
+ "====> bdb_cache_return_entry_%s( %ld ): delete pending (%d)\n",
+ rw ? "w" : "r", id, refcnt );
+#endif
+
+
+ } else {
+ bdb_cache_entry_private_destroy( e );
+ if ( freeit ) {
+ bdb_entry_return( e );
+ }
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
+ "bdb_cache_return_entry_rw: (%ld): deleted (%d)\n",
+ id, refcnt ));
+#else
+ Debug( LDAP_DEBUG_TRACE,
+ "====> bdb_cache_return_entry_%s( %ld ): deleted (%d)\n",
+ rw ? "w" : "r", id, refcnt );
+#endif
+
+ }
+
+ } else {
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
+ "bdb_cache_return_entry_rw: ID %ld:%s returned (%d)\n",
+ id, rw ? "w": "r", refcnt ));
+#else
+ Debug( LDAP_DEBUG_TRACE,
+ "====> bdb_cache_return_entry_%s( %ld ): returned (%d)\n",
+ rw ? "w" : "r", id, refcnt);
+#endif
+
+ }
+}
+
+#define LRU_DELETE( cache, e ) do { \
+ if ( BEI(e)->bei_lruprev != NULL ) { \
+ BEI(BEI(e)->bei_lruprev)->bei_lrunext = BEI(e)->bei_lrunext; \
+ } else { \
+ (cache)->c_lruhead = BEI(e)->bei_lrunext; \
+ } \
+ if ( BEI(e)->bei_lrunext != NULL ) { \
+ BEI(BEI(e)->bei_lrunext)->bei_lruprev = BEI(e)->bei_lruprev; \
+ } else { \
+ (cache)->c_lrutail = BEI(e)->bei_lruprev; \
+ } \
+} while(0)
+
+#define LRU_ADD( cache, e ) do { \
+ BEI(e)->bei_lrunext = (cache)->c_lruhead; \
+ if ( BEI(e)->bei_lrunext != NULL ) { \
+ BEI(BEI(e)->bei_lrunext)->bei_lruprev = (e); \
+ } \
+ (cache)->c_lruhead = (e); \
+ BEI(e)->bei_lruprev = NULL; \
+ if ( (cache)->c_lrutail == NULL ) { \
+ (cache)->c_lrutail = (e); \
+ } \
+} while(0)
+
+/*
+ * cache_add_entry_rw - create and lock an entry in the cache
+ * returns: 0 entry has been created and locked
+ * 1 entry already existed
+ * -1 something bad happened
+ */
+int
+bdb_cache_add_entry_rw(
+ Cache *cache,
+ Entry *e,
+ int rw
+)
+{
+ int i, rc;
+ Entry *ee;
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_ENTRY,
+ "bdb_cache_add_entry_rw: add (%s):%s to cache\n",
+ e->e_dn, rw ? "w" : "r" ));
+#endif
+ /* set cache mutex */
+ ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+ assert( e->e_private == NULL );
+
+ if( bdb_cache_entry_private_init(e) != 0 ) {
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_ERR,
+ "bdb_cache_add_entry_rw: add (%s):%ld private init failed!\n",
+ e->e_dn, e->e_id ));
+#else
+ Debug( LDAP_DEBUG_ANY,
+ "====> bdb_cache_add_entry( %ld ): \"%s\": private init failed!\n",
+ e->e_id, e->e_dn, 0 );
+#endif
+
+
+ return( -1 );
+ }
+
+ if ( avl_insert( &cache->c_dntree, (caddr_t) e,
+ (AVL_CMP) entry_dn_cmp, avl_dup_error ) != 0 )
+ {
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
+ "bdb_cache_add_entry: (%s):%ld already in cache.\n",
+ e->e_dn, e->e_id ));
+#else
+ Debug( LDAP_DEBUG_TRACE,
+ "====> bdb_cache_add_entry( %ld ): \"%s\": already in dn cache\n",
+ e->e_id, e->e_dn, 0 );
+#endif
+
+
+ bdb_cache_entry_private_destroy(e);
+
+ return( 1 );
+ }
+
+ /* id tree */
+ if ( avl_insert( &cache->c_idtree, (caddr_t) e,
+ (AVL_CMP) entry_id_cmp, avl_dup_error ) != 0 )
+ {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
+ "bdb_cache_add_entry: (%s):%ls already in cache.\n",
+ e->e_dn, e->e_id ));
+#else
+ Debug( LDAP_DEBUG_ANY,
+ "====> bdb_cache_add_entry( %ld ): \"%s\": already in id cache\n",
+ e->e_id, e->e_dn, 0 );
+#endif
+
+
+
+ /* delete from dn tree inserted above */
+ if ( avl_delete( &cache->c_dntree, (caddr_t) e,
+ (AVL_CMP) entry_dn_cmp ) == NULL )
+ {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_INFO,
+ "bdb_cache_add_entry: can't delete (%s) from cache.\n",
+ e->e_dn ));
+#else
+ Debug( LDAP_DEBUG_ANY, "====> can't delete from dn cache\n",
+ 0, 0, 0 );
+#endif
+
+ }
+
+ bdb_cache_entry_private_destroy(e);
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+ return( -1 );
+ }
+
+ bdb_cache_entry_rdwr_lock( e, rw );
+
+ /* put the entry into 'CREATING' state */
+ /* will be marked after when entry is returned */
+ BEI(e)->bei_state = CACHE_ENTRY_CREATING;
+ BEI(e)->bei_refcnt = 1;
+
+ /* lru */
+ LRU_ADD( cache, e );
+ if ( ++cache->c_cursize > cache->c_maxsize ) {
+ /*
+ * find the lru entry not currently in use and delete it.
+ * in case a lot of entries are in use, only look at the
+ * first 10 on the tail of the list.
+ */
+ i = 0;
+ while ( cache->c_lrutail != NULL &&
+ BEI(cache->c_lrutail)->bei_refcnt != 0 &&
+ i < 10 )
+ {
+ /* move this in-use entry to the front of the q */
+ ee = cache->c_lrutail;
+ LRU_DELETE( cache, ee );
+ LRU_ADD( cache, ee );
+ i++;
+ }
+
+ /*
+ * found at least one to delete - try to get back under
+ * the max cache size.
+ */
+ while ( cache->c_lrutail != NULL &&
+ BEI(cache->c_lrutail)->bei_refcnt == 0 &&
+ cache->c_cursize > cache->c_maxsize )
+ {
+ e = cache->c_lrutail;
+
+ /* delete from cache and lru q */
+ /* XXX do we need rc ? */
+ rc = bdb_cache_delete_entry_internal( cache, e );
+ bdb_cache_entry_private_destroy( e );
+ bdb_entry_return( e );
+ }
+ }
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+ return( 0 );
+}
+
+/*
+ * cache_update_entry - update a LOCKED entry which has been deleted.
+ * returns: 0 entry has been created and locked
+ * 1 entry already existed
+ * -1 something bad happened
+ */
+int
+bdb_cache_update_entry(
+ Cache *cache,
+ Entry *e
+)
+{
+ int i, rc;
+ Entry *ee;
+
+ /* set cache mutex */
+ ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+ assert( e->e_private );
+
+ if ( avl_insert( &cache->c_dntree, (caddr_t) e,
+ (AVL_CMP) entry_dn_cmp, avl_dup_error ) != 0 )
+ {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
+ "bdb_cache_update_entry: (%s):%ld already in dn cache\n",
+ e->e_dn, e->e_id ));
+#else
+ Debug( LDAP_DEBUG_TRACE,
+ "====> bdb_cache_update_entry( %ld ): \"%s\": already in dn cache\n",
+ e->e_id, e->e_dn, 0 );
+#endif
+
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+ return( 1 );
+ }
+
+ /* id tree */
+ if ( avl_insert( &cache->c_idtree, (caddr_t) e,
+ (AVL_CMP) entry_id_cmp, avl_dup_error ) != 0 )
+ {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
+ "bdb_cache_update_entry: (%s)%ld already in id cache\n",
+ e->e_dn, e->e_id ));
+#else
+ Debug( LDAP_DEBUG_ANY,
+ "====> bdb_cache_update_entry( %ld ): \"%s\": already in id cache\n",
+ e->e_id, e->e_dn, 0 );
+#endif
+
+
+ /* delete from dn tree inserted above */
+ if ( avl_delete( &cache->c_dntree, (caddr_t) e,
+ (AVL_CMP) entry_dn_cmp ) == NULL )
+ {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_INFO,
+ "bdb_cache_update_entry: can't delete (%s)%ld from dn cache.\n",
+ e->e_dn, e->e_id ));
+#else
+ Debug( LDAP_DEBUG_ANY, "====> can't delete from dn cache\n",
+ 0, 0, 0 );
+#endif
+
+ }
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+ return( -1 );
+ }
+
+
+ /* put the entry into 'CREATING' state */
+ /* will be marked after when entry is returned */
+ BEI(e)->bei_state = CACHE_ENTRY_CREATING;
+
+ /* lru */
+ LRU_ADD( cache, e );
+ if ( ++cache->c_cursize > cache->c_maxsize ) {
+ /*
+ * find the lru entry not currently in use and delete it.
+ * in case a lot of entries are in use, only look at the
+ * first 10 on the tail of the list.
+ */
+ i = 0;
+ while ( cache->c_lrutail != NULL &&
+ BEI(cache->c_lrutail)->bei_refcnt != 0 &&
+ i < 10 )
+ {
+ /* move this in-use entry to the front of the q */
+ ee = cache->c_lrutail;
+ LRU_DELETE( cache, ee );
+ LRU_ADD( cache, ee );
+ i++;
+ }
+
+ /*
+ * found at least one to delete - try to get back under
+ * the max cache size.
+ */
+ while ( cache->c_lrutail != NULL &&
+ BEI(cache->c_lrutail)->bei_refcnt == 0 &&
+ cache->c_cursize > cache->c_maxsize )
+ {
+ e = cache->c_lrutail;
+
+ /* delete from cache and lru q */
+ /* XXX do we need rc ? */
+ rc = bdb_cache_delete_entry_internal( cache, e );
+ bdb_cache_entry_private_destroy( e );
+ bdb_entry_return( e );
+ }
+ }
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+ return( 0 );
+}
+
+ID
+bdb_cache_find_entry_ndn2id(
+ Backend *be,
+ Cache *cache,
+ struct berval *ndn
+)
+{
+ Entry e, *ep;
+ ID id;
+ int count = 0;
+
+ /* this function is always called with normalized DN */
+ e.e_nname = *ndn;
+
+try_again:
+ /* set cache mutex */
+ ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+ if ( (ep = (Entry *) avl_find( cache->c_dntree, (caddr_t) &e,
+ (AVL_CMP) entry_dn_cmp )) != NULL )
+ {
+ int state;
+ count++;
+
+ /*
+ * ep now points to an unlocked entry
+ * we do not need to lock the entry if we only
+ * check the state, refcnt, LRU, and id.
+ */
+
+ assert( ep->e_private );
+
+ /* save id */
+ id = ep->e_id;
+ state = BEI(ep)->bei_state;
+
+ /*
+ * entry is deleted or not fully created yet
+ */
+ if ( state != CACHE_ENTRY_READY ) {
+ assert(state != CACHE_ENTRY_UNDEFINED);
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_INFO,
+ "bdb_cache_find_entry_dn2id: (%s) %ld not ready: %d\n",
+ ndn->bv_val, id, state ));
+#else
+ Debug(LDAP_DEBUG_TRACE,
+ "====> bdb_cache_find_entry_dn2id(\"%s\"): %ld (not ready) %d\n",
+ ndn->bv_val, id, state);
+#endif
+
+
+ ldap_pvt_thread_yield();
+ goto try_again;
+ }
+
+ /* lru */
+ LRU_DELETE( cache, ep );
+ LRU_ADD( cache, ep );
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
+ "bdb_cache_find_entry_dn2id: (%s): %ld %d tries\n",
+ ndn->bv_val, id, count ));
+#else
+ Debug(LDAP_DEBUG_TRACE,
+ "====> bdb_cache_find_entry_dn2id(\"%s\"): %ld (%d tries)\n",
+ ndn->bv_val, id, count);
+#endif
+
+
+ } else {
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+ id = NOID;
+ }
+
+ return( id );
+}
+
+/*
+ * cache_find_entry_id - find an entry in the cache, given id
+ */
+
+Entry *
+bdb_cache_find_entry_id(
+ Cache *cache,
+ ID id,
+ int rw
+)
+{
+ Entry e;
+ Entry *ep;
+ int count = 0;
+
+ e.e_id = id;
+
+try_again:
+ /* set cache mutex */
+ ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+ if ( (ep = (Entry *) avl_find( cache->c_idtree, (caddr_t) &e,
+ (AVL_CMP) entry_id_cmp )) != NULL )
+ {
+ int state;
+ ID ep_id;
+
+ count++;
+
+ assert( ep->e_private );
+
+ ep_id = ep->e_id;
+ state = BEI(ep)->bei_state;
+
+ /*
+ * entry is deleted or not fully created yet
+ */
+ if ( state != CACHE_ENTRY_READY ) {
+
+ assert(state != CACHE_ENTRY_UNDEFINED);
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_INFO,
+ "bdb_cache_find_entry_id: (%ld)->%ld not ready (%d).\n",
+ id, ep_id, state ));
+
+#else
+ Debug(LDAP_DEBUG_TRACE,
+ "====> bdb_cache_find_entry_id( %ld ): %ld (not ready) %d\n",
+ id, ep_id, state);
+#endif
+
+
+ ldap_pvt_thread_yield();
+ goto try_again;
+ }
+
+ /* acquire reader lock */
+ if ( bdb_cache_entry_rdwr_trylock(ep, rw) == LDAP_PVT_THREAD_EBUSY ) {
+ /* could not acquire entry lock...
+ * owner cannot free as we have the cache locked.
+ * so, unlock the cache, yield, and try again.
+ */
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_INFO,
+ "bdb_cache_find_entry_id: %ld -> %ld (busy) %d.\n",
+ id, ep_id, state ));
+#else
+ Debug(LDAP_DEBUG_TRACE,
+ "====> bdb_cache_find_entry_id( %ld ): %ld (busy) %d\n",
+ id, ep_id, state);
+#endif
+
+
+ ldap_pvt_thread_yield();
+ goto try_again;
+ }
+
+ /* lru */
+ LRU_DELETE( cache, ep );
+ LRU_ADD( cache, ep );
+
+ BEI(ep)->bei_refcnt++;
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
+ "bdb_cache_find_entry_id: %ld -> %s found %d tries.\n",
+ ep_id, ep->e_dn, count ));
+#else
+ Debug(LDAP_DEBUG_TRACE,
+ "====> bdb_cache_find_entry_id( %ld ) \"%s\" (found) (%d tries)\n",
+ ep_id, ep->e_dn, count);
+#endif
+
+
+ return( ep );
+ }
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+ return( NULL );
+}
+
+/*
+ * cache_delete_entry - delete the entry e from the cache. the caller
+ * should have obtained e (increasing its ref count) via a call to one
+ * of the cache_find_* routines. the caller should *not* call the
+ * cache_return_entry() routine prior to calling cache_delete_entry().
+ * it performs this function.
+ *
+ * returns: 0 e was deleted ok
+ * 1 e was not in the cache
+ * -1 something bad happened
+ */
+int
+bdb_cache_delete_entry(
+ Cache *cache,
+ Entry *e
+)
+{
+ int rc;
+
+ /* set cache mutex */
+ ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+ assert( e->e_private );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_ENTRY,
+ "bdb_cache_delete_entry: delete %ld.\n", e->e_id ));
+#else
+ Debug( LDAP_DEBUG_TRACE, "====> bdb_cache_delete_entry( %ld )\n",
+ e->e_id, 0, 0 );
+#endif
+
+
+ rc = bdb_cache_delete_entry_internal( cache, e );
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+ return( rc );
+}
+
+static int
+bdb_cache_delete_entry_internal(
+ Cache *cache,
+ Entry *e
+)
+{
+ int rc = 0; /* return code */
+
+ /* dn tree */
+ if ( avl_delete( &cache->c_dntree, (caddr_t) e, (AVL_CMP) entry_dn_cmp )
+ == NULL )
+ {
+ rc = -1;
+ }
+
+ /* id tree */
+ if ( avl_delete( &cache->c_idtree, (caddr_t) e, (AVL_CMP) entry_id_cmp )
+ == NULL )
+ {
+ rc = -1;
+ }
+
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* lru */
+ LRU_DELETE( cache, e );
+ cache->c_cursize--;
+
+ /*
+ * flag entry to be freed later by a call to cache_return_entry()
+ */
+ BEI(e)->bei_state = CACHE_ENTRY_DELETED;
+
+ return( 0 );
+}
+
+void
+bdb_cache_release_all( Cache *cache )
+{
+ Entry *e;
+ int rc;
+
+ /* set cache mutex */
+ ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_ENTRY,
+ "bdb_cache_release_all: enter\n" ));
+#else
+ Debug( LDAP_DEBUG_TRACE, "====> bdb_cache_release_all\n", 0, 0, 0 );
+#endif
+
+
+ while ( (e = cache->c_lrutail) != NULL && BEI(e)->bei_refcnt == 0 ) {
+#ifdef LDAP_RDWR_DEBUG
+ assert(!ldap_pvt_thread_rdwr_active(&BEI(e)->bei_rdwr));
+#endif
+
+ /* delete from cache and lru q */
+ /* XXX do we need rc ? */
+ rc = bdb_cache_delete_entry_internal( cache, e );
+ bdb_cache_entry_private_destroy( e );
+ bdb_entry_return( e );
+ }
+
+ if ( cache->c_cursize ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "cache", LDAP_LEVEL_INFO,
+ "bdb_cache_release_all: Entry cache could not be emptied.\n" ));
+#else
+ Debug( LDAP_DEBUG_TRACE, "Entry-cache could not be emptied\n", 0, 0, 0 );
+#endif
+
+ }
+
+ /* free cache mutex */
+ ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+}
+
+#ifdef LDAP_DEBUG
+
+static void
+bdb_lru_print( Cache *cache )
+{
+ Entry *e;
+
+ fprintf( stderr, "LRU queue (head to tail):\n" );
+ for ( e = cache->c_lruhead; e != NULL; e = BEI(e)->bei_lrunext ) {
+ fprintf( stderr, "\tdn \"%20s\" id %ld refcnt %d\n",
+ e->e_dn, e->e_id, BEI(e)->bei_refcnt );
+ }
+ fprintf( stderr, "LRU queue (tail to head):\n" );
+ for ( e = cache->c_lrutail; e != NULL; e = BEI(e)->bei_lruprev ) {
+ fprintf( stderr, "\tdn \"%20s\" id %ld refcnt %d\n",
+ e->e_dn, e->e_id, BEI(e)->bei_refcnt );
+ }
+}
+
+#endif
int manageDSAit = get_manageDSAit( op );
/* get entry */
- rc = bdb_dn2entry( be, NULL, ndn, &e, &matched, 0 );
+ rc = bdb_dn2entry_r( be, NULL, ndn, &e, &matched, 0 );
switch( rc ) {
case DB_NOTFOUND:
refs = is_entry_referral( matched )
? get_entry_referrals( be, conn, op, matched )
: NULL;
- bdb_entry_return( be, matched );
+ bdb_cache_return_entry_r( &bdb->bi_cache, matched );
matched = NULL;
} else {
done:
/* free entry */
if( e != NULL ) {
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_r( &bdb->bi_cache, e );
}
return rc;
if( rc != LDAP_SUCCESS ) return 1;
#endif
+ /* size of the cache in entries */
+ } else if ( strcasecmp( argv[0], "cachesize" ) == 0 ) {
+ if ( argc < 2 ) {
+ fprintf( stderr,
+ "%s: line %d: missing size in \"cachesize <size>\" line\n",
+ fname, lineno );
+ return( 1 );
+ }
+ bdb->bi_cache.c_maxsize = atoi( argv[1] );
+
/* anything else */
} else {
fprintf( stderr, "%s: line %d: "
if( 0 ) {
retry: /* transaction retry */
+ if( e != NULL ) {
+ bdb_cache_return_entry_w(&bdb->bi_cache, e);
+ }
Debug( LDAP_DEBUG_TRACE, "==> bdb_delete: retrying...\n",
0, 0, 0 );
rc = txn_abort( ltid );
}
#endif
/* get parent */
- rc = bdb_dn2entry( be, ltid, &pdn, &p, NULL, 0 );
+ rc = bdb_dn2entry_r( be, ltid, &pdn, &p, NULL, 0 );
switch( rc ) {
case 0:
rc = access_allowed( be, conn, op, p,
children, NULL, ACL_WRITE );
- bdb_entry_return( be, p );
+ bdb_cache_return_entry_r(&bdb->bi_cache, p);
p = NULL;
switch( opinfo.boi_err ) {
}
/* get entry for read/modify/write */
- rc = bdb_dn2entry( be, ltid, ndn, &e, &matched, DB_RMW );
+ rc = bdb_dn2entry_w( be, ltid, ndn, &e, &matched, DB_RMW );
switch( rc ) {
case 0:
refs = is_entry_referral( matched )
? get_entry_referrals( be, conn, op, matched )
: NULL;
- bdb_entry_return( be, matched );
+ bdb_cache_return_entry_r(&bdb->bi_cache, matched );
matched = NULL;
} else {
}
/* delete from id2entry */
- rc = bdb_id2entry_delete( be, ltid, e->e_id );
+ rc = bdb_id2entry_delete( be, ltid, e );
if ( rc != 0 ) {
switch( rc ) {
case DB_LOCK_DEADLOCK:
done:
/* free entry */
if( e != NULL ) {
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_w(&bdb->bi_cache, e);
}
if( ltid != NULL ) {
*/
int
-bdb_dn2entry(
+bdb_dn2entry_rw(
BackendDB *be,
DB_TXN *tid,
struct berval *dn,
Entry **e,
Entry **matched,
- int flags )
+ int flags,
+ int rw )
{
int rc;
ID id, id2 = 0;
- Debug(LDAP_DEBUG_TRACE, "bdb_dn2entry(\"%s\")\n",
+ Debug(LDAP_DEBUG_TRACE, "bdb_dn2entry_rw(\"%s\")\n",
dn->bv_val, 0, 0 );
*e = NULL;
}
if( id2 == 0 ) {
- rc = bdb_id2entry( be, tid, id, e );
+ rc = bdb_id2entry_rw( be, tid, id, e, rw );
} else {
- rc = bdb_id2entry( be, tid, id2, matched );
+ rc = bdb_id2entry_r( be, tid, id2, matched);
}
return rc;
Debug( LDAP_DEBUG_TRACE, "=> bdb_dn2id( \"%s\" )\n", dn->bv_val, 0, 0 );
+ assert (id);
+
+ if ((*id = bdb_cache_find_entry_ndn2id(be,&bdb->bi_cache,dn)) != NOID) {
+ return 0;
+ }
+
DBTzero( &key );
key.size = dn->bv_len + 2;
key.data = ch_malloc( key.size );
struct bdb_info *bdb = (struct bdb_info *) be->be_private;
DB *db = bdb->bi_dn2id->bdi_db;
char *buf, *dn;
+ ID cached_id;
Debug( LDAP_DEBUG_TRACE, "=> bdb_dn2id_matched( \"%s\" )\n", in->bv_val, 0, 0 );
*id = NOID;
- /* fetch it */
- rc = db->get( db, txn, &key, &data, bdb->bi_db_opflags );
+ /* lookup cache */
+ cached_id = bdb_cache_find_entry_ndn2id(be,&bdb->bi_cache,dn);
+
+ if (cached_id != NOID) {
+ rc = 0;
+ *id = cached_id;
+ if ( dn != buf+1 ) {
+ *id2 = *id;
+ }
+ break;
+ } else {
+ /* fetch it */
+ rc = db->get(db, txn, &key, &data, bdb->bi_db_opflags );
+ }
if( rc == DB_NOTFOUND ) {
char *pdn = NULL;
AttributeDescription *group_at
)
{
- struct bdbinfo *li = (struct bdbinfo *) be->be_private;
+ struct bdb_info *bdb = (struct bdb_info *) be->be_private;
struct bdb_op_info *boi = (struct bdb_op_info *) op->o_private;
DB_TXN *txn;
Entry *e;
#endif
} else {
/* can we find group entry */
- rc = bdb_dn2entry( be, txn, gr_ndn, &e, NULL, 0 );
+ rc = bdb_dn2entry_r( be, NULL, gr_ndn, &e, NULL, 0 );
if( rc ) {
if( txn ) {
boi->boi_err = rc;
return_results:
if( target != e ) {
/* free entry */
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_r( &bdb->bi_cache, e );
}
#ifdef NEW_LOGGING
return rc;
}
+/*
+ * This routine adds (or updates) an entry on disk.
+ * The cache should be already be updated.
+ */
+
+
int bdb_id2entry_add(
BackendDB *be,
DB_TXN *tid,
return bdb_id2entry_put(be, tid, e, 0);
}
-int bdb_id2entry(
+int bdb_id2entry_rw(
BackendDB *be,
DB_TXN *tid,
ID id,
- Entry **e )
+ Entry **e,
+ int rw )
{
struct bdb_info *bdb = (struct bdb_info *) be->be_private;
DB *db = bdb->bi_id2entry->bdi_db;
DBT key, data;
struct berval bv;
- int rc;
+ int rc = 0;
*e = NULL;
DBTzero( &data );
data.flags = DB_DBT_MALLOC;
+ if ((*e = bdb_cache_find_entry_id(&bdb->bi_cache, id, rw)) != NULL) {
+ return 0;
+ }
+
/* fetch it */
rc = db->get( db, tid, &key, &data, bdb->bi_db_opflags );
*/
ch_free( data.data );
}
+
+ if (rc == 0 && bdb_cache_add_entry_rw(&bdb->bi_cache, *e, rw) != 0) {
+ if ((*e)->e_private != NULL)
+ free ((*e)->e_private);
+ (*e)->e_private = NULL;
+ bdb_entry_return (*e);
+ if ((*e=bdb_cache_find_entry_id(&bdb->bi_cache,id,rw)) != NULL) {
+ return 0;
+ }
+ }
+
#ifdef BDB_HIER
bdb_fix_dn(be, id, *e);
#endif
+
+ if (rc == 0)
+ bdb_cache_entry_commit(*e);
+
return rc;
}
int bdb_id2entry_delete(
BackendDB *be,
DB_TXN *tid,
- ID id )
+ Entry *e )
{
struct bdb_info *bdb = (struct bdb_info *) be->be_private;
DB *db = bdb->bi_id2entry->bdi_db;
DBT key;
int rc;
+ bdb_cache_delete_entry(&bdb->bi_cache, e);
+
DBTzero( &key );
- key.data = (char *) &id;
+ key.data = (char *) &e->e_id;
key.size = sizeof(ID);
+ /* delete from database */
rc = db->del( db, tid, &key, 0 );
return rc;
}
int bdb_entry_return(
- BackendDB *be,
Entry *e )
{
/* Our entries are allocated in two blocks; the data comes from
* the db itself and the Entry structure and associated pointers
* are allocated in entry_decode. The db data pointer is saved
- * in e_private. Since the Entry structure is allocated as a single
+ * in e_bv. Since the Entry structure is allocated as a single
* block, e_attrs is always a fixed offset from e. The exception
* is when an entry has been modified, in which case we also need
* to free e_attrs.
*/
+ if( !e->e_bv.bv_val ) { /* A regular entry, from do_add */
+ entry_free( e );
+ return 0;
+ }
if( (void *) e->e_attrs != (void *) (e+1)) {
attrs_free( e->e_attrs );
}
+
+ /* See if the DNs were changed by modrdn */
+ if( e->e_nname.bv_val < e->e_bv.bv_val || e->e_nname.bv_val >
+ e->e_bv.bv_val + e->e_bv.bv_len ) {
+ ch_free(e->e_name.bv_val);
+ ch_free(e->e_nname.bv_val);
+ e->e_name.bv_val = NULL;
+ e->e_nname.bv_val = NULL;
+ }
#ifdef BDB_HIER
/* We had to construct the dn and ndn as well, in a single block */
- free( e->e_dn );
+ if( e->e_name.bv_val ) {
+ free( e->e_name.bv_val );
+ }
#endif
- /* In tool mode the e_private buffer is realloc'd, leave it alone */
- if( e->e_private && !(slapMode & SLAP_TOOL_MODE) ) {
- free( e->e_private );
+ /* In tool mode the e_bv buffer is realloc'd, leave it alone */
+ if( !(slapMode & SLAP_TOOL_MODE) ) {
+ free( e->e_bv.bv_val );
}
free( e );
Entry *e,
int rw )
{
- int retval = 0;
-
- if (o && o->o_tag == LDAP_REQ_ADD)
- entry_free(e);
- else
- retval = bdb_entry_return( be, e );
-
- return retval;
+ struct bdb_info *bdb = (struct bdb_info *) be->be_private;
+
+ /* slapMode : SLAP_SERVER_MODE, SLAP_TOOL_MODE,
+ SLAP_TRUNCATE_MODE, SLAP_UNDEFINED_MODE */
+
+ if ( slapMode == SLAP_SERVER_MODE ) {
+ /* free entry and reader or writer lock */
+ bdb_cache_return_entry_rw( &bdb->bi_cache, e, rw );
+ } else {
+ if (e->e_private != NULL)
+ free (e->e_private);
+ e->e_private = NULL;
+ bdb_entry_return ( e );
+ }
+
+ return 0;
}
bdb->bi_dbenv_mode = DEFAULT_MODE;
bdb->bi_txn = 1; /* default to using transactions */
+ bdb->bi_cache.c_maxsize = DEFAULT_CACHE_SIZE;
+
#ifndef NO_THREADS
#if 0
bdb->bi_lock_detect = DB_LOCK_NORUN;
ldap_pvt_thread_mutex_init( &bdb->bi_database_mutex );
ldap_pvt_thread_mutex_init( &bdb->bi_lastid_mutex );
+ ldap_pvt_thread_mutex_init( &bdb->bi_cache.c_mutex );
#ifdef BDB_HIER
ldap_pvt_thread_rdwr_init( &bdb->bi_tree_rdwr );
#endif
free( bdb->bi_databases );
bdb_attr_index_destroy( bdb->bi_attrs );
+ bdb_cache_release_all (&bdb->bi_cache);
+
return 0;
}
}
}
+ bdb_cache_release_all (&bdb->bi_cache);
+
rc = bdb->bi_dbenv->close( bdb->bi_dbenv, 0 );
bdb->bi_dbenv = NULL;
if( rc != 0 ) {
if( 0 ) {
retry: /* transaction retry */
+ if( e != NULL ) {
+ bdb_cache_delete_entry(&bdb->bi_cache, e);
+ bdb_cache_return_entry_w(&bdb->bi_cache, e);
+ }
Debug(LDAP_DEBUG_TRACE,
"bdb_modify: retrying...\n", 0, 0, 0);
rc = txn_abort( ltid );
op->o_private = &opinfo;
/* get entry */
- rc = bdb_dn2entry( be, ltid, ndn, &e, &matched, 0 );
+ rc = bdb_dn2entry_w( be, ltid, ndn, &e, &matched, 0 );
if ( rc != 0 ) {
Debug( LDAP_DEBUG_TRACE,
refs = is_entry_referral( matched )
? get_entry_referrals( be, conn, op, matched )
: NULL;
- bdb_entry_return( be, matched );
+ bdb_cache_return_entry_r (&bdb->bi_cache, matched);
matched = NULL;
} else {
}
if( e != NULL ) {
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_w (&bdb->bi_cache, e);
}
return rc;
}
-
LDAPRDN *new_rdn = NULL;
LDAPRDN *old_rdn = NULL;
- Entry *np = NULL; /* newSuperior Entry */
+ Entry *np = NULL; /* newSuperior Entry */
struct berval *np_dn = NULL; /* newSuperior dn */
struct berval *np_ndn = NULL; /* newSuperior ndn */
struct berval *new_parent_dn = NULL; /* np_dn, p_dn, or NULL */
if( 0 ) {
retry: /* transaction retry */
+ if (e != NULL) {
+ bdb_cache_delete_entry(&bdb->bi_cache, e);
+ bdb_cache_return_entry_w(&bdb->bi_cache, e);
+ }
+ if (p != NULL) {
+ bdb_cache_return_entry_r(&bdb->bi_cache, p);
+ }
+ if (np != NULL) {
+ bdb_cache_return_entry_r(&bdb->bi_cache, np);
+ }
Debug( LDAP_DEBUG_TRACE, "==>bdb_modrdn: retrying...\n", 0, 0, 0 );
rc = txn_abort( ltid );
ltid = NULL;
op->o_private = &opinfo;
/* get entry */
- rc = bdb_dn2entry( be, ltid, ndn, &e, &matched, 0 );
+ rc = bdb_dn2entry_w( be, ltid, ndn, &e, &matched, 0 );
switch( rc ) {
case 0:
BerVarray refs;
if( matched != NULL ) {
- matched_dn = strdup( matched->e_dn );
+ matched_dn = ch_strdup( matched->e_dn );
refs = is_entry_referral( matched )
? get_entry_referrals( be, conn, op, matched )
: NULL;
- bdb_entry_return( be, matched );
+ bdb_cache_return_entry_r( &bdb->bi_cache, matched );
matched = NULL;
} else {
/* Make sure parent entry exist and we can write its
* children.
*/
- rc = bdb_dn2entry( be, ltid, &p_ndn, &p, NULL, 0 );
+ rc = bdb_dn2entry_r( be, ltid, &p_ndn, &p, NULL, 0 );
switch( rc ) {
case 0:
/* newSuperior == entry being moved?, if so ==> ERROR */
/* Get Entry with dn=newSuperior. Does newSuperior exist? */
- rc = bdb_dn2entry( be, ltid, nnewSuperior, &np, NULL, 0 );
+ rc = bdb_dn2entry_r( be, ltid, nnewSuperior, &np, NULL, 0 );
switch( rc ) {
case 0:
goto return_results;
}
+ (void) bdb_cache_delete_entry(&bdb->bi_cache, e);
+
/* Binary format uses a single contiguous block, cannot
* free individual fields. Leave new_dn/new_ndn set so
* they can be individually freed later.
e->e_name = new_dn;
e->e_nname = new_ndn;
+ new_dn.bv_val = NULL;
+ new_ndn.bv_val = NULL;
+
/* add new one */
rc = bdb_dn2id_add( be, ltid, np_ndn, e );
if ( rc != 0 ) {
rc = LDAP_OTHER;
text = "commit failed";
} else {
+ (void) bdb_cache_update_entry(&bdb->bi_cache, e);
Debug( LDAP_DEBUG_TRACE,
"bdb_modrdn: added id=%08lx dn=\"%s\"\n",
e->e_id, e->e_dn, 0 );
rc = LDAP_SUCCESS;
text = NULL;
+ bdb_cache_entry_commit( e );
}
return_results:
/* LDAP v3 Support */
if( np != NULL ) {
- /* free new parent and writer lock */
- bdb_entry_return( be, np );
+ /* free new parent and reader lock */
+ bdb_cache_return_entry_r(&bdb->bi_cache, np);
}
if( p != NULL ) {
- /* free parent and writer lock */
- bdb_entry_return( be, p );
+ /* free parent and reader lock */
+ bdb_cache_return_entry_r(&bdb->bi_cache, p);
}
/* free entry */
if( e != NULL ) {
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_w( &bdb->bi_cache, e );
}
if( ltid != NULL ) {
if( 0 ) {
retry: /* transaction retry */
+ if ( e != NULL ) {
+ bdb_cache_delete_entry(&bdb->bi_cache, e);
+ bdb_cache_return_entry_w(&bdb->bi_cache, e);
+ }
Debug( LDAP_DEBUG_TRACE, "bdb_exop_passwd: retrying...\n", 0, 0, 0 );
rc = txn_abort( ltid );
ltid = NULL;
op->o_private = &opinfo;
/* get entry */
- rc = bdb_dn2entry( be, ltid, dn, &e, NULL, 0 );
+ rc = bdb_dn2entry_w( be, ltid, dn, &e, NULL, 0 );
switch(rc) {
case DB_LOCK_DEADLOCK:
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
*text = NULL;
- bdb_entry_return( be, e );
- e = NULL;
goto retry;
case 0:
break;
switch(rc) {
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
- bdb_entry_return( be, e );
- e = NULL;
goto retry;
}
*text = "entry update failed";
done:
if( e != NULL ) {
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_w( &bdb->bi_cache, e );
}
-
+
if( hash.bv_val != NULL ) {
free( hash.bv_val );
}
/*
* dn2entry.c
*/
-int bdb_dn2entry LDAP_P(( BackendDB *be, DB_TXN *tid,
- struct berval *dn, Entry **e, Entry **matched, int flags ));
+int bdb_dn2entry_rw LDAP_P(( BackendDB *be, DB_TXN *tid,
+ struct berval *dn, Entry **e, Entry **matched, int flags, int rw ));
+#define bdb_dn2entry_r(be, tid, dn, e, m, f) bdb_dn2entry_rw((be), (tid), (dn), (e), (m), (f), 0)
+#define bdb_dn2entry_w(be, tid, dn, e, m, f) bdb_dn2entry_rw((be), (tid), (dn), (e), (m), (f), 1)
/*
* dn2id.c
/*
* entry.c
*/
-int bdb_entry_return( BackendDB *be, Entry *e );
+int bdb_entry_return( Entry *e );
BI_entry_release_rw bdb_entry_release;
/*
BI_acl_group bdb_group;
/*
- * id2entry
+ * id2entry.c
*/
int bdb_id2entry_add(
BackendDB *be,
int bdb_id2entry_delete(
BackendDB *be,
DB_TXN *tid,
- ID id );
+ Entry *e);
-int bdb_id2entry(
+int bdb_id2entry_rw(
BackendDB *be,
DB_TXN *tid,
ID id,
- Entry **e );
+ Entry **e,
+ int rw );
+#define bdb_id2entry_r(be, tid, id, e) bdb_id2entry_rw((be), (tid), (id), (e), 0)
+#define bdb_id2entry_w(be, tid, id, e) bdb_id2entry_rw((be), (tid), (id), (e), 1)
+
+void bdb_entry_free ( Entry *e );
/*
* idl.c
*/
BI_op_extended bdb_exop_passwd;
+
+/*
+ * cache.c
+ */
+
+void bdb_cache_entry_commit( Entry *e );
+void bdb_cache_return_entry_rw( Cache *cache, Entry *e, int rw );
+#define bdb_cache_return_entry_r(c, e) bdb_cache_return_entry_rw((c), (e), 0)
+#define bdb_cache_return_entry_w(c, e) bdb_cache_return_entry_rw((c), (e), 1)
+int bdb_cache_add_entry_rw(
+ Cache *cache,
+ Entry *e,
+ int rw
+);
+int bdb_cache_update_entry(
+ Cache *cache,
+ Entry *e
+);
+ID bdb_cache_find_entry_ndn2id(
+ Backend *be,
+ Cache *cache,
+ struct berval *ndn
+);
+Entry* bdb_cache_find_entry_id(
+ Cache *cache,
+ ID id,
+ int rw
+);
+int bdb_cache_delete_entry(
+ Cache *cache,
+ Entry *e
+);
+void bdb_cache_release_all( Cache *cache );
+
LDAP_END_DECL
#endif /* _PROTO_BDB_H */
}
/* get entry */
- rc = bdb_dn2entry( be, NULL, ndn, &e, &matched, 0 );
+ rc = bdb_dn2entry_r( be, NULL, ndn, &e, &matched, 0 );
switch(rc) {
case DB_NOTFOUND:
Debug( LDAP_DEBUG_TRACE,
"bdb_referrals: dn2entry failed: %s (%d)\n",
db_strerror(rc), rc, 0 );
+ if (e != NULL) {
+ bdb_cache_return_entry_r(&bdb->bi_cache, e);
+ }
+ if (matched != NULL) {
+ bdb_cache_return_entry_r(&bdb->bi_cache, matched);
+ }
send_ldap_result( conn, op, rc=LDAP_OTHER,
NULL, "internal error", NULL, NULL );
return rc;
refs = get_entry_referrals( be, conn, op, matched );
}
- bdb_entry_return( be, matched );
+ bdb_cache_return_entry_r (&bdb->bi_cache, matched);
matched = NULL;
} else if ( default_referral != NULL ) {
rc = LDAP_OTHER;
ber_bvarray_free( refs );
}
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_r(&bdb->bi_cache, e);
return rc;
}
} else
#endif
{
- rc = bdb_dn2entry( be, NULL, nbase, &e, &matched, 0 );
+ rc = bdb_dn2entry_r( be, NULL, nbase, &e, &matched, 0 );
}
switch(rc) {
case 0:
break;
default:
+ if (e != NULL) {
+ bdb_cache_return_entry_w(&bdb->bi_cache, e);
+ }
+ if (matched != NULL) {
+ bdb_cache_return_entry_r(&bdb->bi_cache, matched);
+ }
send_ldap_result( conn, op, rc=LDAP_OTHER,
NULL, "internal error", NULL, NULL );
return rc;
? get_entry_referrals( be, conn, op, matched )
: NULL;
- bdb_entry_return( be, matched );
+ bdb_cache_return_entry_r (&bdb->bi_cache, matched);
matched = NULL;
if( erefs ) {
erefs = get_entry_referrals( be, conn, op, e );
refs = NULL;
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_r( &bdb->bi_cache, e );
e = NULL;
if( erefs ) {
cursor = e->e_id == NOID ? 1 : e->e_id;
if ( e != &slap_entry_root ) {
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_r(&bdb->bi_cache, e);
}
e = NULL;
}
/* get the entry with reader lock */
- rc = bdb_id2entry( be, NULL, id, &e );
+ rc = bdb_id2entry_r( be, NULL, id, &e );
if ( e == NULL ) {
if( !BDB_IDL_IS_RANGE(candidates) ) {
if ( scopeok ) {
/* check size limit */
if ( --slimit == -1 ) {
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_r (&bdb->bi_cache, e);
e = NULL;
send_search_result( conn, op,
rc = LDAP_SIZELIMIT_EXCEEDED, NULL, NULL,
case 1: /* entry not sent */
break;
case -1: /* connection closed */
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_r(&bdb->bi_cache, e);
e = NULL;
rc = LDAP_OTHER;
goto done;
loop_continue:
if( e != NULL ) {
/* free reader lock */
- bdb_entry_return( be, e );
+ bdb_cache_return_entry_r ( &bdb->bi_cache, e );
+ e = NULL;
}
ldap_pvt_thread_yield();
rc = 0;
done:
+ if( e != NULL ) {
+ /* free reader lock */
+ bdb_cache_return_entry_r ( &bdb->bi_cache, e );
+ }
+
if( v2refs ) ber_bvarray_free( v2refs );
if( realbase.bv_val ) ch_free( realbase.bv_val );