Commit 5d2699b7 authored by Kurt Spanier's avatar Kurt Spanier
Browse files

Introduction of first version of transaction processing (TP) into BDB2.

parent 6af16c58
......@@ -254,6 +254,7 @@ bdb2_back_add(
case SLAP_SERVER_MODE:
case SLAP_TIMEDSERVER_MODE:
case SLAP_TOOL_MODE:
case SLAP_TOOLID_MODE:
bdb2i_check_default_attr_index_add( li, e );
break;
}
......
......@@ -131,9 +131,9 @@ struct attrinfo {
typedef struct _bdb2_txn_head {
/* counter and timer to control checkpoints */
size_t txn_cnt;
time_t txn_chkp;
/* log size and timer to control checkpoints */
u_int32_t txn_log;
u_int32_t txn_time;
/* a list of all DB files in use */
BDB2_TXN_FILES *dbFiles;
......@@ -145,10 +145,10 @@ typedef struct _bdb2_txn_head {
#define BDB2_DB_ID2CHILDREN_FILE 3
#define BDB2_DB_OC_IDX_FILE 4
/* a file pointer for the NEXTID file
(must be opened appropriately at backend
entry and closed on leave */
FILE *nextidFP;
/* a database handle for the NEXTID file
(must be opened like all DB files at startup
and closed on shutdown */
LDBM nextidFile;
/* is the default attribute index set to non-none */
int withDefIDX;
......
......@@ -12,11 +12,28 @@
static int
bdb2i_back_db_close_internal( BackendDB *be )
{
Debug( LDAP_DEBUG_TRACE, "bdb2 backend saving nextid\n", 0, 0, 0 );
if ( bdb2i_next_id_save( be ) < 0 ) {
Debug( LDAP_DEBUG_ANY, "bdb2 backend nextid save failed!\n", 0, 0, 0 );
DB_LOCK lock;
/* since close will probably write the NEXTID file,
wee need transaction control */
if ( bdb2i_enter_backend_w( get_dbenv( be ), &lock ) != 0 ) {
return( -1 );
}
if ( slapMode != SLAP_TOOL_MODE ) {
Debug( LDAP_DEBUG_TRACE, "bdb2 backend saving nextid\n", 0, 0, 0 );
if ( bdb2i_next_id_save( be ) < 0 ) {
Debug( LDAP_DEBUG_ANY, "bdb2 backend nextid save failed!\n",
0, 0, 0 );
}
}
/* before closing all files, leave the backend (thus commiting
all writes) and set a last checkpoint */
(void) bdb2i_leave_backend_w( get_dbenv( be ), lock );
(void) bdb2i_set_txn_checkpoint( get_dbenv( be )->tx_info, 1 );
/* close all DB files */
Debug( LDAP_DEBUG_TRACE, "bdb2 backend closing DB files\n", 0, 0, 0 );
bdb2i_txn_close_files( be );
......
......@@ -33,6 +33,7 @@ bdb2i_cache_open(
case SLAP_SERVER_MODE:
case SLAP_TIMEDSERVER_MODE:
case SLAP_TOOL_MODE:
case SLAP_TOOLID_MODE:
{
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
char buf[MAXPATHLEN];
......@@ -64,6 +65,7 @@ bdb2i_cache_close( BackendDB *be, struct dbcache *db )
case SLAP_SERVER_MODE:
case SLAP_TIMEDSERVER_MODE:
case SLAP_TOOL_MODE:
case SLAP_TOOLID_MODE:
return;
default:
......@@ -84,6 +86,7 @@ bdb2i_cache_really_close( BackendDB *be, struct dbcache *db )
case SLAP_SERVER_MODE:
case SLAP_TIMEDSERVER_MODE:
case SLAP_TOOL_MODE:
case SLAP_TOOLID_MODE:
return;
default:
......@@ -104,6 +107,7 @@ bdb2i_cache_flush_all( BackendDB *be )
case SLAP_SERVER_MODE:
case SLAP_TIMEDSERVER_MODE:
case SLAP_TOOL_MODE:
case SLAP_TOOLID_MODE:
return;
default:
......@@ -125,7 +129,7 @@ bdb2i_cache_fetch(
ldbm_datum_init( data );
data = ldbm_fetch( db->dbc_db, key );
data = bdb2i_db_fetch( db->dbc_db, key );
return( data );
}
......@@ -162,7 +166,7 @@ bdb2i_cache_store(
if ( slapMode == SLAP_TIMEDSERVER_MODE )
bdb2i_uncond_start_timing( &time1 );
rc = ldbm_store( db->dbc_db, key, data, flags );
rc = bdb2i_db_store( db->dbc_db, key, data, flags );
if ( slapMode == SLAP_TIMEDSERVER_MODE ) {
char buf[BUFSIZ];
......@@ -187,7 +191,7 @@ bdb2i_cache_delete(
{
int rc;
rc = ldbm_delete( db->dbc_db, key );
rc = bdb2i_db_delete( db->dbc_db, key );
return( rc );
}
......@@ -292,7 +292,7 @@ idl_change_first(
/* delete old key block */
if ( (rc = bdb2i_cache_delete( db, bkey )) != 0 ) {
Debug( LDAP_DEBUG_ANY,
"ldbm_delete of (%s) returns %d\n", bkey.dptr, rc,
"bdb2i_db_delete of (%s) returns %d\n", bkey.dptr, rc,
0 );
return( rc );
}
......
......@@ -144,6 +144,7 @@ bdb2_back_modify(
case SLAP_SERVER_MODE:
case SLAP_TIMEDSERVER_MODE:
case SLAP_TOOL_MODE:
case SLAP_TOOLID_MODE:
bdb2i_check_default_attr_index_mod( li, modlist );
break;
}
......
......@@ -13,78 +13,19 @@
#include "slap.h"
#include "back-bdb2.h"
/* XXX the separate handling of the NEXTID file is in contrast to TP */
/* the NEXTID file is beeing opened during database start-up */
static ID
next_id_read( BackendDB *be )
{
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
BDB2_TXN_HEAD *head = &li->li_txn_head;
FILE* fp = head->nextidFP;
ID id;
char buf[20];
/* set the file pointer to the beginnig of the file */
rewind( fp );
/* read the nextid */
if ( fgets( buf, sizeof(buf), fp ) == NULL ) {
Debug( LDAP_DEBUG_ANY,
"next_id_read: could not fgets nextid from \"%s\"\n",
li->li_nextid_file, 0, 0 );
return NOID;
}
id = atol( buf );
if(id < 1) {
Debug( LDAP_DEBUG_ANY,
"next_id_read %ld: atol(%s) return non-positive integer\n",
id, buf, 0 );
return NOID;
}
return id;
}
/* reading and writing NEXTID is handled in txn.c */
#define next_id_read(be) bdb2i_get_nextid( (be) )
#define next_id_write(be,id) bdb2i_put_nextid( (be), (id) )
/* XXX the separate handling of the NEXTID file is in contrast to TP */
/* the NEXTID file is beeing opened during database start-up */
static int
next_id_write( BackendDB *be, ID id )
{
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
BDB2_TXN_HEAD *head = &li->li_txn_head;
FILE* fp = head->nextidFP;
char buf[20];
int rc = 0;
/* set the file pointer to the beginnig of the file */
rewind( fp );
/* write the nextid */
if ( fprintf( fp, "%ld\n", id ) == EOF ) {
Debug( LDAP_DEBUG_ANY, "next_id_write(%ld): cannot fprintf\n",
id, 0, 0 );
rc = -1;
}
/* if forced flushing of files is in effect, do so */
if( li->li_dbcachewsync && ( fflush( fp ) != 0 )) {
Debug( LDAP_DEBUG_ANY, "next_id_write %ld: cannot fflush\n",
id, 0, 0 );
rc = -1;
}
return rc;
}
int
bdb2i_next_id_save( BackendDB *be )
{
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
ID id = bdb2i_next_id_get( be );
int rc = next_id_write( be, id );
int rc;
rc = next_id_write( be, id );
if (rc == 0) {
li->li_nextid_wrote = id;
}
......
......@@ -26,6 +26,7 @@ bdb2i_enter_backend_rw( DB_ENV *dbEnv, DB_LOCK *lock, int writer )
case SLAP_SERVER_MODE:
case SLAP_TIMEDSERVER_MODE:
case SLAP_TOOL_MODE:
case SLAP_TOOLID_MODE:
if ( ( ret = lock_id( dbEnv->lk_info, &locker )) != 0 ) {
Debug( LDAP_DEBUG_ANY,
......@@ -71,6 +72,14 @@ bdb2i_enter_backend_rw( DB_ENV *dbEnv, DB_LOCK *lock, int writer )
break;
}
/* if we are a writer and we have the backend lock,
start transaction control */
if ( writer && ( ret == 0 )) {
ret = bdb2i_start_transction( dbEnv->tx_info );
}
return( ret );
}
......@@ -78,14 +87,30 @@ bdb2i_enter_backend_rw( DB_ENV *dbEnv, DB_LOCK *lock, int writer )
int
bdb2i_leave_backend_rw( DB_ENV *dbEnv, DB_LOCK lock, int writer )
{
int ret = 0;
/* since one or more error can occure,
we must have several return codes that are or'ed at the end */
int ret_transaction = 0;
int ret_lock = 0;
int ret_chkp = 0;
/* if we are a writer, finish the transaction */
if ( writer ) {
ret_transaction = bdb2i_finish_transaction();
}
/* check whether checkpointing is needed */
ret_transaction |= bdb2i_set_txn_checkpoint( dbEnv->tx_info, 0 );
/* now release the lock */
switch ( slapMode ) {
case SLAP_SERVER_MODE:
case SLAP_TIMEDSERVER_MODE:
case SLAP_TOOL_MODE:
switch( ( ret = lock_put( dbEnv->lk_info, lock ))) {
case SLAP_TOOLID_MODE:
switch( ( ret_lock = lock_put( dbEnv->lk_info, lock ))) {
case 0:
Debug( LDAP_DEBUG_TRACE,
......@@ -109,15 +134,14 @@ bdb2i_leave_backend_rw( DB_ENV *dbEnv, DB_LOCK lock, int writer )
Debug( LDAP_DEBUG_ANY,
"bdb2i_leave_backend() -- %s lock returned ERROR: %s\n",
writer ? "write" : "read", strerror( errno ), 0 );
ret = errno;
ret_lock = errno;
break;
}
break;
}
return( ret );
return( ret_transaction | ret_lock );
}
......@@ -179,7 +179,7 @@ void bdb2i_txn_attr_config LDAP_P((
struct ldbminfo *li,
char *attr,
int open ));
int bdb2i_txn_open_files LDAP_P(( struct ldbminfo *li ));
int bdb2i_txn_open_files LDAP_P(( BackendDB *be ));
void bdb2i_txn_close_files LDAP_P(( BackendDB *be ));
BDB2_TXN_FILES *bdb2i_get_db_file_cache LDAP_P((
struct ldbminfo *li,
......@@ -191,7 +191,16 @@ void bdb2i_check_default_attr_index_add LDAP_P((
void bdb2i_check_default_attr_index_mod LDAP_P((
struct ldbminfo *li,
LDAPModList *modlist ));
ID bdb2i_get_nextid LDAP_P(( BackendDB *be ));
int bdb2i_put_nextid LDAP_P(( BackendDB *be, ID id ));
int bdb2i_db_store LDAP_P(( LDBM ldbm, Datum key, Datum data, int flags ));
int bdb2i_db_delete LDAP_P(( LDBM ldbm, Datum key ));
Datum bdb2i_db_fetch LDAP_P(( LDBM ldbm, Datum key ));
Datum bdb2i_db_firstkey LDAP_P(( LDBM ldbm, DBC **dbch ));
Datum bdb2i_db_nextkey LDAP_P(( LDBM ldbm, Datum key, DBC *dbcp ));
int bdb2i_start_transction LDAP_P(( DB_TXNMGR *txmgr ));
int bdb2i_finish_transaction LDAP_P(( ));
int bdb2i_set_txn_checkpoint LDAP_P(( DB_TXNMGR *txmgr, int forced ));
LDAP_END_DECL
......
......@@ -31,10 +31,14 @@ bdb2i_back_startup_internal(
)
{
struct ldbtype *lty = (struct ldbtype *) bi->bi_private;
DB_ENV *dbEnv = lty->lty_dbenv;
int envFlags = DB_CREATE | DB_THREAD | DB_INIT_LOCK | DB_INIT_MPOOL;
int err = 0;
char *home;
DB_ENV *dbEnv = lty->lty_dbenv;
int envFlags;
int err = 0;
char *home;
/* set the flags for a full-feldged transaction schema */
envFlags = ( DB_CREATE | DB_THREAD | DB_INIT_TXN | DB_INIT_LOG |
DB_INIT_LOCK | DB_INIT_MPOOL );
/* make sure, dbhome is an absolute path */
if ( *lty->lty_dbhome != *DEFAULT_DIRSEP ) {
......@@ -167,7 +171,7 @@ bdb2i_back_db_startup_internal(
return 1;
/* now open all DB files */
if ( bdb2i_txn_open_files( li ) != 0 )
if ( bdb2i_txn_open_files( be ) != 0 )
return 1;
return 0;
......
......@@ -32,6 +32,13 @@ bdb2i_txn_head_init( BDB2_TXN_HEAD *head )
}
/* set defaults for checkpointing */
head->txn_log = BDB2_TXN_CHKP_MAX_LOG;
head->txn_time = BDB2_TXN_CHKP_MAX_TIME;
/* initialize the txn_dirty_mutex */
ldap_pvt_thread_mutex_init( &txn_dirty_mutex );
return 0;
}
......@@ -164,25 +171,37 @@ bdb2i_txn_attr_config(
create it (access to the file must be preceeded by a rewind)
*/
static int
bdb2i_open_nextid( struct ldbminfo *li )
bdb2i_open_nextid( BackendDB *be )
{
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
BDB2_TXN_HEAD *head = &li->li_txn_head;
FILE *fp = NULL;
char *file = li->li_nextid_file;
LDBM db = NULL;
DB_INFO dbinfo;
DB_ENV *dbenv = get_dbenv( be );
char fileName[MAXPATHLEN];
sprintf( fileName, "%s%s%s",
li->li_directory, DEFAULT_DIRSEP, NEXTID_NAME );
/* try to open the file for read and write */
if ((( fp = fopen( file, "r+" )) == NULL ) &&
(( fp = fopen( file, "w+" )) == NULL )) {
memset( &dbinfo, 0, sizeof( dbinfo ));
dbinfo.db_pagesize = DEFAULT_DB_PAGE_SIZE;
dbinfo.db_malloc = ldbm_malloc;
(void) db_open( fileName, DB_RECNO, DB_CREATE | DB_THREAD,
li->li_mode, dbenv, &dbinfo, &db );
if ( db == NULL ) {
Debug( LDAP_DEBUG_ANY,
"bdb2i_open_nextid: could not open \"%s\"\n",
file, 0, 0 );
NEXTID_NAME, 0, 0 );
return( -1 );
}
/* the file is open for read/write */
head->nextidFP = fp;
head->nextidFile = db;
return( 0 );
}
......@@ -192,10 +211,12 @@ bdb2i_open_nextid( struct ldbminfo *li )
additional files may be opened during slapd life-time due to
default indexes (must be configured in slapd.conf;
see bdb2i_txn_attr_config)
also, set the counter and timer for TP checkpointing
*/
int
bdb2i_txn_open_files( struct ldbminfo *li )
bdb2i_txn_open_files( BackendDB *be )
{
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
BDB2_TXN_HEAD *head = &li->li_txn_head;
BDB2_TXN_FILES *dbFile;
int rc;
......@@ -227,18 +248,12 @@ bdb2i_txn_open_files( struct ldbminfo *li )
}
rc = bdb2i_open_nextid( li );
return rc;
}
rc = bdb2i_open_nextid( be );
txn_max_pending_log = head->txn_log;
txn_max_pending_time = head->txn_time;
/* close the NEXTID file */
static void
bdb2i_close_nextid( BDB2_TXN_HEAD *head )
{
fclose( head->nextidFP );
head->nextidFP = NULL;
return rc;
}
......@@ -256,7 +271,7 @@ bdb2i_txn_close_files( BackendDB *be )
}
bdb2i_close_nextid( head );
ldbm_close( head->nextidFile );
}
......@@ -387,3 +402,385 @@ bdb2i_check_default_attr_index_mod( struct ldbminfo *li, LDAPModList *modlist )
}
/* get the next ID from the NEXTID file */
ID
bdb2i_get_nextid( BackendDB *be )
{
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
BDB2_TXN_HEAD *head = &li->li_txn_head;
ID id;
Datum key;
Datum data;
db_recno_t rec = NEXTID_RECNO;
ldbm_datum_init( key );
ldbm_datum_init( data );
key.data = &rec;
key.size = sizeof( rec );
data = bdb2i_db_fetch( head->nextidFile, key );
if ( data.data == NULL ) {
Debug( LDAP_DEBUG_ANY,
"next_id_read: could not get nextid from \"%s\"\n",
NEXTID_NAME, 0, 0 );
return NOID;
}
id = atol( data.data );
ldbm_datum_free( head->nextidFile, data );
if ( id < 1 ) {
Debug( LDAP_DEBUG_ANY,
"next_id_read %ld: return non-positive integer\n",
id, 0, 0 );
return NOID;
}
return( id );
}
int
bdb2i_put_nextid( BackendDB *be, ID id )
{
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
BDB2_TXN_HEAD *head = &li->li_txn_head;
int rc, flags;
Datum key;
Datum data;
db_recno_t rec = NEXTID_RECNO;
char buf[20];
sprintf( buf, "%ld\n", id );
ldbm_datum_init( key );
ldbm_datum_init( data );
key.data = &rec;
key.size = sizeof( rec );
data.data = &buf;
data.size = sizeof( buf );
flags = LDBM_REPLACE;
if ( li->li_dbcachewsync ) flags |= LDBM_SYNC;
if (( rc = bdb2i_db_store( head->nextidFile, key, data, flags )) != 0 ) {
Debug( LDAP_DEBUG_ANY, "next_id_write(%ld): store failed (%d)\n",
id, rc, 0 );
return( -1 );
}
return( rc );
}
/* BDB2 backend-private functions of ldbm_store and ldbm_delete */
int
bdb2i_db_store( LDBM ldbm, Datum key, Datum data, int flags )
{
int rc;
rc = (*ldbm->put)( ldbm, txnid, &key, &data, flags & ~LDBM_SYNC );
rc = (-1 ) * rc;
if ( txnid != NULL ) {
/* if the store was OK, set the dirty flag,
otherwise set the abort flag */
if ( rc == 0 ) {
txn_dirty = 1;
} else {
Debug( LDAP_DEBUG_ANY,
"bdb2i_db_store: transaction failed: aborted.\n",
0, 0, 0 );
txn_do_abort = 1;
}
}
return( rc );
}
int
bdb2i_db_delete( LDBM ldbm, Datum key )
{
int rc;
rc = (*ldbm->del)( ldbm, txnid, &key, 0 );
rc = (-1 ) * rc;
if ( txnid != NULL ) {
/* if the delete was OK, set the dirty flag,
otherwise set the abort flag */
if ( rc == 0 ) {
txn_dirty = 1;
} else {
Debug( LDAP_DEBUG_ANY,
"bdb2i_db_delete: transaction failed: aborted.\n",
0, 0, 0 );
txn_do_abort = 1;
}
}
return( rc );
}
Datum
bdb2i_db_fetch( LDBM ldbm, Datum key )
{
Datum data;
int rc;
ldbm_datum_init( data );
data.flags = DB_DBT_MALLOC;
if ( (rc = (*ldbm->get)( ldbm, txnid, &key, &data, 0 )) != 0 ) {
if (( txnid != NULL ) && ( rc != DB_NOTFOUND )) {
Debug( LDAP_DEBUG_ANY,
"bdb2i_db_fetch: transaction failed: aborted.\n",
0, 0, 0 );
txn_do_abort = 1;