8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 17:23:03 +01:00

Implemented CORE-4263: Database linger

This commit is contained in:
alexpeshkoff 2013-11-14 16:16:24 +00:00
parent ae5e485cc5
commit 120b10a78a
31 changed files with 399 additions and 55 deletions

View File

@ -223,3 +223,16 @@ export FB_EXPECTED_DB=employee
fbsvcmgr host:service_mgr user sysdba password xxx action_db_stats dbname employee sts_data_pages
Certainly any other database action can be used here.
6) Services API extension - using services to temporary turn off linger for database.
(Alex Peshkov, peshkoff@mail.ru, 2013)
Linger is used to optimize performance in some cases (see also sql.extensions/README.linger).
If you want to turn off linger temporary (for next database close) you may use gfix utility or
services. New tag isc_spb_prp_nolinger is added for it (option). Setting isc_spb_prp_nolinger
option turns off linger for the next database close operation and may be used to force closing
database in linger state.
Example:
fbsvcmgr host:service_mgr user sysdba password xxx action_properties dbname employee prp_nolinger

View File

@ -0,0 +1,34 @@
SQL Language Extension: ALTER DATABASE SET/DROP LINGER
Implements capability to manage database linger.
Author:
Alex Peshkoff <peshkoff@mail.ru>
Syntax is:
ALTER DATABASE SET LINGER TO {seconds};
ALTER DATABASE DROP LINGER;
Description:
Makes it possible to set and clear linger value for database.
Database linger makes engine (when running in SS mode) do not close database immediately after
last attachment is closed. This helps to increase performance when database is often opened/closed
with almost zero price.
To set linger for database do:
ALTER DATABASE SET LINGER TO 30; -- will set linger interval to 30 seconds
To reset linger for database do:
ALTER DATABASE DROP LINGER; -- will make engine do not delay closing given database
ALTER DATABASE SET LINGER TO 0; -- another way to clean linger settings
Notice.
Sometimes it may be useful to turn off linger once to force server to close some database not
shutting it down. Dropping linger for it is not good solution - you will have to turn it on
manually later. To perform this task it's better to use GFIX utility with new switch 'NOLinger' -
it makes database be closed immediately when last attachment is gone no matter of linger interval
set in database. Next time linger will work normally. Services API is also available for it -
see details in README.services_extension).

View File

@ -63,6 +63,7 @@ const SINT64 sw_trusted_auth = QUADCONST(0x0000000100000000); // Byte 4, Bit 0
const SINT64 sw_trusted_svc = QUADCONST(0x0000000200000000);
const SINT64 sw_trusted_role = QUADCONST(0x0000000400000000);
const SINT64 sw_fetch_password = QUADCONST(0x0000000800000000);
const SINT64 sw_nolinger = QUADCONST(0x0000001000000000);
enum alice_switches
@ -117,7 +118,8 @@ enum alice_switches
IN_SW_ALICE_TRUSTED_USER = 45,
IN_SW_ALICE_TRUSTED_ROLE = 46,
IN_SW_ALICE_HIDDEN_ONLINE = 47,
IN_SW_ALICE_FETCH_PASSWORD = 48
IN_SW_ALICE_FETCH_PASSWORD = 48,
IN_SW_ALICE_NOLINGER = 49
};
static const char* const ALICE_SW_ASYNC = "ASYNC";
@ -129,7 +131,7 @@ static const char* const ALICE_SW_MODE_RW = "READ_WRITE";
static const Switches::in_sw_tab_t alice_in_sw_table[] =
{
{IN_SW_ALICE_ACTIVATE, isc_spb_prp_activate, "ACTIVATE_SHADOW", sw_activate,
0, ~(sw_activate | sw_user | sw_password), false, 25, 2, NULL},
0, ~(sw_activate | sw_user | sw_password | sw_nolinger), false, 25, 2, NULL},
// msg 25: \t-activate shadow file for database usage
{IN_SW_ALICE_ATTACH, isc_spb_prp_attachments_shutdown, "ATTACH", sw_attach,
sw_shut, 0, false, 26, 2, NULL},
@ -145,7 +147,7 @@ static const Switches::in_sw_tab_t alice_in_sw_table[] =
0, 0, false, 28, 1, NULL},
// msg 28: \t-buffers\tset page buffers <n>
{IN_SW_ALICE_COMMIT, isc_spb_rpr_commit_trans, "COMMIT", sw_commit,
0, ~(sw_commit | sw_user | sw_password), false, 29, 2, NULL},
0, ~(sw_commit | sw_user | sw_password | sw_nolinger), false, 29, 2, NULL},
// msg 29: \t-commit\t\tcommit transaction <tr / all>
{IN_SW_ALICE_CACHE, 0, "CACHE", sw_cache,
sw_shut, 0, false, 30, 2, NULL},
@ -176,14 +178,17 @@ static const Switches::in_sw_tab_t alice_in_sw_table[] =
0, 0, false, 36, 1, NULL},
// msg 36: \t-kill\t\tkill all unavailable shadow files
{IN_SW_ALICE_LIST, isc_spb_rpr_list_limbo_trans, "LIST", sw_list,
0, ~(sw_list | sw_user | sw_password), false, 37, 1, NULL},
0, ~(sw_list | sw_user | sw_password | sw_nolinger), false, 37, 1, NULL},
// msg 37: \t-list\t\tshow limbo transactions
{IN_SW_ALICE_MEND, isc_spb_rpr_mend_db, "MEND", sw_mend | sw_validate | sw_full,
0, ~(sw_no_update | sw_user | sw_password), false, 38, 2, NULL},
0, ~(sw_no_update | sw_user | sw_password | sw_nolinger), false, 38, 2, NULL},
// msg 38: \t-mend\t\tprepare corrupt database for backup
{IN_SW_ALICE_MODE, 0, "MODE", sw_mode,
0, ~(sw_mode | sw_user | sw_password), false, 109, 2, NULL},
0, ~(sw_mode | sw_user | sw_password | sw_nolinger), false, 109, 2, NULL},
// msg 109: \t-mode\t\tread_only or read_write
{IN_SW_ALICE_NOLINGER, isc_spb_prp_nolinger, "NOLINGER", sw_nolinger,
0, sw_shut, false, 121, 3, NULL},
// msg 121: -nolinger do not use linger on database this time (once)
{IN_SW_ALICE_NO_UPDATE, isc_spb_rpr_check_db, "NO_UPDATE", sw_no_update,
sw_validate, 0, false, 39, 1, NULL},
// msg 39: \t-no_update\tread-only validation (-v)
@ -205,20 +210,20 @@ static const Switches::in_sw_tab_t alice_in_sw_table[] =
*/
#endif
{IN_SW_ALICE_ROLLBACK, isc_spb_rpr_rollback_trans, "ROLLBACK", sw_rollback,
0, ~(sw_rollback | sw_user | sw_password), false, 44, 1, NULL},
0, ~(sw_rollback | sw_user | sw_password | sw_nolinger), false, 44, 1, NULL},
// msg 44: \t-rollback\trollback transaction <tr / all>
{IN_SW_ALICE_SET_DB_SQL_DIALECT, isc_spb_prp_set_sql_dialect, "SQL_DIALECT", sw_set_db_dialect,
0, 0, false, 111, 2, NULL},
// msg 111: \t-SQL_dialect\t\set dataabse dialect n
{IN_SW_ALICE_SWEEP, isc_spb_rpr_sweep_db, "SWEEP", sw_sweep,
0, ~(sw_sweep | sw_user | sw_password), false, 45, 2, NULL},
0, ~(sw_sweep | sw_user | sw_password | sw_nolinger), false, 45, 2, NULL},
// msg 45: \t-sweep\t\tforce garbage collection
{IN_SW_ALICE_SHUT, isc_spb_prp_shutdown_mode, "SHUTDOWN", sw_shut,
0, ~(sw_shut | sw_attach | sw_cache | sw_force | sw_tran | sw_user | sw_password),
false, 46, 2, NULL},
// msg 46: \t-shut\t\tshutdown
{IN_SW_ALICE_TWO_PHASE, isc_spb_rpr_recover_two_phase, "TWO_PHASE", sw_two_phase,
0, ~(sw_two_phase | sw_user | sw_password), false, 47, 2, NULL},
0, ~(sw_two_phase | sw_user | sw_password | sw_nolinger), false, 47, 2, NULL},
// msg 47: \t-two_phase\tperform automated two-phase recovery
{IN_SW_ALICE_TRAN, isc_spb_prp_transactions_shutdown, "TRANSACTION", sw_tran,
sw_shut, 0, false, 48, 3, NULL},
@ -233,16 +238,16 @@ static const Switches::in_sw_tab_t alice_in_sw_table[] =
{IN_SW_ALICE_TRUSTED_ROLE, 0, TRUSTED_ROLE_SWITCH, sw_trusted_role,
sw_trusted_svc, (sw_user | sw_password), false, 0, TRUSTED_ROLE_SWITCH_LEN, NULL},
{IN_SW_ALICE_NO_RESERVE, 0, "USE", sw_no_reserve,
0, ~(sw_no_reserve | sw_user | sw_password), false, 49, 1, NULL},
0, ~(sw_no_reserve | sw_user | sw_password | sw_nolinger), false, 49, 1, NULL},
// msg 49: \t-use\t\tuse full or reserve space for versions
{IN_SW_ALICE_USER, 0, "USER", sw_user,
0, (sw_trusted_auth | sw_trusted_svc | sw_trusted_role), false, 50, 4, NULL},
// msg 50: \t-user\t\tdefault user name
{IN_SW_ALICE_VALIDATE, isc_spb_rpr_validate_db, "VALIDATE", sw_validate,
0, ~(sw_validate | sw_user | sw_password), false, 51, 1, NULL},
0, ~(sw_validate | sw_user | sw_password | sw_nolinger), false, 51, 1, NULL},
// msg 51: \t-validate\tvalidate database structure
{IN_SW_ALICE_WRITE, 0, "WRITE", sw_write,
0, ~(sw_write | sw_user | sw_password), false, 52, 1, NULL},
0, ~(sw_write | sw_user | sw_password | sw_nolinger), false, 52, 1, NULL},
// msg 52: \t-write\t\twrite synchronously or asynchronously
#ifdef DEV_BUILD
{IN_SW_ALICE_X, 0, "X", 0,

View File

@ -323,6 +323,9 @@ static void buildDpb(Firebird::ClumpletWriter& dpb, const SINT64 switches)
dpb.insertInt(isc_dpb_set_db_sql_dialect, tdgbl->ALICE_data.ua_db_SQL_dialect);
}
if (switches & sw_nolinger)
dpb.insertTag(isc_dpb_nolinger);
const unsigned char* authBlock;
unsigned int authBlockSize = tdgbl->uSvc->getAuthBlock(&authBlock);

View File

@ -28,6 +28,12 @@ CREATE DOMAIN PLG$PASSWD AS VARCHAR(64) CHARACTER SET BINARY;
COMMIT;
/* Linger is definitely useful for security database */
ALTER DATABASE SET LINGER TO 60; /* one minute */
COMMIT;
/* Table: RDB$USERS */
CREATE TABLE PLG$USERS (
PLG$USER_NAME SEC$USER_NAME NOT NULL PRIMARY KEY,

View File

@ -10056,6 +10056,12 @@ void AlterDatabaseNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
alterCharSetNode.execute(tdbb, dsqlScratch, transaction);
}
if (linger >= 0)
{
DBB.RDB$LINGER.NULL = FALSE;
DBB.RDB$LINGER = linger;
}
if (clauses & CLAUSE_BEGIN_BACKUP)
changeBackupMode(tdbb, transaction, CLAUSE_BEGIN_BACKUP);

View File

@ -1945,6 +1945,7 @@ public:
: DdlNode(p),
create(false),
createLength(0),
linger(-1),
clauses(0),
differenceFile(p),
setDefaultCharSet(p),
@ -1977,7 +1978,7 @@ private:
public:
bool create; // Is the node created with a CREATE DATABASE command?
SLONG createLength;
SLONG createLength, linger;
unsigned clauses;
Firebird::string differenceFile;
Firebird::MetaName setDefaultCharSet;

View File

@ -561,6 +561,7 @@ using namespace Firebird;
%token <metaNamePtr> UNKNOWN
%token <metaNamePtr> USAGE
%token <metaNamePtr> RDB_RECORD_VERSION
%token <metaNamePtr> LINGER
// precedence declarations for expression evaluation
@ -3697,6 +3698,10 @@ db_alter_clause($alterDatabaseNode)
{ $alterDatabaseNode->cryptPlugin = *$3; }
| DECRYPT
{ $alterDatabaseNode->clauses |= AlterDatabaseNode::CLAUSE_DECRYPT; }
| SET LINGER TO long_integer
{ $alterDatabaseNode->linger = $4; }
| DROP LINGER
{ $alterDatabaseNode->linger = 0; }
;
@ -7111,6 +7116,7 @@ non_reserved_word
| RANK
| ROW_NUMBER
| USAGE
| LINGER
;
%%

View File

@ -120,6 +120,7 @@
#define isc_dpb_auth_plugin_list 85
#define isc_dpb_auth_plugin_name 86
#define isc_dpb_config 87
#define isc_dpb_nolinger 88
/**************************************************/
/* clumplet tags used inside isc_dpb_address_path */
@ -403,6 +404,7 @@
#define isc_spb_prp_set_sql_dialect 14
#define isc_spb_prp_activate 0x0100
#define isc_spb_prp_db_online 0x0200
#define isc_spb_prp_nolinger 0x0400
#define isc_spb_prp_force_shutdown 41
#define isc_spb_prp_attachments_shutdown 42
#define isc_spb_prp_transactions_shutdown 43

View File

@ -146,8 +146,9 @@ public:
virtual const char* FB_CARG getConfigFileName() = 0;
virtual IConfig* FB_CARG getDefaultConfig() = 0;
virtual IFirebirdConf* FB_CARG getFirebirdConf() = 0;
virtual void FB_CARG setReleaseDelay(ISC_UINT64 microSeconds) = 0;
};
#define FB_PLUGIN_CONFIG_VERSION (FB_REFCOUNTED_VERSION + 3)
#define FB_PLUGIN_CONFIG_VERSION (FB_REFCOUNTED_VERSION + 4)
// Required to creat instances of given plugin
class IPluginFactory : public IVersioned

View File

@ -39,6 +39,7 @@
const USHORT f_dat_id = 1;
const USHORT f_dat_class = 2;
const USHORT f_dat_charset = 3;
const USHORT f_dat_linger = 4;
// Relation 2 (RDB$FIELDS)

View File

@ -77,6 +77,11 @@ namespace Jrd
Database::~Database()
{
if (dbb_linger_timer)
{
dbb_linger_timer->destroy();
}
{ // scope
SyncLockGuard guard(&dbb_sortbuf_sync, SYNC_EXCLUSIVE, "Database::~Database");
@ -313,4 +318,45 @@ namespace Jrd
return 0;
}
void Database::Linger::handler()
{
JRD_shutdown_database(dbb, SHUT_DBB_RELEASE_POOLS);
}
int Database::Linger::release()
{
if (--refCounter == 0)
{
delete this;
return 0;
}
return 1;
}
void Database::Linger::reset()
{
if (active)
{
TimerInterfacePtr()->stop(this);
active = false;
}
}
void Database::Linger::set(unsigned seconds)
{
if (dbb)
{
TimerInterfacePtr()->start(this, seconds * 1000 * 1000);
active = true;
}
}
void Database::Linger::destroy()
{
dbb = NULL;
reset();
}
} // namespace

View File

@ -301,11 +301,31 @@ public:
bool exist;
};
static Database* create()
class Linger : public Firebird::RefCntIface<Firebird::ITimer, FB_TIMER_VERSION>
{
public:
Linger(Database* a_dbb)
: dbb(a_dbb), active(false)
{ }
void set(unsigned seconds);
void reset();
void destroy();
// ITimer implementation
void FB_CARG handler();
int FB_CARG release();
private:
Database* dbb;
bool active;
};
static Database* create(Firebird::IPluginConfig* pConf)
{
Firebird::MemoryStats temp_stats;
MemoryPool* const pool = MemoryPool::createPool(NULL, temp_stats);
Database* const dbb = FB_NEW(*pool) Database(pool);
Database* const dbb = FB_NEW(*pool) Database(pool, pConf);
pool->setStatsGroup(dbb->dbb_memory_stats);
return dbb;
}
@ -437,6 +457,10 @@ public:
SharedCounter dbb_shared_counter;
CryptoManager* dbb_crypto_manager;
Firebird::RefPtr<ExistenceRefMutex> dbb_init_fini;
Firebird::RefPtr<Linger> dbb_linger_timer;
unsigned dbb_linger_seconds;
time_t dbb_linger_end;
Firebird::RefPtr<Firebird::IPluginConfig> dbb_plugin_config;
// returns true if primary file is located on raw device
bool onRawDevice() const;
@ -464,7 +488,7 @@ public:
void deletePool(MemoryPool* pool);
private:
explicit Database(MemoryPool* p)
Database(MemoryPool* p, Firebird::IPluginConfig* pConf)
: dbb_permanent(p),
dbb_page_manager(this, *p),
dbb_modules(*p),
@ -478,7 +502,10 @@ private:
dbb_tip_cache(NULL),
dbb_creation_date(Firebird::TimeStamp::getCurrentTimeStamp()),
dbb_external_file_directory_list(NULL),
dbb_init_fini(FB_NEW(*getDefaultMemoryPool()) ExistenceRefMutex())
dbb_init_fini(FB_NEW(*getDefaultMemoryPool()) ExistenceRefMutex()),
dbb_linger_seconds(0),
dbb_linger_end(0),
dbb_plugin_config(pConf)
{
dbb_pools.add(p);
}

View File

@ -453,8 +453,8 @@ private:
class JProvider : public Firebird::StdPlugin<Firebird::IProvider, FB_PROVIDER_VERSION>
{
public:
explicit JProvider(Firebird::IPluginConfig*)
: cryptCallback(NULL)
explicit JProvider(Firebird::IPluginConfig* pConf)
: cryptCallback(NULL), pluginConfig(pConf)
{
}
@ -479,6 +479,7 @@ public:
private:
Firebird::ICryptKeyCallback* cryptCallback;
Firebird::IPluginConfig* pluginConfig;
};
inline JStatement::JStatement(dsql_req* handle, JAttachment* ja, Firebird::Array<UCHAR>& meta)

View File

@ -403,6 +403,7 @@ static bool drop_package_header(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool drop_package_body(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool grant_privileges(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool db_crypt(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool set_linger(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
// ----------------------------------------------------------------
@ -493,6 +494,7 @@ static const deferred_task task_table[] =
{ dfw_check_not_null, check_not_null },
{ dfw_store_view_context_type, store_view_context_type },
{ dfw_db_crypt, db_crypt },
{ dfw_set_linger, set_linger },
{ dfw_null, NULL }
};
@ -1526,6 +1528,36 @@ static bool db_crypt(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*
return false;
}
static bool set_linger(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*)
{
/**************************************
*
* s e t _ l i n g e r
*
**************************************
*
* Set linger interval in Database block.
*
**************************************/
SET_TDBB(tdbb);
Database* const dbb = tdbb->getDatabase();
switch (phase)
{
case 1:
case 2:
case 3:
return true;
case 4:
dbb->dbb_linger_seconds = atoi(work->dfw_name.c_str()); // number stored as string
break;
}
return false;
}
static bool check_not_null(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
{
/**************************************

View File

@ -177,3 +177,5 @@
FIELD(fld_os_user , nam_os_user , dtype_varying , 255 , dsc_text_type_metadata , NULL , true)
FIELD(fld_gen_val , nam_gen_val , dtype_int64 , sizeof(SINT64) , 0 , NULL , true)
FIELD(fld_auth_method , nam_auth_method , dtype_varying , 255 , dsc_text_type_ascii , NULL , true)
FIELD(fld_linger , nam_linger , dtype_long , sizeof(SLONG) , 0 , NULL , true)

View File

@ -165,6 +165,8 @@ enum irq_type_t
irq_grant15, // process grant option (generators)
irq_grant16, // process grant option (domains)
irq_linger, // get database linger value
irq_MAX
};

View File

@ -335,7 +335,6 @@ int JProvider::release()
return 1;
}
static void shutdownBeforeUnload()
{
LocalStatus status;
@ -837,6 +836,7 @@ public:
bool dpb_utf8_filename;
ULONG dpb_ext_call_depth;
ULONG dpb_flags; // to OR'd with dbb_flags
bool dpb_nolinger;
// here begin compound objects
// for constructor to work properly dpb_user_name
@ -950,20 +950,22 @@ static VdnResult verifyDatabaseName(const PathName&, ISC_STATUS*, bool);
static void unwindAttach(thread_db* tdbb, const Exception& ex, Firebird::IStatus* userStatus,
Jrd::Attachment* attachment, Database* dbb);
static JAttachment* init(thread_db*, const PathName&, const PathName&, RefPtr<Config>, bool,
const DatabaseOptions&, RefMutexUnlock&);
const DatabaseOptions&, RefMutexUnlock&, IPluginConfig*);
static JAttachment* create_attachment(const PathName&, Database*, const DatabaseOptions&);
static void prepare_tra(thread_db*, jrd_tra*, USHORT, const UCHAR*);
static void start_transaction(thread_db* tdbb, bool transliterate, jrd_tra** tra_handle,
Jrd::Attachment* attachment, unsigned int tpb_length, const UCHAR* tpb);
static void release_attachment(thread_db*, Jrd::Attachment*);
static void rollback(thread_db*, jrd_tra*, const bool);
static bool shutdown_database(Database*, const bool);
static void strip_quotes(string&);
static void purge_attachment(thread_db*, JAttachment*, const bool);
static void purge_attachment(thread_db* tdbb, JAttachment* jAtt, unsigned flags = 0);
static void getUserInfo(UserId&, const DatabaseOptions&, const char*, const RefPtr<Config>*);
static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM);
// purge_attachment() flags
static const unsigned PURGE_FORCE = 0x01;
static const unsigned PURGE_LINGER = 0x02;
TraceFailedConnection::TraceFailedConnection(const char* filename, const DatabaseOptions* options) :
m_filename(filename),
@ -1331,7 +1333,7 @@ JAttachment* FB_CARG JProvider::attachDatabase(IStatus* user_status, const char*
// Unless we're already attached, do some initialization
RefMutexUnlock initGuard;
JAttachment* jAtt = init(tdbb, expanded_name, is_alias ? org_filename : expanded_name,
config, true, options, initGuard);
config, true, options, initGuard, pluginConfig);
dbb = tdbb->getDatabase();
fb_assert(dbb);
@ -1431,6 +1433,9 @@ JAttachment* FB_CARG JProvider::attachDatabase(IStatus* user_status, const char*
SDW_init(tdbb, options.dpb_activate_shadow, options.dpb_delete_shadow);
CCH_init2(tdbb);
// linger
dbb->dbb_linger_seconds = MET_get_linger(tdbb);
// Init complete - we can release dbInitMutex
dbb->dbb_flags &= ~DBB_new;
guardDbInit.leave();
@ -1464,6 +1469,11 @@ JAttachment* FB_CARG JProvider::attachDatabase(IStatus* user_status, const char*
attachment->att_flags |= ATT_no_cleanup;
}
if (options.dpb_nolinger)
{
dbb->dbb_linger_seconds = 0;
}
if (options.dpb_disable_wal)
{
ERR_post(Arg::Gds(isc_lock_timeout) <<
@ -2422,7 +2432,7 @@ JAttachment* FB_CARG JProvider::createDatabase(IStatus* user_status, const char*
// Unless we're already attached, do some initialization
RefMutexUnlock initGuard;
JAttachment* jAtt = init(tdbb, expanded_name, (is_alias ? org_filename : expanded_name),
config, false, options, initGuard);
config, false, options, initGuard, pluginConfig);
dbb = tdbb->getDatabase();
fb_assert(dbb);
@ -2783,12 +2793,16 @@ void JAttachment::freeEngineData(IStatus* user_status)
if (attachment->att_in_use)
status_exception::raise(Arg::Gds(isc_attachment_in_use));
const bool force = engineShutdown ||
unsigned flags = PURGE_LINGER;
if (engineShutdown ||
(dbb->dbb_ast_flags & DBB_shutdown) ||
(attachment->att_flags & ATT_shutdown);
(attachment->att_flags & ATT_shutdown))
{
flags |= PURGE_FORCE;
}
attachment->signalShutdown();
purge_attachment(tdbb, this, force);
purge_attachment(tdbb, this, flags);
att = NULL;
}
@ -2927,7 +2941,7 @@ void JAttachment::dropDatabase(IStatus* user_status)
const jrd_file* file = pageSpace->file;
const Shadow* shadow = dbb->dbb_shadow;
if (shutdown_database(dbb, false))
if (JRD_shutdown_database(dbb))
{
// This point on database is useless
@ -5685,6 +5699,10 @@ void DatabaseOptions::get(const UCHAR* dpb, USHORT dpb_length, bool& invalid_cli
dpb_overwrite = rdr.getInt() != 0;
break;
case isc_dpb_nolinger:
dpb_nolinger = true;
break;
case isc_dpb_sec_attach:
dpb_sec_attach = rdr.getInt() != 0;
if (dpb_sec_attach)
@ -5857,7 +5875,8 @@ static JAttachment* init(thread_db* tdbb,
RefPtr<Config> config,
bool attach_flag, // only for shared cache
const DatabaseOptions& options,
RefMutexUnlock& initGuard)
RefMutexUnlock& initGuard,
IPluginConfig* pConf)
{
/**************************************
*
@ -5933,6 +5952,11 @@ static JAttachment* init(thread_db* tdbb,
tdbb->setDatabase(dbb);
jAtt = create_attachment(alias_name, dbb, options);
if (dbb->dbb_linger_timer)
{
dbb->dbb_linger_timer->reset();
}
tdbb->setAttachment(jAtt->getHandle());
if (options.dpb_config.hasData())
@ -5962,7 +5986,7 @@ static JAttachment* init(thread_db* tdbb,
Config::merge(config, &options.dpb_config);
dbb = Database::create();
dbb = Database::create(pConf);
dbb->dbb_config = config;
dbb->dbb_filename = expanded_name;
@ -6339,19 +6363,39 @@ static void rollback(thread_db* tdbb, jrd_tra* transaction, const bool retaining
}
static bool shutdown_database(Database* dbb, const bool release_pools)
static void setEngineReleaseDelay(Database* dbb)
{
/**************************************
unsigned maxLinger = 0;
{ // scope
MutexLockGuard listGuardForLinger(databases_mutex, FB_FUNCTION);
for (Database* d = databases; d; d = d->dbb_next)
{
if ((!d->dbb_attachments) && (d->dbb_linger_end > maxLinger))
{
maxLinger = d->dbb_linger_end;
}
}
}
++maxLinger; // avoid rounding errors
time_t t = time(NULL);
dbb->dbb_plugin_config->setReleaseDelay(maxLinger > t ? (maxLinger - t) * 1000 * 1000 : 0);
}
bool JRD_shutdown_database(Database* dbb, const unsigned flags)
{
/*************************************************
*
* s h u t d o w n _ d a t a b a s e
* J R D _ s h u t d o w n _ d a t a b a s e
*
**************************************
*************************************************
*
* Functional description
* Shutdown physical database environment.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
ThreadContextHolder tdbb(dbb, NULL);
RefMutexUnlock finiGuard;
@ -6389,6 +6433,29 @@ static bool shutdown_database(Database* dbb, const bool release_pools)
if (dbb->dbb_attachments)
return false;
// Database linger
if ((flags & SHUT_DBB_LINGER) &&
(!(engineShutdown || (dbb->dbb_ast_flags & DBB_shutdown))) &&
(dbb->dbb_linger_seconds > 0) &&
dbb->dbb_config->getSharedCache())
{
if (!dbb->dbb_linger_timer)
{
dbb->dbb_linger_timer = new Database::Linger(dbb);
}
dbb->dbb_linger_end = time(NULL) + dbb->dbb_linger_seconds;
dbb->dbb_linger_timer->set(dbb->dbb_linger_seconds);
setEngineReleaseDelay(dbb);
return false;
}
// Reset provider unload delay if needed
dbb->dbb_linger_end = 0;
setEngineReleaseDelay(dbb);
// Deactivate dbb_init_fini lock
// Since that moment dbb becomes not reusable
dbb->dbb_init_fini->destroy();
@ -6457,7 +6524,7 @@ static bool shutdown_database(Database* dbb, const bool release_pools)
}
}
if (release_pools)
if (flags & SHUT_DBB_RELEASE_POOLS)
{
tdbb->setDatabase(NULL);
Database::destroy(dbb);
@ -6657,7 +6724,7 @@ static void purge_transactions(thread_db* tdbb, Jrd::Attachment* attachment, con
}
static void purge_attachment(thread_db* tdbb, JAttachment* jAtt, const bool force_flag)
static void purge_attachment(thread_db* tdbb, JAttachment* jAtt, unsigned flags)
{
/**************************************
*
@ -6781,7 +6848,7 @@ static void purge_attachment(thread_db* tdbb, JAttachment* jAtt, const bool forc
}
catch (const Exception&)
{
if (!force_flag)
if (!(flags & PURGE_FORCE))
{
attachment->att_flags |= ATT_shutdown;
attachment->att_flags &= ~ATT_purge_started;
@ -6801,12 +6868,12 @@ static void purge_attachment(thread_db* tdbb, JAttachment* jAtt, const bool forc
if (!(dbb->dbb_flags & DBB_bugcheck))
{
// Check for any pending transactions
purge_transactions(tdbb, attachment, force_flag);
purge_transactions(tdbb, attachment, flags & PURGE_FORCE);
}
}
catch (const Exception&)
{
if (!force_flag)
if (!(flags & PURGE_FORCE))
{
attachment->att_flags |= ATT_shutdown;
attachment->att_flags &= ~ATT_purge_started;
@ -6840,8 +6907,8 @@ static void purge_attachment(thread_db* tdbb, JAttachment* jAtt, const bool forc
asyncGuard.leave();
MutexUnlockGuard cout(*attMutex, FB_FUNCTION);
// If there are still attachments, do a partial shutdown
shutdown_database(dbb, true);
// Try to close database if there are no attachments
JRD_shutdown_database(dbb, SHUT_DBB_RELEASE_POOLS | (flags & PURGE_LINGER ? SHUT_DBB_LINGER : 0));
}
@ -7105,7 +7172,7 @@ static void unwindAttach(thread_db* tdbb, const Exception& ex, IStatus* userStat
}
}
shutdown_database(dbb, true);
JRD_shutdown_database(dbb, SHUT_DBB_RELEASE_POOLS);
}
}
catch (const Exception&)
@ -7155,7 +7222,7 @@ namespace
{
// purge attachment, rollback any open transactions
attachment->att_use_count++;
purge_attachment(tdbb, jAtt, true);
purge_attachment(tdbb, jAtt, PURGE_FORCE);
}
catch (const Exception& ex)
{
@ -7241,6 +7308,20 @@ static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM arg)
// Shutdown existing attachments
success = success && shutdownAttachments(attachments);
{ // scope
MutexLockGuard guard(databases_mutex, FB_FUNCTION);
for (Database** ptrDbb = &databases; *ptrDbb;)
{
Database* dbb = *ptrDbb;
JRD_shutdown_database(dbb, SHUT_DBB_RELEASE_POOLS);
if (dbb == *ptrDbb)
{
ptrDbb = &(dbb->dbb_next);
}
}
// No need in databases_mutex any more
}
// Extra shutdown operations
Service::shutdownServices();
}

View File

@ -76,4 +76,10 @@ void JRD_compile(Jrd::thread_db* tdbb, Jrd::Attachment* attachment, Jrd::jrd_req
bool JRD_verify_database_access(const Firebird::PathName&);
void JRD_shutdown_attachments(Jrd::Database* dbb);
void JRD_cancel_operation(Jrd::thread_db* tdbb, Jrd::Attachment* attachment, int option);
bool JRD_shutdown_database(Jrd::Database* dbb, const unsigned flags = 0);
// JRD_shutdown_database() flags
static const unsigned SHUT_DBB_RELEASE_POOLS = 0x01;
static const unsigned SHUT_DBB_LINGER = 0x02;
#endif /* JRD_JRD_PROTO_H */

View File

@ -5009,3 +5009,33 @@ static bool verify_TRG_ignore_perm(thread_db* tdbb, const MetaName& trig_name)
return false;
}
int MET_get_linger(Jrd::thread_db* tdbb)
{
/**************************************
*
* M E T _ g e t _ l i n g e r
*
**************************************
*
* Functional description
* Return linger value for current database
*
**************************************/
SET_TDBB(tdbb);
Jrd::Attachment* attachment = tdbb->getAttachment();
int rc = 0;
AutoCacheRequest request(tdbb, irq_linger, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
DAT IN RDB$DATABASE
{
if (!DAT.RDB$LINGER.NULL)
{
rc = DAT.RDB$LINGER;
}
}
END_FOR
return rc;
}

View File

@ -129,5 +129,5 @@ void MET_get_domain(Jrd::thread_db*, MemoryPool& csbPool, const Firebird::MetaN
Firebird::MetaName MET_get_relation_field(Jrd::thread_db*, MemoryPool& csbPool,
const Firebird::MetaName&, const Firebird::MetaName&, dsc*, Jrd::FieldInfo*);
void MET_update_partners(Jrd::thread_db*);
int MET_get_linger(Jrd::thread_db*);
#endif // JRD_MET_PROTO_H

View File

@ -248,6 +248,7 @@ NAME("RDB$PARAMETER_MECHANISM", nam_prm_mechanism)
NAME("RDB$SOURCE_INFO", nam_src_info)
NAME("RDB$ENGINE_NAME", nam_engine_name)
NAME("RDB$LINGER", nam_linger)
NAME("RDB$PACKAGES", nam_packages)
NAME("RDB$PACKAGE_NAME", nam_pkg_name)

View File

@ -38,6 +38,7 @@ RELATION(nam_database, rel_database, ODS_8_0, rel_persistent)
FIELD(f_dat_id, nam_r_id, fld_r_id, 0, ODS_8_0)
FIELD(f_dat_class, nam_class, fld_class, 1, ODS_8_0)
FIELD(f_dat_charset, nam_charset_name, fld_charset_name, 1, ODS_8_0)
FIELD(f_dat_linger, nam_linger, fld_linger, 1, ODS_12_0)
END_RELATION
// Relation 2 (RDB$FIELDS)

View File

@ -502,7 +502,8 @@ enum dfw_t {
dfw_arg_trg_type, // trigger type
dfw_arg_new_name, // new name
dfw_arg_field_not_null, // set domain to not nullable
dfw_db_crypt // change database encryption status
dfw_db_crypt, // change database encryption status
dfw_set_linger // set database linger
};
// Verb actions

View File

@ -2362,6 +2362,12 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
{
case rel_database:
check_class(tdbb, transaction, org_rpb, new_rpb, f_dat_class);
EVL_field(0, org_rpb->rpb_record, f_dat_linger, &desc1);
EVL_field(0, new_rpb->rpb_record, f_dat_linger, &desc2);
if (MOV_compare(&desc1, &desc2))
{
DFW_post_work(transaction, dfw_set_linger, &desc2, 0);
}
break;
case rel_relations:

View File

@ -3,7 +3,7 @@ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUM
--
('2013-11-05 13:18:17', 'JRD', 0, 752)
('2012-01-23 20:10:30', 'QLI', 1, 532)
('2009-07-16 05:26:11', 'GFIX', 3, 121)
('2013-11-13 15:59:10', 'GFIX', 3, 122)
('1996-11-07 13:39:40', 'GPRE', 4, 1)
('2012-08-27 21:26:00', 'DSQL', 7, 33)
('2013-09-05 12:40:00', 'DYN', 8, 286)

View File

@ -1591,6 +1591,7 @@ COMMIT WORK;
(NULL, 'ALICE_gfix', 'alice.cpp', NULL, 3, 118, NULL, 'empty password file @1', NULL, NULL);
(NULL, 'ALICE_gfix', 'alice.cpp', NULL, 3, 119, NULL, ' -fe(tch_password) fetch password from file', NULL, NULL);
(NULL, 'alice', 'alice.cpp', NULL, 3, 120, NULL, 'usage: gfix [options] <database>', NULL, NULL);
('gfix_opt_nolinger', 'ALICE_gfix', 'alice.c', NULL, 3, 121, NULL, ' -nol(inger) close database ignoring linger setting for it', NULL, NULL);
-- DSQL
('dsql_dbkey_from_non_table', 'MAKE_desc', 'make.c', NULL, 7, 2, NULL, 'Cannot SELECT RDB$DB_KEY from a stored procedure.', NULL, NULL);
('dsql_transitional_numeric', 'dsql_yyparse', 'parse.y', NULL, 7, 3, NULL, 'Precision 10 to 18 changed from DOUBLE PRECISION in SQL dialect 1 to 64-bit scaled integer in SQL dialect 3', NULL, NULL);

View File

@ -400,7 +400,7 @@ int CLIB_ROUTINE main( int argc, char** argv)
const Firebird::RefPtr<Config> defConf(Config::getDefaultConfig());
const char* path = defConf->getSecurityDatabase();
const char dpb[] = {isc_dpb_version1, isc_dpb_gsec_attach, 1, 1, isc_dpb_address_path, 0};
const char dpb[] = {isc_dpb_version1, isc_dpb_sec_attach, 1, 1, isc_dpb_address_path, 0};
isc_attach_database(status, strlen(path), path, &db_handle, sizeof dpb, dpb);
if (status[0] == 1 && status[1] > 0)

View File

@ -390,6 +390,7 @@ const SvcSwitches propertiesOptions[] =
{"prp_transactions_shutdown", putNumericArgument, 0, isc_spb_prp_transactions_shutdown, 0},
{"prp_shutdown_mode", putShutdownMode, 0, isc_spb_prp_shutdown_mode, 0},
{"prp_online_mode", putShutdownMode, 0, isc_spb_prp_online_mode, 0},
{"prp_nolinger", putOption, 0, isc_spb_prp_nolinger, 0},
{0, 0, 0, 0, 0}
};

View File

@ -406,7 +406,8 @@ namespace
RefPtr<ConfigFile> pconfig, const PathName& pconfName,
const PathName& pplugName)
: module(pmodule), regPlugin(preg), defaultConfig(pconfig),
confName(getPool(), pconfName), plugName(getPool(), pplugName)
confName(getPool(), pconfName), plugName(getPool(), pplugName),
delay(DEFAULT_DELAY)
{
if (defaultConfig.hasData())
{
@ -461,6 +462,19 @@ namespace
return plugName.c_str();
}
void setReleaseDelay(ISC_UINT64 microSeconds)
{
#ifdef DEBUG_PLUGINS
fprintf(stderr, "Set delay for ConfiguredPlugin %s:%p\n", plugName.c_str(), this);
#endif
delay = microSeconds > DEFAULT_DELAY ? microSeconds : DEFAULT_DELAY;
}
ISC_UINT64 getReleaseDelay()
{
return delay;
}
// ITimer implementation
void FB_CARG handler()
{ }
@ -473,6 +487,9 @@ namespace
RefPtr<ConfigFile> defaultConfig;
PathName confName;
PathName plugName;
static const FB_UINT64 DEFAULT_DELAY = 1000000; // 1 sec
FB_UINT64 delay;
};
// Provides per-database configuration from databases.conf.
@ -506,6 +523,11 @@ namespace
return firebirdConf;
}
void FB_CARG setReleaseDelay(ISC_UINT64 microSeconds)
{
configuredPlugin->setReleaseDelay(microSeconds);
}
int FB_CARG release()
{
if (--refCounter == 0)
@ -521,9 +543,10 @@ namespace
~FactoryParameter()
{
#ifdef DEBUG_PLUGINS
fprintf(stderr, "~FactoryParameter places configuredPlugin %s in unload query\n", configuredPlugin->getPlugName());
fprintf(stderr, "~FactoryParameter places configuredPlugin %s in unload query for %d seconds\n",
configuredPlugin->getPlugName(), configuredPlugin->getReleaseDelay() / 1000000);
#endif
TimerInterfacePtr()->start(configuredPlugin, 1000000); // 1 sec
TimerInterfacePtr()->start(configuredPlugin, configuredPlugin->getReleaseDelay());
}
RefPtr<ConfiguredPlugin> configuredPlugin;
@ -532,13 +555,17 @@ namespace
IPluginBase* ConfiguredPlugin::factory(IFirebirdConf* firebirdConf)
{
RefPtr<FactoryParameter> par(new FactoryParameter(this, firebirdConf));
FactoryParameter* par = new FactoryParameter(this, firebirdConf);
par->addRef();
IPluginBase* plugin = module->getPlugin(regPlugin).factory->createPlugin(par);
if (plugin)
{
par->addRef();
plugin->setOwner(par);
}
else
{
par->release();
}
return plugin;
}

View File

@ -243,6 +243,7 @@ static const TOK tokens[] =
{LEVEL, "LEVEL", 1, false},
{LIKE, "LIKE", 1, false},
{LIMBO, "LIMBO", 2, true},
{LINGER, "LINGER", 2, true},
{LIST, "LIST", 2, false},
{LN, "LN", 2, false},
{LOCK, "LOCK", 2, true},