8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 11:23:02 +01:00
firebird-mirror/src/jrd/tpc.cpp

522 lines
13 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
* MODULE: tpc.cpp
2004-03-07 08:58:55 +01:00
* DESCRIPTION: TIP Cache for Database
2001-05-23 15:26:42 +02:00
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include "../common/common.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/jrd.h"
#include "../jrd/ods.h"
#include "../jrd/tra.h"
#include "../jrd/pag.h"
#include "../jrd/cch_proto.h"
#include "../jrd/lck_proto.h"
#include "../jrd/tpc_proto.h"
#include "../jrd/tra_proto.h"
2011-05-09 12:15:19 +02:00
using namespace Firebird;
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
namespace Jrd {
2001-05-23 15:26:42 +02:00
2011-05-11 03:18:28 +02:00
TipCache::TipCache(Database* dbb)
: m_dbb(dbb),
m_cache(*m_dbb->dbb_permanent)
2011-05-09 12:15:19 +02:00
{
}
TipCache::~TipCache()
{
clearCache();
}
2011-05-11 03:18:28 +02:00
int TipCache::cacheState(thread_db* tdbb, SLONG number)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* T P C _ c a c h e _ s t a t e
*
**************************************
*
* Functional description
* Get the current state of a transaction in the cache.
*
**************************************/
2011-05-09 12:15:19 +02:00
if (number && m_dbb->dbb_pc_transactions)
2009-06-21 07:46:51 +02:00
{
2011-05-09 12:15:19 +02:00
if (TRA_precommited(tdbb, number, number))
return tra_precommitted;
2001-05-23 15:26:42 +02:00
}
2011-05-11 03:18:28 +02:00
SyncLockGuard sync(&m_sync, SYNC_SHARED, "TipCache::cacheState");
2011-05-09 12:15:19 +02:00
2011-05-10 03:12:14 +02:00
if (!m_cache.getCount())
2009-06-21 07:46:51 +02:00
{
2011-05-09 12:15:19 +02:00
SyncUnlockGuard unlock(sync);
2011-05-11 03:18:28 +02:00
initializeTpc(tdbb, number);
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
2009-08-21 11:45:08 +02:00
// if the transaction is older than the oldest
// transaction in our tip cache, it must be committed
// hvlad: system transaction always committed too
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
TxPage* tip_cache = m_cache[m_cache.getCount() - 1];
if (number < tip_cache->tpc_base || number == 0)
2001-05-23 15:26:42 +02:00
return tra_committed;
2009-08-21 11:45:08 +02:00
// locate the specific TIP cache block for the transaction
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
const SLONG trans_per_tip = m_dbb->dbb_page_manager.transPerTIP;
const SLONG base = number - number % trans_per_tip;
size_t pos;
if (m_cache.find(base, pos))
2009-06-21 07:46:51 +02:00
{
2011-05-09 12:15:19 +02:00
tip_cache = m_cache[pos];
fb_assert(number >= tip_cache->tpc_base);
fb_assert((ULONG) number < (ULONG) (tip_cache->tpc_base + trans_per_tip));
return TRA_state(tip_cache->tpc_transactions, tip_cache->tpc_base, number);
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
2009-08-21 11:45:08 +02:00
// Cover all possibilities by returning active
2001-05-23 15:26:42 +02:00
return tra_active;
}
2011-05-11 03:18:28 +02:00
void TipCache::initializeTpc(thread_db* tdbb, SLONG number)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* T P C _ i n i t i a l i z e _ t p c
*
**************************************
*
* Functional description
* At transaction startup, intialize the tip cache up to
* number. This is used at TRA_start () time.
*
**************************************/
2011-05-11 03:18:28 +02:00
SyncLockGuard sync(&m_sync, SYNC_EXCLUSIVE, "TipCache::initializeTpc");
2011-05-09 12:15:19 +02:00
2011-05-11 03:18:28 +02:00
if (m_cache.isEmpty())
2009-06-21 07:46:51 +02:00
{
2011-05-09 12:15:19 +02:00
sync.unlock();
cacheTransactions(tdbb, 0);
2001-05-23 15:26:42 +02:00
return;
}
2009-08-21 11:45:08 +02:00
// If there is already a cache, extend it if required.
// find the end of the linked list, and cache
// all transactions from that point up to the most recent transaction
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
const SLONG trans_per_tip = m_dbb->dbb_page_manager.transPerTIP;
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
const TxPage* tip_cache = m_cache[m_cache.getCount() - 1];
2001-05-23 15:26:42 +02:00
2009-03-01 16:42:23 +01:00
if ((ULONG) number < (ULONG) (tip_cache->tpc_base + trans_per_tip))
2001-05-23 15:26:42 +02:00
return;
if (tip_cache->tpc_base < MAX_TRA_NUMBER - trans_per_tip)
2011-05-09 12:15:19 +02:00
{
// ensure last_known calculated *before* unlock !!!
const SLONG last_known = tip_cache->tpc_base;
sync.unlock();
cacheTransactions(tdbb, last_known + trans_per_tip);
}
2001-05-23 15:26:42 +02:00
}
2011-05-11 03:18:28 +02:00
void TipCache::setState(thread_db* tdbb, SLONG number, SSHORT state)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* T P C _ s e t _ s t a t e
*
**************************************
*
* Functional description
* Set the state of a particular transaction
* in the TIP cache.
*
**************************************/
2011-05-09 12:15:19 +02:00
const SLONG trans_per_tip = m_dbb->dbb_page_manager.transPerTIP;
const SLONG base = number - number % trans_per_tip;
const SLONG byte = TRANS_OFFSET(number % trans_per_tip);
2003-12-31 06:36:12 +01:00
const SSHORT shift = TRANS_SHIFT(number);
2001-05-23 15:26:42 +02:00
2011-05-11 03:18:28 +02:00
SyncLockGuard sync(&m_sync, SYNC_EXCLUSIVE, "TipCache::setState");
2011-05-09 12:15:19 +02:00
size_t pos = 0;
if (m_cache.find(base, pos))
2003-12-31 06:36:12 +01:00
{
2011-05-09 12:15:19 +02:00
TxPage* tip_cache = m_cache[pos];
fb_assert(number >= tip_cache->tpc_base);
fb_assert((ULONG) number < (ULONG) (tip_cache->tpc_base + trans_per_tip));
UCHAR* address = tip_cache->tpc_transactions + byte;
*address &= ~(TRA_MASK << shift);
*address |= state << shift;
return;
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
2009-08-21 11:45:08 +02:00
// right now we don't set the state of a transaction on a page
// that has not already been cached -- this should probably be done
2001-05-23 15:26:42 +02:00
}
2011-05-11 03:18:28 +02:00
int TipCache::snapshotState(thread_db* tdbb, SLONG number)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* T P C _ s n a p s h o t _ s t a t e
*
**************************************
*
* Functional description
* Get the current state of a transaction.
* Look at the TIP cache first, but if it
2008-12-05 02:20:14 +01:00
* is marked as still alive we must do some
2001-05-23 15:26:42 +02:00
* further checking to see if it really is.
*
**************************************/
2011-05-09 12:15:19 +02:00
if (number && m_dbb->dbb_pc_transactions)
2009-06-21 07:46:51 +02:00
{
2011-05-11 03:18:28 +02:00
if (TRA_precommited(tdbb, number, number))
2001-05-23 15:26:42 +02:00
return tra_precommitted;
}
2001-05-23 15:26:42 +02:00
2011-05-11 03:18:28 +02:00
SyncLockGuard sync(&m_sync, SYNC_SHARED, "TipCache::snapshotState");
2011-05-09 12:15:19 +02:00
2011-05-11 03:18:28 +02:00
if (m_cache.isEmpty())
2011-05-09 12:15:19 +02:00
{
sync.unlock();
cacheTransactions(tdbb, 0);
2011-05-11 03:18:28 +02:00
sync.lock(SYNC_SHARED, "TipCache::snapshotState");
2011-05-09 12:15:19 +02:00
}
2009-08-21 11:45:08 +02:00
// if the transaction is older than the oldest
// transaction in our tip cache, it must be committed
// hvlad: system transaction always committed too
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
TxPage* tip_cache = m_cache[0];
2011-05-11 03:18:28 +02:00
if (number < tip_cache->tpc_base || number == 0)
2001-05-23 15:26:42 +02:00
return tra_committed;
2009-08-21 11:45:08 +02:00
// locate the specific TIP cache block for the transaction
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
const SLONG trans_per_tip = m_dbb->dbb_page_manager.transPerTIP;
const SLONG base = number - number % trans_per_tip;
size_t pos;
if (m_cache.find(base, pos))
{
2011-05-09 12:15:19 +02:00
tip_cache = m_cache[pos];
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
fb_assert(number >= tip_cache->tpc_base);
fb_assert((ULONG) number < (ULONG) (tip_cache->tpc_base + trans_per_tip));
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
const USHORT state = TRA_state(tip_cache->tpc_transactions, tip_cache->tpc_base, number);
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
sync.unlock();
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
// committed or dead transactions always stay that
// way, so no need to check their current state
2001-05-23 15:26:42 +02:00
2011-05-11 03:18:28 +02:00
if (state == tra_committed || state == tra_dead)
2011-05-09 12:15:19 +02:00
return state;
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
// see if we can get a lock on the transaction; if we can't
// then we know it is still active
Lock temp_lock;
temp_lock.lck_dbb = m_dbb;
temp_lock.lck_type = LCK_tra;
temp_lock.lck_owner_handle = LCK_get_owner_handle(tdbb, temp_lock.lck_type);
temp_lock.lck_parent = m_dbb->dbb_lock;
temp_lock.lck_length = sizeof(SLONG);
temp_lock.lck_key.lck_long = number;
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
// If we can't get a lock on the transaction, it must be active.
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
if (!LCK_lock(tdbb, &temp_lock, LCK_read, LCK_NO_WAIT))
{
fb_utils::init_status(tdbb->tdbb_status_vector);
return tra_active;
2001-05-23 15:26:42 +02:00
}
2011-05-09 12:15:19 +02:00
fb_utils::init_status(tdbb->tdbb_status_vector);
LCK_release(tdbb, &temp_lock);
// as a last resort we must look at the TIP page to see
// whether the transaction is committed or dead; to minimize
// having to do this again we will check the state of all
// other transactions on that page
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
return TRA_fetch_state(tdbb, number);
}
// if the transaction has been started since we last looked, extend the cache upward
2011-05-10 03:12:14 +02:00
2011-05-09 12:15:19 +02:00
sync.unlock();
2011-05-11 03:18:28 +02:00
2011-05-09 12:15:19 +02:00
return extendCache(tdbb, number);
2001-05-23 15:26:42 +02:00
}
2011-05-11 03:18:28 +02:00
void TipCache::updateCache(thread_db* tdbb, const Ods::tx_inv_page* tip_page, SLONG sequence)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* T P C _ u p d a t e _ c a c h e
*
**************************************
*
* Functional description
* A TIP page has been fetched into memory,
* so we should take the opportunity to update
* the TIP cache with the state of all transactions
* on that page.
*
**************************************/
2011-05-09 12:15:19 +02:00
const SLONG trans_per_tip = m_dbb->dbb_page_manager.transPerTIP;
2003-12-31 06:36:12 +01:00
const SLONG first_trans = sequence * trans_per_tip;
2001-05-23 15:26:42 +02:00
2009-08-21 11:45:08 +02:00
// while we're in the area we can check to see if there are
// any tip cache pages we can release--this is cheaper and
// easier than finding out when a TIP page is dropped
2001-05-23 15:26:42 +02:00
2011-05-11 03:18:28 +02:00
SyncLockGuard sync(&m_sync, SYNC_EXCLUSIVE, "TipCache::updateCache");
2011-05-09 12:15:19 +02:00
TxPage* tip_cache = NULL;
2011-05-11 03:18:28 +02:00
while (m_cache.hasData())
2009-06-21 07:46:51 +02:00
{
2011-05-09 12:15:19 +02:00
tip_cache = m_cache[0];
2011-05-11 03:18:28 +02:00
2011-05-09 12:15:19 +02:00
if ((ULONG) m_dbb->dbb_oldest_transaction >= (ULONG) (tip_cache->tpc_base + trans_per_tip))
2003-12-31 06:36:12 +01:00
{
2011-05-11 03:18:28 +02:00
m_cache.remove((size_t) 0);
2001-12-24 03:51:06 +01:00
delete tip_cache;
2001-05-23 15:26:42 +02:00
}
else
break;
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
2009-08-21 11:45:08 +02:00
// find the appropriate page in the TIP cache and assign all transaction
// bits -- it's not worth figuring out which ones are actually used
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
size_t pos;
2011-05-11 03:18:28 +02:00
if (m_cache.find(first_trans, pos))
2011-05-09 12:15:19 +02:00
tip_cache = m_cache[pos];
2011-05-11 03:18:28 +02:00
else
{
2011-05-09 12:15:19 +02:00
tip_cache = allocTxPage(tdbb, first_trans);
m_cache.insert(pos, tip_cache);
2003-12-31 06:36:12 +01:00
}
2011-05-11 03:18:28 +02:00
2011-05-09 12:15:19 +02:00
fb_assert(first_trans == tip_cache->tpc_base);
2001-05-23 15:26:42 +02:00
2011-05-11 03:18:28 +02:00
const USHORT len = TRANS_OFFSET(trans_per_tip);
memcpy(tip_cache->tpc_transactions, tip_page->tip_transactions, len);
2001-05-23 15:26:42 +02:00
}
2011-05-09 12:15:19 +02:00
TipCache::TxPage* TipCache::allocTxPage(thread_db* tdbb, SLONG base)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* a l l o c a t e _ t p c
*
**************************************
*
* Functional description
* Create a tip cache block to hold the state
* of all transactions on one page.
*
**************************************/
2011-05-09 12:15:19 +02:00
fb_assert(m_sync.ourExclusiveLock());
const SLONG trans_per_tip = m_dbb->dbb_page_manager.transPerTIP;
2001-05-23 15:26:42 +02:00
2009-08-21 11:45:08 +02:00
// allocate a TIP cache block with enough room for all desired transactions
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
TxPage* tip_cache = FB_NEW_RPT(*m_dbb->dbb_permanent, trans_per_tip / 4) TxPage();
2001-05-23 15:26:42 +02:00
tip_cache->tpc_base = base;
return tip_cache;
}
2011-05-09 12:15:19 +02:00
SLONG TipCache::cacheTransactions(thread_db* tdbb, SLONG oldest)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c a c h e _ t r a n s a c t i o n s
*
**************************************
*
* Functional description
* Cache the state of all the transactions since
* the last time this routine was called, or since
* the oldest interesting transaction.
*
**************************************/
2011-05-09 12:15:19 +02:00
// m_sync should be unlocked here !
2001-05-23 15:26:42 +02:00
2009-08-21 11:45:08 +02:00
// check the header page for the oldest and newest transaction numbers
2001-05-23 15:26:42 +02:00
#ifdef SUPERSERVER_V2
2011-05-09 12:15:19 +02:00
const SLONG top = m_dbb->dbb_next_transaction;
const ULONG hdr_oldest = m_dbb->dbb_oldest_transaction;
2001-05-23 15:26:42 +02:00
#else
2006-05-22 00:07:35 +02:00
WIN window(HEADER_PAGE_NUMBER);
2008-12-25 07:09:37 +01:00
const Ods::header_page* header = (Ods::header_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_header);
const SLONG top = header->hdr_next_transaction;
const SLONG hdr_oldest = header->hdr_oldest_transaction;
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
#endif
2011-05-09 12:15:19 +02:00
// hvlad: No need to cache TIP pages below hdr_oldest just refreshed from
2011-06-24 08:34:16 +02:00
// header page. Moreover out tip cache can now contain a gap between the last
2011-05-09 12:15:19 +02:00
// cached tip page and new pages if our process was idle for long time
oldest = MAX(oldest, hdr_oldest);
2001-05-23 15:26:42 +02:00
2011-05-10 03:12:14 +02:00
// now get the inventory of all transactions, which will automatically
2011-05-09 12:15:19 +02:00
// fill in the tip cache pages
2011-05-11 03:18:28 +02:00
// hvlad: note, call below will call updateCache() which will acquire m_sync
2011-05-09 12:15:19 +02:00
// in exclusive mode. This is the reason why m_sync must be unlocked at the
// entry of this routine
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
TRA_get_inventory(tdbb, NULL, oldest, top);
2001-05-23 15:26:42 +02:00
2011-05-11 03:18:28 +02:00
SyncLockGuard sync(&m_sync, SYNC_EXCLUSIVE, "TipCache::updateCache");
2011-05-09 12:15:19 +02:00
const SLONG trans_per_tip = m_dbb->dbb_page_manager.transPerTIP;
2011-05-11 03:18:28 +02:00
while (m_cache.hasData())
2011-05-09 12:15:19 +02:00
{
TxPage* tip_cache = m_cache[0];
2011-05-11 03:18:28 +02:00
2011-05-09 12:15:19 +02:00
if ((ULONG) (tip_cache->tpc_base + trans_per_tip) < (ULONG) hdr_oldest)
{
2011-05-11 03:18:28 +02:00
m_cache.remove((size_t) 0);
2011-05-09 12:15:19 +02:00
delete tip_cache;
}
else
break;
2001-05-23 15:26:42 +02:00
}
2011-05-09 12:15:19 +02:00
return hdr_oldest;
}
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
void TipCache::clearCache()
{
fb_assert(m_sync.ourExclusiveLock());
2011-05-11 03:18:28 +02:00
while (m_cache.hasData())
{
2011-05-09 12:15:19 +02:00
const size_t pos = m_cache.getCount() - 1;
TxPage* tip_page = m_cache[pos];
2011-05-09 12:15:19 +02:00
m_cache.remove(pos);
delete tip_page;
}
2001-05-23 15:26:42 +02:00
}
2011-05-09 12:15:19 +02:00
int TipCache::extendCache(thread_db* tdbb, SLONG number)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x t e n d _ c a c h e
*
**************************************
*
* Functional description
* Extend the transaction inventory page
2008-12-05 02:20:14 +01:00
* cache to include at least all transactions
* up to the passed transaction, and return
2001-05-23 15:26:42 +02:00
* the state of the passed transaction.
*
**************************************/
2011-05-09 12:15:19 +02:00
// m_sync should be unlocked here !
const SLONG trans_per_tip = m_dbb->dbb_page_manager.transPerTIP;
2001-05-23 15:26:42 +02:00
// find the end of the linked list, and cache
// all transactions from that point up to the
// most recent transaction
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
Sync sync(&m_sync, "extendCache");
sync.lock(SYNC_SHARED);
2011-05-11 03:18:28 +02:00
fb_assert(m_cache.hasData());
2011-05-09 12:15:19 +02:00
TxPage* tip_cache = m_cache[m_cache.getCount() - 1];
if (tip_cache->tpc_base < MAX_TRA_NUMBER - trans_per_tip)
{
2011-05-09 12:15:19 +02:00
// ensure last_known calculated *before* unlock !!!
const SLONG last_known = tip_cache->tpc_base;
sync.unlock();
const SLONG oldest = cacheTransactions(tdbb, last_known + trans_per_tip);
if (number < oldest)
return tra_committed;
2011-05-09 12:15:19 +02:00
sync.lock(SYNC_SHARED);
}
2001-05-23 15:26:42 +02:00
// find the right block for this transaction and return the state
2001-05-23 15:26:42 +02:00
2011-05-09 12:15:19 +02:00
const SLONG base = number - number % trans_per_tip;
size_t pos;
if (m_cache.find(base, pos))
2003-12-31 06:36:12 +01:00
{
2011-05-09 12:15:19 +02:00
tip_cache = m_cache[pos];
fb_assert(number >= tip_cache->tpc_base);
fb_assert((ULONG) number < (ULONG) (tip_cache->tpc_base + trans_per_tip));
return TRA_state(tip_cache->tpc_transactions, tip_cache->tpc_base, number);
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
// we should never get to this point, but if we do the
// safest thing to do is return active
2001-05-23 15:26:42 +02:00
return tra_active;
}
2011-05-10 03:12:14 +02:00
} // namespace Jrd