From: Howard Chu Date: Fri, 25 Jan 2002 07:19:01 +0000 (+0000) Subject: Entry caching. Based on ITS#1545 from Jong Hyuk Choi, jongchoi@us.ibm.com X-Git-Tag: LDBM_PRE_GIANT_RWLOCK~31 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=713e6beb8d0ab8e3493f482f16fe4c42ebffe1ee;p=openldap Entry caching. Based on ITS#1545 from Jong Hyuk Choi, jongchoi@us.ibm.com --- diff --git a/servers/slapd/back-bdb/Makefile.in b/servers/slapd/back-bdb/Makefile.in index edba6a3a17..764b0de6c5 100644 --- a/servers/slapd/back-bdb/Makefile.in +++ b/servers/slapd/back-bdb/Makefile.in @@ -4,12 +4,12 @@ SRCS = init.c tools.c config.c \ 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 diff --git a/servers/slapd/back-bdb/add.c b/servers/slapd/back-bdb/add.c index ce179385dd..c401aae548 100644 --- a/servers/slapd/back-bdb/add.c +++ b/servers/slapd/back-bdb/add.c @@ -64,8 +64,8 @@ bdb_add( } 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 ) { @@ -130,7 +130,7 @@ retry: rc = txn_abort( ltid ); #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: @@ -154,7 +154,7 @@ retry: rc = txn_abort( ltid ); 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 { @@ -180,8 +180,8 @@ retry: rc = txn_abort( ltid ); 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; } @@ -214,10 +214,8 @@ retry: rc = txn_abort( ltid ); 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 ); @@ -226,7 +224,8 @@ retry: rc = txn_abort( ltid ); matched_dn, NULL, refs, NULL ); ber_bvarray_free( refs ); - free( matched_dn ); + bdb_cache_return_entry_r( be, p ); + p = NULL; goto done; } @@ -235,8 +234,8 @@ retry: rc = txn_abort( ltid ); /* 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 { @@ -361,6 +360,13 @@ retry: rc = txn_abort( ltid ); 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 ); @@ -368,6 +374,8 @@ retry: rc = txn_abort( ltid ); text = NULL; } + bdb_cache_entry_commit (e); + return_results: send_ldap_result( conn, op, rc, NULL, text, NULL, NULL ); @@ -379,10 +387,6 @@ return_results: } done: - if (p != NULL) { - /* free parent and writer lock */ - bdb_entry_return( be, p ); - } if( ltid != NULL ) { txn_abort( ltid ); diff --git a/servers/slapd/back-bdb/attribute.c b/servers/slapd/back-bdb/attribute.c index f248163830..298c6f5fdc 100644 --- a/servers/slapd/back-bdb/attribute.c +++ b/servers/slapd/back-bdb/attribute.c @@ -29,7 +29,7 @@ bdb_attribute( 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; @@ -79,7 +79,7 @@ bdb_attribute( } 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: @@ -200,7 +200,7 @@ bdb_attribute( return_results: if( target != e ) { /* free entry */ - bdb_entry_return( be, e ); + bdb_cache_return_entry_r(&bdb->bi_cache, e); } #ifdef NEW_LOGGING diff --git a/servers/slapd/back-bdb/back-bdb.h b/servers/slapd/back-bdb/back-bdb.h index 3ef79b4437..6de08bc2ad 100644 --- a/servers/slapd/back-bdb/back-bdb.h +++ b/servers/slapd/back-bdb/back-bdb.h @@ -75,6 +75,22 @@ LDAP_BEGIN_DECL #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 { @@ -97,6 +113,7 @@ struct bdb_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; diff --git a/servers/slapd/back-bdb/bind.c b/servers/slapd/back-bdb/bind.c index 3ec89f1f03..1c682e4b4b 100644 --- a/servers/slapd/back-bdb/bind.c +++ b/servers/slapd/back-bdb/bind.c @@ -43,7 +43,7 @@ bdb_bind( 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: @@ -67,7 +67,7 @@ bdb_bind( ? 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 { @@ -240,7 +240,7 @@ bdb_bind( 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) */ diff --git a/servers/slapd/back-bdb/cache.c b/servers/slapd/back-bdb/cache.c new file mode 100644 index 0000000000..e850a34e40 --- /dev/null +++ b/servers/slapd/back-bdb/cache.c @@ -0,0 +1,907 @@ +/* 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 + +#include +#include +#include + +#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 diff --git a/servers/slapd/back-bdb/compare.c b/servers/slapd/back-bdb/compare.c index b493dd4c3a..d8abfd40f7 100644 --- a/servers/slapd/back-bdb/compare.c +++ b/servers/slapd/back-bdb/compare.c @@ -32,7 +32,7 @@ bdb_compare( 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: @@ -53,7 +53,7 @@ bdb_compare( 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 { @@ -119,7 +119,7 @@ return_results: done: /* free entry */ if( e != NULL ) { - bdb_entry_return( be, e ); + bdb_cache_return_entry_r( &bdb->bi_cache, e ); } return rc; diff --git a/servers/slapd/back-bdb/config.c b/servers/slapd/back-bdb/config.c index f6ab841ba4..6de743254e 100644 --- a/servers/slapd/back-bdb/config.c +++ b/servers/slapd/back-bdb/config.c @@ -147,6 +147,16 @@ bdb_db_config( 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 \" line\n", + fname, lineno ); + return( 1 ); + } + bdb->bi_cache.c_maxsize = atoi( argv[1] ); + /* anything else */ } else { fprintf( stderr, "%s: line %d: " diff --git a/servers/slapd/back-bdb/delete.c b/servers/slapd/back-bdb/delete.c index 135afd0bcd..5c1588ae16 100644 --- a/servers/slapd/back-bdb/delete.c +++ b/servers/slapd/back-bdb/delete.c @@ -42,6 +42,9 @@ bdb_delete( 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 ); @@ -100,7 +103,7 @@ retry: /* transaction retry */ } #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: @@ -128,7 +131,7 @@ retry: /* transaction retry */ 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 ) { @@ -191,7 +194,7 @@ retry: /* transaction retry */ } /* 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: @@ -219,7 +222,7 @@ retry: /* transaction retry */ 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 { @@ -297,7 +300,7 @@ retry: /* transaction retry */ } /* 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: @@ -371,7 +374,7 @@ return_results: done: /* free entry */ if( e != NULL ) { - bdb_entry_return( be, e ); + bdb_cache_return_entry_w(&bdb->bi_cache, e); } if( ltid != NULL ) { diff --git a/servers/slapd/back-bdb/dn2entry.c b/servers/slapd/back-bdb/dn2entry.c index 767c1571db..a4cffd8b2d 100644 --- a/servers/slapd/back-bdb/dn2entry.c +++ b/servers/slapd/back-bdb/dn2entry.c @@ -18,18 +18,19 @@ */ 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; @@ -46,9 +47,9 @@ bdb_dn2entry( } 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; diff --git a/servers/slapd/back-bdb/dn2id.c b/servers/slapd/back-bdb/dn2id.c index c05dcae223..f336075216 100644 --- a/servers/slapd/back-bdb/dn2id.c +++ b/servers/slapd/back-bdb/dn2id.c @@ -223,6 +223,12 @@ bdb_dn2id( 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 ); @@ -263,6 +269,7 @@ bdb_dn2id_matched( 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 ); @@ -284,8 +291,20 @@ bdb_dn2id_matched( *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; diff --git a/servers/slapd/back-bdb/group.c b/servers/slapd/back-bdb/group.c index c108c8c546..b754c31f14 100644 --- a/servers/slapd/back-bdb/group.c +++ b/servers/slapd/back-bdb/group.c @@ -33,7 +33,7 @@ bdb_group( 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; @@ -88,7 +88,7 @@ bdb_group( #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; @@ -208,7 +208,7 @@ bdb_group( return_results: if( target != e ) { /* free entry */ - bdb_entry_return( be, e ); + bdb_cache_return_entry_r( &bdb->bi_cache, e ); } #ifdef NEW_LOGGING diff --git a/servers/slapd/back-bdb/id2entry.c b/servers/slapd/back-bdb/id2entry.c index 2de0ad47f7..c67359c339 100644 --- a/servers/slapd/back-bdb/id2entry.c +++ b/servers/slapd/back-bdb/id2entry.c @@ -53,6 +53,12 @@ int bdb_id2entry_put( 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, @@ -69,17 +75,18 @@ int bdb_id2entry_update( 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; @@ -90,6 +97,10 @@ int bdb_id2entry( 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 ); @@ -109,53 +120,85 @@ int bdb_id2entry( */ 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 ); @@ -170,12 +213,20 @@ int bdb_entry_release( 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; } diff --git a/servers/slapd/back-bdb/init.c b/servers/slapd/back-bdb/init.c index cecf9d6ddb..36638ed27d 100644 --- a/servers/slapd/back-bdb/init.c +++ b/servers/slapd/back-bdb/init.c @@ -78,6 +78,8 @@ bdb_db_init( BackendDB *be ) 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; @@ -88,6 +90,7 @@ bdb_db_init( BackendDB *be ) 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 @@ -349,6 +352,8 @@ bdb_db_close( BackendDB *be ) free( bdb->bi_databases ); bdb_attr_index_destroy( bdb->bi_attrs ); + bdb_cache_release_all (&bdb->bi_cache); + return 0; } @@ -370,6 +375,8 @@ bdb_db_destroy( BackendDB *be ) } } + bdb_cache_release_all (&bdb->bi_cache); + rc = bdb->bi_dbenv->close( bdb->bi_dbenv, 0 ); bdb->bi_dbenv = NULL; if( rc != 0 ) { diff --git a/servers/slapd/back-bdb/modify.c b/servers/slapd/back-bdb/modify.c index b00b60236c..2f48cb40eb 100644 --- a/servers/slapd/back-bdb/modify.c +++ b/servers/slapd/back-bdb/modify.c @@ -198,6 +198,10 @@ bdb_modify( 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 ); @@ -232,7 +236,7 @@ retry: /* transaction retry */ 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, @@ -261,7 +265,7 @@ retry: /* transaction retry */ 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 { @@ -364,8 +368,7 @@ done: } if( e != NULL ) { - bdb_entry_return( be, e ); + bdb_cache_return_entry_w (&bdb->bi_cache, e); } return rc; } - diff --git a/servers/slapd/back-bdb/modrdn.c b/servers/slapd/back-bdb/modrdn.c index d1398ab308..e3f3e44a4d 100644 --- a/servers/slapd/back-bdb/modrdn.c +++ b/servers/slapd/back-bdb/modrdn.c @@ -46,7 +46,7 @@ bdb_modrdn( 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 */ @@ -70,6 +70,16 @@ bdb_modrdn( 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; @@ -103,7 +113,7 @@ retry: /* transaction retry */ 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: @@ -123,11 +133,11 @@ retry: /* transaction retry */ 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 { @@ -176,7 +186,7 @@ retry: /* transaction retry */ /* 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: @@ -291,7 +301,7 @@ retry: /* transaction retry */ /* 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: @@ -571,6 +581,8 @@ retry: /* transaction retry */ 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. @@ -578,6 +590,9 @@ retry: /* transaction retry */ 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 ) { @@ -630,11 +645,13 @@ retry: /* transaction retry */ 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: @@ -664,18 +681,18 @@ done: /* 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 ) { diff --git a/servers/slapd/back-bdb/passwd.c b/servers/slapd/back-bdb/passwd.c index 3858ac29cb..c0a38fe920 100644 --- a/servers/slapd/back-bdb/passwd.c +++ b/servers/slapd/back-bdb/passwd.c @@ -86,6 +86,10 @@ bdb_exop_passwd( 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; @@ -119,7 +123,7 @@ retry: /* transaction retry */ 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: @@ -174,8 +178,6 @@ retry: /* transaction retry */ case DB_LOCK_DEADLOCK: case DB_LOCK_NOTGRANTED: *text = NULL; - bdb_entry_return( be, e ); - e = NULL; goto retry; case 0: break; @@ -191,8 +193,6 @@ retry: /* transaction retry */ switch(rc) { case DB_LOCK_DEADLOCK: case DB_LOCK_NOTGRANTED: - bdb_entry_return( be, e ); - e = NULL; goto retry; } *text = "entry update failed"; @@ -212,9 +212,9 @@ retry: /* transaction retry */ 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 ); } diff --git a/servers/slapd/back-bdb/proto-bdb.h b/servers/slapd/back-bdb/proto-bdb.h index 64b83c2cdf..86d7e1bf5d 100644 --- a/servers/slapd/back-bdb/proto-bdb.h +++ b/servers/slapd/back-bdb/proto-bdb.h @@ -57,8 +57,10 @@ bdb_db_cache( /* * 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 @@ -103,7 +105,7 @@ bdb_dn2idl( /* * entry.c */ -int bdb_entry_return( BackendDB *be, Entry *e ); +int bdb_entry_return( Entry *e ); BI_entry_release_rw bdb_entry_release; /* @@ -127,7 +129,7 @@ int bdb_filter_candidates( BI_acl_group bdb_group; /* - * id2entry + * id2entry.c */ int bdb_id2entry_add( BackendDB *be, @@ -142,13 +144,18 @@ int bdb_id2entry_update( 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 @@ -282,6 +289,40 @@ int bdb_modify_internal( */ 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 */ diff --git a/servers/slapd/back-bdb/referral.c b/servers/slapd/back-bdb/referral.c index 64ee0caace..64f6182566 100644 --- a/servers/slapd/back-bdb/referral.c +++ b/servers/slapd/back-bdb/referral.c @@ -36,7 +36,7 @@ bdb_referrals( } /* 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: @@ -47,6 +47,12 @@ bdb_referrals( 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; @@ -68,7 +74,7 @@ bdb_referrals( 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; @@ -113,6 +119,6 @@ bdb_referrals( ber_bvarray_free( refs ); } - bdb_entry_return( be, e ); + bdb_cache_return_entry_r(&bdb->bi_cache, e); return rc; } diff --git a/servers/slapd/back-bdb/search.c b/servers/slapd/back-bdb/search.c index 9b9272610b..7645674c15 100644 --- a/servers/slapd/back-bdb/search.c +++ b/servers/slapd/back-bdb/search.c @@ -78,7 +78,7 @@ bdb_search( } else #endif { - rc = bdb_dn2entry( be, NULL, nbase, &e, &matched, 0 ); + rc = bdb_dn2entry_r( be, NULL, nbase, &e, &matched, 0 ); } switch(rc) { @@ -86,6 +86,12 @@ bdb_search( 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; @@ -104,7 +110,7 @@ bdb_search( ? 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 ) { @@ -135,7 +141,7 @@ bdb_search( 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 ) { @@ -245,7 +251,7 @@ bdb_search( 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; @@ -296,7 +302,7 @@ bdb_search( } /* 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) ) { @@ -418,7 +424,7 @@ bdb_search( 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, @@ -437,7 +443,7 @@ bdb_search( 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; @@ -457,7 +463,8 @@ bdb_search( 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(); @@ -469,6 +476,11 @@ loop_continue: 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 );