File mvcc_active_tran.cpp¶
File List > cubrid > src > transaction > mvcc_active_tran.cpp
Go to the documentation of this file
/*
* Copyright 2008 Search Solution Corporation
* Copyright 2016 CUBRID Corporation
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
//
// MVCC active transactions map
//
#include "mvcc_active_tran.hpp"
#include "log_impl.h"
#include <cstring>
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"
mvcc_active_tran::mvcc_active_tran ()
: m_bit_area (NULL)
, m_bit_area_start_mvccid (MVCCID_FIRST)
, m_bit_area_length (0)
, m_long_tran_mvccids (NULL)
, m_long_tran_mvccids_length (0)
, m_initialized (false)
{
}
mvcc_active_tran::~mvcc_active_tran ()
{
delete [] m_bit_area;
delete [] m_long_tran_mvccids;
}
void
mvcc_active_tran::initialize ()
{
if (m_initialized)
{
return;
}
m_bit_area = new unit_type[BITAREA_MAX_SIZE] ();
m_bit_area_start_mvccid = MVCCID_FIRST;
m_bit_area_length = 0;
m_long_tran_mvccids = new MVCCID[long_tran_max_size ()] ();
m_long_tran_mvccids_length = 0;
m_initialized = true;
}
void
mvcc_active_tran::finalize ()
{
delete [] m_bit_area;
m_bit_area = NULL;
delete [] m_long_tran_mvccids;
m_long_tran_mvccids = NULL;
m_initialized = false;
}
void
mvcc_active_tran::reset ()
{
if (!m_initialized)
{
return;
}
if (m_bit_area_length > 0)
{
// clear bits
std::memset (m_bit_area, 0, get_bit_area_memsize ());
}
m_bit_area_length = 0;
m_bit_area_start_mvccid = MVCCID_NULL;
m_long_tran_mvccids_length = 0;
check_valid ();
}
MVCCID
mvcc_active_tran::get_bit_area_start_mvccid ()
{
return m_bit_area_start_mvccid;
}
size_t
mvcc_active_tran::long_tran_max_size ()
{
return logtb_get_number_of_total_tran_indices ();
}
size_t
mvcc_active_tran::bit_size_to_unit_size (size_t bit_count)
{
return (bit_count + UNIT_BIT_COUNT - 1) / UNIT_BIT_COUNT;
}
size_t
mvcc_active_tran::units_to_bits (size_t unit_count)
{
return unit_count * UNIT_BIT_COUNT;
}
size_t
mvcc_active_tran::units_to_bytes (size_t unit_count)
{
return unit_count * UNIT_BYTE_COUNT;
}
mvcc_active_tran::unit_type
mvcc_active_tran::get_mask_of (size_t bit_offset)
{
return ((unit_type) 1) << (bit_offset & 0x3F);
}
size_t
mvcc_active_tran::get_bit_offset (MVCCID mvccid) const
{
return static_cast<size_t> (mvccid - m_bit_area_start_mvccid);
}
MVCCID
mvcc_active_tran::get_mvccid (size_t bit_offset) const
{
return m_bit_area_start_mvccid + bit_offset;
}
mvcc_active_tran::unit_type *
mvcc_active_tran::get_unit_of (size_t bit_offset) const
{
return m_bit_area + (bit_offset / UNIT_BIT_COUNT);
}
bool
mvcc_active_tran::is_set (size_t bit_offset) const
{
return ((*get_unit_of (bit_offset)) & get_mask_of (bit_offset)) != 0;
}
size_t
mvcc_active_tran::get_area_size () const
{
return bit_size_to_unit_size (m_bit_area_length);
}
size_t
mvcc_active_tran::get_bit_area_memsize () const
{
return units_to_bytes (get_area_size ());
}
size_t
mvcc_active_tran::get_long_tran_memsize () const
{
return m_long_tran_mvccids_length * sizeof (MVCCID);
}
MVCCID
mvcc_active_tran::compute_highest_completed_mvccid () const
{
assert (m_bit_area != NULL && m_bit_area_start_mvccid >= MVCCID_FIRST);
if (m_bit_area_length == 0)
{
return m_bit_area_start_mvccid - 1;
}
/* compute highest highest_bit_pos and highest_completed_bit_area */
size_t end_position = m_bit_area_length - 1;
unit_type *highest_completed_bit_area;
size_t highest_bit_position;
unit_type bits;
size_t bit_pos;
size_t count_bits;
for (highest_completed_bit_area = get_unit_of (end_position); highest_completed_bit_area >= m_bit_area;
--highest_completed_bit_area)
{
bits = *highest_completed_bit_area;
if (bits == 0)
{
continue;
}
for (bit_pos = 0, count_bits = UNIT_BIT_COUNT / 2; count_bits > 0; count_bits /= 2)
{
if (bits >= (1ULL << count_bits))
{
bit_pos += count_bits;
bits >>= count_bits;
}
}
assert (bit_pos < UNIT_BIT_COUNT);
highest_bit_position = bit_pos;
break;
}
if (highest_completed_bit_area < m_bit_area)
{
// not found
return m_bit_area_start_mvccid - 1;
}
else
{
return get_mvccid (units_to_bits (highest_completed_bit_area - m_bit_area) + highest_bit_position);
}
}
MVCCID
mvcc_active_tran::compute_lowest_active_mvccid () const
{
assert (m_bit_area != NULL);
if (m_long_tran_mvccids_length > 0 && m_long_tran_mvccids != NULL)
{
/* long time transactions are ordered */
return m_long_tran_mvccids[0];
}
if (m_bit_area_length == 0)
{
return m_bit_area_start_mvccid;
}
/* find the lowest bit 0 */
size_t end_position = m_bit_area_length - 1;
unit_type *end_bit_area = get_unit_of (end_position);
unit_type *lowest_active_bit_area;
size_t lowest_bit_pos = 0;
unit_type bits;
size_t bit_pos;
size_t count_bits;
unit_type mask;
for (lowest_active_bit_area = m_bit_area; lowest_active_bit_area <= end_bit_area; ++lowest_active_bit_area)
{
bits = *lowest_active_bit_area;
if (bits == ALL_COMMITTED)
{
lowest_bit_pos += UNIT_BIT_COUNT;
continue;
}
/* find least significant bit 0 position */
for (bit_pos = 0, count_bits = UNIT_BIT_COUNT / 2; count_bits > 0; count_bits /= 2)
{
mask = (1ULL << count_bits) - 1;
if ((bits & mask) == mask)
{
bit_pos += count_bits;
bits >>= count_bits;
}
}
lowest_bit_pos += bit_pos;
break;
}
/* compute lowest_active_mvccid */
if (lowest_active_bit_area > end_bit_area)
{
/* didn't find 0 bit */
return get_mvccid (m_bit_area_length);
}
else
{
return get_mvccid (lowest_bit_pos);
}
}
void
mvcc_active_tran::copy_to (mvcc_active_tran &dest, copy_safety safety) const
{
assert (m_initialized && dest.m_initialized);
if (safety == copy_safety::THREAD_SAFE)
{
check_valid ();
dest.check_valid ();
}
size_t new_bit_area_memsize = get_bit_area_memsize ();
size_t old_bit_area_memsize = dest.get_bit_area_memsize ();
char *dest_bit_area = (char *) dest.m_bit_area;
if (new_bit_area_memsize > 0)
{
std::memcpy (dest_bit_area, m_bit_area, new_bit_area_memsize);
}
if (old_bit_area_memsize > new_bit_area_memsize)
{
// clear
std::memset (dest_bit_area + new_bit_area_memsize, 0, old_bit_area_memsize - new_bit_area_memsize);
}
if (m_long_tran_mvccids_length > 0)
{
std::memcpy (dest.m_long_tran_mvccids, m_long_tran_mvccids, get_long_tran_memsize ());
}
dest.m_bit_area_start_mvccid = m_bit_area_start_mvccid;
dest.m_bit_area_length = m_bit_area_length;
dest.m_long_tran_mvccids_length = m_long_tran_mvccids_length;
if (safety == copy_safety::THREAD_SAFE)
{
dest.check_valid ();
}
}
bool
mvcc_active_tran::is_active (MVCCID mvccid) const
{
if (MVCC_ID_PRECEDES (mvccid, m_bit_area_start_mvccid))
{
/* check long time transactions */
if (m_long_tran_mvccids != NULL)
{
for (size_t i = 0; i < m_long_tran_mvccids_length; i++)
{
if (mvccid == m_long_tran_mvccids[i])
{
return true;
}
}
}
// is committed
return false;
}
else if (m_bit_area_length == 0)
{
return true;
}
else
{
size_t position = get_bit_offset (mvccid);
if (position < m_bit_area_length)
{
return !is_set (position);
}
else
{
return true;
}
}
}
void
mvcc_active_tran::remove_long_transaction (MVCCID mvccid)
{
/* Safe guard: */
assert (m_long_tran_mvccids_length > 0);
size_t i;
for (i = 0; i < m_long_tran_mvccids_length - 1; i++)
{
if (m_long_tran_mvccids[i] == mvccid)
{
size_t memsize = (m_long_tran_mvccids_length - i - 1) * sizeof (MVCCID);
std::memmove (&m_long_tran_mvccids[i], &m_long_tran_mvccids[i + 1], memsize);
break;
}
}
assert ((i < m_long_tran_mvccids_length - 1) || m_long_tran_mvccids[i] == mvccid);
--m_long_tran_mvccids_length;
check_valid ();
}
void
mvcc_active_tran::add_long_transaction (MVCCID mvccid)
{
assert (m_long_tran_mvccids_length < long_tran_max_size ());
assert (m_long_tran_mvccids_length == 0 || m_long_tran_mvccids[m_long_tran_mvccids_length - 1] < mvccid);
m_long_tran_mvccids[m_long_tran_mvccids_length++] = mvccid;
}
void
mvcc_active_tran::ltrim_area (size_t trim_size)
{
if (trim_size == 0)
{
return;
}
size_t new_memsize = (get_area_size () - trim_size) * sizeof (unit_type);
if (new_memsize > 0)
{
std::memmove (m_bit_area, &m_bit_area[trim_size], new_memsize);
}
size_t trimmed_bits = units_to_bits (trim_size);
m_bit_area_length -= trimmed_bits;
m_bit_area_start_mvccid += trimmed_bits;
// clear moved units
std::memset (&m_bit_area[get_area_size ()], ALL_ACTIVE, trim_size * sizeof (unit_type));
#if !defined (NDEBUG)
// verify untouched units are also zero
for (size_t i = get_area_size () + trim_size; i < BITAREA_MAX_SIZE; i++)
{
assert (m_bit_area[i] == ALL_ACTIVE);
}
#endif // DEBUG
assert (new_memsize == get_bit_area_memsize ());
}
void
mvcc_active_tran::set_bitarea_mvccid (MVCCID mvccid)
{
const size_t CLEANUP_THRESHOLD = UNIT_BIT_COUNT;
const size_t LONG_TRAN_THRESHOLD = BITAREA_MAX_BITS - long_tran_max_size ();
assert (mvccid >= m_bit_area_start_mvccid);
size_t position = get_bit_offset (mvccid);
if (position >= BITAREA_MAX_BITS)
{
// force cleanup_migrate_to_long_transations
cleanup_migrate_to_long_transations ();
position = get_bit_offset (mvccid);
}
assert (position < BITAREA_MAX_BITS); // is this a guaranteed?
if (position >= m_bit_area_length)
{
// extend area size; it is enough to update bit_area_length since all data is already zero
m_bit_area_length = position + 1;
}
unit_type mask = get_mask_of (position);
unit_type *p_area = get_unit_of (position);
*p_area |= mask;
check_valid ();
if (m_bit_area_length > CLEANUP_THRESHOLD)
{
// trim all committed units from bit_area
size_t first_not_all_committed;
for (first_not_all_committed = 0; first_not_all_committed < get_area_size (); first_not_all_committed++)
{
if (m_bit_area[first_not_all_committed] != ALL_COMMITTED)
{
break;
}
}
ltrim_area (first_not_all_committed);
check_valid ();
}
if (m_bit_area_length > LONG_TRAN_THRESHOLD)
{
cleanup_migrate_to_long_transations ();
}
}
void
mvcc_active_tran::cleanup_migrate_to_long_transations ()
{
const size_t BITAREA_SIZE_AFTER_CLEANUP = 16;
size_t delete_count = get_area_size () - BITAREA_SIZE_AFTER_CLEANUP;
unit_type bits;
unit_type mask;
size_t bit_pos;
MVCCID long_tran_mvccid;
for (size_t i = 0; i < delete_count; i++)
{
bits = m_bit_area[i];
// iterate on bits and find active MVCCID's
for (bit_pos = 0, mask = 1, long_tran_mvccid = get_mvccid (i * UNIT_BIT_COUNT);
bit_pos < UNIT_BIT_COUNT && bits != ALL_COMMITTED;
++bit_pos, mask <<= 1, ++long_tran_mvccid)
{
if ((bits & mask) == 0)
{
add_long_transaction (long_tran_mvccid);
/* set the bit to in order to break faster */
bits |= mask;
}
}
}
ltrim_area (delete_count);
check_valid ();
}
void
mvcc_active_tran::set_inactive_mvccid (MVCCID mvccid)
{
/* check whether is long transaction */
if (MVCC_ID_PRECEDES (mvccid, m_bit_area_start_mvccid))
{
remove_long_transaction (mvccid);
}
else
{
set_bitarea_mvccid (mvccid);
}
}
void
mvcc_active_tran::reset_start_mvccid (MVCCID mvccid)
{
m_bit_area_start_mvccid = mvccid;
if (m_initialized)
{
check_valid ();
}
}
void
mvcc_active_tran::reset_active_transactions ()
{
std::memset (m_bit_area, 0, BITAREA_MAX_MEMSIZE);
m_bit_area_length = 0;
m_long_tran_mvccids_length = 0;
}
void
mvcc_active_tran::check_valid () const
{
#if !defined (NDEBUG)
// all bits after bit_area_length must be 0
if ((m_bit_area_length % UNIT_BIT_COUNT) != 0)
{
// we need to test bits after bit_area_length in same unit
size_t last_bit_pos = m_bit_area_length - 1;
unit_type last_unit = *get_unit_of (last_bit_pos);
for (size_t i = (last_bit_pos + 1) ; i < UNIT_BIT_COUNT; i++)
{
if ((get_mask_of (i) & last_unit) != 0)
{
assert (false);
}
}
}
for (unit_type *p_area = get_unit_of (m_bit_area_length) + 1; p_area < m_bit_area + BITAREA_MAX_SIZE; ++p_area)
{
if (*p_area != ALL_ACTIVE)
{
assert (false);
}
}
// all long transaction should be ordered and smaller than bit_area_start_mvccid
for (size_t i = 0; i < m_long_tran_mvccids_length; i++)
{
assert (MVCC_ID_PRECEDES (m_long_tran_mvccids[i], m_bit_area_start_mvccid));
assert (i == 0 || MVCC_ID_PRECEDES (m_long_tran_mvccids[i - 1], m_long_tran_mvccids[i]));
}
#endif // debug
}