--- parser3/src/main/Attic/pa_db_connection.C 2001/10/23 12:41:05 1.2 +++ parser3/src/main/Attic/pa_db_connection.C 2001/10/26 16:27:17 1.15 @@ -1,17 +1,146 @@ /** @file - Parser: Charset connection implementation. + Parser: sql driver connection implementation. Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com) Author: Alexander Petrosyan (http://design.ru/paf) - $Id: pa_db_connection.C,v 1.2 2001/10/23 12:41:05 parser Exp $ + $Id: pa_db_connection.C,v 1.15 2001/10/26 16:27:17 paf Exp $ */ #include "pa_config_includes.h" #ifdef HAVE_LIBDB #include "pa_db_connection.h" +#include "pa_db_table.h" #include "pa_exception.h" +#include "pa_threads.h" +#include "pa_stack.h" +#include "pa_common.h" + +// defines + +#define DB_ERROR_PREFIX "db_err: " +#define DB_EXCEPTION_NO_LOG_MESSAGE1 DB_ERROR_PREFIX"Ignoring log file" +#define DB_EXCEPTION_NO_LOG_MESSAGE2 DB_ERROR_PREFIX"log_get: unable to find checkpoint record" + +// consts + +/// @test increase +const int DB_CHECKPOINT_MINUTES=1; + +const int EXPIRE_UNUSED_TABLE_SECONDS=60; +const int CHECK_EXPIRED_TABLES_SECONDS=EXPIRE_UNUSED_TABLE_SECONDS*2; + +// callbacks + +static void db_paniccall(DB_ENV *dbenv, int error) { + throw Exception(0, 0, + 0, + "db_panic: %s (%d)", + strerror(error), error); +} + +static void db_errcall(const char *, char *buffer) { + throw Exception(0, 0, + 0, + DB_ERROR_PREFIX "%s", + buffer); +} + +static void expire_table(const Hash::Key& key, Hash::Val *& value, void *info) { + DB_Connection& table=*static_cast(value); + time_t older_dies=reinterpret_cast(info); + + if(table.expired(older_dies)) { + table.~DB_Connection(); value=0; + } +} + +// DB_Connection + +DB_Connection::DB_Connection(Pool& apool, const String& adb_home) : Pooled(apool), + time_used(0), + fdb_home(adb_home), + table_cache(apool), + prev_expiration_pass_time(0) { + //_asm int 3; + char DB_DATA_DIR__VALUE[MAX_STRING]; + char DB_LOG_DIR__VALUE[MAX_STRING]; + char DB_TMP_DIR__VALUE[MAX_STRING]; + + const char *db_home_cstr=fdb_home.cstr(String::UL_FILE_SPEC); + + snprintf(DB_DATA_DIR__VALUE, MAX_STRING, "DB_DATA_DIR %s", db_home_cstr); + snprintf(DB_LOG_DIR__VALUE, MAX_STRING, "DB_LOG_DIR %s", db_home_cstr); + snprintf(DB_TMP_DIR__VALUE, MAX_STRING, "DB_TMP_DIR %s", db_home_cstr); + + char *db_config[] = { + DB_DATA_DIR__VALUE, + DB_LOG_DIR__VALUE, + DB_TMP_DIR__VALUE, + 0 + }; + + u_int32_t flags= + DB_THREAD + | DB_CREATE + | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN; + + try { + // 1: trying to open with SOFT RECOVER option set + memset(&dbenv, 0, sizeof(dbenv)); + check("db_appinit soft recover", &fdb_home, db_appinit( + db_home_cstr, + db_config, + &dbenv, + flags | DB_RECOVER + )); + } catch(const Exception& e) { + if( + strncmp( + e.comment(), + DB_EXCEPTION_NO_LOG_MESSAGE1, + strlen(DB_EXCEPTION_NO_LOG_MESSAGE1))==0 || + strncmp( + e.comment(), + DB_EXCEPTION_NO_LOG_MESSAGE2, + strlen(DB_EXCEPTION_NO_LOG_MESSAGE2))==0) { + // no log = no SOFT RECOVER + // 2.1: trying to open with NO RECOVER option set + memset(&dbenv, 0, sizeof(dbenv)); + check("db_appinit no recover", &fdb_home, db_appinit( + db_home_cstr, + db_config, + &dbenv, + flags + )); + } else { + // some serious error + // 2.2: trying to open with FATAL RECOVER option set + memset(&dbenv, 0, sizeof(dbenv)); + check("db_appinit fatal recover", &fdb_home, db_appinit( + db_home_cstr, + db_config, + &dbenv, + flags | DB_RECOVER_FATAL + )); + } + } + + // error handlers + dbenv.db_paniccall=db_paniccall; + dbenv.db_errcall=db_errcall; +} + +DB_Connection::~DB_Connection() { + // close tables + table_cache.for_each(expire_table, + reinterpret_cast(0/* =in the past = expire[close] all*/)); + + // destroy connection data + check("db_appexit", &fdb_home, db_appexit(&dbenv)); +} + void DB_Connection::check(const char *operation, const String *source, int error) { switch(error) { @@ -19,104 +148,78 @@ void DB_Connection::check(const char *op // no error break; - case DB_KEYEXIST: - // DB_KEYEXIST is a "normal" return, so should not be - // thrown as an error - break; - - case DB_RUNRECOVERY: - // mark as unsafe, so not to cache it - needs_recovery=true; - throw Exception(0, 0, - &ffile_spec, - "db %s error, run recovery utility", - operation); - default: throw Exception(0, 0, - &ffile_spec, + source, "db %s error: %s (%d)", operation, strerror(error), error); } } -DB_Connection::DB_Connection(Pool& pool, const String& afile_spec, DB_ENV& adbenv) : Pooled(pool), - fdbenv(adbenv), - ffile_spec(afile_spec), - fservices_pool(0), db(0), ftid(0), needs_recovery(false), - time_used(0) { -} +DB_Table_ptr DB_Connection::get_table_ptr(const String& request_file_name, const String *source) { + // checkpoint + check("checkpoint", source, + txn_checkpoint(dbenv.tx_info, 0/*kbyte*/, DB_CHECKPOINT_MINUTES/*min*/)); + + // first trying to get cached table + DB_Table *result=get_table_from_cache(request_file_name); + if(!result) { // no cached table + // make global_file_name C-string on global pool + const char *request_file_name_cstr=request_file_name.cstr(String::UL_AS_IS); + char *global_file_name_cstr=(char *)malloc(strlen(request_file_name_cstr)+1); + strcpy(global_file_name_cstr, request_file_name_cstr); + // make global_file_name string on global pool + String& global_file_name=*new(this->pool()) String(this->pool(), global_file_name_cstr); + + // allocate in global pool + // associate with services[request] + // NOTE: never freed up! + result=new(this->pool()) DB_Table(this->pool(), global_file_name, *this); + // cache it + put_table_to_cache(global_file_name, *result); + } -void DB_Connection::connect() { - // open + // return auto-it + return DB_Table_ptr(result); +} +void DB_Connection::clear_dbfile(const String& file_name) { + // open&clear + DB *db; DB_INFO dbinfo; memset(&dbinfo, 0, sizeof(dbinfo)); - check("open/create", &ffile_spec, db_open( - ffile_spec.cstr(String::UL_FILE_SPEC), + check("open(clear)", &file_name, db_open( + file_name.cstr(String::UL_FILE_SPEC), PA_DB_ACCESS_METHOD, - DB_CREATE /* used in single thread, no need for |DB_THREAD*/, + DB_CREATE | DB_TRUNCATE/* used in single thread, no need for |DB_THREAD*/, 0666, - &fdbenv, &dbinfo, &db)); + &dbenv, &dbinfo, &db)); + + check("close", &file_name, db->close(db, 0/*flags*/)); db=0; } -/// @todo this one of reasons of not having ^try for now -void DB_Connection::disconnect() { - check("close", &ffile_spec, db->close(db, 0/*flags*/)); db=0; + +// table cache +/// @todo get rid of memory spending Stack [zeros deep inside got accumulated] +DB_Table *DB_Connection::get_table_from_cache(const String& file_name) { + SYNCHRONIZED; + + return static_cast(table_cache.get(file_name)); } -/// @test string pieces [get/put preserve lang] -void DB_Connection::put(const String& key, const String& data) { - // key - const char *cstr_key=key.cstr(String::UL_AS_IS); - DBT dbt_key={ - (void *)cstr_key, - key.size() - }; +void DB_Connection::put_table_to_cache(const String& file_name, DB_Table& table) { + SYNCHRONIZED; - // data - const char *cstr_data=data.cstr(String::UL_AS_IS); - DBT dbt_data={ - (void *)cstr_data, - data.size() - }; - check("put", &key, db->put(db, ftid, &dbt_key, &dbt_data, 0/*flags*/)); + table_cache.put(file_name, &table); } -String *DB_Connection::get(const String& key) { - // key - const char *cstr_key=key.cstr(String::UL_AS_IS); - DBT dbt_key={ - (void *)cstr_key, - key.size() - }; +void DB_Connection::maybe_expire_table_cache() { + time_t now=time(0); - // data - DBT dbt_data={0}; // must be zeroed - int error=db->get(db, ftid, &dbt_key, &dbt_data, 0/*flags*/); - if(error==DB_NOTFOUND) - return 0; - else { - check("get", &key, error); - - if(dbt_data.size) { - char *request_data=(char *)malloc(dbt_data.size); - memcpy(request_data, dbt_data.data, dbt_data.size); - return NEW String(pool(), request_data, dbt_data.size, true/*tainted*/); - } else - return NEW String(pool()); - } -} - -void DB_Connection::_delete(const String& key) { - // key - const char *cstr_key=key.cstr(String::UL_AS_IS); - DBT dbt_key={ - (void *)cstr_key, - key.size() - }; + if(prev_expiration_pass_time(now-EXPIRE_UNUSED_TABLE_SECONDS)); - int error=db->del(db, ftid, &dbt_key, 0/*flags*/); - if(error!=DB_NOTFOUND) - check("del", &key, error); + prev_expiration_pass_time=now; + } } #endif