]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/dbcache.c
cbc28d0a18aa17593edee63012fe368aec6c096e
[openldap] / servers / slapd / back-ldbm / dbcache.c
1 /* ldbmcache.c - maintain a cache of open ldbm files */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2005 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/errno.h>
22 #include <ac/socket.h>
23 #include <ac/string.h>
24 #include <ac/time.h>
25 #include <ac/unistd.h>
26 #include <sys/stat.h>
27
28 #include "slap.h"
29 #include "back-ldbm.h"
30 #include <ldap_rq.h>
31
32 DBCache *
33 ldbm_cache_open(
34     Backend     *be,
35     const char  *name,
36     const char  *suffix,
37     int         flags
38 )
39 {
40         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
41         int             i, lru, empty;
42         time_t          oldtime;
43         char            buf[MAXPATHLEN];
44 #ifdef HAVE_ST_BLKSIZE
45         struct stat     st;
46 #endif
47
48         if (li->li_envdirok)
49                 sprintf( buf, "%s%s", name, suffix );
50         else
51                 sprintf( buf, "%s" LDAP_DIRSEP "%s%s",
52                         li->li_directory, name, suffix );
53
54         if( li->li_dblocking ) {
55                 flags |= LDBM_LOCKING;
56         } else {
57                 flags |= LDBM_NOLOCKING;
58         }
59         
60         if( li->li_dbwritesync ) {
61                 flags |= LDBM_SYNC;
62         } else {
63                 flags |= LDBM_NOSYNC;
64         }
65         
66         Debug( LDAP_DEBUG_TRACE, "=> ldbm_cache_open( \"%s\", %d, %o )\n", buf,
67             flags, li->li_mode );
68
69
70         empty = MAXDBCACHE;
71
72         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
73         do {
74                 lru = 0;
75                 oldtime = 1;
76                 for ( i = 0; i < MAXDBCACHE; i++ ) {
77                         /* see if this slot is free */
78                         if ( li->li_dbcache[i].dbc_name == NULL) {
79                                 if (empty == MAXDBCACHE)
80                                         empty = i;
81                                 continue;
82                         }
83
84                         if ( strcmp( li->li_dbcache[i].dbc_name, buf ) == 0 ) {
85                                 /* already open - return it */
86                                 if (li->li_dbcache[i].dbc_flags != flags
87                                         && li->li_dbcache[i].dbc_refcnt == 0)
88                                 {
89                                         /* we don't want to use an open cache with different
90                                          * permissions (esp. if we need write but the open
91                                          * cache is read-only).  So close this one if
92                                          * possible, and re-open below.
93                                          *
94                                          * FIXME:  what about the case where the refcount
95                                          * is > 0?  right now, we're using it anyway and
96                                          * just praying.  Can there be more than one open
97                                          * cache to the same db?
98                                          *
99                                          * Also, it's really only necessary to compare the
100                                          * read-only flag, instead of all of the flags,
101                                          * but for now I'm checking all of them.
102                                          */
103                                         lru = i;
104                                         empty = MAXDBCACHE;
105                                         break;
106                                 }
107                                 li->li_dbcache[i].dbc_refcnt++;
108                                 Debug( LDAP_DEBUG_TRACE,
109                                     "<= ldbm_cache_open (cache %d)\n", i, 0, 0 );
110
111                                 ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
112                                 return( &li->li_dbcache[i] );
113                         }
114
115                         /* keep track of lru db */
116                         if (( li->li_dbcache[i].dbc_refcnt == 0 ) &&
117                               (( oldtime == 1 ) ||
118                               ( li->li_dbcache[i].dbc_lastref < oldtime )) )
119                         {
120                                 lru = i;
121                                 oldtime = li->li_dbcache[i].dbc_lastref;
122                         }
123                 }
124
125                 i = empty;
126                 if ( i == MAXDBCACHE ) {
127                         /* no empty slots, not already open - close lru and use that slot */
128                         if ( li->li_dbcache[lru].dbc_refcnt == 0 ) {
129                                 i = lru;
130                                 ldbm_close( li->li_dbcache[i].dbc_db );
131                                 free( li->li_dbcache[i].dbc_name );
132                                 li->li_dbcache[i].dbc_name = NULL;
133                         } else {
134                                 Debug( LDAP_DEBUG_ANY,
135                                     "ldbm_cache_open no unused db to close - waiting\n",
136                                     0, 0, 0 );
137
138                                 ldap_pvt_thread_cond_wait( &li->li_dbcache_cv,
139                                             &li->li_dbcache_mutex );
140                                 /* after waiting for a free slot, go back to square
141                                  * one: look for an open cache for this db, or an
142                                  * empty slot, or an unref'ed cache, or wait again.
143                                  */
144                         }
145                 }
146         } while (i == MAXDBCACHE);
147
148         if ( (li->li_dbcache[i].dbc_db = ldbm_open( li->li_dbenv, buf, flags, li->li_mode,
149             li->li_dbcachesize )) == NULL )
150         {
151                 int err = errno;
152                 Debug( LDAP_DEBUG_TRACE,
153                     "<= ldbm_cache_open NULL \"%s\" errno=%d reason=\"%s\")\n",
154                     buf, err, err > -1 && err < sys_nerr ?
155                     sys_errlist[err] : "unknown" );
156
157                 ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
158                 return( NULL );
159         }
160         li->li_dbcache[i].dbc_name = ch_strdup( buf );
161         li->li_dbcache[i].dbc_refcnt = 1;
162         li->li_dbcache[i].dbc_lastref = slap_get_time();
163         li->li_dbcache[i].dbc_flags = flags;
164         li->li_dbcache[i].dbc_dirty = 0;
165 #ifdef HAVE_ST_BLKSIZE
166         if ( stat( buf, &st ) == 0 ) {
167                 li->li_dbcache[i].dbc_blksize = st.st_blksize;
168         } else
169 #endif
170         {
171                 li->li_dbcache[i].dbc_blksize = DEFAULT_BLOCKSIZE;
172         }
173         li->li_dbcache[i].dbc_maxids = (li->li_dbcache[i].dbc_blksize /
174             sizeof(ID)) - ID_BLOCK_IDS_OFFSET;
175         li->li_dbcache[i].dbc_maxindirect = ( SLAPD_LDBM_MIN_MAXIDS /
176             li->li_dbcache[i].dbc_maxids ) + 1;
177
178         assert( li->li_dbcache[i].dbc_maxindirect < 256 );
179
180         Debug( LDAP_DEBUG_ARGS,
181             "ldbm_cache_open (blksize %ld) (maxids %d) (maxindirect %d)\n",
182             li->li_dbcache[i].dbc_blksize, li->li_dbcache[i].dbc_maxids,
183             li->li_dbcache[i].dbc_maxindirect );
184
185         Debug( LDAP_DEBUG_TRACE, "<= ldbm_cache_open (opened %d)\n", i, 0, 0 );
186
187         ldap_pvt_thread_mutex_init( &li->li_dbcache[i].dbc_write_mutex );
188
189         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
190         return( &li->li_dbcache[i] );
191 }
192
193 void
194 ldbm_cache_close( Backend *be, DBCache *db )
195 {
196         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
197
198         if( li->li_dbwritesync && db->dbc_dirty ) {
199                 ldbm_sync( db->dbc_db );
200                 db->dbc_dirty = 0;
201         }
202
203         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
204         if ( --db->dbc_refcnt <= 0 ) {
205                 db->dbc_refcnt = 0;
206                 ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
207         }
208         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
209 }
210
211 void
212 ldbm_cache_really_close( Backend *be, DBCache *db )
213 {
214         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
215
216         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
217         if ( --db->dbc_refcnt <= 0 ) {
218                 db->dbc_refcnt = 0;
219                 ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
220                 ldbm_close( db->dbc_db );
221                 free( db->dbc_name );
222                 db->dbc_name = NULL;
223                 ldap_pvt_thread_mutex_destroy( &db->dbc_write_mutex );
224         }
225         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
226 }
227
228 void
229 ldbm_cache_flush_all( Backend *be )
230 {
231         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
232         int             i;
233
234         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
235         for ( i = 0; i < MAXDBCACHE; i++ ) {
236                 if ( li->li_dbcache[i].dbc_name != NULL ) {
237                         Debug( LDAP_DEBUG_TRACE, "ldbm flushing db (%s)\n",
238                             li->li_dbcache[i].dbc_name, 0, 0 );
239
240                         ldbm_sync( li->li_dbcache[i].dbc_db );
241                         li->li_dbcache[i].dbc_dirty = 0;
242                         if ( li->li_dbcache[i].dbc_refcnt != 0 ) {
243                                 Debug( LDAP_DEBUG_TRACE,
244                                        "refcnt = %d, couldn't close db (%s)\n",
245                                        li->li_dbcache[i].dbc_refcnt,
246                                        li->li_dbcache[i].dbc_name, 0 );
247
248                         } else {
249                                 Debug( LDAP_DEBUG_TRACE,
250                                        "ldbm closing db (%s)\n",
251                                        li->li_dbcache[i].dbc_name, 0, 0 );
252
253                                 ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
254                                 ldbm_close( li->li_dbcache[i].dbc_db );
255                                 free( li->li_dbcache[i].dbc_name );
256                                 li->li_dbcache[i].dbc_name = NULL;
257                         }
258                 }
259         }
260         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
261 }
262
263 void
264 ldbm_cache_sync( Backend *be )
265 {
266         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
267         int             i;
268         int             do_log = 1;
269
270         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
271         for ( i = 0; i < MAXDBCACHE; i++ ) {
272                 if ( li->li_dbcache[i].dbc_name != NULL && li->li_dbcache[i].dbc_dirty ) {
273                         if ( do_log ) {
274                                 do_log = 0;
275                                 Debug( LDAP_DEBUG_TRACE, "syncing %s\n",
276                                         li->li_directory, 0, 0 );
277                         }
278                         Debug(  LDAP_DEBUG_TRACE, "ldbm syncing db (%s)\n",
279                                 li->li_dbcache[i].dbc_name, 0, 0 );
280                         ldbm_sync( li->li_dbcache[i].dbc_db );
281                         li->li_dbcache[i].dbc_dirty = 0;
282                 }
283         }
284         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
285 }
286
287 #if 0 /* macro in proto-back-ldbm.h */
288 Datum
289 ldbm_cache_fetch(
290     DBCache     *db,
291     Datum               key
292 )
293 {
294         return ldbm_fetch( db->dbc_db, key );
295 }
296 #endif /* 0 */
297
298 int
299 ldbm_cache_store(
300     DBCache     *db,
301     Datum               key,
302     Datum               data,
303     int                 flags
304 )
305 {
306         int     rc;
307
308         db->dbc_dirty = 1;
309         rc = ldbm_store( db->dbc_db, key, data, flags );
310
311         return( rc );
312 }
313
314 int
315 ldbm_cache_delete(
316     DBCache     *db,
317     Datum               key
318 )
319 {
320         int     rc;
321
322         db->dbc_dirty = 1;
323         rc = ldbm_delete( db->dbc_db, key );
324
325         return( rc );
326 }
327
328 void *
329 ldbm_cache_sync_daemon(
330         void *ctx,
331         void *arg
332 )
333 {
334         struct re_s *rtask = arg;
335         Backend *be = rtask->arg;
336         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
337
338         /* If server is idle, or we've already waited the limit */
339         if ( li->li_dbsyncwaitcount == li->li_dbsyncwaitn || 
340                 ldap_pvt_thread_pool_backload(&connection_pool) < 2 ) {
341                 rtask->interval.tv_sec = li->li_dbsyncfreq;
342                 li->li_dbsyncwaitcount = 0;
343                 ldbm_cache_sync( be );
344         } else {
345                 rtask->interval.tv_sec = li->li_dbsyncwaitinterval;
346                 li->li_dbsyncwaitcount++;
347                 Debug( LDAP_DEBUG_TRACE, "delay #%d syncing %s\n", 
348                         li->li_dbsyncwaitcount, li->li_directory, 0 );
349         }
350
351         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
352         ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
353         ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
354         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
355         return NULL;
356 }