Skip to content

Commit

Permalink
Implemented CORE-4263: Database linger
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexPeshkoff committed Nov 14, 2013
1 parent ae5e485 commit 120b10a
Show file tree
Hide file tree
Showing 31 changed files with 399 additions and 55 deletions.
13 changes: 13 additions & 0 deletions doc/README.services_extension
Expand Up @@ -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
34 changes: 34 additions & 0 deletions doc/sql.extensions/README.linger
@@ -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).
29 changes: 17 additions & 12 deletions src/alice/aliceswi.h
Expand Up @@ -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
Expand Down Expand Up @@ -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";
Expand All @@ -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},
Expand All @@ -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},
Expand Down Expand Up @@ -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)
Expand All @@ -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},
Expand All @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions src/alice/exe.cpp
Expand Up @@ -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);

Expand Down
6 changes: 6 additions & 0 deletions src/dbs/security.sql
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions src/dsql/DdlNodes.epp
Expand Up @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion src/dsql/DdlNodes.h
Expand Up @@ -1945,6 +1945,7 @@ class AlterDatabaseNode : public DdlNode
: DdlNode(p),
create(false),
createLength(0),
linger(-1),
clauses(0),
differenceFile(p),
setDefaultCharSet(p),
Expand Down Expand Up @@ -1977,7 +1978,7 @@ class AlterDatabaseNode : public DdlNode

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;
Expand Down
6 changes: 6 additions & 0 deletions src/dsql/parse.y
Expand Up @@ -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

Expand Down Expand Up @@ -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; }
;


Expand Down Expand Up @@ -7111,6 +7116,7 @@ non_reserved_word
| RANK
| ROW_NUMBER
| USAGE
| LINGER
;

%%
Expand Down
2 changes: 2 additions & 0 deletions src/include/consts_pub.h
Expand Up @@ -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 */
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/include/firebird/Plugin.h
Expand Up @@ -146,8 +146,9 @@ class IPluginConfig : public IRefCounted
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
Expand Down
1 change: 1 addition & 0 deletions src/include/gen/ids.h
Expand Up @@ -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)
Expand Down
46 changes: 46 additions & 0 deletions src/jrd/Database.cpp
Expand Up @@ -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");

Expand Down Expand Up @@ -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
35 changes: 31 additions & 4 deletions src/jrd/Database.h
Expand Up @@ -301,11 +301,31 @@ class Database : public pool_alloc<type_dbb>
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;
}
Expand Down Expand Up @@ -437,6 +457,10 @@ class Database : public pool_alloc<type_dbb>
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;
Expand Down Expand Up @@ -464,7 +488,7 @@ class Database : public pool_alloc<type_dbb>
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),
Expand All @@ -478,7 +502,10 @@ class Database : public pool_alloc<type_dbb>
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);
}
Expand Down

0 comments on commit 120b10a

Please sign in to comment.