2007-09-21 06:24:02 +04:00
/*
persistent store logic
Copyright ( C ) Andrew Tridgell 2007
Copyright ( C ) Ronnie Sahlberg 2007
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
# include "lib/events/events.h"
# include "system/filesys.h"
# include "system/wait.h"
# include "db_wrap.h"
# include "lib/tdb/include/tdb.h"
# include "../include/ctdb_private.h"
struct ctdb_persistent_state {
struct ctdb_context * ctdb ;
struct ctdb_req_control * c ;
const char * errormsg ;
uint32_t num_pending ;
int32_t status ;
} ;
/*
called when a node has acknowledged a ctdb_control_update_record call
*/
static void ctdb_persistent_callback ( struct ctdb_context * ctdb ,
int32_t status , TDB_DATA data ,
const char * errormsg ,
void * private_data )
{
struct ctdb_persistent_state * state = talloc_get_type ( private_data ,
struct ctdb_persistent_state ) ;
2007-09-21 09:19:33 +04:00
2007-09-21 06:24:02 +04:00
if ( status ! = 0 ) {
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_ERR , ( " ctdb_persistent_callback failed with status %d (%s) \n " ,
2007-09-21 06:24:02 +04:00
status , errormsg ) ) ;
state - > status = status ;
state - > errormsg = errormsg ;
}
state - > num_pending - - ;
if ( state - > num_pending = = 0 ) {
ctdb_request_control_reply ( state - > ctdb , state - > c , NULL , state - > status , state - > errormsg ) ;
talloc_free ( state ) ;
}
}
2007-09-21 09:19:33 +04:00
/*
called if persistent store times out
*/
static void ctdb_persistent_store_timeout ( struct event_context * ev , struct timed_event * te ,
struct timeval t , void * private_data )
{
struct ctdb_persistent_state * state = talloc_get_type ( private_data , struct ctdb_persistent_state ) ;
ctdb_request_control_reply ( state - > ctdb , state - > c , NULL , - 1 , " timeout in ctdb_persistent_state " ) ;
talloc_free ( state ) ;
}
2007-09-21 06:24:02 +04:00
/*
store a persistent record - called from a ctdb client when it has updated
a record in a persistent database . The client will have the record
locked for the duration of this call . The client is the dmaster when
this call is made
*/
int32_t ctdb_control_persistent_store ( struct ctdb_context * ctdb ,
struct ctdb_req_control * c ,
TDB_DATA recdata , bool * async_reply )
{
struct ctdb_persistent_state * state ;
int i ;
2008-05-22 10:29:46 +04:00
state = talloc_zero ( ctdb , struct ctdb_persistent_state ) ;
2007-09-21 06:24:02 +04:00
CTDB_NO_MEMORY ( ctdb , state ) ;
state - > ctdb = ctdb ;
2007-09-24 03:57:14 +04:00
state - > c = c ;
2007-09-21 06:24:02 +04:00
2007-09-21 09:44:13 +04:00
for ( i = 0 ; i < ctdb - > vnn_map - > size ; i + + ) {
struct ctdb_node * node = ctdb - > nodes [ ctdb - > vnn_map - > map [ i ] ] ;
2007-09-21 06:24:02 +04:00
int ret ;
/* only send to active nodes */
if ( node - > flags & NODE_FLAGS_INACTIVE ) {
continue ;
}
/* don't send to ourselves */
if ( node - > pnn = = ctdb - > pnn ) {
continue ;
}
ret = ctdb_daemon_send_control ( ctdb , node - > pnn , 0 , CTDB_CONTROL_UPDATE_RECORD ,
c - > client_id , 0 , recdata ,
ctdb_persistent_callback , state ) ;
if ( ret = = - 1 ) {
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_ERR , ( " Unable to send CTDB_CONTROL_UPDATE_RECORD to pnn %u \n " , node - > pnn ) ) ;
2007-09-21 06:24:02 +04:00
talloc_free ( state ) ;
return - 1 ;
}
state - > num_pending + + ;
}
if ( state - > num_pending = = 0 ) {
talloc_free ( state ) ;
return 0 ;
}
/* we need to wait for the replies */
* async_reply = true ;
2007-09-21 09:19:33 +04:00
2007-09-24 03:57:14 +04:00
/* need to keep the control structure around */
talloc_steal ( state , c ) ;
/* but we won't wait forever */
2007-09-21 09:19:33 +04:00
event_add_timed ( ctdb - > ev , state ,
timeval_current_ofs ( ctdb - > tunable . control_timeout , 0 ) ,
ctdb_persistent_store_timeout , state ) ;
2007-09-21 06:24:02 +04:00
return 0 ;
}
struct ctdb_persistent_lock_state {
struct ctdb_db_context * ctdb_db ;
TDB_DATA key ;
TDB_DATA data ;
struct ctdb_ltdb_header * header ;
struct tdb_context * tdb ;
struct ctdb_req_control * c ;
} ;
/*
2008-05-22 06:47:33 +04:00
called with a lock held by a lockwait child
2007-09-21 06:24:02 +04:00
*/
static int ctdb_persistent_store ( struct ctdb_persistent_lock_state * state )
{
struct ctdb_ltdb_header oldheader ;
int ret ;
/* fetch the old header and ensure the rsn is less than the new rsn */
ret = ctdb_ltdb_fetch ( state - > ctdb_db , state - > key , & oldheader , NULL , NULL ) ;
if ( ret ! = 0 ) {
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_ERR , ( " Failed to fetch old record for db_id 0x%08x in ctdb_persistent_store \n " ,
2007-09-21 06:24:02 +04:00
state - > ctdb_db - > db_id ) ) ;
return - 1 ;
}
if ( oldheader . rsn > = state - > header - > rsn ) {
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_CRIT , ( " existing header for db_id 0x%08x has larger RSN %llu than new RSN %llu in ctdb_persistent_store \n " ,
2007-09-24 03:57:14 +04:00
state - > ctdb_db - > db_id ,
( unsigned long long ) oldheader . rsn , ( unsigned long long ) state - > header - > rsn ) ) ;
2007-09-21 06:24:02 +04:00
return - 1 ;
}
2008-05-22 06:47:33 +04:00
ret = ctdb_ltdb_persistent_store ( state - > ctdb_db , state - > key , state - > header , state - > data ) ;
2007-09-21 06:24:02 +04:00
if ( ret ! = 0 ) {
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_CRIT , ( " Failed to store record for db_id 0x%08x in ctdb_persistent_store \n " ,
2007-09-21 06:24:02 +04:00
state - > ctdb_db - > db_id ) ) ;
return - 1 ;
}
return 0 ;
}
/*
called when we get the lock on the given record
at this point the lockwait child holds a lock on our behalf
*/
static void ctdb_persistent_lock_callback ( void * private_data )
{
struct ctdb_persistent_lock_state * state = talloc_get_type ( private_data ,
struct ctdb_persistent_lock_state ) ;
int ret ;
ret = tdb_chainlock_mark ( state - > tdb , state - > key ) ;
if ( ret ! = 0 ) {
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_ERR , ( " Failed to mark lock in ctdb_persistent_lock_callback \n " ) ) ;
2007-09-21 06:24:02 +04:00
ctdb_request_control_reply ( state - > ctdb_db - > ctdb , state - > c , NULL , ret , NULL ) ;
return ;
}
ret = ctdb_persistent_store ( state ) ;
ctdb_request_control_reply ( state - > ctdb_db - > ctdb , state - > c , NULL , ret , NULL ) ;
tdb_chainlock_unmark ( state - > tdb , state - > key ) ;
2008-05-22 10:29:46 +04:00
talloc_free ( state ) ;
2007-09-21 06:24:02 +04:00
}
/*
called if our lockwait child times out
*/
static void ctdb_persistent_lock_timeout ( struct event_context * ev , struct timed_event * te ,
struct timeval t , void * private_data )
{
struct ctdb_persistent_lock_state * state = talloc_get_type ( private_data ,
struct ctdb_persistent_lock_state ) ;
ctdb_request_control_reply ( state - > ctdb_db - > ctdb , state - > c , NULL , - 1 , " timeout in ctdb_persistent_lock " ) ;
talloc_free ( state ) ;
}
/*
update a record on this node if the new record has a higher rsn than the
current record
*/
int32_t ctdb_control_update_record ( struct ctdb_context * ctdb ,
struct ctdb_req_control * c , TDB_DATA recdata ,
bool * async_reply )
{
struct ctdb_rec_data * rec = ( struct ctdb_rec_data * ) & recdata . dptr [ 0 ] ;
struct ctdb_db_context * ctdb_db ;
uint32_t db_id = rec - > reqid ;
struct ctdb_persistent_lock_state * state ;
2008-05-22 10:29:46 +04:00
struct lockwait_handle * handle ;
2007-09-21 06:24:02 +04:00
if ( ctdb - > recovery_mode ! = CTDB_RECOVERY_NORMAL ) {
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_DEBUG , ( " rejecting ctdb_control_update_record when recovery active \n " ) ) ;
2007-09-21 06:24:02 +04:00
return - 1 ;
}
ctdb_db = find_ctdb_db ( ctdb , db_id ) ;
if ( ctdb_db = = NULL ) {
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_ERR , ( " Unknown database 0x%08x in ctdb_control_update_record \n " , db_id ) ) ;
2007-09-21 06:24:02 +04:00
return - 1 ;
}
2008-05-22 10:29:46 +04:00
state = talloc ( ctdb , struct ctdb_persistent_lock_state ) ;
2007-09-21 06:24:02 +04:00
CTDB_NO_MEMORY ( ctdb , state ) ;
state - > ctdb_db = ctdb_db ;
state - > c = c ;
state - > tdb = ctdb_db - > ltdb - > tdb ;
state - > key . dptr = & rec - > data [ 0 ] ;
state - > key . dsize = rec - > keylen ;
state - > data . dptr = & rec - > data [ rec - > keylen ] ;
state - > data . dsize = rec - > datalen ;
if ( state - > data . dsize < sizeof ( struct ctdb_ltdb_header ) ) {
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_CRIT , ( " Invalid data size %u in ctdb_control_update_record \n " ,
2007-09-24 03:57:14 +04:00
( unsigned ) state - > data . dsize ) ) ;
2008-05-22 10:29:46 +04:00
talloc_free ( state ) ;
2007-09-21 06:24:02 +04:00
return - 1 ;
}
state - > header = ( struct ctdb_ltdb_header * ) & state - > data . dptr [ 0 ] ;
state - > data . dptr + = sizeof ( struct ctdb_ltdb_header ) ;
state - > data . dsize - = sizeof ( struct ctdb_ltdb_header ) ;
2008-05-22 06:47:33 +04:00
#if 0
2008-05-22 07:12:53 +04:00
/* We can not take out a lock here ourself since if this persistent
2008-05-22 06:47:33 +04:00
database needs safe transaction writes we can not be holding
a lock on the database .
Therefore we always create a lock wait child to take out and hold
the lock for us .
*/
2007-09-21 06:24:02 +04:00
ret = tdb_chainlock_nonblock ( state - > tdb , state - > key ) ;
if ( ret = = 0 ) {
ret = ctdb_persistent_store ( state ) ;
tdb_chainunlock ( state - > tdb , state - > key ) ;
2008-05-22 10:29:46 +04:00
talloc_free ( state ) ;
2007-09-21 06:24:02 +04:00
return ret ;
}
2008-05-22 06:47:33 +04:00
# endif
2007-09-21 06:24:02 +04:00
/* wait until we have a lock on this record */
handle = ctdb_lockwait ( ctdb_db , state - > key , ctdb_persistent_lock_callback , state ) ;
if ( handle = = NULL ) {
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_ERR , ( " Failed to setup lockwait handler in ctdb_control_update_record \n " ) ) ;
2008-05-22 10:29:46 +04:00
talloc_free ( state ) ;
2007-09-21 06:24:02 +04:00
return - 1 ;
}
2008-05-22 10:29:46 +04:00
/* we need to wait for the replies */
2007-09-21 06:24:02 +04:00
* async_reply = true ;
2008-05-22 10:29:46 +04:00
/* need to keep the control structure around */
talloc_steal ( state , c ) ;
/* but we won't wait forever */
event_add_timed ( ctdb - > ev , state , timeval_current_ofs ( 3 , 0 ) ,
2007-09-21 06:24:02 +04:00
ctdb_persistent_lock_timeout , state ) ;
return 0 ;
}