diff --git a/cubrid/CMakeLists.txt b/cubrid/CMakeLists.txt index d33ad8402e4..db587d313eb 100644 --- a/cubrid/CMakeLists.txt +++ b/cubrid/CMakeLists.txt @@ -501,6 +501,7 @@ set(TRANSACTION_HEADERS ) set(STORAGE_SOURCES + ${STORAGE_DIR}/bestspace.cpp ${STORAGE_DIR}/btree.c ${STORAGE_DIR}/btree_load.c ${STORAGE_DIR}/btree_unique.cpp @@ -530,6 +531,7 @@ set(STORAGE_SOURCES ${STORAGE_DIR}/tde.c ) set(STORAGE_HEADERS + ${STORAGE_DIR}/bestspace.hpp ${STORAGE_DIR}/btree_unique.hpp ${STORAGE_DIR}/double_write_buffer.hpp ${STORAGE_DIR}/ftab_set.hpp diff --git a/sa/CMakeLists.txt b/sa/CMakeLists.txt index 2589702ee02..6c485350bd7 100644 --- a/sa/CMakeLists.txt +++ b/sa/CMakeLists.txt @@ -505,6 +505,7 @@ set(TRANSACTION_HEADERS ) set(STORAGE_SOURCES + ${STORAGE_DIR}/bestspace.cpp ${STORAGE_DIR}/btree.c ${STORAGE_DIR}/btree_load.c ${STORAGE_DIR}/btree_unique.cpp @@ -536,6 +537,7 @@ set(STORAGE_SOURCES ${STORAGE_DIR}/tde.c ) set(STORAGE_HEADERS + ${STORAGE_DIR}/bestspace.hpp ${STORAGE_DIR}/btree_unique.hpp ${STORAGE_DIR}/ftab_set.hpp ${STORAGE_DIR}/record_descriptor.hpp @@ -780,6 +782,12 @@ target_link_libraries(cubridsa cascci ) +if(UNIX) + target_include_directories(cubridsa PUBLIC ${TBB_INCLUDES}) + target_link_libraries(cubridsa PRIVATE ${TBB_LIBS}) + add_dependencies(cubridsa ${TBB_TARGETS}) +endif(UNIX) + add_dependencies(cubridsa gen_csql_grammar gen_csql_lexer diff --git a/src/base/perf_monitor.c b/src/base/perf_monitor.c index 2866ee5cf6a..3daf626d1bb 100644 --- a/src/base/perf_monitor.c +++ b/src/base/perf_monitor.c @@ -379,17 +379,6 @@ PSTAT_METADATA pstat_Metadata[] = { PSTAT_METADATA_INIT_COUNTER_TIMER (PSTAT_HEAP_VACUUM_EXECUTE, "heap_vacuum_execute"), PSTAT_METADATA_INIT_COUNTER_TIMER (PSTAT_HEAP_VACUUM_LOG, "heap_vacuum_log"), - /* Execution statistics for the heap manager */ - /* best space info */ - PSTAT_METADATA_INIT_COUNTER_TIMER (PSTAT_HEAP_STATS_SYNC_BESTSPACE, "heap_stats_sync_bestspace"), - PSTAT_METADATA_INIT_SINGLE_PEEK (PSTAT_HF_NUM_STATS_ENTRIES, "Num_heap_stats_bestspace_entries"), - PSTAT_METADATA_INIT_SINGLE_ACC (PSTAT_HF_NUM_STATS_MAXED, "Num_heap_stats_bestspace_maxed"), - PSTAT_METADATA_INIT_COUNTER_TIMER (PSTAT_HF_BEST_SPACE_ADD, "bestspace_add"), - PSTAT_METADATA_INIT_COUNTER_TIMER (PSTAT_HF_BEST_SPACE_DEL, "bestspace_del"), - PSTAT_METADATA_INIT_COUNTER_TIMER (PSTAT_HF_BEST_SPACE_FIND, "bestspace_find"), - PSTAT_METADATA_INIT_COUNTER_TIMER (PSTAT_HF_HEAP_FIND_PAGE_BEST_SPACE, "heap_find_page_bestspace"), - PSTAT_METADATA_INIT_COUNTER_TIMER (PSTAT_HF_HEAP_FIND_BEST_PAGE, "heap_find_best_page"), - /* B-tree detailed statistics. */ PSTAT_METADATA_INIT_COUNTER_TIMER (PSTAT_BT_FIX_OVF_OIDS, "bt_fix_ovf_oids"), PSTAT_METADATA_INIT_COUNTER_TIMER (PSTAT_BT_UNIQUE_RLOCKS, "bt_unique_rlocks"), @@ -4053,7 +4042,6 @@ perfmon_get_peek_stats (UINT64 * stats) /* fixme(rem) - will be fixed in stattool patch */ #if defined (SERVER_MODE) || defined (SA_MODE) stats[pstat_Metadata[PSTAT_PC_NUM_CACHE_ENTRIES].start_offset] = xcache_get_entry_count (); - stats[pstat_Metadata[PSTAT_HF_NUM_STATS_ENTRIES].start_offset] = heap_get_best_space_num_stats_entries (); stats[pstat_Metadata[PSTAT_QM_NUM_HOLDABLE_CURSORS].start_offset] = session_get_number_of_holdable_cursors (); #endif /* defined (SERVER_MODE) || defined (SA_MODE) */ } diff --git a/src/base/perf_monitor.h b/src/base/perf_monitor.h index ac00a4305f4..4203bdd791a 100644 --- a/src/base/perf_monitor.h +++ b/src/base/perf_monitor.h @@ -453,17 +453,6 @@ typedef enum PSTAT_HEAP_VACUUM_EXECUTE, PSTAT_HEAP_VACUUM_LOG, - /* Execution statistics for the heap manager */ - /* best space info */ - PSTAT_HEAP_STATS_SYNC_BESTSPACE, - PSTAT_HF_NUM_STATS_ENTRIES, - PSTAT_HF_NUM_STATS_MAXED, - PSTAT_HF_BEST_SPACE_ADD, - PSTAT_HF_BEST_SPACE_DEL, - PSTAT_HF_BEST_SPACE_FIND, - PSTAT_HF_HEAP_FIND_PAGE_BEST_SPACE, - PSTAT_HF_HEAP_FIND_BEST_PAGE, - /* B-tree ops detailed statistics. */ PSTAT_BT_FIX_OVF_OIDS, PSTAT_BT_UNIQUE_RLOCKS, diff --git a/src/base/system_parameter.c b/src/base/system_parameter.c index 3ad1dd32916..7be82cb3c5d 100644 --- a/src/base/system_parameter.c +++ b/src/base/system_parameter.c @@ -801,6 +801,7 @@ static const char sysprm_ha_conf_file_name[] = "cubrid_ha.conf"; #define PRM_NAME_LOG_POSTPONE_CACHE_SIZE "postpone_cache_size" #define PRM_NAME_HARDWARE_AFFINITY "hardware_affinity" +#define PRM_NAME_BESTSPACE_SHARD_COUNT "bestspace_shard_count" // #endregion @@ -1197,12 +1198,13 @@ SYSPRM_PARAM prm_Def[] = { (DUP_PRM_FUNC) NULL}, {PRM_ID_HF_MAX_BESTSPACE_ENTRIES, PRM_NAME_HF_MAX_BESTSPACE_ENTRIES, - (PRM_FOR_SERVER | PRM_HIDDEN | PRM_USER_CHANGE), + (PRM_FOR_SERVER | PRM_HIDDEN | PRM_USER_CHANGE | PRM_OBSOLETED), PRM_INTEGER, PRM_CLEAR_DYNAMIC_FLAG, {false, {.i = 1000000 /* 110 M */ }}, {false, {.i = 1000000}}, - NULL_SYSPRM_PARAM_VALUE, NULL_SYSPRM_PARAM_VALUE, + NULL_SYSPRM_PARAM_VALUE, + NULL_SYSPRM_PARAM_VALUE, (char *) NULL, (DUP_PRM_FUNC) NULL, (DUP_PRM_FUNC) NULL}, @@ -5384,6 +5386,18 @@ SYSPRM_PARAM prm_Def[] = { (char *) NULL, (DUP_PRM_FUNC) NULL, (DUP_PRM_FUNC) NULL}, + {PRM_ID_BESTSPACE_SHARD_COUNT, + PRM_NAME_BESTSPACE_SHARD_COUNT, + (PRM_FOR_SERVER), + PRM_INTEGER, + PRM_CLEAR_DYNAMIC_FLAG, + {false, {.i = 8}}, + {false, {.i = 8}}, + {false, {.i = 28}}, + {false, {.i = 1}}, + (char *) NULL, + (DUP_PRM_FUNC) NULL, + (DUP_PRM_FUNC) NULL} }; SYSPRM_INDIRECT_POS prm_Def_session_idx[DIM (prm_Def)]; diff --git a/src/base/system_parameter.h b/src/base/system_parameter.h index 1a68317671d..72c3f6000ce 100644 --- a/src/base/system_parameter.h +++ b/src/base/system_parameter.h @@ -535,8 +535,10 @@ enum param_id PRM_ID_HARDWARE_AFFINITY, + PRM_ID_BESTSPACE_SHARD_COUNT, + /* change PRM_LAST_ID when adding new system parameters */ - PRM_LAST_ID = PRM_ID_HARDWARE_AFFINITY + PRM_LAST_ID = PRM_ID_BESTSPACE_SHARD_COUNT }; typedef enum param_id PARAM_ID; diff --git a/src/parser/parse_tree.c b/src/parser/parse_tree.c index 6f19ede540a..78acacfc3c7 100644 --- a/src/parser/parse_tree.c +++ b/src/parser/parse_tree.c @@ -144,8 +144,6 @@ PT_RESERVED_NAME pt_Reserved_name_table[] = { {"p_offset_to_free_area", RESERVED_P_OFFSET_TO_FREE_AREA, DB_TYPE_INTEGER} , {"p_is_saving", RESERVED_P_IS_SAVING, DB_TYPE_INTEGER} - , - {"p_update_best", RESERVED_P_UPDATE_BEST, DB_TYPE_INTEGER} /* key info attributes */ , diff --git a/src/parser/parse_tree.h b/src/parser/parse_tree.h index 2d8cb0e40bf..b3002be863a 100644 --- a/src/parser/parse_tree.h +++ b/src/parser/parse_tree.h @@ -2539,7 +2539,6 @@ typedef enum RESERVED_P_CONT_FREE, RESERVED_P_OFFSET_TO_FREE_AREA, RESERVED_P_IS_SAVING, - RESERVED_P_UPDATE_BEST, /* Reserved key info names */ RESERVED_KEY_VOLUMEID, @@ -2568,7 +2567,7 @@ typedef enum RESERVED_LAST_RECORD_INFO = RESERVED_T_MVCC_PREV_VERSION_LSA, RESERVED_FIRST_PAGE_INFO = RESERVED_P_CLASS_OID, - RESERVED_LAST_PAGE_INFO = RESERVED_P_UPDATE_BEST, + RESERVED_LAST_PAGE_INFO = RESERVED_P_IS_SAVING, RESERVED_FIRST_KEY_INFO = RESERVED_KEY_VOLUMEID, RESERVED_LAST_KEY_INFO = RESERVED_KEY_OVERFLOW_OIDS, diff --git a/src/parser/show_meta.c b/src/parser/show_meta.c index 9278f2314c5..25f980358a0 100644 --- a/src/parser/show_meta.c +++ b/src/parser/show_meta.c @@ -223,7 +223,6 @@ metadata_of_slotted_page_header (void) {"Total_free_area", "int"}, {"Contiguous_free_area", "int"}, {"Free_space_offset", "int"}, - {"Need_update_best_hint", "int"}, {"Is_saving", "int"}, {"Flags", "int"} }; @@ -304,21 +303,11 @@ metadata_of_heap_header (SHOW_ONLY_ALL flag) {"Header_page_id", "int"}, {"Overflow_vfid", "varchar(64)"}, {"Next_vpid", "varchar(64)"}, + {"Last_vpid", "varchar(64)"}, {"Unfill_space", "int"}, - {"Estimates_num_pages", "bigint"}, - {"Estimates_num_recs", "bigint"}, - {"Estimates_avg_rec_len", "int"}, - {"Estimates_num_high_best", "int"}, - {"Estimates_num_others_high_best", "int"}, - {"Estimates_head", "int"}, - {"Estimates_best_list", "varchar(512)"}, - {"Estimates_num_second_best", "int"}, - {"Estimates_head_second_best", "int"}, - {"Estimates_tail_second_best", "int"}, - {"Estimates_num_substitutions", "int"}, - {"Estimates_second_best_list", "varchar(256)"}, - {"Estimates_last_vpid", "varchar(64)"}, - {"Estimates_full_search_vpid", "varchar(64)"} + {"Num_pages", "bigint"}, + {"Num_recs", "bigint"}, + {"Avg_rec_len", "int"} }; static const SHOWSTMT_COLUMN_ORDERBY orderby[] = { diff --git a/src/parser/xasl_generation.c b/src/parser/xasl_generation.c index 275e2a514da..237bd25e31f 100644 --- a/src/parser/xasl_generation.c +++ b/src/parser/xasl_generation.c @@ -27929,8 +27929,6 @@ pt_reserved_id_to_valuelist_index (PARSER_CONTEXT * parser, PT_RESERVED_NAME_ID return HEAP_PAGE_INFO_OFFSET_TO_FREE_AREA; case RESERVED_P_IS_SAVING: return HEAP_PAGE_INFO_IS_SAVING; - case RESERVED_P_UPDATE_BEST: - return HEAP_PAGE_INFO_UPDATE_BEST; /* Key info names */ case RESERVED_KEY_VOLUMEID: diff --git a/src/query/parallel/px_scan/px_scan_input_handler_heap.cpp b/src/query/parallel/px_scan/px_scan_input_handler_heap.cpp index dba52c1d441..5fceec49ed6 100644 --- a/src/query/parallel/px_scan/px_scan_input_handler_heap.cpp +++ b/src/query/parallel/px_scan/px_scan_input_handler_heap.cpp @@ -23,6 +23,7 @@ #include "px_scan_input_handler_heap.hpp" #include "error_code.h" #include "bit.h" +#include "heap_file.h" // XXX: SHOULD BE THE LAST INCLUDE HEADER #include "memory_wrapper.hpp" @@ -158,6 +159,15 @@ namespace parallel_scan return S_ERROR; } + if (heap_page_is_bestspace (thread_p, m_tl_scan_cache->page_watcher.pgptr)) + { + pgbuf_ordered_unfix (thread_p, &m_tl_scan_cache->page_watcher); + PGBUF_CLEAR_WATCHER (&m_tl_scan_cache->page_watcher); + /* the manual advance is only needed on the S_SUCCESS return path */ + found = false; + continue; + } + *vpid = m_tl_vpid; m_tl_pgoffset++; m_tl_vpid.pageid++; diff --git a/src/query/vacuum.c b/src/query/vacuum.c index 9b58f159738..e01577e2403 100644 --- a/src/query/vacuum.c +++ b/src/query/vacuum.c @@ -32,6 +32,7 @@ #include "boot_sr.h" #include "btree.h" #include "dbtype.h" +#include "bestspace.hpp" #include "heap_file.h" #include "lockfree_circular_queue.hpp" #include "log_append.hpp" @@ -1677,7 +1678,7 @@ vacuum_heap_page (THREAD_ENTRY * thread_p, VACUUM_HEAP_OBJECT * heap_objects, in (void) pgbuf_check_page_ptype (thread_p, helper.home_page, PAGE_HEAP); #endif /* !NDEBUG */ - helper.initial_home_free_space = spage_get_free_space_without_saving (thread_p, helper.home_page, NULL); + helper.initial_home_free_space = spage_get_free_space_without_saving (thread_p, helper.home_page); if (HFID_IS_NULL (hfid)) { @@ -2416,23 +2417,8 @@ vacuum_heap_record (THREAD_ENTRY * thread_p, VACUUM_HEAP_HELPER * helper) spage_vacuum_slot (thread_p, helper->forward_page, helper->forward_oid.slotid, true); - if (prm_get_integer_value (PRM_ID_HF_MAX_BESTSPACE_ENTRIES) > 0) - { - int freespace = spage_get_free_space_without_saving (thread_p, helper->forward_page, NULL); - - if (freespace > HEAP_DROP_FREE_SPACE) - { - /* - * NOTE: - * By checking the freespace > HEAP_DROP_FREE_SPACE condition, heap_Bestspace->bestspace_mutex contention is reduced - * and the unnecessarily frequent extraction from heap_Bestspace->vpid_ht due to small free space is prevented in heap_stats_find_page_in_bestspace(). - * And Passing the prev_freespace argument to 0 is a trick to get heap_stats_add_bestspace() called from heap_stats_update(). - * - * This part will be refactored right away in the related issue, at which time this comment will be removed. - */ - heap_stats_update (thread_p, helper->forward_page, &helper->hfid, 0); - } - } + /* add candidate page into bestspace candidates */ + heap_add_bestpage (thread_p, &helper->hfid, helper->forward_page); VACUUM_PERF_HEAP_TRACK_EXECUTE (thread_p, helper); @@ -2613,7 +2599,7 @@ vacuum_heap_page_log_and_reset (THREAD_ENTRY * thread_p, VACUUM_HEAP_HELPER * he if (update_best_space_stat == true && helper->initial_home_free_space != -1) { assert (!HFID_IS_NULL (&helper->hfid)); - heap_stats_update (thread_p, helper->home_page, &helper->hfid, helper->initial_home_free_space); + heap_add_bestpage (thread_p, &helper->hfid, helper->home_page, helper->initial_home_free_space); } VACUUM_PERF_HEAP_TRACK_EXECUTE (thread_p, helper); @@ -6422,6 +6408,7 @@ vacuum_rv_notify_dropped_file (THREAD_ENTRY * thread_p, LOG_RCV * rcv) { (void) heap_delete_hfid_from_cache (thread_p, class_oid); } + cubstorage::bestspaces.destroy (&rcv_data->vfid); /* Success */ return NO_ERROR; diff --git a/src/storage/bestspace.cpp b/src/storage/bestspace.cpp new file mode 100644 index 00000000000..d7b309bae62 --- /dev/null +++ b/src/storage/bestspace.cpp @@ -0,0 +1,1618 @@ +/* + * + * 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. + * + */ + +// +// bestspace.cpp - bestspace in memory +// + +#include "bestspace.hpp" +#include "xserver_interface.h" +#include "heap_file.h" +#include "slotted_page.h" +#include "error_manager.h" + +#include +#include +#include +#include + +// XXX: SHOULD BE THE LAST INCLUDE HEADER +#include "memory_wrapper.hpp" + +#define STATS_INC(id, value) \ + do \ + { \ + if (m_stats.enabled.load ()) \ + { \ + m_stats.id.fetch_add (value); \ + } \ + } \ + while (0) + +static std::uint64_t +monotonic_seconds () noexcept +{ + return static_cast (std::chrono::duration_cast + (std::chrono::steady_clock::now ().time_since_epoch ()).count ()); +} + + +static int +wait_for_shard_allocation (THREAD_ENTRY *thread_p, void *args) +{ + std::size_t *retry; + bool continue_check; + + retry = static_cast (args); + assert (retry != nullptr); + + if (*retry < 20) + { + std::this_thread::yield (); + } + else + { + std::this_thread::sleep_for (std::chrono::microseconds (10)); + } + (*retry)++; + + if (logtb_is_interrupted (thread_p, true, &continue_check)) + { + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INTERRUPTED, 0); + return ER_INTERRUPTED; + } + + return NO_ERROR; +} + +namespace cubstorage +{ + ////////////////////////////////////////////////////////////////////////// + // base class + ////////////////////////////////////////////////////////////////////////// + + bestspace::bitmap::bitmap () noexcept + : m_bits (0) + { + } + + bool + bestspace::bitmap::empty () + { + return m_bits == 0; + } + + void + bestspace::bitmap::set (std::size_t index) + { + assert (index < BITS_PER_BYTE); + + m_bits |= (0x1 << index); + } + + void + bestspace::bitmap::clear (std::size_t index) + { + assert (index < BITS_PER_BYTE); + + m_bits &= ~ (0x1 << index); + } + + std::size_t + bestspace::bitmap::find (std::array &pos, std::size_t length) + { + std::size_t size; + std::size_t i; + + size = 0; + for (i = 0; i < length; i++) + { + if (m_bits & (0x1 << i)) + { + pos[size++] = i; + } + } + + return size; + } + + bestspace::L1::L1 () noexcept + : m_freespace (0) + , m_volid (NULL_VOLID) + , m_pageid (NULL_PAGEID) + { + static_assert (offsetof (L1, m_freespace) == offsetof (bestspace_entry, freespace), "offset must be same"); + static_assert (offsetof (L1, m_volid) == offsetof (bestspace_entry, volid), "offset must be same"); + static_assert (offsetof (L1, m_pageid) == offsetof (bestspace_entry, pageid), "offset must be same"); + } + + std::uint16_t + bestspace::L1::get_freespace () + { + return m_freespace; + } + + void + bestspace::L1::set_freespace (std::uint16_t size) + { + m_freespace = size; + } + + VPID + bestspace::L1::get_vpid () + { + return { m_pageid, m_volid }; + } + + void + bestspace::L1::set_vpid (VPID vpid) + { + m_pageid = vpid.pageid; + m_volid = vpid.volid; + } + + bestspace::L2::L2 () noexcept + : m_freespace () + { + } + + std::size_t + bestspace::L2::find (tier minimum, std::array &pos) + { + assert (minimum >= tier::FS1 && minimum <= tier::FS8); + + return m_freespace[static_cast (minimum)].find (pos); + } + + std::size_t + bestspace::L2::collect (std::array &tiers) + { + std::size_t size; + tier i; + + size = 0; + for (i = tier::FS1; i <= tier::FS8; i++) + { + if (!m_freespace[static_cast (i)].empty ()) + { + tiers[size++] = i; + } + } + + return size; + } + + bool + bestspace::L2::empty (tier fs) + { + assert (fs > tier::FS0); + + return m_freespace[static_cast (fs)].empty (); + } + + void + bestspace::L2::clear () + { + uint64_t val; + + val = 0; + std::memcpy (static_cast (m_freespace.data ()), &val, sizeof (uint64_t)); + } + + void + bestspace::L2::clear (std::size_t index) + { + uint64_t val; + + assert (index < BITS_PER_BYTE); + + std::memcpy (&val, m_freespace.data (), sizeof (uint64_t)); + val &= ~ (0x0101010101010101ULL << index); + std::memcpy (static_cast (m_freespace.data ()), &val, sizeof (uint64_t)); + } + + void + bestspace::L2::set (tier fs, std::size_t index) + { + assert (fs > tier::FS0); + assert (index < BITS_PER_BYTE); + + m_freespace[static_cast (fs)].set (index); + } + + bestspace::L3::L3 () noexcept + : m_freespace () + { + } + + std::size_t + bestspace::L3::find (tier minimum, std::array &pos) + { + assert (minimum >= tier::FS1 && minimum <= tier::FS8); + + return m_freespace[static_cast (minimum)].find (pos); + } + + void + bestspace::L3::clear () + { + uint64_t val; + + val = 0; + std::memcpy (static_cast (m_freespace.data ()), &val, sizeof (uint64_t)); + } + + void + bestspace::L3::clear (std::size_t index) + { + uint64_t val; + + assert (index < BITS_PER_BYTE); + + std::memcpy (&val, m_freespace.data (), sizeof (uint64_t)); + val &= ~ (0x0101010101010101ULL << index); + std::memcpy (static_cast (m_freespace.data ()), &val, sizeof (uint64_t)); + } + + void + bestspace::L3::set (tier fs, std::size_t index) + { + assert (fs > tier::FS0); + assert (index < BITS_PER_BYTE); + + m_freespace[static_cast (fs)].set (index); + } + + bestspace::shard::shard (bestspace &parent) noexcept + : m_allocating (false) + , m_L3 () + , m_L2 () + , m_L1 () + , m_parent (parent) + , m_num_pages (0) + , m_recs_num (0) + , m_recs_sumlen (0) + , m_stats { false, 0, 0, 0, 0, 0, 0, 0 } + { + } + + void + bestspace::shard::reset (const bestspace_entry entries[ENTRIES_PER_SHARD]) + { + std::array tiers; + std::size_t length; + std::size_t i, j; + tier fs; + L3 l3; + L2 l2; + L1 l1; + + // L1 + for (i = 0; i < ENTRIES_PER_SHARD; i++) + { + l1.set_vpid ({ entries[i].pageid, entries[i].volid }); + l1.set_freespace (entries[i].freespace); + m_L1[i].store (l1); + } + // L2 + for (i = 0; i < L3_FANOUT; i++) + { + l2.clear (); + for (j = 0; j < L2_FANOUT; j++) + { + fs = size_to_tier (entries[i * L2_FANOUT + j].freespace); + if (fs > tier::FS0) + { + l2.set (fs, j); + } + } + m_L2[i].store (l2); + } + // L3 + l3.clear (); + for (i = 0; i < L3_FANOUT; i++) + { + l2 = m_L2[i].load (); + length = l2.collect (tiers); + for (j = 0; j < length; j++) + { + l3.set (tiers[j], i); + } + } + m_L3.store (l3); + } + + bestspace::status + bestspace::shard::find (OID *class_oid, HFID *hfid, std::uint16_t needed_size, std::uint16_t consume_size, + std::size_t bias, PGBUF_WATCHER &page_watcher) + { + status error; + tier minimum; + + STATS_INC (request, 1); + + // convert and advance + minimum = size_to_tier (needed_size); + if (minimum < tier::FS8) + { + minimum++; + } + + error = L3_find (class_oid, minimum, needed_size, consume_size, bias, page_watcher); + if (error == status::FOUND || error == status::FAILURE) + { + return error; + } + + assert (error == status::NOT_FOUND || error == status::CONTENDED); + + return allocate (hfid, consume_size, page_watcher); + } + + void + bestspace::shard::add_estimates (int num_pages, std::uint64_t recs_num, std::uint64_t recs_sumlen) + { + m_num_pages.fetch_add (num_pages); + m_recs_num.fetch_add (recs_num); + m_recs_sumlen.fetch_add (recs_sumlen); + } + + void + bestspace::shard::subtract_estimates (int num_pages, std::uint64_t recs_num, std::uint64_t recs_sumlen) + { + m_num_pages.fetch_sub (num_pages); + m_recs_num.fetch_sub (recs_num); + m_recs_sumlen.fetch_sub (recs_sumlen); + } + + void + bestspace::shard::get_estimates (int &num_pages, std::uint64_t &recs_num, std::uint64_t &recs_sumlen) + { + num_pages += m_num_pages.load (); + recs_num += m_recs_num.load (); + recs_sumlen += m_recs_sumlen.load (); + } + + void + bestspace::shard::get_stats (std::uint32_t &request, std::uint32_t &advanced_shard, std::uint32_t &fetch_L3, + std::uint32_t &fetch_L2, std::uint32_t &fetch_L1, std::uint32_t &found, std::uint32_t &allocated) + { + request += m_stats.request.load (); + advanced_shard += m_stats.advance_shard.load (); + fetch_L3 += m_stats.fetch_L3.load (); + fetch_L2 += m_stats.fetch_L2.load (); + fetch_L1 += m_stats.fetch_L1.load (); + found += m_stats.found.load (); + allocated += m_stats.allocated.load (); + } + + void + bestspace::shard::to_entries (bestspace_entry *entries) + { + std::size_t i; + L1 l1; + + for (i = 0; i < ENTRIES_PER_SHARD; i++) + { + l1 = m_L1[i].load (); + std::memcpy (&entries[i], &l1, sizeof (bestspace_entry)); + } + } + + bestspace::status + bestspace::shard::L3_find (OID *class_oid, tier minimum, std::uint16_t needed_size, std::uint16_t consume_size, + std::size_t bias, PGBUF_WATCHER &page_watcher) + { + std::array pos; + std::size_t length, i; + bool contended = false; + status error; + L3 l3; + + assert (minimum > tier::FS0); + + for (; minimum <= tier::FS8; minimum++) + { + l3 = m_L3.load (); + + STATS_INC (fetch_L3, 1); + + length = l3.find (minimum, pos); + for (i = 0; i < length; i++) + { + error = L2_find (class_oid, minimum, needed_size, consume_size, pos[ (i + bias) % length], bias, + page_watcher); + if (error == status::FOUND || error == status::FAILURE) + { + return error; + } + if (error == status::CONTENDED) + { + contended = true; + } + } + } + + return contended ? status::CONTENDED : status::NOT_FOUND; + } + + void + bestspace::shard::L3_update (std::size_t l2_index) + { + std::array tiers; + std::size_t length, i; + L3 expected, desired; + L2 l2; + + while (true) + { + expected = m_L3.load (); + do + { + desired = expected; + + l2 = m_L2[l2_index].load (); + length = l2.collect (tiers); + desired.clear (l2_index); + for (i = 0; i < length; i++) + { + desired.set (tiers[i], l2_index); + } + + if (desired == expected) + { + return; + } + } + while (!m_L3.compare_exchange_strong (expected, desired)); + + if (l2 == m_L2[l2_index].load ()) + { + break; + } + } + } + + bestspace::status + bestspace::shard::L2_find (OID *class_oid, tier minimum, std::uint16_t needed_size, std::uint16_t consume_size, + std::size_t l2_index, std::size_t bias, PGBUF_WATCHER &page_watcher) + { + std::array pos; + std::size_t length, i; + status error; + bool contended = false; + L2 l2; + + assert (minimum > tier::FS0); + + for (; minimum <= tier::FS8; minimum++) + { + l2 = m_L2[l2_index].load (); + + STATS_INC (fetch_L2, 1); + + length = l2.find (minimum, pos); + for (i = 0; i < length; i++) + { + error = L1_find (class_oid, needed_size, consume_size, l2_index, pos[ (i + bias) % length], page_watcher); + if (error == status::FOUND || error == status::FAILURE) + { + return error; + } + if (error == status::CONTENDED) + { + contended = true; + } + } + } + + return contended ? status::CONTENDED : status::NOT_FOUND; + } + + void + bestspace::shard::L2_update (std::size_t l2_index, std::size_t l1_index) + { + tier tier_to, tier_now; + L2 expected, desired; + L1 l1; + + while (true) + { + expected = m_L2[l2_index].load (); + do + { + desired = expected; + desired.clear (l1_index); + + l1 = m_L1[l2_index * L2_FANOUT + l1_index].load (); + tier_to = size_to_tier (l1.get_freespace ()); + if (tier_to > tier::FS0) + { + desired.set (tier_to, l1_index); + } + + if (desired == expected) + { + return; + } + } + while (!m_L2[l2_index].compare_exchange_strong (expected, desired)); + + tier_now = size_to_tier (m_L1[l2_index * L2_FANOUT + l1_index].load ().get_freespace ()); + if (tier_now == tier_to) + { + break; + } + } + + L3_update (l2_index); + } + + bestspace::status + bestspace::shard::L1_find (OID *class_oid, std::uint16_t needed_size, std::uint16_t consume_size, + std::size_t l2_index, std::size_t l1_index, PGBUF_WATCHER &page_watcher) + { + cubthread::entry *thread_p = thread_get_thread_entry_info (); + std::size_t freespace; + VPID vpid, old_vpid; + L1 expected, desired; + OID page_class_oid; + status error; + + STATS_INC (fetch_L1, 1); + + assert (PGBUF_IS_CLEAN_WATCHER (&page_watcher)); + + // first, check the recorded free space + expected = m_L1[l2_index * L2_FANOUT + l1_index].load (); + if (expected.get_freespace () < needed_size) + { + // there is no enough space + return status::NOT_FOUND; + } + + // now, fix a page to check the actual free space + old_vpid = expected.get_vpid (); + + error = L1_fix (l2_index, l1_index, expected, old_vpid, page_watcher); + if (error != status::SUCCESS) + { + return error; + } + + // is this page still belongs to the class (class_oid) ? + if (pgbuf_get_page_ptype (thread_p, page_watcher.pgptr) != PAGE_HEAP || + heap_get_class_oid_from_page (thread_p, page_watcher.pgptr, &page_class_oid) != NO_ERROR + || !OID_EQ (&page_class_oid, class_oid)) + { + L1_remove (l2_index, l1_index, expected); + pgbuf_ordered_unfix (thread_p, &page_watcher); + return status::NOT_FOUND; + } + + // L1 might be changed + expected = m_L1[l2_index * L2_FANOUT + l1_index].load (); + // newest + vpid = expected.get_vpid (); + // store the old and get the actual free space + freespace = spage_max_space_for_new_record (thread_p, page_watcher.pgptr); + if (freespace < needed_size) + { + // there is no enough space and the free space information is wrong + if (VPID_EQ (&vpid, &old_vpid)) + { + desired = expected; + desired.set_freespace (freespace); + + // and I'm the only one that can modify this L1 + if (m_L1[l2_index * L2_FANOUT + l1_index].compare_exchange_strong (expected, desired)) + { + L2_update (l2_index, l1_index); + } + } + pgbuf_ordered_unfix (thread_p, &page_watcher); + return status::NOT_FOUND; + } + + // there is enough space + if (VPID_EQ (&vpid, &old_vpid)) + { + desired = expected; + desired.set_freespace (freespace - consume_size); + + // and I'm the only one that can modify this L1 + if (m_L1[l2_index * L2_FANOUT + l1_index].compare_exchange_strong (expected, desired)) + { + L2_update (l2_index, l1_index); + } + } + + STATS_INC (found, 1); + + return status::FOUND; + } + + bestspace::status + bestspace::shard::L1_fix (std::size_t l2_index, std::size_t l1_index, L1 l1, VPID vpid, PGBUF_WATCHER &page_watcher) + { + cubthread::entry *thread_p; + int wait_msecs; + int error_code; + + thread_p = thread_get_thread_entry_info (); + wait_msecs = xlogtb_reset_wait_msecs (thread_p, LK_FORCE_ZERO_WAIT); + error_code = pgbuf_ordered_fix (thread_p, &vpid, OLD_PAGE_MAYBE_DEALLOCATED, PGBUF_LATCH_WRITE, &page_watcher); + (void) xlogtb_reset_wait_msecs (thread_p, wait_msecs); + if (error_code != NO_ERROR) + { + if (error_code == ER_LK_PAGE_TIMEOUT) + { + er_clear (); + return status::CONTENDED; + } + if (error_code == ER_PB_BAD_PAGEID || er_errid () == ER_PB_BAD_PAGEID) + { + L1_remove (l2_index, l1_index, l1); + er_clear (); + return status::NOT_FOUND; + } + if (er_errid () == NO_ERROR) + { + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_FAILED, 0); + } + return status::FAILURE; + } + return status::SUCCESS; + } + + void + bestspace::shard::L1_remove (std::size_t l2_index, std::size_t l1_index, L1 expected) + { + L1 desired; + + desired.set_freespace (0); + desired.set_vpid (vpid_Null_vpid); + if (m_L1[l2_index * L2_FANOUT + l1_index].compare_exchange_strong (expected, desired)) + { + L2_update (l2_index, l1_index); + } + } + + bestspace::status + bestspace::shard::allocate_mark () + { + bool expected; + + expected = m_allocating.load (); + do + { + if (expected) + { + return status::ALLOCATING; + } + } + while (!m_allocating.compare_exchange_strong (expected, true)); + + return status::SUCCESS; + } + + void + bestspace::shard::allocate_unmark () + { + assert (m_allocating.load ()); + + m_allocating.store (false); + } + + void + bestspace::shard::allocate_pick_victims (std::array &residents, + std::array, ALLOC_BATCH_SIZE> &victims) + { + std::size_t pos; + std::size_t i; + int freespace; + L1 l1; + + victims.fill (std::make_pair (std::numeric_limits::max (), std::numeric_limits::max ())); + for (i = 0; i < L3_FANOUT * L2_FANOUT; i++) + { + l1 = m_L1[i].load (); + freespace = l1.get_freespace (); + + // add residents list + residents[i] = l1.get_vpid (); + + if (freespace >= victims[ALLOC_BATCH_SIZE - 1].second) + { + continue; + } + + pos = ALLOC_BATCH_SIZE - 1; + while (pos > 0 && freespace < victims[pos - 1].second) + { + victims[pos] = victims[pos - 1]; + pos--; + } + + victims[pos] = std::make_pair (i, freespace); + } + } + + std::size_t + bestspace::shard::allocate_pick_candidates (std::array &residents, + std::array, ALLOC_BATCH_SIZE> &victims, + std::array &candidates) + { + std::size_t num_residents = L3_FANOUT * L2_FANOUT; + bestspace_entry buffer[ALLOC_BATCH_SIZE - 1]; + std::size_t num_buffer; + std::size_t num_candidates; + std::size_t i, j; + tier minimum; + + minimum = size_to_tier (victims[ALLOC_BATCH_SIZE - 1].second); + if (minimum >= tier::FS8) + { + // it's better to allocate the new pages if the biggest size of victims is bigger than FS7 + return 0; + } + + num_buffer = m_parent.pop_candidates (buffer, ALLOC_BATCH_SIZE - 1); + + num_candidates = 0; + for (i = 0; i < num_buffer; i++) + { + if (size_to_tier (buffer[i].freespace) <= minimum) + { + continue; + } + for (j = 0; j < num_residents; j++) + { + if (buffer[i].volid == residents[j].volid && + buffer[i].pageid == residents[j].pageid) + { + break; + } + } + if (j == num_residents) + { + candidates[num_candidates] = buffer[i]; + num_candidates++; + + residents[num_residents].volid = buffer[i].volid; + residents[num_residents].pageid = buffer[i].pageid; + num_residents++; + } + } + return num_candidates; + } + + int + bestspace::shard::allocate_new_pages (HFID *hfid, std::size_t num_candidates, + std::array &candidates, PGBUF_WATCHER &page_watcher) + { + std::array vpids; + cubthread::entry *thread_p = thread_get_thread_entry_info (); + int freespace; + int error; + int i; + + error = heap_alloc_new_pages (thread_p, hfid, ALLOC_BATCH_SIZE - num_candidates, vpids.data (), &page_watcher); + if (error != NO_ERROR) + { + return error; + } + m_num_pages.fetch_add (ALLOC_BATCH_SIZE - num_candidates); + + STATS_INC (allocated, ALLOC_BATCH_SIZE - num_candidates); + + freespace = spage_max_space_for_new_record (thread_p, page_watcher.pgptr); + // page_watcher.pgptr fixes the page pointer of the last candidates + for (i = ALLOC_BATCH_SIZE - 1; i >= static_cast (num_candidates); i--) + { + candidates[i].freespace = freespace; + candidates[i].volid = vpids[ALLOC_BATCH_SIZE - 1 - i].volid; + candidates[i].pageid = vpids[ALLOC_BATCH_SIZE - 1 - i].pageid; + } + return NO_ERROR; + } + + void + bestspace::shard::allocate_replace_pages (std::array, ALLOC_BATCH_SIZE> + &victims, std::array &candidates) + { + std::size_t i; + L1 l1; + + // renew the L1s + for (i = 0; i < ALLOC_BATCH_SIZE - 1; i++) + { + l1.set_freespace (candidates[i].freespace); + l1.set_vpid ({ candidates[i].pageid, candidates[i].volid }); + m_L1[victims[i].first].store (l1); + + L2_update (victims[i].first / L2_FANOUT, victims[i].first % L2_FANOUT); + } + if (candidates[i].freespace > victims[i].second) + { + l1.set_freespace (candidates[i].freespace); + l1.set_vpid ({ candidates[i].pageid, candidates[i].volid }); + m_L1[victims[i].first].store (l1); + + L2_update (victims[i].first / L2_FANOUT, victims[i].first % L2_FANOUT); + } + } + + bestspace::status + bestspace::shard::allocate (HFID *hfid, std::uint16_t consume_size, PGBUF_WATCHER &page_watcher) + { + std::array residents; + std::array, ALLOC_BATCH_SIZE> victims; // index, freespace + std::array candidates; + std::size_t num_candidates; + int error; + + // set allcating bit + if (allocate_mark () != status::SUCCESS) + { + STATS_INC (advance_shard, 1); + + return status::ALLOCATING; + } + + // pick four pages with the smallest free space as replacement victims. + allocate_pick_victims (residents, victims); + + // pick four replacement candidates with more freespace than the victim pages above. + num_candidates = allocate_pick_candidates (residents, victims, candidates); + assert (num_candidates <= ALLOC_BATCH_SIZE - 1); + + // allocate the pages at least one + error = allocate_new_pages (hfid, num_candidates, candidates, page_watcher); + if (error != NO_ERROR) + { + allocate_unmark (); + return status::FAILURE; + } + // candidates are four available pages at this point + + // reserve the page + candidates[ALLOC_BATCH_SIZE - 1].freespace -= consume_size; + + // replace L1s + allocate_replace_pages (victims, candidates); + + allocate_unmark (); + return status::FOUND; + } + + bestspace::candidate_queue::candidate_queue () + : m_size (0) + , m_mutex () + { + std::size_t i; + + std::lock_guard lock (m_mutex); + + for (i = 0; i < MAX_CANDIDATES_QUEUE_SIZE; i++) + { + m_array[i].set_null (); + } + } + + void + bestspace::candidate_queue::reset () + { + std::size_t i; + + std::lock_guard lock (m_mutex); + + for (i = 0; i < MAX_CANDIDATES_QUEUE_SIZE; i++) + { + m_array[i].set_null (); + } + m_size = 0; + } + + bool + bestspace::candidate_queue::try_push (bestspace_entry candidate) + { + std::unique_lock ulock (m_mutex, std::try_to_lock); + + // try to acquire + if (!ulock.owns_lock ()) + { + return false; + } + + remove_if_exist (candidate); + + // not enough space to be candidate + if (m_size == MAX_CANDIDATES_QUEUE_SIZE && candidate.freespace <= m_array[0].freespace) + { + return true; + } + + insert (candidate); + + return true; + } + + void + bestspace::candidate_queue::push (bestspace_entry candidate) + { + std::lock_guard lock (m_mutex); + + remove_if_exist (candidate); + + // not enough space to be candidate + if (m_size == MAX_CANDIDATES_QUEUE_SIZE && candidate.freespace <= m_array[0].freespace) + { + return; + } + + insert (candidate); + } + + std::size_t + bestspace::candidate_queue::pop (bestspace_entry *candidates, std::size_t num_candidates) + { + std::size_t i; + + std::lock_guard lock (m_mutex); + + for (i = 0; i < num_candidates && m_size > 0; i++) + { + candidates[i] = m_array[m_size - 1]; + m_size--; + } + return i; + } + + void + bestspace::candidate_queue::remove_if_exist (bestspace_entry &candidate) + { + int i; + + // remove the duplicate if the candidate already exists + for (i = 0; i < static_cast (m_size); i++) + { + if (candidate.volid == m_array[i].volid && candidate.pageid == m_array[i].pageid) + { + std::memmove (m_array.data () + i, m_array.data () + i + 1, sizeof (bestspace_entry) * (m_size - i - 1)); + m_size--; + break; + } + } + } + + void + bestspace::candidate_queue::insert (bestspace_entry &candidate) + { + int i; + + // remove the duplicate if the candidate already exists + for (i = 0; i < static_cast (m_size); i++) + { + if (candidate.freespace < m_array[i].freespace) + { + break; + } + } + + if (m_size == MAX_CANDIDATES_QUEUE_SIZE) + { + assert (i != 0); + + if (i > 1) + { + std::memmove (m_array.data (), m_array.data () + 1, sizeof (bestspace_entry) * (i - 1)); + } + m_array[i - 1] = candidate; + } + else + { + if (i != static_cast (m_size)) + { + std::memmove (m_array.data () + i + 1, m_array.data () + i, sizeof (bestspace_entry) * (m_size - i)); + } + m_array[i] = candidate; + m_size++; + } + } + + bestspace::bestspace (std::size_t shard_count, int num_pages, std::uint64_t recs_num, std::uint64_t recs_sumlen, + std::uint16_t unfill_space) + : m_shards () + , m_unfill_space (unfill_space) + , m_num_pages (num_pages) + , m_recs_num (recs_num) + , m_recs_sumlen (recs_sumlen) + { + assert (shard_count > 0); + + // last updated time + m_last_updated.store (monotonic_seconds ()); + + // create shards + for (std::size_t i = 0; i < shard_count; i++) + { + m_shards.emplace_back (*this); + } + } + + void + bestspace::reset (const bestspace_entry *entries, std::size_t num_entries) + { + std::array shard_entries; + std::size_t entries_to_copy; + std::size_t entry_index; + std::size_t i, j; + + assert (num_entries <= m_shards.size () * ENTRIES_PER_SHARD); + + // candidates + m_candidates.reset (); + + // shards + entry_index = 0; + for (i = 0; i < m_shards.size (); i++) + { + entries_to_copy = entry_index < num_entries ? num_entries - entry_index : 0; + entries_to_copy = MIN (entries_to_copy, ENTRIES_PER_SHARD); + if (entries_to_copy > 0) + { + std::memcpy (shard_entries.data (), entries + entry_index, entries_to_copy * sizeof (bestspace_entry)); + } + for (j = entries_to_copy; j < ENTRIES_PER_SHARD; j++) + { + shard_entries[j].set_null (); + } + + m_shards[i].reset (shard_entries.data ()); + entry_index += entries_to_copy; + } + } + + void + bestspace::try_push_candidates (bestspace_entry *candidates, std::size_t num_candidates) + { + std::size_t i; + + for (i = 0; i < num_candidates; i++) + { + m_candidates.try_push (candidates[i]); + } + } + + void + bestspace::push_candidates (bestspace_entry *candidates, std::size_t num_candidates) + { + std::size_t i; + + for (i = 0; i < num_candidates; i++) + { + m_candidates.push (candidates[i]); + } + } + + std::size_t + bestspace::pop_candidates (bestspace_entry *candidates, std::size_t num_candidates) + { + return m_candidates.pop (candidates, num_candidates); + } + + bool + bestspace::updatable () + { + constexpr std::uint64_t UPDATE_TIME_THRESHOLD = 30; + std::uint64_t last_updated, now; + + last_updated = m_last_updated.load (); + now = monotonic_seconds (); + if (now >= last_updated && now - last_updated >= UPDATE_TIME_THRESHOLD) + { + return m_last_updated.compare_exchange_strong (last_updated, now); + } + return false; + } + + int + bestspace::find (cubthread::entry &thread_ref, OID *class_oid, HFID *hfid, std::uint16_t size, bool is_newrec, + PGBUF_WATCHER &page_watcher) + { + int consume_size, needed_size; + std::size_t shard, bias; + int errid; + + assert (size > 0 && size < DB_PAGESIZE); + assert (!heap_is_big_length (size)); + + // early return or clear stale error to avoid error corruption in below path + errid = er_errid_if_has_error (); + if (errid != NO_ERROR) + { + return errid; + } + er_clear (); + + if (hfid == NULL || HFID_IS_NULL (hfid) || + page_watcher.next != NULL || page_watcher.prev != NULL || page_watcher.pgptr != NULL) + { + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_FAILED, 0); + return ER_FAILED; + } + + // init + PGBUF_INIT_WATCHER (&page_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + + // strategy + consume_size = static_cast (size) + SPAGE_SLOT_SIZE; + needed_size = consume_size + m_unfill_space; + if (needed_size > heap_nonheader_page_capacity ()) + { + needed_size = consume_size; + } + shard = 0; + bias = 0; + + // find + return find_from_shards (thread_ref, class_oid, hfid, shard, needed_size, consume_size, bias, is_newrec, page_watcher); + } + + bestspace::tier + bestspace::size_to_tier (std::uint16_t size) + { + assert (size <= DB_PAGESIZE); + + // FS0: 1-7% + // FS1: 8-15% + // FS2: 16-24% + // FS3: 25-34% + // FS4: 35-45% + // FS5: 46-57% + // FS6: 58-70% + // FS7: 71-84% + // FS8: 85-100% + static constexpr std::int16_t threshold[] = { 7, 15, 24, 34, 45, 57, 70, 84 }; + std::int16_t percentage; + std::int8_t i; + + percentage = size * 100 / DB_PAGESIZE; + for (i = 0; i < static_cast (tier::FS8) + 1; i++) + { + if (percentage <= threshold[i]) + { + return static_cast (i - 1); + } + } + return tier::FS8; + } + + void + bestspace::set_estimates (int num_pages, std::uint64_t recs_num, std::uint64_t recs_sumlen) + { + std::size_t i; + int shard_num_pages; + std::uint64_t shard_recs_num, shard_recs_sumlen; + + // It’s better for the estimates to be higher than the actual values rather than lower. + m_num_pages.store (num_pages); + m_recs_num.store (recs_num); + m_recs_sumlen.store (recs_sumlen); + + // and sub + for (i = 0; i < m_shards.size (); i++) + { + shard_num_pages = 0; + shard_recs_num = 0; + shard_recs_sumlen = 0; + m_shards[i].get_estimates (shard_num_pages, shard_recs_num, shard_recs_sumlen); + m_shards[i].subtract_estimates (shard_num_pages, shard_recs_num, shard_recs_sumlen); + } + } + + void + bestspace::get_estimates (int &num_pages, std::uint64_t &recs_num, std::uint64_t &recs_sumlen) + { + std::size_t i; + + num_pages = m_num_pages.load (); + recs_num = m_recs_num.load (); + recs_sumlen = m_recs_sumlen.load (); + for (i = 0; i < m_shards.size (); i++) + { + m_shards[i].get_estimates (num_pages, recs_num, recs_sumlen); + } + } + + void + bestspace::get_stats (std::uint32_t &request, std::uint32_t &advanced_shard, std::uint32_t &fetch_L3, + std::uint32_t &fetch_L2, std::uint32_t &fetch_L1, std::uint32_t &found, std::uint32_t &allocated) + { + std::size_t i; + + request = 0; + advanced_shard = 0; + fetch_L3 = 0; + fetch_L2 = 0; + fetch_L1 = 0; + found = 0; + allocated = 0; + for (i = 0; i < m_shards.size (); i++) + { + m_shards[i].get_stats (request, advanced_shard, fetch_L3, fetch_L2, fetch_L1, found, allocated); + } + } + + std::size_t + bestspace::get_num_shards () + { + return m_shards.size (); + } + + void + bestspace::to_entries (bestspace_entry *entries) + { + std::size_t i; + + for (i = 0; i < m_shards.size (); i++) + { + m_shards[i].to_entries (entries + i * ENTRIES_PER_SHARD); + } + } + + int + bestspace::find_from_shards (cubthread::entry &thread_ref, OID *class_oid, HFID *hfid, std::size_t shard, + std::uint16_t needed_size, std::uint16_t consume_size, std::size_t bias, bool is_newrec, PGBUF_WATCHER &page_watcher) + { + std::size_t retry; + std::size_t i; + status error; + int errid; + + retry = 0; + while (true) + { + for (i = 0; i < m_shards.size (); i++) + { + error = m_shards[ (shard + i) % m_shards.size ()].find ( + class_oid, + hfid, + needed_size, + consume_size, + bias, + page_watcher); + assert (error == status::FOUND || + error == status::ALLOCATING || + error == status::FAILURE); + if (error == status::FOUND) + { + m_shards[ (shard + i) % m_shards.size ()].add_estimates (0, is_newrec ? 1 : 0, consume_size - SPAGE_SLOT_SIZE); + return NO_ERROR; + } + if (error == status::FAILURE) + { + ASSERT_ERROR (); + errid = er_errid (); + return errid != NO_ERROR ? errid : ER_FAILED; + } + } + + assert (error == status::ALLOCATING); + + errid = pgbuf_ordered_callback (&thread_ref, wait_for_shard_allocation, &retry); + if (errid != NO_ERROR) + { + return errid; + } + } + + // impossible ! + assert (false); + return ER_FAILED; + } + + ////////////////////////////////////////////////////////////////////////// + // bestspace register/unregister + ////////////////////////////////////////////////////////////////////////// + + bestspace_registry::registry_cache::registry_cache () + : head (nullptr) + , size (0) + , generation (0) + { + } + + bestspace_registry::registry_cache::~registry_cache () + { + registry_entry *next; + + while (head) + { + next = head->next; + delete head; + head = next; + } + } + + bestspace_registry::bestspace_registry () + : m_head (nullptr) + , m_mutex () + , m_generation (1) + { + } + + bestspace_registry::~bestspace_registry () + { + registry_entry *node; + + std::lock_guard lock (m_mutex); + + while (m_head) + { + node = m_head; + m_head = m_head->next; + + delete node->entry; + delete node; + } + } + + void + bestspace_registry::create (HFID *hfid, std::size_t shard_count, bestspace_entry *entries, std::size_t num_entries, + bestspace_entry *candidates, std::size_t num_candidates, int num_pages, std::uint64_t recs_num, + std::uint64_t recs_sumlen, std::uint16_t unfill_space) + { + registry_entry *node; + + node = new registry_entry; + node->hfid = *hfid; + node->entry = new bestspace (shard_count, num_pages, recs_num, recs_sumlen, unfill_space); + node->entry->reset (entries, num_entries); + node->entry->push_candidates (candidates, num_candidates); + + std::lock_guard lock (m_mutex); + + assert (!find_entry (m_head, hfid)); + insert_entry (m_head, node); + } + + void + bestspace_registry::destroy (const VFID *vfid) + { + registry_entry *node; + + std::lock_guard lock (m_mutex); + + while ((node = get_node_from_list (m_head, vfid))) + { + m_generation.fetch_add (1); + destroy_entry (node); + } + } + + void + bestspace_registry::destroy (const HFID *hfid) + { + registry_entry *node; + + std::lock_guard lock (m_mutex); + + while ((node = get_node_from_list (m_head, hfid))) + { + m_generation.fetch_add (1); + destroy_entry (node); + } + } + + bestspace * + bestspace_registry::find (HFID *hfid) + { + bestspace *entry; + + entry = find_from_cache (hfid); + if (entry) + { + return entry; + } + return find_from_global (hfid); + } + + bestspace * + bestspace_registry::find_from_cache (HFID *hfid) + { + registry_entry *cache; + std::uint64_t generation; + + generation = m_generation.load (); + if (TLS_cache.generation != generation) + { + TLS_cache.generation = generation; + invalidate_entries (TLS_cache.head); + return nullptr; + } + + cache = get_node_from_list (TLS_cache.head, hfid); + if (!cache) + { + return nullptr; + } + + // make this cache the first (LRU) + insert_entry (TLS_cache.head, cache); + return cache->entry; + } + + bestspace * + bestspace_registry::find_from_global (HFID *hfid) + { + registry_entry *cache; + bestspace *entry; + + std::unique_lock ulock (m_mutex); + + auto pair = find_entry (m_head, hfid); + if (!pair) + { + // invalid class oid and hfid + return nullptr; + } + entry = (pair->second)->entry; + + ulock.unlock (); + + // register in TLS list + if (TLS_cache.size < TLS_MAX_SIZE) + { + cache = new registry_entry; + TLS_cache.size++; + } + else + { + cache = get_tail_from_list (TLS_cache.head); + } + cache->hfid = *hfid; + cache->entry = entry; + + insert_entry (TLS_cache.head, cache); + return entry; + } + + void + bestspace_registry::insert_entry (registry_entry *&head, registry_entry *entry) + { + entry->next = head; + head = entry; + } + + void + bestspace_registry::destroy_entry (registry_entry *entry) + { + delete entry->entry; + delete entry; + } + + std::optional> + bestspace_registry::find_entry (registry_entry *head, const VFID *vfid) + { + registry_entry *prev; + + for (prev = nullptr; head; prev = head, head = head->next) + { + if (VFID_EQ (&head->hfid.vfid, vfid)) + { + return std::make_pair (prev, head); + } + } + return std::nullopt; + } + + std::optional> + bestspace_registry::find_entry (registry_entry *head, const HFID *hfid) + { + registry_entry *prev; + + for (prev = nullptr; head; prev = head, head = head->next) + { + if (HFID_EQ (&head->hfid, hfid)) + { + return std::make_pair (prev, head); + } + } + return std::nullopt; + } + + void + bestspace_registry::invalidate_entries (registry_entry *head) + { + while (head) + { + HFID_SET_NULL (&head->hfid); + head->entry = nullptr; + head = head->next; + } + } + + bestspace_registry::registry_entry * + bestspace_registry::get_node_from_list (registry_entry *&head, const VFID *vfid) + { + auto pair = find_entry (head, vfid); + if (!pair) + { + return nullptr; + } + + if (pair->first) + { + (pair->first)->next = (pair->second)->next; + } + else + { + head = (pair->second)->next; + } + return pair->second; + } + + bestspace_registry::registry_entry * + bestspace_registry::get_node_from_list (registry_entry *&head, const HFID *hfid) + { + auto pair = find_entry (head, hfid); + if (!pair) + { + return nullptr; + } + + if (pair->first) + { + (pair->first)->next = (pair->second)->next; + } + else + { + head = (pair->second)->next; + } + return pair->second; + } + + bestspace_registry::registry_entry * + bestspace_registry::get_tail_from_list (registry_entry *&head) + { + registry_entry *prev, *curr; + + if (!head) + { + return nullptr; + } + for (prev = nullptr, curr = head; curr->next; prev = curr, curr = curr->next); + if (prev) + { + prev->next = nullptr; + } + else + { + head = nullptr; + } + return curr; + } + + ////////////////////////////////////////////////////////////////////////// + // bestspace registry + ////////////////////////////////////////////////////////////////////////// + + bestspace_registry bestspaces; +} diff --git a/src/storage/bestspace.hpp b/src/storage/bestspace.hpp new file mode 100644 index 00000000000..0cf0e51645f --- /dev/null +++ b/src/storage/bestspace.hpp @@ -0,0 +1,470 @@ +/* + * + * 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. + * + */ + +// +// bestspace.hpp - bestspace in memory +// + +#ifndef _BESTSPACE_HPP_ +#define _BESTSPACE_HPP_ + +#include "thread_entry.hpp" +#include "page_buffer.h" +#include "storage_common.h" +#include "dbtype_def.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cubstorage +{ + ////////////////////////////////////////////////////////////////////////// + // export class + ////////////////////////////////////////////////////////////////////////// + + typedef struct bestspace_entry BESTSPACE_ENTRY; + struct bestspace_entry + { + std::uint16_t freespace; + short volid; + int32_t pageid; + + void set_null () + { + freespace = 0; + volid = NULL_VOLID; + pageid = NULL_PAGEID; + } + }; + + static_assert (sizeof (bestspace_entry) == 8, "bestspace_entry must be 8 bytes"); + static_assert (offsetof (bestspace_entry, freespace) == 0, "freespace must be placed at first"); + static_assert (offsetof (bestspace_entry, volid) == 2, "volid must be placed at second"); + static_assert (offsetof (bestspace_entry, pageid) == 4, "pageid must be placed at last"); + + ////////////////////////////////////////////////////////////////////////// + // base class + ////////////////////////////////////////////////////////////////////////// + + class bestspace + { + public: + static constexpr std::size_t BITS_PER_BYTE = std::numeric_limits::digits; + static constexpr std::size_t MAX_CANDIDATES_QUEUE_SIZE = 128; + static constexpr std::size_t MAX_SHARD_PAGE_COUNT = 4; + static constexpr std::size_t ALLOC_BATCH_SIZE = 4; + static constexpr std::size_t L3_FANOUT = 8; + static constexpr std::size_t L2_FANOUT = 8; + static constexpr std::size_t ENTRIES_PER_SHARD = L3_FANOUT * L2_FANOUT; + static constexpr std::size_t DEFAULT_SHARD_COUNT = 8; + + enum class tier : std::int8_t + { + FS0 = -1, // 1-7% + FS1 = 0, // 8-15% + FS2, // 16-24% + FS3, // 25-34% + FS4, // 35-45% + FS5, // 46-57% + FS6, // 58-70% + FS7, // 71-84% + FS8, // 85-100% + FSEND // END + }; + + private: + enum class status + { + NOT_FOUND, + FOUND, + CONTENDED, + ALLOCATING, + SUCCESS, + FAILURE + }; + + friend tier &operator++ (tier &v) + { + if (v < tier::FSEND) + { + v = static_cast (static_cast (v) + 1); + } + return v; + } + + friend tier operator++ (tier &v, int) + { + tier result = v; + ++v; + return result; + } + + template + struct alignas (64) atomic_wrapper + { + std::atomic value; + + atomic_wrapper () + : value () + { + } + + atomic_wrapper (T val) + : value (val) + { + } + + T load () const noexcept + { + return value.load (); + } + + void store (T desired) noexcept + { + value.store (desired); + } + + bool compare_exchange_strong (T &expected, T desired) noexcept + { + return value.compare_exchange_strong (expected, desired); + } + }; + + class bitmap + { + public: + bitmap () noexcept; + ~bitmap () = default; + + bool empty (); + + void set (std::size_t index); + void clear (std::size_t index); + + std::size_t find (std::array &pos, std::size_t length = BITS_PER_BYTE); + + private: + std::uint8_t m_bits; + }; + + class L1 + { + public: + L1 () noexcept; + ~L1 () = default; + + std::uint16_t get_freespace (); + void set_freespace (std::uint16_t size); + + VPID get_vpid (); + void set_vpid (VPID vpid); + + private: + std::uint16_t m_freespace; + + short m_volid; + int32_t m_pageid; + }; + + class L2 + { + public: + L2 () noexcept; + ~L2 () = default; + + std::size_t find (tier minimum, std::array &pos); + + std::size_t collect (std::array &tiers); + + bool empty (tier fs); + + void clear (); + void clear (std::size_t index); + void set (tier fs, std::size_t index); + + friend bool operator== (const L2 &lhs, const L2 &rhs) + { + return std::memcmp (lhs.m_freespace.data (), rhs.m_freespace.data (), 8) == 0; + } + + private: + std::array m_freespace; + }; + + class L3 + { + public: + L3 () noexcept; + ~L3 () = default; + + std::size_t find (tier minimum, std::array &pos); + + void clear (); + void clear (std::size_t index); + void set (tier fs, std::size_t index); + + friend bool operator== (const L3 &lhs, const L3 &rhs) + { + return std::memcmp (lhs.m_freespace.data (), rhs.m_freespace.data (), 8) == 0; + } + + private: + std::array m_freespace; + }; + + class alignas (64) shard + { + public: + shard (bestspace &parent) noexcept; + ~shard () = default; + + void reset (const bestspace_entry entries[ENTRIES_PER_SHARD]); + + status find (OID *class_oid, HFID *hfid, std::uint16_t needed_size, std::uint16_t consume_size, + std::size_t bias, PGBUF_WATCHER &page_watcher); + + void add_estimates (int num_pages, std::uint64_t recs_num, std::uint64_t recs_sumlen); + void subtract_estimates (int num_pages, std::uint64_t recs_num, std::uint64_t recs_sumlen); + void get_estimates (int &num_pages, std::uint64_t &recs_num, std::uint64_t &recs_sumlen); + void get_stats (std::uint32_t &request, std::uint32_t &advanced_shard, std::uint32_t &fetch_L3, std::uint32_t &fetch_L2, + std::uint32_t &fetch_L1, std::uint32_t &found, std::uint32_t &allocated); + + void to_entries (bestspace_entry *entries); + + private: + // core + atomic_wrapper m_allocating; + + atomic_wrapper m_L3; + atomic_wrapper m_L2[L3_FANOUT]; + atomic_wrapper m_L1[L3_FANOUT * L2_FANOUT]; + + // information per shard + bestspace &m_parent; + + std::atomic m_num_pages; + std::atomic m_recs_num; + std::atomic m_recs_sumlen; + + // stats + struct + { + std::atomic enabled; + + std::atomic request; + std::atomic advance_shard; + + std::atomic fetch_L3; + std::atomic fetch_L2; + std::atomic fetch_L1; + + std::atomic found; + std::atomic allocated; + } m_stats; + + status L3_find (OID *class_oid, tier minimum, std::uint16_t needed_size, std::uint16_t consume_size, + std::size_t bias, PGBUF_WATCHER &page_watcher); + void L3_update (std::size_t l2_index); + + status L2_find (OID *class_oid, tier minimum, std::uint16_t needed_size, std::uint16_t consume_size, + std::size_t l2_index, std::size_t bias, PGBUF_WATCHER &page_watcher); + void L2_update (std::size_t l2_index, std::size_t l1_index); + + status L1_find (OID *class_oid, std::uint16_t needed_size, std::uint16_t consume_size, std::size_t l2_index, + std::size_t l1_index, PGBUF_WATCHER &page_watcher); + status L1_fix (std::size_t l2_index, std::size_t l1_index, L1 l1, VPID vpid, PGBUF_WATCHER &page_watcher); + void L1_remove (std::size_t l2_index, std::size_t l1_index, L1 l1); + + status allocate_mark (); + void allocate_unmark (); + + void allocate_pick_victims (std::array &residents, + std::array, ALLOC_BATCH_SIZE> &victims); + std::size_t allocate_pick_candidates (std::array &residents, + std::array, ALLOC_BATCH_SIZE> &victims, + std::array &candidates); + + int allocate_new_pages (HFID *hfid, std::size_t num_candidates, + std::array &candidates, PGBUF_WATCHER &page_watcher); + void allocate_replace_pages (std::array, ALLOC_BATCH_SIZE> &victims, + std::array &candidates); + + status allocate (HFID *hfid, std::uint16_t consume_size, PGBUF_WATCHER &page_watcher); + }; + + class candidate_queue + { + public: + candidate_queue (); + ~candidate_queue () = default; + + void reset (); + + bool try_push (bestspace_entry candidate); + void push (bestspace_entry candidate); + + std::size_t pop (bestspace_entry *candidates, std::size_t num_candidates); + + private: + std::array m_array; + std::size_t m_size; + + std::mutex m_mutex; + + void remove_if_exist (bestspace_entry &candidate); + void insert (bestspace_entry &candidate); + }; + + public: + explicit bestspace (std::size_t shard_count, int num_pages, std::uint64_t recs_num, std::uint64_t recs_sumlen, + std::uint16_t unfill_space); + ~bestspace () = default; + + void reset (const bestspace_entry *entries, std::size_t num_entries); + + void try_push_candidates (bestspace_entry *candidates, std::size_t num_candidates); + void push_candidates (bestspace_entry *candidates, std::size_t num_candidates); + std::size_t pop_candidates (bestspace_entry *candidates, std::size_t num_candidates); + + bool updatable (); + + int find (cubthread::entry &thread_ref, OID *class_oid, HFID *hfid, std::uint16_t size, bool is_newrec, + PGBUF_WATCHER &page_watcher); + + static tier size_to_tier (std::uint16_t size); + + void set_estimates (int num_pages, std::uint64_t recs_num, std::uint64_t recs_sumlen); + void get_estimates (int &num_pages, std::uint64_t &recs_num, std::uint64_t &recs_sumlen); + void get_stats (std::uint32_t &request, std::uint32_t &advanced_shard, std::uint32_t &fetch_L3, std::uint32_t &fetch_L2, + std::uint32_t &fetch_L1, std::uint32_t &found, std::uint32_t &allocated); + + std::size_t get_num_shards (); + + void to_entries (bestspace_entry *entries); + + private: + std::deque m_shards; + candidate_queue m_candidates; + + // parameter + std::uint16_t m_unfill_space; + + // statistics + std::atomic m_num_pages; + std::atomic m_recs_num; + std::atomic m_recs_sumlen; + + std::atomic m_last_updated; + + int find_from_shards (cubthread::entry &thread_ref, OID *class_oid, HFID *hfid, std::size_t shard, + std::uint16_t needed_size, std::uint16_t consume_size, std::size_t bias, bool is_newrec, PGBUF_WATCHER &page_watcher); + + static_assert (sizeof (bitmap) == 1, "bestspace::bitmap must be 1 byte"); + static_assert (std::is_trivially_copyable::value, "bestspace::bitmap must be trivially copyable"); + + static_assert (sizeof (L1) == 8, "bestspace::L1 must be 8 bytes"); + static_assert (sizeof (L2) == 8, "bestspace::L2 must be 8 bytes"); + static_assert (sizeof (L3) == 8, "bestspace::L3 must be 8 bytes"); + static_assert (std::atomic::is_always_lock_free, "std::atomic must be lock-free"); + static_assert (std::atomic::is_always_lock_free, "std::atomic must be lock-free"); + static_assert (std::atomic::is_always_lock_free, "std::atomic must be lock-free"); + static_assert (sizeof (atomic_wrapper) == 64, "bestspace::atomic_wrapper must be 64 bytes"); + static_assert (sizeof (atomic_wrapper) == 64, "bestspace::atomic_wrapper must be 64 bytes"); + static_assert (sizeof (atomic_wrapper) == 64, "bestspace::atomic_wrapper must be 64 bytes"); + static_assert (alignof (atomic_wrapper) == 64, "bestspace::atomic_wrapper must be aligned as 64 bytes"); + static_assert (alignof (atomic_wrapper) == 64, "bestspace::atomic_wrapper must be aligned as 64 bytes"); + static_assert (alignof (atomic_wrapper) == 64, "bestspace::atomic_wrapper must be aligned as 64 bytes"); + + static_assert (sizeof (shard) == 4800, "bestspace::shard must be 4800 bytes"); + static_assert (alignof (shard) == 64, "bestspace::shard must be aligned as 64 bytes"); + }; + + ////////////////////////////////////////////////////////////////////////// + // bestspace register/unregister + ////////////////////////////////////////////////////////////////////////// + + class bestspace_registry + { + private: + struct registry_entry + { + HFID hfid; + bestspace *entry; + + registry_entry *next; + }; + + struct registry_cache + { + registry_entry *head; + std::size_t size; + std::size_t generation; + + registry_cache (); + ~registry_cache (); + }; + + public: + bestspace_registry (); + ~bestspace_registry (); + + void create (HFID *hfid, std::size_t shard_count, bestspace_entry *entries, std::size_t num_entries, + bestspace_entry *candidates, std::size_t num_candidates, int num_pages, std::uint64_t recs_num, + std::uint64_t recs_sumlen, std::uint16_t unfill_space); + void destroy (const VFID *vfid); + void destroy (const HFID *hfid); + + bestspace *find (HFID *hfid); + + void show_stats (); + + private: + registry_entry *m_head; + std::mutex m_mutex; + + alignas (64) std::atomic m_generation; + + static constexpr std::size_t TLS_MAX_SIZE = 40; + inline static thread_local registry_cache TLS_cache; + + bestspace *find_from_cache (HFID *hfid); + bestspace *find_from_global (HFID *hfid); + + void insert_entry (registry_entry *&head, registry_entry *entry); + void destroy_entry (registry_entry *entry); + std::optional> find_entry (registry_entry *head, const VFID *vfid); + std::optional> find_entry (registry_entry *head, const HFID *hfid); + + void invalidate_entries (registry_entry *head); + + registry_entry *get_node_from_list (registry_entry *&head, const VFID *vfid); + registry_entry *get_node_from_list (registry_entry *&head, const HFID *hfid); + registry_entry *get_tail_from_list (registry_entry *&head); + }; + + extern bestspace_registry bestspaces; +} + +#endif // _BESTSPACE_HPP_ diff --git a/src/storage/btree.c b/src/storage/btree.c index a6b7105053e..5853fe17bf9 100644 --- a/src/storage/btree.c +++ b/src/storage/btree.c @@ -27627,7 +27627,7 @@ btree_split_node_and_advance (THREAD_ENTRY * thread_p, BTID_INT * btid_int, DB_V btree_get_max_new_data_size (thread_p, btid_int, *crt_page, node_type, max_key_len, insert_helper, false); /* Split is needed if there is a risk that inserted data doesn't fit the root node. */ - need_split = (max_new_data_size > spage_get_free_space_without_saving (thread_p, *crt_page, NULL)); + need_split = (max_new_data_size > spage_get_free_space_without_saving (thread_p, *crt_page)); /* If root node should suffer changes, its latch must be promoted to exclusive. */ if (insert_helper->nonleaf_latch_mode == PGBUF_LATCH_READ @@ -27900,7 +27900,7 @@ btree_split_node_and_advance (THREAD_ENTRY * thread_p, BTID_INT * btid_int, DB_V max_new_data_size = btree_get_max_new_data_size (thread_p, btid_int, child_page, node_type, max_key_len, insert_helper, false); - need_split = max_new_data_size > spage_get_free_space_without_saving (thread_p, child_page, NULL); + need_split = max_new_data_size > spage_get_free_space_without_saving (thread_p, child_page); /* If split is needed, we first need to make sure current node is latched exclusively. A new entry must be added. * Promoting latch from read to write may be required if the node is not already latched exclusively. Node is not @@ -28552,7 +28552,7 @@ btree_key_insert_does_leaf_need_split (THREAD_ENTRY * thread_p, BTID_INT * btid_ if (search_key->result == BTREE_KEY_FOUND) { /* Does a new object fit the page? */ - return (BTREE_OBJECT_MAX_SIZE > spage_get_free_space_without_saving (thread_p, leaf_page, NULL)); + return (BTREE_OBJECT_MAX_SIZE > spage_get_free_space_without_saving (thread_p, leaf_page)); } else { @@ -34449,7 +34449,7 @@ btree_key_online_index_IB_insert_list (THREAD_ENTRY * thread_p, BTID_INT * btid_ bool key_already_in_page = false; int new_ent_size = btree_get_max_new_data_size (thread_p, btid_int, *leaf_page, BTREE_LEAF_NODE, key_len, &helper->insert_helper, key_already_in_page); - if (new_ent_size > spage_get_free_space_without_saving (thread_p, *leaf_page, NULL)) + if (new_ent_size > spage_get_free_space_without_saving (thread_p, *leaf_page)) { /* no more space in page */ perfmon_inc_stat (thread_p, PSTAT_BT_ONLINE_NUM_RETRY); diff --git a/src/storage/btree_load.c b/src/storage/btree_load.c index be2771f4fb7..1b4418f51c7 100644 --- a/src/storage/btree_load.c +++ b/src/storage/btree_load.c @@ -3311,6 +3311,13 @@ get_next_vpid (THREAD_ENTRY * thread_p, ftab_set & ftab, FILE_PARTIAL_SECTOR * f return S_ERROR; } + if (heap_page_is_bestspace (thread_p, scan_cache->page_watcher.pgptr)) + { + pgbuf_ordered_unfix (thread_p, &scan_cache->page_watcher); + PGBUF_CLEAR_WATCHER (&scan_cache->page_watcher); + continue; + } + return S_SUCCESS; } } diff --git a/src/storage/heap_file.c b/src/storage/heap_file.c index 0f0f222f94e..40dcf5622f8 100644 --- a/src/storage/heap_file.c +++ b/src/storage/heap_file.c @@ -33,6 +33,7 @@ #include #include +#include "bestspace.hpp" #include "heap_file.h" #include "deduplicate_key.h" @@ -88,8 +89,6 @@ static int rv; #endif /* not SERVER_MODE */ -#define HEAP_BESTSPACE_SYNC_THRESHOLD (0.1f) - /* ATTRIBUTE LOCATION */ #define OR_FIXED_ATTRIBUTES_OFFSET_BY_OBJ(obj, nvars) \ @@ -100,9 +99,6 @@ static int rv; #define HEAP_CLASSREPR_MAXCACHE 1024 -#define HEAP_STATS_ENTRY_MHT_EST_SIZE 1000 -#define HEAP_STATS_ENTRY_FREELIST_SIZE 1000 - #define HEAP_DEBUG_SCANCACHE_INITPATTERN (12345) #if defined(CUBRID_DEBUG) @@ -185,68 +181,55 @@ typedef enum * Heap file header */ -#define HEAP_NUM_BEST_SPACESTATS 10 - -/* calculate an index of best array */ -#define HEAP_STATS_NEXT_BEST_INDEX(i) \ - (((i) + 1) % HEAP_NUM_BEST_SPACESTATS) -#define HEAP_STATS_PREV_BEST_INDEX(i) \ - (((i) == 0) ? (HEAP_NUM_BEST_SPACESTATS - 1) : ((i) - 1)); - typedef struct heap_hdr_stats HEAP_HDR_STATS; struct heap_hdr_stats { /* the first must be class_oid */ OID class_oid; + VFID ovf_vfid; /* Overflow file identifier (if any) */ + VPID next_vpid; /* Next page (i.e., the 2nd page of heap file) */ + VPID last_vpid; /* Last page */ + int unfill_space; /* Stop inserting when page has run below this. leave it for updates */ + + int num_pages; /* Estimation of number of user heap pages. Consult file manager if accurate number is needed */ + uint64_t num_recs; /* Estimation of number of objects in heap */ + uint64_t recs_sumlen; /* Estimation total length of records */ + struct { - int num_pages; /* Estimation of number of heap pages. Consult file manager if accurate number is - * needed */ - int num_recs; /* Estimation of number of objects in heap */ - float recs_sumlen; /* Estimation total length of records */ - int num_other_high_best; /* Total of other believed known best pages, which are not included in the best array - * and we believe they have at least HEAP_DROP_FREE_SPACE */ - int num_high_best; /* Number of pages in the best array that we believe have at least - * HEAP_DROP_FREE_SPACE. When this number goes to zero and there is at least other - * HEAP_NUM_BEST_SPACESTATS best pages, we look for them. */ - int num_substitutions; /* Number of page substitutions. This will be used to insert a new second best page - * into second best hints. */ - int num_second_best; /* Number of second best hints. The hints are in "second_best" array. They are used - * when finding new best pages. See the function "heap_stats_sync_bestspace". */ - int head_second_best; /* Index of head of second best hints. */ - int tail_second_best; /* Index of tail of second best hints. A new second best hint will be stored on this - * index. */ - int head; /* Head of best circular array */ - VPID last_vpid; /* todo: move out of estimates */ - VPID full_search_vpid; - VPID second_best[HEAP_NUM_BEST_SPACESTATS]; - HEAP_BESTSPACE best[HEAP_NUM_BEST_SPACESTATS]; - } estimates; /* Probably, the set of pages with more free space on the heap. Changes to any values - * of this array (either page or the free space for the page) are not logged since - * these values are only used for hints. These values may not be accurate at any given - * time and the entries may contain duplicated pages. */ - - int reserve0_for_future; /* Nothing reserved for future */ - int reserve1_for_future; /* Nothing reserved for future */ - int reserve2_for_future; /* Nothing reserved for future */ -}; + std::size_t num_candidates; + // *INDENT-OFF* + cubstorage::bestspace_entry candidates[cubstorage::bestspace::MAX_CANDIDATES_QUEUE_SIZE]; + // *INDENT-ON* -typedef struct heap_stats_entry HEAP_STATS_ENTRY; -struct heap_stats_entry -{ - HFID hfid; /* heap file identifier */ - HEAP_BESTSPACE best; /* best space info */ - HEAP_STATS_ENTRY *next; + std::size_t num_shards; + + std::size_t num_pages; + VPID pages[cubstorage::bestspace::MAX_SHARD_PAGE_COUNT]; + } bestspace; + + int reserve0; /* Nothing reserved for future */ + int reserve1; /* Nothing reserved for future */ + int reserve2; /* Nothing reserved for future */ }; /* Define heap page flags. */ +#define HEAP_PAGE_FLAG_BESTSPACE 0x00000001 #define HEAP_PAGE_FLAG_VACUUM_STATUS_MASK 0xC0000000 #define HEAP_PAGE_FLAG_VACUUM_ONCE 0x80000000 #define HEAP_PAGE_FLAG_VACUUM_UNKNOWN 0x40000000 +#define HEAP_PAGE_IS_BESTSPACE(chain) \ + (((chain)->flags & HEAP_PAGE_FLAG_BESTSPACE) != 0) + +#define HEAP_PAGE_SET_BESTSPACE(chain) \ + ((chain)->flags |= HEAP_PAGE_FLAG_BESTSPACE) + +#define HEAP_BESTSPACE_ENTRIES_SLOTID (HEAP_HEADER_AND_CHAIN_SLOTID + 1) + #define HEAP_PAGE_SET_VACUUM_STATUS(chain, status) \ do \ { \ @@ -280,7 +263,7 @@ struct heap_chain VPID prev_vpid; /* Previous page */ VPID next_vpid; /* Next page */ MVCCID max_mvccid; /* Max MVCCID of any MVCC operations in page. */ - INT32 flags; /* Flags for heap page. 2 bits are used for vacuum state. */ + INT32 flags; /* Flags for heap page. High 2 bits are used for vacuum state. */ }; #define HEAP_CHK_ADD_UNFOUND_RELOCOIDS 100 @@ -471,17 +454,6 @@ struct heap_chnguess int nbytes; /* Number of bytes in bitindex. It must be aligned to multiples of 4 bytes (integers) */ }; -typedef struct heap_stats_bestspace_cache HEAP_STATS_BESTSPACE_CACHE; -struct heap_stats_bestspace_cache -{ - int num_stats_entries; /* number of cache entries in use */ - MHT_TABLE *hfid_ht; /* HFID Hash table for best space */ - MHT_TABLE *vpid_ht; /* VPID Hash table for best space */ - int free_list_count; /* number of entries in free */ - HEAP_STATS_ENTRY *free_list; - pthread_mutex_t bestspace_mutex; -}; - typedef struct heap_show_scan_ctx HEAP_SHOW_SCAN_CTX; struct heap_show_scan_ctx { @@ -491,7 +463,6 @@ struct heap_show_scan_ctx static int heap_Maxslotted_reclength; static int heap_Slotted_overhead = 4; /* sizeof (SPAGE_SLOT) */ -static const int heap_Find_best_page_limit = 100; static HEAP_CLASSREPR_CACHE *heap_Classrepr = NULL; static HEAP_CHNGUESS heap_Guesschn_area = { NULL, NULL, NULL, false, 0, @@ -500,10 +471,6 @@ static HEAP_CHNGUESS heap_Guesschn_area = { NULL, NULL, NULL, false, 0, static HEAP_CHNGUESS *heap_Guesschn = NULL; -static HEAP_STATS_BESTSPACE_CACHE heap_Bestspace_cache_area = { 0, NULL, NULL, 0, NULL, PTHREAD_MUTEX_INITIALIZER }; - -static HEAP_STATS_BESTSPACE_CACHE *heap_Bestspace = NULL; - static HEAP_HFID_TABLE heap_Hfid_table_area = { LF_HASH_TABLE_INITIALIZER, LF_ENTRY_DESCRIPTOR_INITIALIZER, LF_FREELIST_INITIALIZER, false }; @@ -581,9 +548,6 @@ static HEAP_HFID_TABLE *heap_Hfid_table = NULL; } \ while (false) -#define heap_bestspace_log(...) \ - if (prm_get_bool_value (PRM_ID_DEBUG_BESTSPACE)) _er_log_debug (ARG_FILE_LINE, __VA_ARGS__) - #if defined (NDEBUG) static PAGE_PTR heap_scan_pb_lock_and_fetch (THREAD_ENTRY * thread_p, const VPID * vpid_ptr, PAGE_FETCH_MODE fetch_mode, LOCK lock, HEAP_SCANCACHE * scan_cache, PGBUF_WATCHER * pg_watcher); @@ -618,23 +582,6 @@ static int heap_classrepr_entry_free (HEAP_CLASSREPR_ENTRY * cache_entry); static OR_CLASSREP *heap_classrepr_get_from_record (THREAD_ENTRY * thread_p, REPR_ID * last_reprid, const OID * class_oid, RECDES * class_recdes, REPR_ID reprid); -static int heap_stats_get_min_freespace (HEAP_HDR_STATS * heap_hdr); -static int heap_stats_update_internal (THREAD_ENTRY * thread_p, const HFID * hfid, VPID * lotspace_vpid, - int free_space); -static void heap_stats_put_second_best (HEAP_HDR_STATS * heap_hdr, VPID * vpid); -static int heap_stats_get_second_best (HEAP_HDR_STATS * heap_hdr, VPID * vpid); -#if defined(ENABLE_UNUSED_FUNCTION) -static int heap_stats_quick_num_fit_in_bestspace (HEAP_BESTSPACE * bestspace, int num_entries, int unit_size, - int unfill_space); -#endif -static HEAP_FINDSPACE heap_stats_find_page_in_bestspace (THREAD_ENTRY * thread_p, const HFID * hfid, - HEAP_BESTSPACE * bestspace, int *idx_badspace, - int record_length, int needed_space, - HEAP_SCANCACHE * scan_cache, PGBUF_WATCHER * pg_watcher); -static PAGE_PTR heap_stats_find_best_page (THREAD_ENTRY * thread_p, const HFID * hfid, int needed_space, bool isnew_rec, - HEAP_SCANCACHE * space_cache, PGBUF_WATCHER * pg_watcher); -static int heap_stats_sync_bestspace (THREAD_ENTRY * thread_p, const HFID * hfid, HEAP_HDR_STATS * heap_hdr, - VPID * hdr_vpid, bool scan_all, bool can_cycle); static int heap_get_last_page (THREAD_ENTRY * thread_p, const HFID * hfid, HEAP_HDR_STATS * heap_hdr, HEAP_SCANCACHE * scan_cache, VPID * last_vpid, PGBUF_WATCHER * pg_watcher); @@ -644,6 +591,34 @@ static int heap_vpid_alloc (THREAD_ENTRY * thread_p, const HFID * hfid, PAGE_PTR HEAP_SCANCACHE * scan_cache, PGBUF_WATCHER * new_pg_watcher); static VPID *heap_vpid_remove (THREAD_ENTRY * thread_p, const HFID * hfid, HEAP_HDR_STATS * heap_hdr, VPID * rm_vpid); +static void heap_bestspace_clear_candidates (cubstorage::bestspace_entry * candidates, std::size_t * num_candidates, + std::size_t max_candidates); +static void heap_bestspace_add_candidate (cubstorage::bestspace_entry * candidates, std::size_t * num_candidates, + std::size_t max_candidates, cubstorage::bestspace_entry * candidate); +static int heap_bestspace_fix_page (THREAD_ENTRY * thread_p, const HFID * hfid, const VPID * vpid, + PGBUF_WATCHER * page_watcher); + +static int heap_create_bestspace (THREAD_ENTRY * thread_p, HFID * hfid, HEAP_HDR_STATS * heap_hdr, + cubstorage::bestspace_entry * first_entry); +static int heap_update_bestspace_chain (THREAD_ENTRY * thread_p, const OID * class_oid, const HFID * hfid, + const VPID * pages, int num_pages); +static int heap_update_bestspace_entries (THREAD_ENTRY * thread_p, const HFID * hfid, const VPID * pages, + int num_pages, const cubstorage::bestspace_entry * entries, int num_entries); +static int heap_take_bestspace (THREAD_ENTRY * thread_p, HFID * hfid, PGBUF_WATCHER * header_watcher, + HEAP_HDR_STATS * header, cubstorage::bestspace_entry * entries, + cubstorage::bestspace_entry * candidates); + +// *INDENT-OFF* +static int heap_update_bestspace (THREAD_ENTRY * thread_p, const HFID * hfid, cubstorage::bestspace *bestspace); +static cubstorage::bestspace *heap_build_bestspace (THREAD_ENTRY * thread_p, OID * class_oid, HFID * hfid, + PGBUF_WATCHER * header_watcher); +static cubstorage::bestspace *heap_find_bestspace (THREAD_ENTRY * thread_p, OID * class_oid, HFID * hfid, + PGBUF_WATCHER * header_watcher); +// *INDENT-ON* + +STATIC_INLINE int heap_find_bestpage (THREAD_ENTRY * thread_p, OID * class_oid, HFID * hfid, std::uint16_t size, + bool is_newrec, PGBUF_WATCHER * page_watcher); + static int heap_create_internal (THREAD_ENTRY * thread_p, HFID * hfid, const OID * class_oid, const bool reuse_oid); static const HFID *heap_reuse (THREAD_ENTRY * thread_p, const HFID * hfid, const OID * class_oid, const bool reuse_oid); static bool heap_delete_all_page_records (THREAD_ENTRY * thread_p, const VPID * vpid, PAGE_PTR pgptr); @@ -742,9 +717,6 @@ static int heap_chnguess_finalize (void); static int heap_chnguess_decache (const OID * oid); static int heap_chnguess_remove_entry (const void *oid_key, void *ent, void *xignore); -static int heap_stats_bestspace_initialize (void); -static int heap_stats_bestspace_finalize (void); - static int heap_get_spage_type (void); static bool heap_is_reusable_oid (const FILE_TYPE file_type); @@ -768,14 +740,9 @@ static SCAN_CODE heap_attrinfo_transform_columns_to_disk (THREAD_ENTRY * thread_ static SCAN_CODE heap_attrinfo_transform_to_disk_internal (THREAD_ENTRY * thread_p, HEAP_CACHE_ATTRINFO * attr_info, RECDES * old_recdes, record_descriptor * new_recdes, int lob_create_flag); -static int heap_stats_del_bestspace_by_vpid (THREAD_ENTRY * thread_p, VPID * vpid); -static int heap_stats_del_bestspace_by_hfid (THREAD_ENTRY * thread_p, const HFID * hfid); -#if defined (ENABLE_UNUSED_FUNCTION) -static HEAP_BESTSPACE heap_stats_get_bestspace_by_vpid (THREAD_ENTRY * thread_p, VPID * vpid); -#endif /* ENABLE_UNUSED_FUNCTION */ -static HEAP_STATS_ENTRY *heap_stats_add_bestspace (THREAD_ENTRY * thread_p, const HFID * hfid, VPID * vpid, - int freespace); -static int heap_stats_entry_free (THREAD_ENTRY * thread_p, void *data, void *args); + +static int heap_update_statistics (THREAD_ENTRY * thread_p, const HFID * hfid, HEAP_HDR_STATS * heap_hdr, + PGBUF_WATCHER * header_watcher); static int heap_get_partitions_from_subclasses (THREAD_ENTRY * thread_p, const OID * subclasses, int *parts_count, OR_PARTITION * partitions); static int heap_class_get_partition_info (THREAD_ENTRY * thread_p, const OID * class_oid, OR_PARTITION * partition_info, @@ -783,14 +750,6 @@ static int heap_class_get_partition_info (THREAD_ENTRY * thread_p, const OID * c static int heap_get_partition_attributes (THREAD_ENTRY * thread_p, const OID * cls_oid, ATTR_ID * type_id, ATTR_ID * values_id); static int heap_get_class_subclasses (THREAD_ENTRY * thread_p, const OID * class_oid, int *count, OID ** subclasses); -static unsigned int heap_hash_vpid (const void *key_vpid, unsigned int htsize); -static int heap_compare_vpid (const void *key_vpid1, const void *key_vpid2); -static unsigned int heap_hash_hfid (const void *key_hfid, unsigned int htsize); -static int heap_compare_hfid (const void *key_hfid1, const void *key_hfid2); - -static char *heap_bestspace_to_string (char *buf, int buf_size, const HEAP_BESTSPACE * hb); - -static int fill_string_to_buffer (char **start, char *end, const char *str); static SCAN_CODE heap_get_record_info (THREAD_ENTRY * thread_p, const OID oid, RECDES * recdes, RECDES forward_recdes, PGBUF_WATCHER * page_watcher, HEAP_SCANCACHE * scan_cache, bool ispeeking, @@ -933,306 +892,6 @@ static int heap_update_and_log_header (THREAD_ENTRY * thread_p, const HFID * hfi const PGBUF_WATCHER heap_header_watcher, HEAP_HDR_STATS * heap_hdr, const VPID new_next_vpid, const VPID new_last_vpid, const int new_num_pages); -/* - * heap_hash_vpid () - Hash a page identifier - * return: hash value - * key_vpid(in): VPID to hash - * htsize(in): Size of hash table - */ -static unsigned int -heap_hash_vpid (const void *key_vpid, unsigned int htsize) -{ - const VPID *vpid = (VPID *) key_vpid; - - return ((vpid->pageid | ((unsigned int) vpid->volid) << 24) % htsize); -} - -/* - * heap_compare_vpid () - Compare two vpids keys for hashing - * return: int (key_vpid1 == key_vpid2 ?) - * key_vpid1(in): First key - * key_vpid2(in): Second key - */ -static int -heap_compare_vpid (const void *key_vpid1, const void *key_vpid2) -{ - const VPID *vpid1 = (VPID *) key_vpid1; - const VPID *vpid2 = (VPID *) key_vpid2; - - return VPID_EQ (vpid1, vpid2); -} - -/* - * heap_hash_hfid () - Hash a file identifier - * return: hash value - * key_hfid(in): HFID to hash - * htsize(in): Size of hash table - */ -static unsigned int -heap_hash_hfid (const void *key_hfid, unsigned int htsize) -{ - const HFID *hfid = (HFID *) key_hfid; - - return ((hfid->hpgid | ((unsigned int) hfid->vfid.volid) << 24) % htsize); -} - -/* - * heap_compare_hfid () - Compare two hfids keys for hashing - * return: int (key_hfid1 == key_hfid2 ?) - * key_hfid1(in): First key - * key_hfid2(in): Second key - */ -static int -heap_compare_hfid (const void *key_hfid1, const void *key_hfid2) -{ - const HFID *hfid1 = (HFID *) key_hfid1; - const HFID *hfid2 = (HFID *) key_hfid2; - - return HFID_EQ (hfid1, hfid2); -} - -/* - * heap_stats_entry_free () - release all memory occupied by an best space - * return: NO_ERROR - * data(in): a best space associated with the key - * args(in): NULL (not used here, but needed by mht_map) - */ -static int -heap_stats_entry_free (THREAD_ENTRY * thread_p, void *data, void *args) -{ - HEAP_STATS_ENTRY *ent; - - ent = (HEAP_STATS_ENTRY *) data; - assert_release (ent != NULL); - - if (ent) - { - if (heap_Bestspace->free_list_count < HEAP_STATS_ENTRY_FREELIST_SIZE) - { - ent->next = heap_Bestspace->free_list; - heap_Bestspace->free_list = ent; - - heap_Bestspace->free_list_count++; - } - else - { - free_and_init (ent); - } - } - - return NO_ERROR; -} - -/* - * heap_stats_add_bestspace () - - */ -static HEAP_STATS_ENTRY * -heap_stats_add_bestspace (THREAD_ENTRY * thread_p, const HFID * hfid, VPID * vpid, int freespace) -{ - HEAP_STATS_ENTRY *ent; - int rc; - PERF_UTIME_TRACKER time_best_space = PERF_UTIME_TRACKER_INITIALIZER; - - assert (prm_get_integer_value (PRM_ID_HF_MAX_BESTSPACE_ENTRIES) > 0); - - PERF_UTIME_TRACKER_START (thread_p, &time_best_space); - - rc = pthread_mutex_lock (&heap_Bestspace->bestspace_mutex); - - ent = (HEAP_STATS_ENTRY *) mht_get (heap_Bestspace->vpid_ht, vpid); - - if (ent) - { - ent->best.freespace = freespace; - goto end; - } - - if (heap_Bestspace->num_stats_entries >= prm_get_integer_value (PRM_ID_HF_MAX_BESTSPACE_ENTRIES)) - { - er_set (ER_NOTIFICATION_SEVERITY, ARG_FILE_LINE, ER_HF_MAX_BESTSPACE_ENTRIES, 1, - prm_get_integer_value (PRM_ID_HF_MAX_BESTSPACE_ENTRIES)); - - perfmon_inc_stat (thread_p, PSTAT_HF_NUM_STATS_MAXED); - - ent = NULL; - goto end; - } - - if (heap_Bestspace->free_list_count > 0) - { - assert_release (heap_Bestspace->free_list != NULL); - - ent = heap_Bestspace->free_list; - if (ent == NULL) - { - goto end; - } - heap_Bestspace->free_list = ent->next; - ent->next = NULL; - - heap_Bestspace->free_list_count--; - } - else - { - ent = (HEAP_STATS_ENTRY *) malloc (sizeof (HEAP_STATS_ENTRY)); - if (ent == NULL) - { - er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (HEAP_STATS_ENTRY)); - - goto end; - } - } - - HFID_COPY (&ent->hfid, hfid); - ent->best.vpid = *vpid; - ent->best.freespace = freespace; - ent->next = NULL; - - if (mht_put (heap_Bestspace->vpid_ht, &ent->best.vpid, ent) == NULL) - { - assert_release (false); - (void) heap_stats_entry_free (thread_p, ent, NULL); - ent = NULL; - goto end; - } - - if (mht_put_new (heap_Bestspace->hfid_ht, &ent->hfid, ent) == NULL) - { - assert_release (false); - (void) mht_rem (heap_Bestspace->vpid_ht, &ent->best.vpid, NULL, NULL); - (void) heap_stats_entry_free (thread_p, ent, NULL); - ent = NULL; - goto end; - } - - heap_Bestspace->num_stats_entries++; - -end: - - assert (mht_count (heap_Bestspace->vpid_ht) == mht_count (heap_Bestspace->hfid_ht)); - - pthread_mutex_unlock (&heap_Bestspace->bestspace_mutex); - - PERF_UTIME_TRACKER_TIME (thread_p, &time_best_space, PSTAT_HF_BEST_SPACE_ADD); - - return ent; -} - -/* - * heap_stats_del_bestspace_by_hfid () - - * return: deleted count - * - * hfid(in): - */ -static int -heap_stats_del_bestspace_by_hfid (THREAD_ENTRY * thread_p, const HFID * hfid) -{ - HEAP_STATS_ENTRY *ent; - int del_cnt = 0; - int rc; - PERF_UTIME_TRACKER time_best_space = PERF_UTIME_TRACKER_INITIALIZER; - - PERF_UTIME_TRACKER_START (thread_p, &time_best_space); - - rc = pthread_mutex_lock (&heap_Bestspace->bestspace_mutex); - - while ((ent = (HEAP_STATS_ENTRY *) mht_get2 (heap_Bestspace->hfid_ht, hfid, NULL)) != NULL) - { - (void) mht_rem2 (heap_Bestspace->hfid_ht, &ent->hfid, ent, NULL, NULL); - (void) mht_rem (heap_Bestspace->vpid_ht, &ent->best.vpid, NULL, NULL); - (void) heap_stats_entry_free (thread_p, ent, NULL); - ent = NULL; - - del_cnt++; - } - - assert (del_cnt <= heap_Bestspace->num_stats_entries); - - heap_Bestspace->num_stats_entries -= del_cnt; - - assert (mht_count (heap_Bestspace->vpid_ht) == mht_count (heap_Bestspace->hfid_ht)); - pthread_mutex_unlock (&heap_Bestspace->bestspace_mutex); - - PERF_UTIME_TRACKER_TIME (thread_p, &time_best_space, PSTAT_HF_BEST_SPACE_DEL); - - return del_cnt; -} - -/* - * heap_stats_del_bestspace_by_vpid () - - * return: NO_ERROR - * - * vpid(in): - */ -static int -heap_stats_del_bestspace_by_vpid (THREAD_ENTRY * thread_p, VPID * vpid) -{ - HEAP_STATS_ENTRY *ent; - int rc; - PERF_UTIME_TRACKER time_best_space = PERF_UTIME_TRACKER_INITIALIZER; - - PERF_UTIME_TRACKER_START (thread_p, &time_best_space); - rc = pthread_mutex_lock (&heap_Bestspace->bestspace_mutex); - - ent = (HEAP_STATS_ENTRY *) mht_get (heap_Bestspace->vpid_ht, vpid); - if (ent == NULL) - { - goto end; - } - - (void) mht_rem2 (heap_Bestspace->hfid_ht, &ent->hfid, ent, NULL, NULL); - (void) mht_rem (heap_Bestspace->vpid_ht, &ent->best.vpid, NULL, NULL); - (void) heap_stats_entry_free (thread_p, ent, NULL); - ent = NULL; - - heap_Bestspace->num_stats_entries--; - -end: - assert (mht_count (heap_Bestspace->vpid_ht) == mht_count (heap_Bestspace->hfid_ht)); - - pthread_mutex_unlock (&heap_Bestspace->bestspace_mutex); - - PERF_UTIME_TRACKER_TIME (thread_p, &time_best_space, PSTAT_HF_BEST_SPACE_DEL); - - return NO_ERROR; -} - -#if defined (ENABLE_UNUSED_FUNCTION) -/* - * heap_stats_get_bestspace_by_vpid () - - * return: NO_ERROR - * - * vpid(in): - */ -static HEAP_BESTSPACE -heap_stats_get_bestspace_by_vpid (THREAD_ENTRY * thread_p, VPID * vpid) -{ - HEAP_STATS_ENTRY *ent; - HEAP_BESTSPACE best; - int rc; - - best.freespace = -1; - VPID_SET_NULL (&best.vpid); - - rc = pthread_mutex_lock (&heap_Bestspace->bestspace_mutex); - - ent = (HEAP_STATS_ENTRY *) mht_get (heap_Bestspace->vpid_ht, vpid); - if (ent == NULL) - { - goto end; - } - - best = ent->best; - -end: - assert (mht_count (heap_Bestspace->vpid_ht) == mht_count (heap_Bestspace->hfid_ht)); - - pthread_mutex_unlock (&heap_Bestspace->bestspace_mutex); - - return best; -} -#endif /* ENABLE_UNUSED_FUNCTION */ - /* * Scan page buffer and latch page manipulation */ @@ -2912,2292 +2571,2059 @@ heap_classrepr_dump_anyfixed (void) #endif /* DEBUG_CLASSREPR_CACHE */ /* - * heap_stats_get_min_freespace () - Minimal space to consider a page for statistics - * return: int minspace - * heap_hdr(in): Current header of heap + * heap_get_last_page () - Get the last page pointer. + * return: error code + * hfid(in): Object heap file identifier + * heap_hdr(in): The heap header structure + * scan_cache(in): Scan cache + * last_vpid(out): VPID of the last page * - * Note: Find the minimal space to consider to continue caching a page - * for statistics. + * Note: The last vpid is saved on heap header. We log it and should be the right VPID. */ static int -heap_stats_get_min_freespace (HEAP_HDR_STATS * heap_hdr) +heap_get_last_page (THREAD_ENTRY * thread_p, const HFID * hfid, HEAP_HDR_STATS * heap_hdr, HEAP_SCANCACHE * scan_cache, + VPID * last_vpid, PGBUF_WATCHER * pg_watcher) { - int min_freespace; - int header_size; - - header_size = OR_MVCC_MAX_HEADER_SIZE; - - /* - * Don't cache as a good space page if page does not have at least - * unfill_space + one record - */ + int error_code = NO_ERROR; - if (heap_hdr->estimates.num_recs > 0) - { - min_freespace = (int) (heap_hdr->estimates.recs_sumlen / heap_hdr->estimates.num_recs); + assert (pg_watcher != NULL); + assert (last_vpid != NULL); + assert (!VPID_ISNULL (&heap_hdr->last_vpid)); - if (min_freespace < (header_size + 20)) - { - min_freespace = header_size + 20; /* Assume very small records */ - } - } - else + *last_vpid = heap_hdr->last_vpid; + pg_watcher->pgptr = heap_scan_pb_lock_and_fetch (thread_p, last_vpid, OLD_PAGE, X_LOCK, scan_cache, pg_watcher); + if (pg_watcher->pgptr == NULL) { - min_freespace = header_size + 20; /* Assume very small records */ + ASSERT_ERROR_AND_SET (error_code); + return error_code; } - min_freespace += heap_hdr->unfill_space; - - min_freespace = MIN (min_freespace, HEAP_DROP_FREE_SPACE); +#if !defined (NDEBUG) + { + RECDES recdes; + HEAP_CHAIN *chain; + if (spage_get_record (thread_p, pg_watcher->pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) + { + assert (false); + pgbuf_ordered_unfix (thread_p, pg_watcher); + return ER_FAILED; + } + chain = (HEAP_CHAIN *) recdes.data; + assert (VPID_ISNULL (&chain->next_vpid)); + } +#endif /* !NDEBUG */ - return min_freespace; + return NO_ERROR; } /* - * heap_stats_update () - Update one header hinted page space statistics - * return: NO_ERROR - * pgptr(in): Page pointer - * hfid(in): Object heap file identifier - * prev_freespace(in): + * heap_get_last_vpid () - Get last heap page VPID from heap file header * - * NOTE: There should be at least HEAP_DROP_FREE_SPACE in order to - * insert this page to best hint array. - * If we cannot fix a heap header page due to holding it by - * others, we will postpone this updating until next deletion. - * In this case, unfortunately, if some record is not deleted - * from this page in the future, we may not use this page until - * heap_stats_sync_bestspace function searches all pages. + * return : Error code + * thread_p (in) : Thread entry + * hfid (in) : Heap file identifier + * last_vpid (out) : Last heap page VPID */ -void -heap_stats_update (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, const HFID * hfid, int prev_freespace) +STATIC_INLINE int +heap_get_last_vpid (THREAD_ENTRY * thread_p, const HFID * hfid, VPID * last_vpid) { - VPID *vpid; - int freespace, error; - bool need_update; + PGBUF_WATCHER watcher_heap_header; + VPID vpid_heap_header; + HEAP_HDR_STATS *hdr_stats = NULL; - freespace = spage_get_free_space_without_saving (thread_p, pgptr, &need_update); - if (prm_get_integer_value (PRM_ID_HF_MAX_BESTSPACE_ENTRIES) > 0) - { - if (prev_freespace < freespace) - { - vpid = pgbuf_get_vpid_ptr (pgptr); - assert_release (vpid != NULL); + int error_code = NO_ERROR; - (void) heap_stats_add_bestspace (thread_p, hfid, vpid, freespace); - } - } + PGBUF_INIT_WATCHER (&watcher_heap_header, PGBUF_ORDERED_HEAP_HDR, hfid); - if (need_update || prev_freespace <= HEAP_DROP_FREE_SPACE) + VPID_SET_NULL (last_vpid); + + vpid_heap_header.volid = hfid->vfid.volid; + vpid_heap_header.pageid = hfid->hpgid; + error_code = pgbuf_ordered_fix (thread_p, &vpid_heap_header, OLD_PAGE, PGBUF_LATCH_READ, &watcher_heap_header); + if (error_code != NO_ERROR) { - if (freespace > HEAP_DROP_FREE_SPACE) - { - vpid = pgbuf_get_vpid_ptr (pgptr); - assert_release (vpid != NULL); + ASSERT_ERROR (); + return error_code; + } - error = heap_stats_update_internal (thread_p, hfid, vpid, freespace); - if (error != NO_ERROR) - { - spage_set_need_update_best_hint (thread_p, pgptr, true); - } - else if (need_update == true) - { - spage_set_need_update_best_hint (thread_p, pgptr, false); - } - } - else if (need_update == true) - { - spage_set_need_update_best_hint (thread_p, pgptr, false); - } + hdr_stats = heap_get_header_stats_ptr (thread_p, watcher_heap_header.pgptr); + if (hdr_stats == NULL) + { + assert_release (false); + pgbuf_ordered_unfix (thread_p, &watcher_heap_header); + return ER_FAILED; } + *last_vpid = hdr_stats->last_vpid; + pgbuf_ordered_unfix (thread_p, &watcher_heap_header); + return NO_ERROR; } /* - * heap_stats_update_internal () - Update one header hinted page space statistics - * return: NO_ERROR - * hfid(in): Object heap file identifier - * lotspace_vpid(in): Page which has a lot of free space - * free_space(in): The free space on the page + * heap_get_header_stats_ptr () - Get pointer to heap header statistics. * - * Note: Update header hinted best space page information. This - * function is used during deletions and updates when the free - * space on the page is greater than HEAP_DROP_FREE_SPACE. + * return : Pointer to heap header statistics + * page_header (in) : Heap header page */ -static int -heap_stats_update_internal (THREAD_ENTRY * thread_p, const HFID * hfid, VPID * lotspace_vpid, int free_space) +STATIC_INLINE HEAP_HDR_STATS * +heap_get_header_stats_ptr (THREAD_ENTRY * thread_p, PAGE_PTR page_header) { - HEAP_HDR_STATS *heap_hdr; /* Header of heap structure */ - PAGE_PTR hdr_pgptr = NULL; /* Page pointer to header page */ - VPID vpid; /* Page-volume identifier */ - RECDES recdes; /* Header record descriptor */ - LOG_DATA_ADDR addr; /* Address of logging data */ - int i, best; - int ret = NO_ERROR; - - /* Retrieve the header of heap */ - vpid.volid = hfid->vfid.volid; - vpid.pageid = hfid->hpgid; - - /* - * We do not want to wait for the following operation. - * So, if we cannot lock the page return. - */ - hdr_pgptr = pgbuf_fix (thread_p, &vpid, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_CONDITIONAL_LATCH); - if (hdr_pgptr == NULL) - { - /* Page is busy or other type of error */ - goto exit_on_error; - } - -#if !defined (NDEBUG) - (void) pgbuf_check_page_ptype (thread_p, hdr_pgptr, PAGE_HEAP); -#endif /* !NDEBUG */ - - /* - * Peek the header record to find statistics for insertion. - * Update the statistics directly. - */ - if (spage_get_record (thread_p, hdr_pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) - { - goto exit_on_error; - } - - heap_hdr = (HEAP_HDR_STATS *) recdes.data; - best = heap_hdr->estimates.head; - - if (free_space >= heap_stats_get_min_freespace (heap_hdr)) - { - /* - * We do not compare with the current stored values since these values - * may not be accurate at all. When the given one is supposed to be - * accurate. - */ - - /* - * Find a good place to insert this page - */ - for (i = 0; i < HEAP_NUM_BEST_SPACESTATS; i++) - { - if (VPID_ISNULL (&heap_hdr->estimates.best[best].vpid) - || heap_hdr->estimates.best[best].freespace <= HEAP_DROP_FREE_SPACE) - { - break; - } - - best = HEAP_STATS_NEXT_BEST_INDEX (best); - } - - if (VPID_ISNULL (&heap_hdr->estimates.best[best].vpid)) - { - heap_hdr->estimates.num_high_best++; - assert (heap_hdr->estimates.num_high_best <= HEAP_NUM_BEST_SPACESTATS); - } - else if (heap_hdr->estimates.best[best].freespace > HEAP_DROP_FREE_SPACE) - { - heap_hdr->estimates.num_other_high_best++; - - heap_stats_put_second_best (heap_hdr, &heap_hdr->estimates.best[best].vpid); - } - /* - * Now substitute the entry with the new information - */ - - heap_hdr->estimates.best[best].freespace = free_space; - heap_hdr->estimates.best[best].vpid = *lotspace_vpid; - - heap_hdr->estimates.head = HEAP_STATS_NEXT_BEST_INDEX (best); - - /* - * The changes to the statistics are not logged. They are fixed - * automatically sooner or later - */ + RECDES recdes; - addr.vfid = &hfid->vfid; - addr.pgptr = hdr_pgptr; - addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; - log_skip_logging (thread_p, &addr); - pgbuf_set_dirty (thread_p, hdr_pgptr, FREE); - hdr_pgptr = NULL; - } - else + if (spage_get_record (thread_p, page_header, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) { - pgbuf_unfix_and_init (thread_p, hdr_pgptr); + assert_release (false); + return NULL; } + return (HEAP_HDR_STATS *) recdes.data; +} - return ret; +/* + * heap_copy_header_stats () - Copy heap header statistics + * + * return : Error code + * page_header (in) : Heap header page + * header_stats (out) : Heap header statistics + */ +STATIC_INLINE int +heap_copy_header_stats (THREAD_ENTRY * thread_p, PAGE_PTR page_header, HEAP_HDR_STATS * header_stats) +{ + RECDES recdes; -exit_on_error: - if (hdr_pgptr) + recdes.data = (char *) header_stats; + recdes.area_size = sizeof (*header_stats); + if (spage_get_record (thread_p, page_header, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, COPY) != S_SUCCESS) { - pgbuf_unfix_and_init (thread_p, hdr_pgptr); + assert_release (false); + return ER_FAILED; } - - return (ret == NO_ERROR) ? ER_FAILED : ret; + return NO_ERROR; } /* - * heap_stats_put_second_best () - Put a free page into second best hint array - * return: void - * heap_hdr(in): Statistics of heap file - * vpid(in): VPID to be added + * heap_get_chain_ptr () - Get pointer to chain in heap page * - * NOTE: A free page is not always inserted to the second best hint array. - * Second best hints will be collected for every 1000 pages in order - * to increase randomness for "emptying contiguous pages" scenario. + * return : Pointer to chain in heap page + * page_heap (in) : Heap page */ -static void -heap_stats_put_second_best (HEAP_HDR_STATS * heap_hdr, VPID * vpid) +STATIC_INLINE HEAP_CHAIN * +heap_get_chain_ptr (THREAD_ENTRY * thread_p, PAGE_PTR page_heap) { - int tail; + RECDES recdes; - if (heap_hdr->estimates.num_substitutions++ % 1000 == 0) + if (spage_get_record (thread_p, page_heap, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) { - tail = heap_hdr->estimates.tail_second_best; - - heap_hdr->estimates.second_best[tail] = *vpid; - heap_hdr->estimates.tail_second_best = HEAP_STATS_NEXT_BEST_INDEX (tail); - - if (heap_hdr->estimates.num_second_best == HEAP_NUM_BEST_SPACESTATS) - { - assert (heap_hdr->estimates.head_second_best == tail); - heap_hdr->estimates.head_second_best = heap_hdr->estimates.tail_second_best; - } - else - { - assert (heap_hdr->estimates.num_second_best < HEAP_NUM_BEST_SPACESTATS); - heap_hdr->estimates.num_second_best++; - } - - /* If both head and tail refer to the same index, the number of second best hints is - * HEAP_NUM_BEST_SPACESTATS(10). */ - assert (heap_hdr->estimates.num_second_best != 0); - assert ((heap_hdr->estimates.tail_second_best > heap_hdr->estimates.head_second_best) - ? ((heap_hdr->estimates.tail_second_best - heap_hdr->estimates.head_second_best) - == heap_hdr->estimates.num_second_best) - : ((10 + heap_hdr->estimates.tail_second_best - heap_hdr->estimates.head_second_best) - == heap_hdr->estimates.num_second_best)); - - heap_hdr->estimates.num_substitutions = 1; + assert_release (false); + return NULL; } + return (HEAP_CHAIN *) recdes.data; } /* - * heap_stats_put_second_best () - Get a free page from second best hint array - * return: NO_ERROR or ER_FAILED - * heap_hdr(in): Statistics of heap file - * vpid(out): VPID to get + * heap_copy_chain () - Copy chain from heap page + * + * return : Error code + * page_heap (in) : Heap page + * chain (out) : Heap chain */ -static int -heap_stats_get_second_best (HEAP_HDR_STATS * heap_hdr, VPID * vpid) +STATIC_INLINE int +heap_copy_chain (THREAD_ENTRY * thread_p, PAGE_PTR page_heap, HEAP_CHAIN * chain) { - int head; - - assert (vpid != NULL); + RECDES recdes; - if (heap_hdr->estimates.num_second_best == 0) + if (spage_get_record (thread_p, page_heap, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) { - assert (heap_hdr->estimates.tail_second_best == heap_hdr->estimates.head_second_best); - VPID_SET_NULL (vpid); + assert_release (false); return ER_FAILED; } - - head = heap_hdr->estimates.head_second_best; - - heap_hdr->estimates.num_second_best--; - heap_hdr->estimates.head_second_best = HEAP_STATS_NEXT_BEST_INDEX (head); - - /* If both head and tail refer to the same index, the number of second best hints is 0. */ - assert (heap_hdr->estimates.num_second_best < HEAP_NUM_BEST_SPACESTATS); - assert ((heap_hdr->estimates.tail_second_best >= heap_hdr->estimates.head_second_best) - ? ((heap_hdr->estimates.tail_second_best - heap_hdr->estimates.head_second_best) - == heap_hdr->estimates.num_second_best) - : ((HEAP_NUM_BEST_SPACESTATS + heap_hdr->estimates.tail_second_best - heap_hdr->estimates.head_second_best) - == heap_hdr->estimates.num_second_best)); - - *vpid = heap_hdr->estimates.second_best[head]; + assert (recdes.length >= (int) sizeof (*chain)); + memcpy (chain, recdes.data, sizeof (*chain)); return NO_ERROR; } -#if defined(ENABLE_UNUSED_FUNCTION) /* - * heap_stats_quick_num_fit_in_bestspace () - Guess the number of unit_size entries that - * can fit in best space - * return: number of units - * bestspace(in): Array of best pages along with their freespace - * (The freespace fields may be updated as a SIDE EFFECT) - * num_entries(in): Number of estimated entries in best space. - * unit_size(in): Units of this size - * unfill_space(in): Unfill space on the pages + * heap_page_is_bestspace () - Check whether a heap page is a bestspace storage page. * - * Note: Find the number of units of "unit_size" that can fit in - * current betsspace. + * return : true if page has the bestspace page flag + * page_heap (in) : Heap page */ -static int -heap_stats_quick_num_fit_in_bestspace (HEAP_BESTSPACE * bestspace, int num_entries, int unit_size, int unfill_space) +bool +heap_page_is_bestspace (THREAD_ENTRY * thread_p, PAGE_PTR page_heap) { - int total_nunits = 0; - int i; + RECDES recdes; - if (unit_size <= 0) + if (spage_get_record (thread_p, page_heap, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) { - return ER_FAILED; + assert_release (false); + return false; + } + + if (recdes.length != sizeof (HEAP_CHAIN)) + { + /* heap header page */ + return false; } + return HEAP_PAGE_IS_BESTSPACE ((HEAP_CHAIN *) recdes.data); +} - for (i = 0; i < num_entries; i++) +/* + * check_supplemental_log () - check if appending supplemental log is available + * + * return : available or not + */ +STATIC_INLINE bool +check_supplemental_log (THREAD_ENTRY * thread_p, OID * classoid) +{ + /* The value for PRM_ID_SUPPLEMENTAL_LOG is required to be greater than 0 if supplemental log is to be appended + * no_supplemental_log is used to block duplicated supplemental logs. So this value should be false if supplemental log is to be appended + */ + if (prm_get_integer_value (PRM_ID_SUPPLEMENTAL_LOG) > 0 && !thread_p->no_supplemental_log && !OID_ISNULL (classoid)) { - if ((bestspace[i].freespace - unfill_space) >= unit_size) + if (!oid_is_system_class (classoid)) { - /* - * How many min_spaces can fit in this page - */ - total_nunits += (bestspace[i].freespace - unfill_space) / unit_size; + return true; } } - return total_nunits; + return false; } -#endif /* - * heap_stats_find_page_in_bestspace () - Find a page within best space - * statistics with the needed space - * return: HEAP_FINDPSACE (found, not found, or error) - * hfid(in): Object heap file identifier - * bestspace(in): Array of best pages along with their freespace - * (The freespace fields may be updated as a SIDE EFFECT) - * idx_badspace(in/out): An index into best space with no so good space. - * needed_space(in): The needed space. - * scan_cache(in): Scan cache if any - * pgptr(out): Best page with enough space or NULL - * - * Note: Search for a page within the best space cache which has the - * needed space. The free space fields of best space cache along - * with some other index information are updated (as a side - * effect) as the best space cache is accessed. - */ -static HEAP_FINDSPACE -heap_stats_find_page_in_bestspace (THREAD_ENTRY * thread_p, const HFID * hfid, HEAP_BESTSPACE * bestspace, - int *idx_badspace, int record_length, int needed_space, HEAP_SCANCACHE * scan_cache, - PGBUF_WATCHER * pg_watcher) -{ -#define BEST_PAGE_SEARCH_MAX_COUNT 100 - - HEAP_FINDSPACE found; - int old_wait_msecs; - int notfound_cnt; - HEAP_STATS_ENTRY *ent; - HEAP_BESTSPACE best; - int rc; - int idx_worstspace; - int i, best_array_index = -1; - bool hash_is_available; - bool best_hint_is_used; - PERF_UTIME_TRACKER time_best_space = PERF_UTIME_TRACKER_INITIALIZER; - PERF_UTIME_TRACKER time_find_page_best_space = PERF_UTIME_TRACKER_INITIALIZER; + * heap_vpid_init_new () - FILE_INIT_PAGE_FUNC for heap non-header pages + * + * return : Error code + * thread_p (in) : Thread entry + * page (in) : New heap file page + * args (in) : HEAP_CHAIN * + */ +static int +heap_vpid_init_new (THREAD_ENTRY * thread_p, PAGE_PTR page, void *args) +{ + LOG_DATA_ADDR addr = LOG_DATA_ADDR_INITIALIZER; + HEAP_CHAIN chain; + RECDES recdes; + INT16 slotid; + int sp_success; - assert (PGBUF_IS_CLEAN_WATCHER (pg_watcher)); + assert (page != NULL); + assert (args != NULL); - PERF_UTIME_TRACKER_START (thread_p, &time_find_page_best_space); + chain = *(HEAP_CHAIN *) args; /* get chain from args. it is already initialized */ - /* - * If a page is busy, don't wait continue looking for other pages in our - * statistics. This will improve some contentions on the heap at the - * expenses of storage. - */ + /* initialize new page. */ + addr.pgptr = page; + pgbuf_set_page_ptype (thread_p, addr.pgptr, PAGE_HEAP); - /* LK_FORCE_ZERO_WAIT doesn't set error when deadlock occurs */ - old_wait_msecs = xlogtb_reset_wait_msecs (thread_p, LK_FORCE_ZERO_WAIT); + /* initialize the page and chain it with the previous last allocated page */ + spage_initialize (thread_p, addr.pgptr, heap_get_spage_type (), HEAP_MAX_ALIGN, SAFEGUARD_RVSPACE); - found = HEAP_FINDSPACE_NOTFOUND; - notfound_cnt = 0; - best_array_index = 0; - hash_is_available = prm_get_integer_value (PRM_ID_HF_MAX_BESTSPACE_ENTRIES) > 0; + recdes.area_size = recdes.length = sizeof (chain); + recdes.type = REC_HOME; + recdes.data = (char *) &chain; - while (found == HEAP_FINDSPACE_NOTFOUND) + sp_success = spage_insert (thread_p, addr.pgptr, &recdes, &slotid); + if (sp_success != SP_SUCCESS || slotid != HEAP_HEADER_AND_CHAIN_SLOTID) { - best.freespace = -1; /* init */ - best_hint_is_used = false; + assert (false); - if (hash_is_available) + /* initialization has failed !! */ + if (sp_success != SP_SUCCESS) { - PERF_UTIME_TRACKER_START (thread_p, &time_best_space); - rc = pthread_mutex_lock (&heap_Bestspace->bestspace_mutex); - - while (notfound_cnt < BEST_PAGE_SEARCH_MAX_COUNT - && (ent = (HEAP_STATS_ENTRY *) mht_get2 (heap_Bestspace->hfid_ht, hfid, NULL)) != NULL) - { - if (ent->best.freespace >= needed_space) - { - best = ent->best; - assert (best.freespace > 0 && best.freespace <= PGLENGTH_MAX); - break; - } - - /* remove in memory bestspace */ - (void) mht_rem2 (heap_Bestspace->hfid_ht, &ent->hfid, ent, NULL, NULL); - (void) mht_rem (heap_Bestspace->vpid_ht, &ent->best.vpid, NULL, NULL); - (void) heap_stats_entry_free (thread_p, ent, NULL); - ent = NULL; - - heap_Bestspace->num_stats_entries--; - - notfound_cnt++; - } - - pthread_mutex_unlock (&heap_Bestspace->bestspace_mutex); - PERF_UTIME_TRACKER_TIME (thread_p, &time_best_space, PSTAT_HF_BEST_SPACE_FIND); + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); } + return ER_FAILED; + } - if (best.freespace == -1) - { - /* Maybe PRM_ID_HF_MAX_BESTSPACE_ENTRIES <= 0 or There is no best space in heap_Bestspace hashtable. We will - * use bestspace hint in heap_header. */ - while (best_array_index < HEAP_NUM_BEST_SPACESTATS) - { - if (bestspace[best_array_index].freespace >= needed_space) - { - best.vpid = bestspace[best_array_index].vpid; - best.freespace = bestspace[best_array_index].freespace; - assert (best.freespace > 0 && best.freespace <= PGLENGTH_MAX); - best_hint_is_used = true; - break; - } - best_array_index++; - } - } + log_append_undoredo_data (thread_p, RVHF_NEWPAGE, &addr, 0, recdes.length, NULL, recdes.data); + pgbuf_set_dirty (thread_p, addr.pgptr, DONT_FREE); + return NO_ERROR; +} - if (best.freespace == -1) - { - break; /* not found, exit loop */ - } - - /* If page could not be fixed, we will interrogate er_errid () to see the error type. If an error is already - * set, the interrogation will be corrupted. - * Make sure an error is not set. - */ - if (er_errid () != NO_ERROR) - { - if (er_errid () == ER_INTERRUPTED) - { - /* interrupt arrives at any time */ - break; - } -#if defined (SERVER_MODE) - // ignores a warning and expects no other errors - assert (er_errid_if_has_error () == NO_ERROR); -#endif /* SERVER_MODE */ - er_clear (); - } +/* + * heap_vpid_alloc () - allocate, fetch, and initialize a new page + * return: error code + * hfid(in): Object heap file identifier + * hdr_pgptr(in): The heap page header + * heap_hdr(in): The heap header structure + * scan_cache(in): Scan cache + * new_pg_watcher(out): watcher for new page. + * + * Note: Allocate and initialize a new heap page. The heap header is + * updated to reflect a newly allocated best space page and + * the set of best space pages information may be updated to + * include the previous best1 space page. + */ +static int +heap_vpid_alloc (THREAD_ENTRY * thread_p, const HFID * hfid, PAGE_PTR hdr_pgptr, HEAP_HDR_STATS * heap_hdr, + HEAP_SCANCACHE * scan_cache, PGBUF_WATCHER * new_pg_watcher) +{ + VPID vpid; /* Volume and page identifiers */ + LOG_DATA_ADDR addr = LOG_DATA_ADDR_INITIALIZER; /* Address of logging data */ + int best; + VPID last_vpid; + PGBUF_WATCHER last_pg_watcher; + HEAP_CHAIN new_page_chain; + HEAP_HDR_STATS heap_hdr_prev = *heap_hdr; - pg_watcher->pgptr = heap_scan_pb_lock_and_fetch (thread_p, &best.vpid, OLD_PAGE, X_LOCK, scan_cache, pg_watcher); - if (pg_watcher->pgptr == NULL) - { - /* - * Either we timeout and we want to continue in this case, or - * we have another kind of problem. - */ - switch (er_errid ()) - { - case NO_ERROR: - /* In case of latch-timeout in pgbuf_fix, the timeout error(ER_LK_PAGE_TIMEOUT) is not set, because lock - * wait time is LK_FORCE_ZERO_WAIT. So we will just continue to find another page. */ - break; + int error_code = NO_ERROR; - case ER_INTERRUPTED: - found = HEAP_FINDSPACE_ERROR; - break; + assert (PGBUF_IS_CLEAN_WATCHER (new_pg_watcher)); - default: - /* - * Something went wrong, we are unable to fetch this page. - */ - if (best_hint_is_used == true) + PGBUF_INIT_WATCHER (&last_pg_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + addr.vfid = &hfid->vfid; + addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; - { - assert (best_array_index < HEAP_NUM_BEST_SPACESTATS); - bestspace[best_array_index].freespace = 0; - } - else - { - (void) heap_stats_del_bestspace_by_vpid (thread_p, &best.vpid); - } - found = HEAP_FINDSPACE_ERROR; + error_code = heap_get_last_page (thread_p, hfid, heap_hdr, scan_cache, &last_vpid, &last_pg_watcher); + if (error_code != NO_ERROR) + { + ASSERT_ERROR (); + return error_code; + } + if (last_pg_watcher.pgptr == NULL) + { + /* something went wrong, return error */ + assert_release (false); + return ER_FAILED; + } + assert (!VPID_ISNULL (&last_vpid)); - /* Do not allow unexpected errors. */ - assert (false); - break; - } - } - else - { - best.freespace = spage_max_space_for_new_record (thread_p, pg_watcher->pgptr); - if (best.freespace >= needed_space) - { - /* - * Decrement by only the amount space needed by the caller. Don't - * include the unfill factor - */ - best.freespace -= record_length + heap_Slotted_overhead; - found = HEAP_FINDSPACE_FOUND; - } + log_sysop_start (thread_p); - if (hash_is_available) - { - /* Add or refresh the free space of the page */ - (void) heap_stats_add_bestspace (thread_p, hfid, &best.vpid, best.freespace); - } + /* init chain for new page */ + new_page_chain.class_oid = heap_hdr->class_oid; + new_page_chain.prev_vpid = last_vpid; + VPID_SET_NULL (&new_page_chain.next_vpid); + new_page_chain.max_mvccid = MVCCID_NULL; + new_page_chain.flags = 0; + HEAP_PAGE_SET_VACUUM_STATUS (&new_page_chain, HEAP_PAGE_VACUUM_NONE); - if (best_hint_is_used == true) - { - assert (VPID_EQ (&best.vpid, &(bestspace[best_array_index].vpid))); - assert (best_array_index < HEAP_NUM_BEST_SPACESTATS); + /* allocate new page and initialize it */ + error_code = file_alloc (thread_p, &hfid->vfid, heap_vpid_init_new, &new_page_chain, &vpid, NULL); + if (error_code != NO_ERROR) + { + ASSERT_ERROR (); + goto error; + } - bestspace[best_array_index].freespace = best.freespace; - } + /* add link from previous last page */ + addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; - if (found != HEAP_FINDSPACE_FOUND) - { - pgbuf_ordered_unfix (thread_p, pg_watcher); - } - } + if (last_pg_watcher.pgptr == hdr_pgptr) + { + heap_hdr->next_vpid = vpid; + /* will be logged later */ + } + else + { + HEAP_CHAIN *chain, chain_prev; - if (found == HEAP_FINDSPACE_NOTFOUND) + /* get chain */ + chain = heap_get_chain_ptr (thread_p, last_pg_watcher.pgptr); + if (chain == NULL) { - if (best_hint_is_used) - { - /* Increment best_array_index for next search */ - best_array_index++; - } - else - { - notfound_cnt++; - } + assert_release (false); + error_code = ER_FAILED; + goto error; } + /* update chain */ + /* save old chain for logging */ + chain_prev = *chain; + /* change next link */ + chain->next_vpid = vpid; + + /* log change */ + addr.pgptr = last_pg_watcher.pgptr; + log_append_undoredo_data (thread_p, RVHF_CHAIN, &addr, sizeof (HEAP_CHAIN), sizeof (HEAP_CHAIN), &chain_prev, + chain); + pgbuf_set_dirty (thread_p, addr.pgptr, DONT_FREE); } - idx_worstspace = 0; - for (i = 0; i < HEAP_NUM_BEST_SPACESTATS; i++) - { - /* find worst space in bestspace */ - if (bestspace[idx_worstspace].freespace > bestspace[i].freespace) - { - idx_worstspace = i; - } + pgbuf_ordered_unfix (thread_p, &last_pg_watcher); - /* update bestspace of heap header page if found best page at memory hash table */ - if (best_hint_is_used == false && found == HEAP_FINDSPACE_FOUND && VPID_EQ (&best.vpid, &bestspace[i].vpid)) - { - bestspace[i].freespace = best.freespace; - } + /* last page hint */ + heap_hdr->last_vpid = vpid; + heap_hdr->num_pages++; + + /* we really have nothing to lose from logging stats here and also it is good to have a certain last VPID. */ + addr.pgptr = hdr_pgptr; + log_append_undoredo_data (thread_p, RVHF_STATS, &addr, sizeof (HEAP_HDR_STATS), sizeof (HEAP_HDR_STATS), + &heap_hdr_prev, heap_hdr); + log_sysop_commit (thread_p); + + /* fix new page */ + new_pg_watcher->pgptr = heap_scan_pb_lock_and_fetch (thread_p, &vpid, OLD_PAGE, X_LOCK, scan_cache, new_pg_watcher); + if (new_pg_watcher->pgptr == NULL) + { + ASSERT_ERROR_AND_SET (error_code); + return error_code; } - /* - * Set the idx_badspace to the index with the smallest free space - * which may not be accurate. This is used for future lookups (where to - * start) into the findbest space ring. - */ - *idx_badspace = idx_worstspace; + return NO_ERROR; - /* - * Reset back the timeout value of the transaction - */ - (void) xlogtb_reset_wait_msecs (thread_p, old_wait_msecs); - PERF_UTIME_TRACKER_TIME (thread_p, &time_find_page_best_space, PSTAT_HF_HEAP_FIND_PAGE_BEST_SPACE); +error: + assert (error_code != NO_ERROR); + + if (last_pg_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &last_pg_watcher); + } + log_sysop_abort (thread_p); - return found; + return error_code; } /* - * heap_stats_find_best_page () - Find a page with the needed space. - * return: pointer to page with enough space or NULL + * heap_vpid_remove () - Deallocate a heap page + * return: rm_vpid on success or NULL on error * hfid(in): Object heap file identifier - * needed_space(in): The minimal space needed - * isnew_rec(in): Are we inserting a new record to the heap ? - * scan_cache(in/out): Scan cache used to estimate the best space pages - * pg_watcher(out): watcher for a found page. + * heap_hdr(in): The heap header stats + * rm_vpid(in): Page to remove * - * Note: Find a page among the set of best pages of the heap which has - * the needed space. If we do not find any page, a new page is - * allocated. The heap header and the scan cache may be updated - * as a side effect to reflect more accurate space on some of the - * set of best pages. + * Note: The given page is removed from the heap. The linked list of heap + * pages is updated to remove this page, and the heap header may + * be updated if this page was part of the statistics. */ -static PAGE_PTR -heap_stats_find_best_page (THREAD_ENTRY * thread_p, const HFID * hfid, int needed_space, bool isnew_rec, - HEAP_SCANCACHE * scan_cache, PGBUF_WATCHER * pg_watcher) +static VPID * +heap_vpid_remove (THREAD_ENTRY * thread_p, const HFID * hfid, HEAP_HDR_STATS * heap_hdr, VPID * rm_vpid) { - VPID vpid; /* Volume and page identifiers */ - LOG_DATA_ADDR addr_hdr; /* Address of logging data */ - RECDES hdr_recdes; /* Record descriptor to point to space statistics */ - HEAP_HDR_STATS *heap_hdr; /* Heap header */ - VPID *hdr_vpidp; - int total_space; - int try_find, try_sync; - int num_pages_found; - float other_high_best_ratio; - PGBUF_WATCHER hdr_page_watcher; - int error_code = NO_ERROR; - PERF_UTIME_TRACKER time_find_best_page = PERF_UTIME_TRACKER_INITIALIZER; - - assert (!heap_is_big_length (needed_space)); - assert (scan_cache == NULL || scan_cache->cache_last_fix_page == false || scan_cache->page_watcher.pgptr == NULL); + RECDES rm_recdes; /* Record descriptor which holds the chain of the page to be removed */ + HEAP_CHAIN *rm_chain; /* Chain information of the page to be removed */ + VPID vpid; /* Real identifier of previous page */ + LOG_DATA_ADDR addr; /* Log address of previous page */ + RECDES recdes; /* Record descriptor to page header */ + HEAP_CHAIN chain; /* Chain to next and prev page */ + int sp_success; + int i; + PGBUF_WATCHER rm_pg_watcher; + PGBUF_WATCHER prev_pg_watcher; - PERF_UTIME_TRACKER_START (thread_p, &time_find_best_page); + PGBUF_INIT_WATCHER (&rm_pg_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + PGBUF_INIT_WATCHER (&prev_pg_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); /* - * Try to use the space cache for as much information as possible to avoid - * fetching and updating the header page a lot. + * Make sure that this is not the header page since the header page cannot + * be removed. If the header page is removed.. the heap is gone */ - PGBUF_INIT_WATCHER (&hdr_page_watcher, PGBUF_ORDERED_HEAP_HDR, hfid); + if (rm_vpid->pageid == hfid->hpgid && rm_vpid->volid == hfid->vfid.volid) + { + er_log_debug (ARG_FILE_LINE, "heap_vpid_remove: Trying to remove header page = %d|%d of heap file = %d|%d|%d", + (int) rm_vpid->volid, rm_vpid->pageid, (int) hfid->vfid.volid, hfid->vfid.fileid, hfid->hpgid); + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + goto error; + } + + /* Get the chain record */ + rm_pg_watcher.pgptr = heap_scan_pb_lock_and_fetch (thread_p, rm_vpid, OLD_PAGE, X_LOCK, NULL, &rm_pg_watcher); + if (rm_pg_watcher.pgptr == NULL) + { + /* Look like a system error. Unable to obtain chain header record */ + goto error; + } + + if (spage_get_record (thread_p, rm_pg_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &rm_recdes, PEEK) != S_SUCCESS) + { + /* Look like a system error. Unable to obtain chain header record */ + goto error; + } + + rm_chain = (HEAP_CHAIN *) rm_recdes.data; /* - * Get the heap header in exclusive mode since it is going to be changed. + * UPDATE PREVIOUS PAGE * - * Note: to avoid any possibilities of deadlocks, I should not have any locks - * on the heap at this moment. - * That is, we must assume that locking the header of the heap in - * exclusive mode, the rest of the heap is locked. + * Update chain next field of previous last page + * If previous page is the heap header page, it contains a heap header + * instead of a chain. */ - vpid.volid = hfid->vfid.volid; - vpid.pageid = hfid->hpgid; - - addr_hdr.vfid = &hfid->vfid; - addr_hdr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; + vpid = rm_chain->prev_vpid; + addr.vfid = &hfid->vfid; + addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; - error_code = pgbuf_ordered_fix (thread_p, &vpid, OLD_PAGE, PGBUF_LATCH_WRITE, &hdr_page_watcher); - if (error_code != NO_ERROR) + prev_pg_watcher.pgptr = heap_scan_pb_lock_and_fetch (thread_p, &vpid, OLD_PAGE, X_LOCK, NULL, &prev_pg_watcher); + if (prev_pg_watcher.pgptr == NULL) { - /* something went wrong. Unable to fetch header page */ - ASSERT_ERROR (); + /* something went wrong, return */ goto error; } - assert (hdr_page_watcher.pgptr != NULL); - -#if !defined (NDEBUG) - (void) pgbuf_check_page_ptype (thread_p, hdr_page_watcher.pgptr, PAGE_HEAP); -#endif /* !NDEBUG */ - if (spage_get_record (thread_p, hdr_page_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &hdr_recdes, PEEK) != S_SUCCESS) + if (rm_pg_watcher.page_was_unfixed) { - assert (false); - pgbuf_ordered_unfix (thread_p, &hdr_page_watcher); + /* TODO : unexpected: need to reconsider the algorithm, if this is an ordinary case */ + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PB_UNEXPECTED_PAGE_REFIX, 4, rm_vpid->volid, rm_vpid->pageid, + vpid.volid, vpid.pageid); goto error; } - heap_hdr = (HEAP_HDR_STATS *) hdr_recdes.data; + /* + * Make sure that the page to be removed is not referenced on the heap + * statistics + */ - if (isnew_rec == true) - { - heap_hdr->estimates.num_recs += 1; - } - heap_hdr->estimates.recs_sumlen += (float) needed_space; + assert (heap_hdr != NULL); - /* Take into consideration the unfill factor for pages with objects */ - total_space = needed_space + heap_Slotted_overhead + heap_hdr->unfill_space; - if (heap_is_big_length (total_space)) + if (VPID_EQ (&heap_hdr->last_vpid, rm_vpid)) { - total_space = needed_space + heap_Slotted_overhead; + /* If the page is the last page of the heap file, update the hint */ + heap_hdr->last_vpid = rm_chain->prev_vpid; } - try_find = 0; - while (true) + /* + * Is previous page the header page ? + */ + if (vpid.pageid == hfid->hpgid && vpid.volid == hfid->vfid.volid) { - try_find++; - assert (pg_watcher->pgptr == NULL); - if (heap_stats_find_page_in_bestspace (thread_p, hfid, heap_hdr->estimates.best, &(heap_hdr->estimates.head), - needed_space, total_space, scan_cache, pg_watcher) == HEAP_FINDSPACE_ERROR) - { - ASSERT_ERROR (); - assert (pg_watcher->pgptr == NULL); - pgbuf_ordered_unfix (thread_p, &hdr_page_watcher); - goto error; - } - if (pg_watcher->pgptr != NULL) - { - /* found the page */ - break; - } - - assert (hdr_page_watcher.page_was_unfixed == false); - - if (heap_hdr->estimates.num_other_high_best <= 0 || heap_hdr->estimates.num_pages <= 0) - { - assert (heap_hdr->estimates.num_pages > 0); - other_high_best_ratio = 0; - } - else - { - other_high_best_ratio = - (float) heap_hdr->estimates.num_other_high_best / (float) heap_hdr->estimates.num_pages; - } - - if (try_find >= 2 || other_high_best_ratio < HEAP_BESTSPACE_SYNC_THRESHOLD) - { - /* We stop to find free pages if: (1) we have tried to do it twice (2) it is first trying but we have no - * hints Regarding (2), we will find free pages by heap_stats_sync_bestspace only if we know that a free page - * exists somewhere. and (num_other_high_best/total page) > HEAP_BESTSPACE_SYNC_THRESHOLD. - * num_other_high_best means the number of free pages existing somewhere in the heap file. */ - break; - } - /* - * The followings will try to find free pages and fill best hints with them. + * PREVIOUS PAGE IS THE HEADER PAGE. + * It contains a heap header instead of a chain record */ - - if (scan_cache != NULL) - { - assert (HFID_EQ (hfid, &scan_cache->node.hfid)); - assert (scan_cache->file_type != FILE_UNKNOWN_TYPE); - } - - hdr_vpidp = pgbuf_get_vpid_ptr (hdr_page_watcher.pgptr); - - try_sync = 0; - do - { - try_sync++; - heap_bestspace_log ("heap_stats_find_best_page: call heap_stats_sync_bestspace() " - "hfid { vfid { fileid %d volid %d } hpgid %d } hdr_vpid { pageid %d volid %d } " - "scan_all %d ", hfid->vfid.fileid, hfid->vfid.volid, hfid->hpgid, hdr_vpidp->pageid, - hdr_vpidp->volid, 0); - - num_pages_found = heap_stats_sync_bestspace (thread_p, hfid, heap_hdr, hdr_vpidp, false, true); - if (num_pages_found < 0) - { - pgbuf_ordered_unfix (thread_p, &hdr_page_watcher); - ASSERT_ERROR (); - goto error; - } - } - while (num_pages_found == 0 && try_sync <= 2); - - /* If we cannot find free pages, give up. */ - if (num_pages_found <= 0) - { - break; - } + heap_hdr->next_vpid = rm_chain->next_vpid; } - - if (pg_watcher->pgptr == NULL) + else { /* - * None of the best pages has the needed space, allocate a new page. - * Set the head to the index with the smallest free space, which may not - * be accurate. + * PREVIOUS PAGE IS NOT THE HEADER PAGE. + * It contains a chain... + * We need to make sure that there is not references to the page to delete + * in the statistics of the heap header */ - if (heap_vpid_alloc (thread_p, hfid, hdr_page_watcher.pgptr, heap_hdr, scan_cache, pg_watcher) != NO_ERROR) + + /* NOW check the PREVIOUS page */ + /* Get the chain record */ + if (spage_get_record (thread_p, prev_pg_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) { - ASSERT_ERROR (); - pgbuf_ordered_unfix (thread_p, &hdr_page_watcher); + /* Look like a system error. Unable to obtain header record */ goto error; } - assert (pg_watcher->pgptr != NULL || er_errid () == ER_INTERRUPTED - || er_errid () == ER_FILE_NOT_ENOUGH_PAGES_IN_DATABASE); - } - - addr_hdr.pgptr = hdr_page_watcher.pgptr; - log_skip_logging (thread_p, &addr_hdr); - pgbuf_ordered_set_dirty_and_free (thread_p, &hdr_page_watcher); - - PERF_UTIME_TRACKER_TIME (thread_p, &time_find_best_page, PSTAT_HF_HEAP_FIND_BEST_PAGE); - - return pg_watcher->pgptr; - -error: - PERF_UTIME_TRACKER_TIME (thread_p, &time_find_best_page, PSTAT_HF_HEAP_FIND_BEST_PAGE); - return NULL; -} - -/* - * heap_stats_sync_bestspace () - Synchronize the statistics of best space - * return: the number of pages found - * hfid(in): Heap file identifier - * heap_hdr(in): Heap header (Heap header page should be acquired in - * exclusive mode) - * hdr_vpid(in): - * scan_all(in): Scan the whole heap or stop after HEAP_NUM_BEST_SPACESTATS - * best pages have been found. - * can_cycle(in): True, it allows to go back to beginning of the heap. - * FALSE, don't go back to beginning of the heap. FALSE is used - * when it is known that there is not free space at the - * beginning of heap. For example, it can be used when we - * pre-allocate. pages - * - * Note: Synchronize for best space, so that we can reuse heap space as - * much as possible. - * - * Note: This function does not do any logging. - */ -static int -heap_stats_sync_bestspace (THREAD_ENTRY * thread_p, const HFID * hfid, HEAP_HDR_STATS * heap_hdr, VPID * hdr_vpid, - bool scan_all, bool can_cycle) -{ - int i, best, num_high_best, num_other_best, start_pos; - VPID vpid = { NULL_PAGEID, NULL_VOLID }; - VPID start_vpid = { NULL_PAGEID, NULL_VOLID }; - VPID next_vpid = { NULL_PAGEID, NULL_VOLID }; - VPID stopat_vpid = { NULL_PAGEID, NULL_VOLID }; - int num_pages = 0; - int num_recs = 0; - float recs_sumlen = 0.0; - int free_space = 0; - int ret = NO_ERROR; - int npages = 0, nrecords = 0, rec_length; - int num_iterations = 0, max_iterations; - HEAP_BESTSPACE *best_pages_hint_p; - bool iterate_all = false; - bool search_all = false; - PGBUF_WATCHER pg_watcher; - PGBUF_WATCHER old_pg_watcher; - PERF_UTIME_TRACKER timer_sync_best_space = PERF_UTIME_TRACKER_INITIALIZER; + /* Copy the chain record to memory.. so we can log the changes */ + memcpy (&chain, recdes.data, sizeof (chain)); - PERF_UTIME_TRACKER_START (thread_p, &timer_sync_best_space); + /* Modify the chain of the previous page in memory */ + chain.next_vpid = rm_chain->next_vpid; - PGBUF_INIT_WATCHER (&pg_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); - PGBUF_INIT_WATCHER (&old_pg_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + /* Log the desired changes.. and then change the header */ + addr.pgptr = prev_pg_watcher.pgptr; + log_append_undoredo_data (thread_p, RVHF_CHAIN, &addr, sizeof (chain), sizeof (chain), recdes.data, &chain); - best = 0; - start_pos = -1; - num_high_best = num_other_best = 0; + /* Now change the record */ + recdes.area_size = recdes.length = sizeof (chain); + recdes.type = REC_HOME; + recdes.data = (char *) &chain; - if (scan_all != true) - { - if (prm_get_integer_value (PRM_ID_HF_MAX_BESTSPACE_ENTRIES) > 0) - { - search_all = true; - start_pos = -1; - next_vpid = heap_hdr->estimates.full_search_vpid; - start_vpid = next_vpid; - } - else + sp_success = spage_update (thread_p, prev_pg_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes); + if (sp_success != SP_SUCCESS) { - if (heap_hdr->estimates.num_high_best > 0) - { - /* Use recently inserted one first. */ - start_pos = HEAP_STATS_PREV_BEST_INDEX (heap_hdr->estimates.head); - for (i = 0; i < HEAP_NUM_BEST_SPACESTATS; i++) - { - if (!VPID_ISNULL (&heap_hdr->estimates.best[start_pos].vpid)) - { - next_vpid = heap_hdr->estimates.best[start_pos].vpid; - start_vpid = next_vpid; - break; - } - - start_pos = HEAP_STATS_PREV_BEST_INDEX (start_pos); - } - } - else + /* + * This look like a system error, size did not change, so why did it + * fail + */ + if (sp_success != SP_ERROR) { - /* If there are hint pages in second best array, we will try to use it first. Otherwise, we will search - * all pages in the file. */ - if (heap_hdr->estimates.num_second_best > 0) - { - if (heap_stats_get_second_best (heap_hdr, &next_vpid) != NO_ERROR) - { - /* This should not be happened. */ - assert (false); - search_all = true; - } - } - else - { - search_all = true; - } - - if (search_all == true) - { - assert (VPID_ISNULL (&next_vpid)); - next_vpid = heap_hdr->estimates.full_search_vpid; - } - - start_vpid = next_vpid; - start_pos = -1; + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); } + goto error; } - if (can_cycle == true) - { - stopat_vpid = next_vpid; - } } - if (VPID_ISNULL (&next_vpid)) - { - /* - * Start from beginning of heap due to lack of statistics. - */ - next_vpid.volid = hfid->vfid.volid; - next_vpid.pageid = hfid->hpgid; - start_vpid = next_vpid; - start_pos = -1; - can_cycle = false; - } + /* Now set dirty, free and unlock the previous page */ + pgbuf_ordered_set_dirty_and_free (thread_p, &prev_pg_watcher); /* - * Note that we do not put any locks on the pages that we are scanning - * since the best space array is only used for hints, and it is OK - * if it is a little bit wrong. + * UPDATE NEXT PAGE + * + * Update chain previous field of next page */ - best_pages_hint_p = heap_hdr->estimates.best; - num_iterations = 0; - max_iterations = MIN ((int) (heap_hdr->estimates.num_pages * 0.2), heap_Find_best_page_limit); - max_iterations = MAX (max_iterations, HEAP_NUM_BEST_SPACESTATS); - - while (!VPID_ISNULL (&next_vpid) || can_cycle == true) + if (!(VPID_ISNULL (&rm_chain->next_vpid))) { - if (can_cycle == true && VPID_ISNULL (&next_vpid)) + vpid = rm_chain->next_vpid; + addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; + + prev_pg_watcher.pgptr = heap_scan_pb_lock_and_fetch (thread_p, &vpid, OLD_PAGE, X_LOCK, NULL, &prev_pg_watcher); + if (prev_pg_watcher.pgptr == NULL) { - /* - * Go back to beginning of heap looking for good pages with a lot of - * free space - */ - next_vpid.volid = hfid->vfid.volid; - next_vpid.pageid = hfid->hpgid; - can_cycle = false; + /* something went wrong, return */ + goto error; } - while ((scan_all == true || num_high_best < HEAP_NUM_BEST_SPACESTATS) && !VPID_ISNULL (&next_vpid) - && (can_cycle == true || !VPID_EQ (&next_vpid, &stopat_vpid))) + /* Get the chain record */ + if (spage_get_record (thread_p, prev_pg_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) { - if (scan_all == false) - { - if (++num_iterations > max_iterations) - { - heap_bestspace_log ("heap_stats_sync_bestspace: num_iterations %d best %d " - "next_vpid { pageid %d volid %d }\n", num_iterations, num_high_best, - next_vpid.pageid, next_vpid.volid); - - /* TODO: Do we really need to update the last scanned */ - /* in case we found less than 10 pages. */ - /* It is obivous we didn't find any pages. */ - if (start_pos != -1 && num_high_best == 0) - { - /* Delete a starting VPID. */ - VPID_SET_NULL (&best_pages_hint_p[start_pos].vpid); - best_pages_hint_p[start_pos].freespace = 0; - - heap_hdr->estimates.num_high_best--; - } - iterate_all = true; - break; - } - } - - vpid = next_vpid; - ret = pgbuf_ordered_fix (thread_p, &vpid, OLD_PAGE_PREVENT_DEALLOC, PGBUF_LATCH_READ, &pg_watcher); - if (ret != NO_ERROR) - { - break; - } -#if !defined (NDEBUG) - (void) pgbuf_check_page_ptype (thread_p, pg_watcher.pgptr, PAGE_HEAP); -#endif /* !NDEBUG */ - - if (old_pg_watcher.pgptr != NULL) - { - pgbuf_ordered_unfix (thread_p, &old_pg_watcher); - } + /* Look like a system error. Unable to obtain header record */ + goto error; + } - ret = heap_vpid_next (thread_p, hfid, pg_watcher.pgptr, &next_vpid); - if (ret != NO_ERROR) - { - assert (false); - pgbuf_ordered_unfix (thread_p, &pg_watcher); - break; - } - if (search_all) - { - /* Save the last position to be searched next time. */ - heap_hdr->estimates.full_search_vpid = next_vpid; - } + /* Copy the chain record to memory.. so we can log the changes */ + memcpy (&chain, recdes.data, sizeof (chain)); - spage_collect_statistics (pg_watcher.pgptr, &npages, &nrecords, &rec_length); + /* Modify the chain of the next page in memory */ + chain.prev_vpid = rm_chain->prev_vpid; - num_pages += npages; - num_recs += nrecords; - recs_sumlen += rec_length; + /* Log the desired changes.. and then change the header */ + addr.pgptr = prev_pg_watcher.pgptr; + log_append_undoredo_data (thread_p, RVHF_CHAIN, &addr, sizeof (chain), sizeof (chain), recdes.data, &chain); - free_space = spage_max_space_for_new_record (thread_p, pg_watcher.pgptr); + /* Now change the record */ + recdes.area_size = recdes.length = sizeof (chain); + recdes.type = REC_HOME; + recdes.data = (char *) &chain; - /* TODO: if the value returned by heap_stats_get_min_freespace (...) changes, this condition should be checked. */ - if ( /* free_space >= heap_stats_get_min_freespace (heap_hdr) && */ free_space > HEAP_DROP_FREE_SPACE) + sp_success = spage_update (thread_p, prev_pg_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes); + if (sp_success != SP_SUCCESS) + { + /* + * This look like a system error, size did not change, so why did it + * fail + */ + if (sp_success != SP_ERROR) { - if (prm_get_integer_value (PRM_ID_HF_MAX_BESTSPACE_ENTRIES) > 0) - { - (void) heap_stats_add_bestspace (thread_p, hfid, &vpid, free_space); - } - - if (num_high_best < HEAP_NUM_BEST_SPACESTATS) - { - best_pages_hint_p[best].vpid = vpid; - best_pages_hint_p[best].freespace = free_space; - - best = HEAP_STATS_NEXT_BEST_INDEX (best); - num_high_best++; - } - else - { - num_other_best++; - } + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); } - - pgbuf_replace_watcher (thread_p, &pg_watcher, &old_pg_watcher); - } - - assert (pg_watcher.pgptr == NULL); - if (old_pg_watcher.pgptr != NULL) - { - pgbuf_ordered_unfix (thread_p, &old_pg_watcher); + goto error; } - if (scan_all == false - && (iterate_all == true || num_high_best == HEAP_NUM_BEST_SPACESTATS - || (can_cycle == false && VPID_EQ (&next_vpid, &stopat_vpid)))) - { - break; - } + /* Now set dirty, free and unlock the next page */ - VPID_SET_NULL (&next_vpid); + pgbuf_ordered_set_dirty_and_free (thread_p, &prev_pg_watcher); } - heap_bestspace_log ("heap_stats_sync_bestspace: scans from {%d|%d} to {%d|%d}, num_iterations(%d) " - "max_iterations(%d) num_high_best(%d)\n", start_vpid.volid, start_vpid.pageid, vpid.volid, - vpid.pageid, num_iterations, max_iterations, num_high_best); - - /* If we have scanned all pages, we should update all statistics even if we have not found any hints. This logic is - * used to handle "select count(*) from table". */ - if (scan_all == false && num_high_best == 0 && heap_hdr->estimates.num_second_best == 0) - { - goto end; - } + /* Free the page to be deallocated and deallocate the page */ + pgbuf_ordered_unfix (thread_p, &rm_pg_watcher); - if (num_high_best < HEAP_NUM_BEST_SPACESTATS) + if (file_dealloc (thread_p, &hfid->vfid, rm_vpid, FILE_HEAP) != NO_ERROR) { - for (i = best; i < HEAP_NUM_BEST_SPACESTATS; i++) - { - VPID_SET_NULL (&best_pages_hint_p[i].vpid); - best_pages_hint_p[i].freespace = 0; - } + ASSERT_ERROR (); + goto error; } - heap_hdr->estimates.head = best; /* reinit */ - heap_hdr->estimates.num_high_best = num_high_best; - assert (heap_hdr->estimates.head >= 0 && heap_hdr->estimates.head < HEAP_NUM_BEST_SPACESTATS - && heap_hdr->estimates.num_high_best <= HEAP_NUM_BEST_SPACESTATS); + return rm_vpid; - if (scan_all == true || heap_hdr->estimates.num_pages <= num_pages) +error: + if (rm_pg_watcher.pgptr != NULL) { - /* - * We scan the whole heap. - * Reset its statistics with new found statistics - */ - heap_hdr->estimates.num_other_high_best = num_other_best; - heap_hdr->estimates.num_pages = num_pages; - heap_hdr->estimates.num_recs = num_recs; - heap_hdr->estimates.recs_sumlen = recs_sumlen; + pgbuf_ordered_unfix (thread_p, &rm_pg_watcher); } - else + if (addr.pgptr != NULL) { - /* - * We did not scan the whole heap. - * We reset only some of its statistics since we do not have any idea - * which ones are better the ones that are currently recorded or the ones - * just found. - */ - heap_hdr->estimates.num_other_high_best -= heap_hdr->estimates.num_high_best; - - if (heap_hdr->estimates.num_other_high_best < num_other_best) - { - heap_hdr->estimates.num_other_high_best = num_other_best; - } - - if (num_recs > heap_hdr->estimates.num_recs || recs_sumlen > heap_hdr->estimates.recs_sumlen) - { - heap_hdr->estimates.num_pages = num_pages; - heap_hdr->estimates.num_recs = num_recs; - heap_hdr->estimates.recs_sumlen = recs_sumlen; - } + pgbuf_ordered_unfix (thread_p, &prev_pg_watcher); } -end: - PERF_UTIME_TRACKER_TIME (thread_p, &timer_sync_best_space, PSTAT_HEAP_STATS_SYNC_BESTSPACE); - - return num_high_best; + return NULL; } /* - * heap_get_last_page () - Get the last page pointer. - * return: error code - * hfid(in): Object heap file identifier - * heap_hdr(in): The heap header structure - * scan_cache(in): Scan cache - * last_vpid(out): VPID of the last page - * - * Note: The last vpid is saved on heap header. We log it and should be the right VPID. + * heap_remove_page_on_vacuum () - Remove heap page from heap file during + * vacuum process. Function is trying to + * be as least intrusive as possible and all + * required pages are latched conditionally. + * Give up on any failed operation. + * + * return : True if page was deallocated, false if not. + * thread_p (in) : Thread entry. + * page_ptr (in) : Pointer to page being deallocated. + * hfid (in) : Heap file identifier. */ -static int -heap_get_last_page (THREAD_ENTRY * thread_p, const HFID * hfid, HEAP_HDR_STATS * heap_hdr, HEAP_SCANCACHE * scan_cache, - VPID * last_vpid, PGBUF_WATCHER * pg_watcher) +bool +heap_remove_page_on_vacuum (THREAD_ENTRY * thread_p, PAGE_PTR * page_ptr, HFID * hfid) { - int error_code = NO_ERROR; + VPID page_vpid = VPID_INITIALIZER; /* VPID of page being removed. */ + VPID prev_vpid = VPID_INITIALIZER; /* VPID of previous page. */ + VPID next_vpid = VPID_INITIALIZER; /* VPID of next page. */ + VPID header_vpid = VPID_INITIALIZER; /* VPID of heap header page. */ + HEAP_HDR_STATS heap_hdr; /* Header header & stats. */ + HEAP_CHAIN chain; /* Heap page header used to read and update page links. */ + RECDES copy_recdes; /* Record to copy header from pages. */ + /* Buffer used for copy record. */ + char copy_recdes_buffer[MAX (sizeof (HEAP_CHAIN), sizeof (HEAP_HDR_STATS)) + MAX_ALIGNMENT]; + RECDES update_recdes; /* Record containing updated header data. */ + int i = 0; /* Iterator. */ + bool is_system_op_started = false; /* Set to true once system operation is started. */ + PGBUF_WATCHER crt_watcher; /* Watcher for current page. */ + PGBUF_WATCHER header_watcher; /* Watcher for header page. */ + PGBUF_WATCHER prev_watcher; /* Watcher for previous page. */ + PGBUF_WATCHER next_watcher; /* Watcher for next page. */ - assert (pg_watcher != NULL); - assert (last_vpid != NULL); - assert (!VPID_ISNULL (&heap_hdr->estimates.last_vpid)); + /* Assert expected arguments. */ + /* Page to remove must be fixed. */ + assert (page_ptr != NULL && *page_ptr != NULL); + /* Page to remove must be empty. */ + assert (spage_number_of_records (*page_ptr) <= 1); + /* Heap file identifier must be known. */ + assert (hfid != NULL && !HFID_IS_NULL (hfid)); - *last_vpid = heap_hdr->estimates.last_vpid; - pg_watcher->pgptr = heap_scan_pb_lock_and_fetch (thread_p, last_vpid, OLD_PAGE, X_LOCK, scan_cache, pg_watcher); - if (pg_watcher->pgptr == NULL) + /* Get VPID of page to be removed. */ + pgbuf_get_vpid (*page_ptr, &page_vpid); + + if (page_vpid.pageid == hfid->hpgid && page_vpid.volid == hfid->vfid.volid) { - ASSERT_ERROR_AND_SET (error_code); - return error_code; + /* Cannot remove heap file header page. */ + return false; } -#if !defined (NDEBUG) - { - RECDES recdes; - HEAP_CHAIN *chain; - if (spage_get_record (thread_p, pg_watcher->pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) - { - assert (false); - pgbuf_ordered_unfix (thread_p, pg_watcher); - return ER_FAILED; - } - chain = (HEAP_CHAIN *) recdes.data; - assert (VPID_ISNULL (&chain->next_vpid)); - } -#endif /* !NDEBUG */ + /* Use page watchers to do the ordered fix. */ + PGBUF_INIT_WATCHER (&crt_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + PGBUF_INIT_WATCHER (&header_watcher, PGBUF_ORDERED_HEAP_HDR, hfid); + PGBUF_INIT_WATCHER (&prev_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + PGBUF_INIT_WATCHER (&next_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); - return NO_ERROR; -} + /* Current page is already fixed. Just attach watcher. */ + pgbuf_attach_watcher (thread_p, *page_ptr, PGBUF_LATCH_WRITE, hfid, &crt_watcher); -/* - * heap_get_last_vpid () - Get last heap page VPID from heap file header - * - * return : Error code - * thread_p (in) : Thread entry - * hfid (in) : Heap file identifier - * last_vpid (out) : Last heap page VPID - */ -STATIC_INLINE int -heap_get_last_vpid (THREAD_ENTRY * thread_p, const HFID * hfid, VPID * last_vpid) -{ - PGBUF_WATCHER watcher_heap_header; - VPID vpid_heap_header; - HEAP_HDR_STATS *hdr_stats = NULL; + /* Header vpid. */ + header_vpid.volid = hfid->vfid.volid; + header_vpid.pageid = hfid->hpgid; - int error_code = NO_ERROR; + /* Fix required pages: Heap header page. Previous page (always exists). Next page (if exists). */ - PGBUF_INIT_WATCHER (&watcher_heap_header, PGBUF_ORDERED_HEAP_HDR, hfid); + /* Fix header page first, because it has higher priority. */ + if (pgbuf_ordered_fix (thread_p, &header_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, &header_watcher) != NO_ERROR) + { + /* Give up. */ + vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, + "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, page_vpid.pageid); + goto error; + } + assert (header_watcher.pgptr != NULL); - VPID_SET_NULL (last_vpid); + if (crt_watcher.page_was_unfixed) + { + *page_ptr = crt_watcher.pgptr; /* home was refixed */ + } - vpid_heap_header.volid = hfid->vfid.volid; - vpid_heap_header.pageid = hfid->hpgid; - error_code = pgbuf_ordered_fix (thread_p, &vpid_heap_header, OLD_PAGE, PGBUF_LATCH_READ, &watcher_heap_header); - if (error_code != NO_ERROR) + /* Get previous and next page VPID's. */ + if (heap_vpid_prev (thread_p, hfid, *page_ptr, &prev_vpid) != NO_ERROR + || heap_vpid_next (thread_p, hfid, *page_ptr, &next_vpid) != NO_ERROR) { - ASSERT_ERROR (); - return error_code; + /* Give up. */ + vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, + "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, page_vpid.pageid); + goto error; } - hdr_stats = heap_get_header_stats_ptr (thread_p, watcher_heap_header.pgptr); - if (hdr_stats == NULL) + /* Fix previous page if it is not same as header. */ + if (!VPID_ISNULL (&prev_vpid) && !VPID_EQ (&prev_vpid, &header_vpid)) { - assert_release (false); - pgbuf_ordered_unfix (thread_p, &watcher_heap_header); - return ER_FAILED; + if (pgbuf_ordered_fix (thread_p, &prev_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, &prev_watcher) != NO_ERROR) + { + /* Give up. */ + vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, + "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, + page_vpid.pageid); + goto error; + } } - *last_vpid = hdr_stats->estimates.last_vpid; - pgbuf_ordered_unfix (thread_p, &watcher_heap_header); - return NO_ERROR; -} -/* - * heap_get_header_stats_ptr () - Get pointer to heap header statistics. - * - * return : Pointer to heap header statistics - * page_header (in) : Heap header page - */ -STATIC_INLINE HEAP_HDR_STATS * -heap_get_header_stats_ptr (THREAD_ENTRY * thread_p, PAGE_PTR page_header) -{ - RECDES recdes; + /* Fix next page if current page is not last in heap file. */ + if (!VPID_ISNULL (&next_vpid)) + { + if (pgbuf_ordered_fix (thread_p, &next_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, &next_watcher) != NO_ERROR) + { + /* Give up. */ + vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, + "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, + page_vpid.pageid); + goto error; + } + } - if (spage_get_record (thread_p, page_header, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) + /* All pages are fixed. */ + + if (crt_watcher.page_was_unfixed) { - assert_release (false); - return NULL; + *page_ptr = crt_watcher.pgptr; /* home was refixed */ + + if (spage_number_of_records (crt_watcher.pgptr) > 1) + { + /* Current page has new data. It is no longer a candidate for removal. */ + vacuum_er_log (VACUUM_ER_LOG_HEAP, + "Candidate heap page %d|%d to remove was changed and has new data.", page_vpid.volid, + page_vpid.pageid); + goto error; + } } - return (HEAP_HDR_STATS *) recdes.data; -} -/* - * heap_copy_header_stats () - Copy heap header statistics - * - * return : Error code - * page_header (in) : Heap header page - * header_stats (out) : Heap header statistics - */ -STATIC_INLINE int -heap_copy_header_stats (THREAD_ENTRY * thread_p, PAGE_PTR page_header, HEAP_HDR_STATS * header_stats) -{ - RECDES recdes; + /* recheck the dealloc flag after all latches are acquired */ + if (pgbuf_has_prevent_dealloc (crt_watcher.pgptr)) + { + /* Even though we have fixed all required pages, somebody was doing a heap scan, and already reached our page. We + * cannot deallocate it. */ + vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, + "Candidate heap page %d|%d to remove has waiters.", page_vpid.volid, page_vpid.pageid); + goto error; + } - recdes.data = (char *) header_stats; - recdes.area_size = sizeof (*header_stats); - if (spage_get_record (thread_p, page_header, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, COPY) != S_SUCCESS) + /* if we are here, the page should not be accessed by any active or vacuum workers. Active workers are prevented + * from accessing it through heap scan, and direct references should not exist. + * the function would not be called if any other vacuum workers would try to access the page. */ + if (pgbuf_has_any_waiters (crt_watcher.pgptr)) { - assert_release (false); - return ER_FAILED; + assert (false); + vacuum_er_log_error (VACUUM_ER_LOG_HEAP, "%s", "Unexpected page waiters"); + goto error; } - return NO_ERROR; -} + /* all good, we can deallocate the page */ -/* - * heap_get_chain_ptr () - Get pointer to chain in heap page - * - * return : Pointer to chain in heap page - * page_heap (in) : Heap page - */ -STATIC_INLINE HEAP_CHAIN * -heap_get_chain_ptr (THREAD_ENTRY * thread_p, PAGE_PTR page_heap) -{ - RECDES recdes; + /* Start changes under the protection of system operation. */ + log_sysop_start (thread_p); + is_system_op_started = true; - if (spage_get_record (thread_p, page_heap, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) + /* Remove page from statistics in header page. */ + copy_recdes.data = PTR_ALIGN (copy_recdes_buffer, MAX_ALIGNMENT); + copy_recdes.area_size = sizeof (heap_hdr); + if (spage_get_record (thread_p, header_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, ©_recdes, COPY) != S_SUCCESS) { assert_release (false); - return NULL; + vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, + "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, page_vpid.pageid); + goto error; } - return (HEAP_CHAIN *) recdes.data; -} + memcpy (&heap_hdr, copy_recdes.data, sizeof (heap_hdr)); -/* - * heap_copy_chain () - Copy chain from heap page - * - * return : Error code - * page_heap (in) : Heap page - * chain (out) : Heap chain - */ -STATIC_INLINE int -heap_copy_chain (THREAD_ENTRY * thread_p, PAGE_PTR page_heap, HEAP_CHAIN * chain) -{ - RECDES recdes; + if (VPID_EQ (&heap_hdr.last_vpid, &page_vpid)) + { + VPID_COPY (&heap_hdr.last_vpid, &prev_vpid); + } + if (VPID_EQ (&prev_vpid, &header_vpid)) + { + /* Update next link. */ + VPID_COPY (&heap_hdr.next_vpid, &next_vpid); + } - if (spage_get_record (thread_p, page_heap, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) + /* Update header and log changes. */ + update_recdes.data = (char *) &heap_hdr; + update_recdes.length = sizeof (heap_hdr); + if (spage_update (thread_p, header_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &update_recdes) != SP_SUCCESS) { assert_release (false); - return ER_FAILED; + vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, + "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, page_vpid.pageid); + goto error; } - assert (recdes.length >= (int) sizeof (*chain)); - memcpy (chain, recdes.data, sizeof (*chain)); - return NO_ERROR; -} + log_append_undoredo_data2 (thread_p, RVHF_STATS, &hfid->vfid, header_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, + sizeof (heap_hdr), sizeof (heap_hdr), copy_recdes.data, update_recdes.data); + pgbuf_set_dirty (thread_p, header_watcher.pgptr, DONT_FREE); -/* - * check_supplemental_log () - check if appending supplemental log is available - * - * return : available or not - */ -STATIC_INLINE bool -check_supplemental_log (THREAD_ENTRY * thread_p, OID * classoid) -{ - /* The value for PRM_ID_SUPPLEMENTAL_LOG is required to be greater than 0 if supplemental log is to be appended - * no_supplemental_log is used to block duplicated supplemental logs. So this value should be false if supplemental log is to be appended - */ - if (prm_get_integer_value (PRM_ID_SUPPLEMENTAL_LOG) > 0 && !thread_p->no_supplemental_log && !OID_ISNULL (classoid)) + /* Update links in previous and next page. */ + + if (prev_watcher.pgptr != NULL) { - if (!oid_is_system_class (classoid)) + /* Next link in previous page. */ + assert (!VPID_EQ (&header_vpid, &prev_vpid)); + copy_recdes.area_size = sizeof (chain); + if (spage_get_record (thread_p, prev_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, ©_recdes, COPY) != + S_SUCCESS) { - return true; + assert_release (false); + vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, + "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, + page_vpid.pageid); + goto error; } + memcpy (&chain, copy_recdes.data, copy_recdes.length); + VPID_COPY (&chain.next_vpid, &next_vpid); + update_recdes.data = (char *) &chain; + update_recdes.length = sizeof (chain); + if (spage_update (thread_p, prev_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &update_recdes) != SP_SUCCESS) + { + assert_release (false); + vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, + "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, + page_vpid.pageid); + goto error; + } + log_append_undoredo_data2 (thread_p, RVHF_CHAIN, &hfid->vfid, prev_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, + sizeof (chain), sizeof (chain), copy_recdes.data, update_recdes.data); + pgbuf_set_dirty (thread_p, prev_watcher.pgptr, DONT_FREE); } - return false; -} - -/* - * heap_vpid_init_new () - FILE_INIT_PAGE_FUNC for heap non-header pages - * - * return : Error code - * thread_p (in) : Thread entry - * page (in) : New heap file page - * args (in) : HEAP_CHAIN * - */ -static int -heap_vpid_init_new (THREAD_ENTRY * thread_p, PAGE_PTR page, void *args) -{ - LOG_DATA_ADDR addr = LOG_DATA_ADDR_INITIALIZER; - HEAP_CHAIN chain; - RECDES recdes; - INT16 slotid; - int sp_success; - - assert (page != NULL); - assert (args != NULL); - - chain = *(HEAP_CHAIN *) args; /* get chain from args. it is already initialized */ - - /* initialize new page. */ - addr.pgptr = page; - pgbuf_set_page_ptype (thread_p, addr.pgptr, PAGE_HEAP); - - /* initialize the page and chain it with the previous last allocated page */ - spage_initialize (thread_p, addr.pgptr, heap_get_spage_type (), HEAP_MAX_ALIGN, SAFEGUARD_RVSPACE); - - recdes.area_size = recdes.length = sizeof (chain); - recdes.type = REC_HOME; - recdes.data = (char *) &chain; - - sp_success = spage_insert (thread_p, addr.pgptr, &recdes, &slotid); - if (sp_success != SP_SUCCESS || slotid != HEAP_HEADER_AND_CHAIN_SLOTID) + if (next_watcher.pgptr != NULL) { - assert (false); + /* Previous link in next page. */ + copy_recdes.area_size = sizeof (chain); + if (spage_get_record (thread_p, next_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, ©_recdes, COPY) != + S_SUCCESS) + { + assert_release (false); + vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, + "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, + page_vpid.pageid); + goto error; + } + memcpy (&chain, copy_recdes.data, sizeof (chain)); + VPID_COPY (&chain.prev_vpid, &prev_vpid); + update_recdes.data = (char *) &chain; + update_recdes.length = sizeof (chain); - /* initialization has failed !! */ - if (sp_success != SP_SUCCESS) + if (spage_update (thread_p, next_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &update_recdes) != SP_SUCCESS) { - er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + assert_release (false); + vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, + "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, + page_vpid.pageid); + goto error; } - return ER_FAILED; + log_append_undoredo_data2 (thread_p, RVHF_CHAIN, &hfid->vfid, next_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, + sizeof (chain), sizeof (chain), copy_recdes.data, update_recdes.data); + pgbuf_set_dirty (thread_p, next_watcher.pgptr, DONT_FREE); } - log_append_undoredo_data (thread_p, RVHF_NEWPAGE, &addr, 0, recdes.length, NULL, recdes.data); - pgbuf_set_dirty (thread_p, addr.pgptr, DONT_FREE); - return NO_ERROR; -} - -/* - * heap_vpid_alloc () - allocate, fetch, and initialize a new page - * return: error code - * hfid(in): Object heap file identifier - * hdr_pgptr(in): The heap page header - * heap_hdr(in): The heap header structure - * scan_cache(in): Scan cache - * new_pg_watcher(out): watcher for new page. - * - * Note: Allocate and initialize a new heap page. The heap header is - * updated to reflect a newly allocated best space page and - * the set of best space pages information may be updated to - * include the previous best1 space page. - */ -static int -heap_vpid_alloc (THREAD_ENTRY * thread_p, const HFID * hfid, PAGE_PTR hdr_pgptr, HEAP_HDR_STATS * heap_hdr, - HEAP_SCANCACHE * scan_cache, PGBUF_WATCHER * new_pg_watcher) -{ - VPID vpid; /* Volume and page identifiers */ - LOG_DATA_ADDR addr = LOG_DATA_ADDR_INITIALIZER; /* Address of logging data */ - int best; - VPID last_vpid; - PGBUF_WATCHER last_pg_watcher; - HEAP_CHAIN new_page_chain; - HEAP_HDR_STATS heap_hdr_prev = *heap_hdr; - - int error_code = NO_ERROR; - - assert (PGBUF_IS_CLEAN_WATCHER (new_pg_watcher)); + /* Unfix current page. */ + pgbuf_ordered_unfix_and_init (thread_p, *page_ptr, &crt_watcher); + /* Deallocate current page. */ + if (file_dealloc (thread_p, &hfid->vfid, &page_vpid, FILE_HEAP) != NO_ERROR) + { + ASSERT_ERROR (); + vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, + "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, page_vpid.pageid); + goto error; + } - PGBUF_INIT_WATCHER (&last_pg_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); - addr.vfid = &hfid->vfid; - addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; + /* Finished. */ + log_sysop_commit (thread_p); + is_system_op_started = false; - error_code = heap_get_last_page (thread_p, hfid, heap_hdr, scan_cache, &last_vpid, &last_pg_watcher); - if (error_code != NO_ERROR) + /* Unfix all pages. */ + if (next_watcher.pgptr != NULL) { - ASSERT_ERROR (); - return error_code; + pgbuf_ordered_unfix (thread_p, &next_watcher); } - if (last_pg_watcher.pgptr == NULL) + if (prev_watcher.pgptr != NULL) { - /* something went wrong, return error */ - assert_release (false); - return ER_FAILED; + pgbuf_ordered_unfix (thread_p, &prev_watcher); } - assert (!VPID_ISNULL (&last_vpid)); - - log_sysop_start (thread_p); + pgbuf_ordered_unfix (thread_p, &header_watcher); - /* init chain for new page */ - new_page_chain.class_oid = heap_hdr->class_oid; - new_page_chain.prev_vpid = last_vpid; - VPID_SET_NULL (&new_page_chain.next_vpid); - new_page_chain.max_mvccid = MVCCID_NULL; - new_page_chain.flags = 0; - HEAP_PAGE_SET_VACUUM_STATUS (&new_page_chain, HEAP_PAGE_VACUUM_NONE); + /* Page removed successfully. */ + vacuum_er_log (VACUUM_ER_LOG_HEAP, "Successfully remove heap page %d|%d.", page_vpid.volid, page_vpid.pageid); + return true; - /* allocate new page and initialize it */ - error_code = file_alloc (thread_p, &hfid->vfid, heap_vpid_init_new, &new_page_chain, &vpid, NULL); - if (error_code != NO_ERROR) +error: + if (is_system_op_started) { - ASSERT_ERROR (); - goto error; + log_sysop_abort (thread_p); + } + if (next_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &next_watcher); + } + if (prev_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &prev_watcher); + } + if (header_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &header_watcher); + } + if (*page_ptr != NULL) + { + if (*page_ptr != crt_watcher.pgptr) + { + /* jumped to here while fixing pages */ + assert (crt_watcher.page_was_unfixed); + *page_ptr = crt_watcher.pgptr; + } + assert (crt_watcher.pgptr == *page_ptr); + pgbuf_ordered_unfix_and_init (thread_p, *page_ptr, &crt_watcher); + } + else + { + assert (crt_watcher.pgptr == NULL); } + /* Page was not removed. */ + return false; +} - /* add link from previous last page */ - addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; +/* + * heap_vpid_next () - Find next page of heap + * return: NO_ERROR + * hfid(in): Object heap file identifier + * pgptr(in): Current page pointer + * next_vpid(in/out): Next volume-page identifier + * + * Note: Find the next page of heap file. + */ +int +heap_vpid_next (THREAD_ENTRY * thread_p, const HFID * hfid, PAGE_PTR pgptr, VPID * next_vpid) +{ + HEAP_CHAIN *chain; /* Chain to next and prev page */ + HEAP_HDR_STATS *heap_hdr; /* Header of heap file */ + RECDES recdes; /* Record descriptor to page header */ + int ret = NO_ERROR; - if (last_pg_watcher.pgptr == hdr_pgptr) +#if !defined (NDEBUG) + (void) pgbuf_check_page_ptype (thread_p, pgptr, PAGE_HEAP); +#endif /* !NDEBUG */ + + /* Get either the heap header or chain record */ + if (spage_get_record (thread_p, pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) { - heap_hdr->next_vpid = vpid; - /* will be logged later */ + /* Unable to get header/chain record for the given page */ + VPID_SET_NULL (next_vpid); + ret = ER_FAILED; } else { - HEAP_CHAIN *chain, chain_prev; - - /* get chain */ - chain = heap_get_chain_ptr (thread_p, last_pg_watcher.pgptr); - if (chain == NULL) + pgbuf_get_vpid (pgptr, next_vpid); + /* Is this the header page ? */ + if (next_vpid->pageid == hfid->hpgid && next_vpid->volid == hfid->vfid.volid) { - assert_release (false); - error_code = ER_FAILED; - goto error; + heap_hdr = (HEAP_HDR_STATS *) recdes.data; + *next_vpid = heap_hdr->next_vpid; + } + else + { + chain = (HEAP_CHAIN *) recdes.data; + *next_vpid = chain->next_vpid; } - /* update chain */ - /* save old chain for logging */ - chain_prev = *chain; - /* change next link */ - chain->next_vpid = vpid; - - /* log change */ - addr.pgptr = last_pg_watcher.pgptr; - log_append_undoredo_data (thread_p, RVHF_CHAIN, &addr, sizeof (HEAP_CHAIN), sizeof (HEAP_CHAIN), &chain_prev, - chain); - pgbuf_set_dirty (thread_p, addr.pgptr, DONT_FREE); } - pgbuf_ordered_unfix (thread_p, &last_pg_watcher); + return ret; +} - /* now update header statistics for best1 space page. the changes to the statistics are not logged. */ - /* last page hint */ - heap_hdr->estimates.last_vpid = vpid; - heap_hdr->estimates.num_pages++; +/* + * heap_vpid_prev () - Find previous page of heap + * return: NO_ERROR + * hfid(in): Object heap file identifier + * pgptr(in): Current page pointer + * prev_vpid(in/out): Previous volume-page identifier + * + * Note: Find the previous page of heap file. + */ +int +heap_vpid_prev (THREAD_ENTRY * thread_p, const HFID * hfid, PAGE_PTR pgptr, VPID * prev_vpid) +{ + HEAP_CHAIN *chain; /* Chain to next and prev page */ + RECDES recdes; /* Record descriptor to page header */ + int ret = NO_ERROR; + +#if !defined (NDEBUG) + (void) pgbuf_check_page_ptype (thread_p, pgptr, PAGE_HEAP); +#endif /* !NDEBUG */ - best = heap_hdr->estimates.head; - heap_hdr->estimates.head = HEAP_STATS_NEXT_BEST_INDEX (best); - if (VPID_ISNULL (&heap_hdr->estimates.best[best].vpid)) + /* Get either the header or chain record */ + if (spage_get_record (thread_p, pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) { - heap_hdr->estimates.num_high_best++; - assert (heap_hdr->estimates.num_high_best <= HEAP_NUM_BEST_SPACESTATS); + /* Unable to get header/chain record for the given page */ + VPID_SET_NULL (prev_vpid); + ret = ER_FAILED; } else { - if (heap_hdr->estimates.best[best].freespace > HEAP_DROP_FREE_SPACE) + pgbuf_get_vpid (pgptr, prev_vpid); + /* Is this the header page ? */ + if (prev_vpid->pageid == hfid->hpgid && prev_vpid->volid == hfid->vfid.volid) + { + VPID_SET_NULL (prev_vpid); + } + else { - heap_hdr->estimates.num_other_high_best++; - heap_stats_put_second_best (heap_hdr, &heap_hdr->estimates.best[best].vpid); + chain = (HEAP_CHAIN *) recdes.data; + *prev_vpid = chain->prev_vpid; } } - heap_hdr->estimates.best[best].vpid = vpid; - heap_hdr->estimates.best[best].freespace = DB_PAGESIZE; + return ret; +} + +/* + * heap_manager_initialize () - + * return: NO_ERROR + * + * Note: Initialization process of the heap file module. Find the + * maximum size of an object that can be inserted in the heap. + * Objects that overpass this size are stored in overflow. + */ +int +heap_manager_initialize (void) +{ + int ret; + + heap_Maxslotted_reclength = + DB_ALIGN_BELOW (spage_max_record_size () - DB_ALIGN (sizeof (HEAP_CHAIN), HEAP_MAX_ALIGN) - + 8 * SPAGE_SLOT_SIZE /* margin */ , HEAP_MAX_ALIGN); + heap_Slotted_overhead = SPAGE_SLOT_SIZE; - if (prm_get_integer_value (PRM_ID_HF_MAX_BESTSPACE_ENTRIES) > 0) + /* Initialize the class representation cache */ + ret = heap_chnguess_initialize (); + if (ret != NO_ERROR) { - (void) heap_stats_add_bestspace (thread_p, hfid, &vpid, heap_hdr->estimates.best[best].freespace); + return ret; } - /* we really have nothing to lose from logging stats here and also it is good to have a certain last VPID. */ - addr.pgptr = hdr_pgptr; - log_append_undoredo_data (thread_p, RVHF_STATS, &addr, sizeof (HEAP_HDR_STATS), sizeof (HEAP_HDR_STATS), - &heap_hdr_prev, heap_hdr); - log_sysop_commit (thread_p); - - /* fix new page */ - new_pg_watcher->pgptr = heap_scan_pb_lock_and_fetch (thread_p, &vpid, OLD_PAGE, X_LOCK, scan_cache, new_pg_watcher); - if (new_pg_watcher->pgptr == NULL) + ret = heap_classrepr_initialize_cache (); + if (ret != NO_ERROR) { - ASSERT_ERROR_AND_SET (error_code); - return error_code; + return ret; } - return NO_ERROR; + /* Initialize class OID->HFID cache */ + ret = heap_initialize_hfid_table (); -error: - assert (error_code != NO_ERROR); + return ret; +} - if (last_pg_watcher.pgptr != NULL) +/* + * heap_manager_finalize () - Terminate the heap manager + * return: NO_ERROR + * Note: Deallocate any cached structure. + */ +int +heap_manager_finalize (void) +{ + int ret; + + ret = heap_chnguess_finalize (); + if (ret != NO_ERROR) { - pgbuf_ordered_unfix (thread_p, &last_pg_watcher); + return ret; } - log_sysop_abort (thread_p); - return error_code; + ret = heap_classrepr_finalize_cache (); + if (ret != NO_ERROR) + { + return ret; + } + + heap_finalize_hfid_table (); + + return ret; } /* - * heap_vpid_remove () - Deallocate a heap page - * return: rm_vpid on success or NULL on error - * hfid(in): Object heap file identifier - * heap_hdr(in): The heap header stats - * rm_vpid(in): Page to remove - * - * Note: The given page is removed from the heap. The linked list of heap - * pages is updated to remove this page, and the heap header may - * be updated if this page was part of the statistics. + * heap_bestspace_clear_candidates () - Clear persistent bestspace candidates. + * return: + * heap_hdr(in/out): Heap header */ -static VPID * -heap_vpid_remove (THREAD_ENTRY * thread_p, const HFID * hfid, HEAP_HDR_STATS * heap_hdr, VPID * rm_vpid) +static void +heap_bestspace_clear_candidates (cubstorage::bestspace_entry * candidates, std::size_t * num_candidates, + std::size_t max_candidates) { - RECDES rm_recdes; /* Record descriptor which holds the chain of the page to be removed */ - HEAP_CHAIN *rm_chain; /* Chain information of the page to be removed */ - VPID vpid; /* Real identifier of previous page */ - LOG_DATA_ADDR addr; /* Log address of previous page */ - RECDES recdes; /* Record descriptor to page header */ - HEAP_CHAIN chain; /* Chain to next and prev page */ - int sp_success; - int i; - PGBUF_WATCHER rm_pg_watcher; - PGBUF_WATCHER prev_pg_watcher; - - PGBUF_INIT_WATCHER (&rm_pg_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); - PGBUF_INIT_WATCHER (&prev_pg_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + std::size_t i; - /* - * Make sure that this is not the header page since the header page cannot - * be removed. If the header page is removed.. the heap is gone - */ + assert (candidates); - if (rm_vpid->pageid == hfid->hpgid && rm_vpid->volid == hfid->vfid.volid) + if (num_candidates) { - er_log_debug (ARG_FILE_LINE, "heap_vpid_remove: Trying to remove header page = %d|%d of heap file = %d|%d|%d", - (int) rm_vpid->volid, rm_vpid->pageid, (int) hfid->vfid.volid, hfid->vfid.fileid, hfid->hpgid); - er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); - goto error; + *num_candidates = 0; + } + for (i = 0; i < max_candidates; i++) + { + candidates[i].set_null (); } +} - /* Get the chain record */ - rm_pg_watcher.pgptr = heap_scan_pb_lock_and_fetch (thread_p, rm_vpid, OLD_PAGE, X_LOCK, NULL, &rm_pg_watcher); - if (rm_pg_watcher.pgptr == NULL) +/* + * heap_bestspace_add_candidate () - Add one persistent bestspace candidate in descending freespace order. + * return: + * heap_hdr(in/out): Heap header + * candidate(in): Candidate to add + */ +static void +heap_bestspace_add_candidate (cubstorage::bestspace_entry * candidates, std::size_t * num_candidates, + std::size_t max_candidates, cubstorage::bestspace_entry * candidate) +{ + std::size_t pos; + + assert (candidates && candidate); + assert (*num_candidates <= max_candidates); + + if (*num_candidates == max_candidates && candidate->freespace <= candidates[max_candidates - 1].freespace) { - /* Look like a system error. Unable to obtain chain header record */ - goto error; + return; } - if (spage_get_record (thread_p, rm_pg_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &rm_recdes, PEEK) != S_SUCCESS) + pos = MIN (*num_candidates, max_candidates - 1); + while (pos > 0 && candidates[pos - 1].freespace < candidate->freespace) { - /* Look like a system error. Unable to obtain chain header record */ - goto error; + if (pos < max_candidates) + { + candidates[pos] = candidates[pos - 1]; + } + pos--; } - rm_chain = (HEAP_CHAIN *) rm_recdes.data; + candidates[pos] = *candidate; + if (*num_candidates < max_candidates) + { + (*num_candidates)++; + } +} - /* - * UPDATE PREVIOUS PAGE - * - * Update chain next field of previous last page - * If previous page is the heap header page, it contains a heap header - * instead of a chain. - */ +/* + * heap_bestspace_fix_page () - Ordered-fix a persistent bestspace metadata page. + * return: NO_ERROR if successful, error code otherwise + * hfid(in): + * vpid(in): + * page_watcher(out): + */ +static int +heap_bestspace_fix_page (THREAD_ENTRY * thread_p, const HFID * hfid, const VPID * vpid, PGBUF_WATCHER * page_watcher) +{ + int error; - vpid = rm_chain->prev_vpid; - addr.vfid = &hfid->vfid; - addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; + assert (thread_p); + assert (hfid); + assert (vpid); + assert (page_watcher); - prev_pg_watcher.pgptr = heap_scan_pb_lock_and_fetch (thread_p, &vpid, OLD_PAGE, X_LOCK, NULL, &prev_pg_watcher); - if (prev_pg_watcher.pgptr == NULL) + PGBUF_INIT_WATCHER (page_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + error = pgbuf_ordered_fix (thread_p, vpid, OLD_PAGE, PGBUF_LATCH_WRITE, page_watcher); + if (error != NO_ERROR) { - /* something went wrong, return */ - goto error; + ASSERT_ERROR_AND_SET (error); + return error; } - if (rm_pg_watcher.page_was_unfixed) + if (!heap_page_is_bestspace (thread_p, page_watcher->pgptr)) { - /* TODO : unexpected: need to reconsider the algorithm, if this is an ordinary case */ - er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PB_UNEXPECTED_PAGE_REFIX, 4, rm_vpid->volid, rm_vpid->pageid, - vpid.volid, vpid.pageid); - goto error; + pgbuf_ordered_unfix (thread_p, page_watcher); + + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + return ER_GENERIC_ERROR; } - /* - * Make sure that the page to be removed is not referenced on the heap - * statistics - */ + return NO_ERROR; +} + +/* + * heap_create_bestspace () - Create a bestspace for heap file + * return: + * hfid(in/out): + * class_oid(in): + */ +static int +heap_create_bestspace (THREAD_ENTRY * thread_p, HFID * hfid, HEAP_HDR_STATS * heap_hdr, + cubstorage::bestspace_entry * first_entry) +{ + char pgbuf[IO_MAX_PAGE_SIZE + MAX_ALIGNMENT]; + LOG_DATA_ADDR addr = LOG_DATA_ADDR_INITIALIZER; + cubstorage::bestspace_entry * entries; + PAGE_PTR page_ptr = NULL; + HEAP_CHAIN page_chain; + VPID header_vpid; + VPID null_vpid; + const VPID *prev_vpid; + const VPID *next_vpid; + PGBUF_WATCHER chain_page_watcher; + PGBUF_WATCHER page_watcher; + std::size_t max_entries, max_pages; + int max_shards, min_shards; + int remaining_entries; + int entries_per_page; + int page_capacity; + int entries_count; + int sp_success, error; + RECDES recdes; + INT16 slotid; + int i, j; + assert (thread_p != NULL); + assert (hfid != NULL); assert (heap_hdr != NULL); - /* - * We cannot break in the following loop since a best page could be - * duplicated - */ - for (i = 0; i < HEAP_NUM_BEST_SPACESTATS; i++) + /* candidates */ + heap_bestspace_clear_candidates (heap_hdr->bestspace.candidates, &heap_hdr->bestspace.num_candidates, + cubstorage::bestspace::MAX_CANDIDATES_QUEUE_SIZE); + + /* shards */ + heap_hdr->bestspace.num_shards = prm_get_integer_value (PRM_ID_BESTSPACE_SHARD_COUNT); + + /* calculate the needed pages */ + error = sysprm_get_range (PRM_ID_BESTSPACE_SHARD_COUNT, &min_shards, &max_shards); + if (error != NO_ERROR) { - if (VPID_EQ (&heap_hdr->estimates.best[i].vpid, rm_vpid)) - { - VPID_SET_NULL (&heap_hdr->estimates.best[i].vpid); - heap_hdr->estimates.best[i].freespace = 0; - heap_hdr->estimates.head = i; - } + assert (false); + return ER_FAILED; } + max_entries = max_shards * cubstorage::bestspace::ENTRIES_PER_SHARD; - if (VPID_EQ (&heap_hdr->estimates.last_vpid, rm_vpid)) + page_capacity = spage_max_record_size () - DB_ALIGN (sizeof (HEAP_CHAIN), HEAP_MAX_ALIGN) - SPAGE_SLOT_SIZE; + entries_per_page = page_capacity / sizeof (cubstorage::bestspace_entry); + if (entries_per_page <= 0) { - /* If the page is the last page of the heap file, update the hint */ - heap_hdr->estimates.last_vpid = rm_chain->prev_vpid; + assert (false); + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNABLE_TO_CREATE_HEAP, 1, + fileio_get_volume_label (hfid->vfid.volid, PEEK)); + return ER_HEAP_UNABLE_TO_CREATE_HEAP; } - /* - * Is previous page the header page ? - */ - if (vpid.pageid == hfid->hpgid && vpid.volid == hfid->vfid.volid) + /* bestspace pages */ + max_pages = cubstorage::bestspace::MAX_SHARD_PAGE_COUNT; + heap_hdr->bestspace.num_pages = (max_entries + entries_per_page - 1) / entries_per_page; + if (heap_hdr->bestspace.num_pages > max_pages) { - /* - * PREVIOUS PAGE IS THE HEADER PAGE. - * It contains a heap header instead of a chain record - */ - heap_hdr->next_vpid = rm_chain->next_vpid; + assert (false); + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNABLE_TO_CREATE_HEAP, 1, + fileio_get_volume_label (hfid->vfid.volid, PEEK)); + return ER_HEAP_UNABLE_TO_CREATE_HEAP; } - else + for (i = 0; i < (int) max_pages; i++) { - /* - * PREVIOUS PAGE IS NOT THE HEADER PAGE. - * It contains a chain... - * We need to make sure that there is not references to the page to delete - * in the statistics of the heap header - */ - - /* NOW check the PREVIOUS page */ - /* Get the chain record */ - if (spage_get_record (thread_p, prev_pg_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) - { - /* Look like a system error. Unable to obtain header record */ - goto error; - } - - /* Copy the chain record to memory.. so we can log the changes */ - memcpy (&chain, recdes.data, sizeof (chain)); - - /* Modify the chain of the previous page in memory */ - chain.next_vpid = rm_chain->next_vpid; + VPID_SET_NULL (&heap_hdr->bestspace.pages[i]); + } + page_chain.class_oid = heap_hdr->class_oid; + VPID_SET_NULL (&page_chain.prev_vpid); + VPID_SET_NULL (&page_chain.next_vpid); + page_chain.max_mvccid = MVCCID_NULL; + page_chain.flags = 0; + HEAP_PAGE_SET_VACUUM_STATUS (&page_chain, HEAP_PAGE_VACUUM_NONE); + HEAP_PAGE_SET_BESTSPACE (&page_chain); + error = + file_alloc_multiple (thread_p, &hfid->vfid, heap_vpid_init_new, &page_chain, heap_hdr->bestspace.num_pages, + heap_hdr->bestspace.pages); + if (error != NO_ERROR) + { + ASSERT_ERROR (); + return error; + } - /* Log the desired changes.. and then change the header */ - addr.pgptr = prev_pg_watcher.pgptr; - log_append_undoredo_data (thread_p, RVHF_CHAIN, &addr, sizeof (chain), sizeof (chain), recdes.data, &chain); + header_vpid.volid = hfid->vfid.volid; + header_vpid.pageid = hfid->hpgid; + VPID_SET_NULL (&null_vpid); - /* Now change the record */ - recdes.area_size = recdes.length = sizeof (chain); - recdes.type = REC_HOME; - recdes.data = (char *) &chain; + for (i = 0; i < (int) heap_hdr->bestspace.num_pages; i++) + { + prev_vpid = (i == 0) ? &header_vpid : &heap_hdr->bestspace.pages[i - 1]; + next_vpid = (i == (int) heap_hdr->bestspace.num_pages - 1) ? &null_vpid : &heap_hdr->bestspace.pages[i + 1]; - sp_success = spage_update (thread_p, prev_pg_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes); - if (sp_success != SP_SUCCESS) + error = + heap_add_chain_links (thread_p, hfid, &heap_hdr->bestspace.pages[i], next_vpid, prev_vpid, + &chain_page_watcher, false, false); + if (error != NO_ERROR) { - /* - * This look like a system error, size did not change, so why did it - * fail - */ - if (sp_success != SP_ERROR) - { - er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); - } - goto error; + ASSERT_ERROR (); + return error; } - } - /* Now set dirty, free and unlock the previous page */ - pgbuf_ordered_set_dirty_and_free (thread_p, &prev_pg_watcher); - - /* - * UPDATE NEXT PAGE - * - * Update chain previous field of next page - */ + assert (heap_hdr->bestspace.num_pages > 0); + heap_hdr->next_vpid = heap_hdr->bestspace.pages[0]; + heap_hdr->last_vpid = heap_hdr->bestspace.pages[heap_hdr->bestspace.num_pages - 1]; - if (!(VPID_ISNULL (&rm_chain->next_vpid))) + /* insert entries */ + entries = (cubstorage::bestspace_entry *) PTR_ALIGN (pgbuf, MAX_ALIGNMENT); + remaining_entries = max_entries; + for (i = 0; i < (int) heap_hdr->bestspace.num_pages; i++) { - vpid = rm_chain->next_vpid; - addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; - - prev_pg_watcher.pgptr = heap_scan_pb_lock_and_fetch (thread_p, &vpid, OLD_PAGE, X_LOCK, NULL, &prev_pg_watcher); - if (prev_pg_watcher.pgptr == NULL) + entries_count = MIN (remaining_entries, entries_per_page); + assert (entries_count > 0); + for (j = 0; j < entries_count; j++) { - /* something went wrong, return */ - goto error; + entries[j].set_null (); } - /* Get the chain record */ - if (spage_get_record (thread_p, prev_pg_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) + /* first entry is the heap header page when the bestspace being created */ + if (i == 0) { - /* Look like a system error. Unable to obtain header record */ - goto error; + entries[0] = *first_entry; } - /* Copy the chain record to memory.. so we can log the changes */ - memcpy (&chain, recdes.data, sizeof (chain)); - - /* Modify the chain of the next page in memory */ - chain.prev_vpid = rm_chain->prev_vpid; - - /* Log the desired changes.. and then change the header */ - addr.pgptr = prev_pg_watcher.pgptr; - log_append_undoredo_data (thread_p, RVHF_CHAIN, &addr, sizeof (chain), sizeof (chain), recdes.data, &chain); + error = heap_bestspace_fix_page (thread_p, hfid, &heap_hdr->bestspace.pages[i], &page_watcher); + if (error != NO_ERROR) + { + return error; + } + page_ptr = page_watcher.pgptr; - /* Now change the record */ - recdes.area_size = recdes.length = sizeof (chain); + recdes.area_size = entries_count * sizeof (cubstorage::bestspace_entry); + recdes.length = recdes.area_size; recdes.type = REC_HOME; - recdes.data = (char *) &chain; + recdes.data = (char *) entries; - sp_success = spage_update (thread_p, prev_pg_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes); - if (sp_success != SP_SUCCESS) + /* insert into slotted page */ + sp_success = spage_insert (thread_p, page_ptr, &recdes, &slotid); + if (sp_success != SP_SUCCESS || slotid != HEAP_BESTSPACE_ENTRIES_SLOTID) { - /* - * This look like a system error, size did not change, so why did it - * fail - */ - if (sp_success != SP_ERROR) + if (sp_success == SP_SUCCESS) { er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + error = ER_GENERIC_ERROR; } - goto error; + else if (sp_success != SP_ERROR) + { + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNABLE_TO_CREATE_HEAP, 1, + fileio_get_volume_label (hfid->vfid.volid, PEEK)); + error = ER_HEAP_UNABLE_TO_CREATE_HEAP; + } + else + { + ASSERT_ERROR_AND_SET (error); + } + pgbuf_ordered_unfix (thread_p, &page_watcher); + page_ptr = NULL; + return error; } - /* Now set dirty, free and unlock the next page */ - - pgbuf_ordered_set_dirty_and_free (thread_p, &prev_pg_watcher); - } - - /* Free the page to be deallocated and deallocate the page */ - pgbuf_ordered_unfix (thread_p, &rm_pg_watcher); - - if (file_dealloc (thread_p, &hfid->vfid, rm_vpid, FILE_HEAP) != NO_ERROR) - { - ASSERT_ERROR (); - goto error; - } - - (void) heap_stats_del_bestspace_by_vpid (thread_p, rm_vpid); - - return rm_vpid; + /* undo deallocates newly created bestspace pages with the heap file. */ + /* RVHF_INSERT can be seemed this data is target of HA, so uses RVHF_INSERT_NEWHOME to avoid this */ + addr.vfid = &hfid->vfid; + addr.pgptr = page_ptr; + addr.offset = slotid; + log_append_redo_recdes (thread_p, RVHF_INSERT_NEWHOME, &addr, &recdes); + pgbuf_set_dirty (thread_p, page_ptr, DONT_FREE); + pgbuf_ordered_unfix (thread_p, &page_watcher); + page_ptr = NULL; -error: - if (rm_pg_watcher.pgptr != NULL) - { - pgbuf_ordered_unfix (thread_p, &rm_pg_watcher); - } - if (addr.pgptr != NULL) - { - pgbuf_ordered_unfix (thread_p, &prev_pg_watcher); + remaining_entries -= entries_count; } + assert (remaining_entries == 0); - return NULL; + return NO_ERROR; } /* - * heap_remove_page_on_vacuum () - Remove heap page from heap file during - * vacuum process. Function is trying to - * be as least intrusive as possible and all - * required pages are latched conditionally. - * Give up on any failed operation. - * - * return : True if page was deallocated, false if not. - * thread_p (in) : Thread entry. - * page_ptr (in) : Pointer to page being deallocated. - * hfid (in) : Heap file identifier. + * heap_update_bestspace_chain () - Update chain records of persistent bestspace pages. + * return: NO_ERROR if successful, error code otherwise */ -bool -heap_remove_page_on_vacuum (THREAD_ENTRY * thread_p, PAGE_PTR * page_ptr, HFID * hfid) +static int +heap_update_bestspace_chain (THREAD_ENTRY * thread_p, const OID * class_oid, const HFID * hfid, const VPID * pages, + int num_pages) { - VPID page_vpid = VPID_INITIALIZER; /* VPID of page being removed. */ - VPID prev_vpid = VPID_INITIALIZER; /* VPID of previous page. */ - VPID next_vpid = VPID_INITIALIZER; /* VPID of next page. */ - VPID header_vpid = VPID_INITIALIZER; /* VPID of heap header page. */ - HEAP_HDR_STATS heap_hdr; /* Header header & stats. */ - HEAP_CHAIN chain; /* Heap page header used to read and update page links. */ - RECDES copy_recdes; /* Record to copy header from pages. */ - /* Buffer used for copy record. */ - char copy_recdes_buffer[MAX (sizeof (HEAP_CHAIN), sizeof (HEAP_HDR_STATS)) + MAX_ALIGNMENT]; - RECDES update_recdes; /* Record containing updated header data. */ - int i = 0; /* Iterator. */ - bool is_system_op_started = false; /* Set to true once system operation is started. */ - PGBUF_WATCHER crt_watcher; /* Watcher for current page. */ - PGBUF_WATCHER header_watcher; /* Watcher for header page. */ - PGBUF_WATCHER prev_watcher; /* Watcher for previous page. */ - PGBUF_WATCHER next_watcher; /* Watcher for next page. */ + PGBUF_WATCHER page_watcher; + PAGE_PTR page_ptr = NULL; + HEAP_CHAIN chain; + LOG_DATA_ADDR addr = LOG_DATA_ADDR_INITIALIZER; + RECDES recdes; + int error_code = NO_ERROR; + int i; - /* Assert expected arguments. */ - /* Page to remove must be fixed. */ - assert (page_ptr != NULL && *page_ptr != NULL); - /* Page to remove must be empty. */ - assert (spage_number_of_records (*page_ptr) <= 1); - /* Heap file identifier must be known. */ - assert (hfid != NULL && !HFID_IS_NULL (hfid)); + assert (thread_p); + assert (class_oid && hfid); + assert (pages && num_pages > 0); - /* Get VPID of page to be removed. */ - pgbuf_get_vpid (*page_ptr, &page_vpid); + addr.vfid = &hfid->vfid; - if (page_vpid.pageid == hfid->hpgid && page_vpid.volid == hfid->vfid.volid) + for (i = 0; i < num_pages; i++) { - /* Cannot remove heap file header page. */ - return false; - } - - /* Use page watchers to do the ordered fix. */ - PGBUF_INIT_WATCHER (&crt_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); - PGBUF_INIT_WATCHER (&header_watcher, PGBUF_ORDERED_HEAP_HDR, hfid); - PGBUF_INIT_WATCHER (&prev_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); - PGBUF_INIT_WATCHER (&next_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + if (VPID_ISNULL (&pages[i])) + { + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + error_code = ER_GENERIC_ERROR; + goto error_exit; + } - /* Current page is already fixed. Just attach watcher. */ - pgbuf_attach_watcher (thread_p, *page_ptr, PGBUF_LATCH_WRITE, hfid, &crt_watcher); + error_code = heap_bestspace_fix_page (thread_p, hfid, &pages[i], &page_watcher); + if (error_code != NO_ERROR) + { + goto error_exit; + } + page_ptr = page_watcher.pgptr; - /* Header vpid. */ - header_vpid.volid = hfid->vfid.volid; - header_vpid.pageid = hfid->hpgid; + if (heap_copy_chain (thread_p, page_ptr, &chain) != NO_ERROR) + { + error_code = ER_FAILED; + goto error_exit; + } - /* Fix required pages: Heap header page. Previous page (always exists). Next page (if exists). */ + COPY_OID (&chain.class_oid, class_oid); + chain.max_mvccid = MVCCID_NULL; + chain.flags = 0; + HEAP_PAGE_SET_VACUUM_STATUS (&chain, HEAP_PAGE_VACUUM_NONE); + HEAP_PAGE_SET_BESTSPACE (&chain); - /* Fix header page first, because it has higher priority. */ - if (pgbuf_ordered_fix (thread_p, &header_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, &header_watcher) != NO_ERROR) - { - /* Give up. */ - vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, - "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, page_vpid.pageid); - goto error; - } - assert (header_watcher.pgptr != NULL); + recdes.area_size = recdes.length = sizeof (chain); + recdes.type = REC_HOME; + recdes.data = (char *) &chain; - if (crt_watcher.page_was_unfixed) - { - *page_ptr = crt_watcher.pgptr; /* home was refixed */ + addr.pgptr = page_ptr; + addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; + log_append_redo_data (thread_p, RVHF_CHAIN, &addr, sizeof (chain), &chain); + if (spage_update (thread_p, page_ptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes) != SP_SUCCESS) + { + error_code = ER_FAILED; + goto error_exit; + } + + pgbuf_set_dirty (thread_p, page_ptr, DONT_FREE); + pgbuf_ordered_unfix (thread_p, &page_watcher); + page_ptr = NULL; } - /* Get previous and next page VPID's. */ - if (heap_vpid_prev (thread_p, hfid, *page_ptr, &prev_vpid) != NO_ERROR - || heap_vpid_next (thread_p, hfid, *page_ptr, &next_vpid) != NO_ERROR) + return NO_ERROR; + +error_exit: + if (page_ptr != NULL) { - /* Give up. */ - vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, - "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, page_vpid.pageid); - goto error; + pgbuf_ordered_unfix (thread_p, &page_watcher); + page_ptr = NULL; } - /* Fix previous page if it is not same as header. */ - if (!VPID_ISNULL (&prev_vpid) && !VPID_EQ (&prev_vpid, &header_vpid)) + return error_code; +} + +/* + * heap_update_bestspace_entries () - update persistent bestspace entries. + * return: + */ +static int +heap_update_bestspace_entries (THREAD_ENTRY * thread_p, const HFID * hfid, const VPID * pages, int num_pages, + const cubstorage::bestspace_entry * update_entries, int num_update_entries) +{ + char pgbuf[IO_MAX_PAGE_SIZE + MAX_ALIGNMENT]; + LOG_DATA_ADDR addr = LOG_DATA_ADDR_INITIALIZER; + cubstorage::bestspace_entry * entries = NULL; + PGBUF_WATCHER page_watcher; + PAGE_PTR page_ptr = NULL; + RECDES recdes; + int num_entries_in_page, num_entries_to_copy; + int remaining_entries; + int entry_index = 0; + int error_code = NO_ERROR; + int i, j; + + assert (update_entries && num_update_entries > 0); + + addr.vfid = &hfid->vfid; + + remaining_entries = num_update_entries; + for (i = 0; i < (int) num_pages; i++) { - if (pgbuf_ordered_fix (thread_p, &prev_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, &prev_watcher) != NO_ERROR) + if (VPID_ISNULL (&pages[i])) { - /* Give up. */ - vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, - "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, - page_vpid.pageid); - goto error; + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + error_code = ER_GENERIC_ERROR; + goto error_exit; } - } - /* Fix next page if current page is not last in heap file. */ - if (!VPID_ISNULL (&next_vpid)) - { - if (pgbuf_ordered_fix (thread_p, &next_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, &next_watcher) != NO_ERROR) + error_code = heap_bestspace_fix_page (thread_p, hfid, &pages[i], &page_watcher); + if (error_code != NO_ERROR) { - /* Give up. */ - vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, - "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, - page_vpid.pageid); - goto error; + goto error_exit; } - } + page_ptr = page_watcher.pgptr; - /* All pages are fixed. */ + if (spage_get_record (thread_p, page_ptr, HEAP_BESTSPACE_ENTRIES_SLOTID, &recdes, PEEK) != S_SUCCESS) + { + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + error_code = ER_GENERIC_ERROR; + goto error_exit; + } - if (crt_watcher.page_was_unfixed) - { - *page_ptr = crt_watcher.pgptr; /* home was refixed */ + if (recdes.length <= 0 || recdes.length % sizeof (cubstorage::bestspace_entry) != 0) + { + /* Something was wrong. */ + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + error_code = ER_GENERIC_ERROR; + goto error_exit; + } - if (spage_number_of_records (crt_watcher.pgptr) > 1) + entries = (cubstorage::bestspace_entry *) PTR_ALIGN (pgbuf, MAX_ALIGNMENT); + num_entries_in_page = recdes.length / sizeof (cubstorage::bestspace_entry); + num_entries_to_copy = MIN (remaining_entries, num_entries_in_page); + if (num_entries_to_copy > 0) { - /* Current page has new data. It is no longer a candidate for removal. */ - vacuum_er_log (VACUUM_ER_LOG_HEAP, - "Candidate heap page %d|%d to remove was changed and has new data.", page_vpid.volid, - page_vpid.pageid); - goto error; + memcpy (entries, update_entries + entry_index, num_entries_to_copy * sizeof (cubstorage::bestspace_entry)); + remaining_entries -= num_entries_to_copy; + entry_index += num_entries_to_copy; + } + for (j = num_entries_to_copy; j < num_entries_in_page; j++) + { + entries[j].set_null (); + } + + recdes.area_size = num_entries_in_page * sizeof (cubstorage::bestspace_entry); + recdes.length = recdes.area_size; + recdes.type = REC_HOME; + recdes.data = (char *) entries; + + addr.pgptr = page_ptr; + addr.offset = HEAP_BESTSPACE_ENTRIES_SLOTID; + log_append_redo_recdes (thread_p, RVHF_UPDATE_BESTSPACE_ENTRIES, &addr, &recdes); + if (spage_update (thread_p, page_ptr, HEAP_BESTSPACE_ENTRIES_SLOTID, &recdes) != SP_SUCCESS) + { + error_code = ER_FAILED; + goto error_exit; } + + pgbuf_set_dirty (thread_p, page_ptr, DONT_FREE); + pgbuf_ordered_unfix (thread_p, &page_watcher); + page_ptr = NULL; } + assert (remaining_entries == 0); - /* recheck the dealloc flag after all latches are acquired */ - if (pgbuf_has_prevent_dealloc (crt_watcher.pgptr)) + return NO_ERROR; + +error_exit: + if (page_ptr != NULL) { - /* Even though we have fixed all required pages, somebody was doing a heap scan, and already reached our page. We - * cannot deallocate it. */ - vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, - "Candidate heap page %d|%d to remove has waiters.", page_vpid.volid, page_vpid.pageid); - goto error; + pgbuf_ordered_unfix (thread_p, &page_watcher); + page_ptr = NULL; } - /* if we are here, the page should not be accessed by any active or vacuum workers. Active workers are prevented - * from accessing it through heap scan, and direct references should not exist. - * the function would not be called if any other vacuum workers would try to access the page. */ - if (pgbuf_has_any_waiters (crt_watcher.pgptr)) + return error_code; +} + +/* + * heap_update_bestspace () - synchronize in-memory bestspace entries and estimates with disk. + * return: + */ +// *INDENT-OFF* +static int +heap_update_bestspace (THREAD_ENTRY * thread_p, const HFID * hfid, cubstorage::bestspace *bestspace) +// *INDENT-ON* +{ + // *INDENT-OFF* + cubstorage::bestspace_entry *entries = NULL; + // *INDENT-ON* + PGBUF_WATCHER header_watcher; + HEAP_HDR_STATS *heap_hdr; + LOG_DATA_ADDR addr = LOG_DATA_ADDR_INITIALIZER; + VPID vpids[cubstorage::bestspace::MAX_SHARD_PAGE_COUNT]; + int num_vpids; + VPID header_vpid; + std::size_t num_entries; + std::size_t num_shards; + std::uint64_t num_recs, recs_sumlen; + int num_pages; + int error = NO_ERROR; + + assert (thread_p); + assert (hfid); + assert (bestspace); + + num_shards = bestspace->get_num_shards (); + num_entries = num_shards * cubstorage::bestspace::ENTRIES_PER_SHARD; + entries = (cubstorage::bestspace_entry *) malloc (num_entries * sizeof (cubstorage::bestspace_entry)); + if (!entries) { - assert (false); - vacuum_er_log_error (VACUUM_ER_LOG_HEAP, "%s", "Unexpected page waiters"); - goto error; + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, + num_entries * sizeof (cubstorage::bestspace_entry)); + return ER_OUT_OF_VIRTUAL_MEMORY; } - /* all good, we can deallocate the page */ - /* Start changes under the protection of system operation. */ - log_sysop_start (thread_p); - is_system_op_started = true; + header_vpid.volid = hfid->vfid.volid; + header_vpid.pageid = hfid->hpgid; + PGBUF_INIT_WATCHER (&header_watcher, PGBUF_ORDERED_HEAP_HDR, hfid); - /* Remove page from statistics in header page. */ - copy_recdes.data = PTR_ALIGN (copy_recdes_buffer, MAX_ALIGNMENT); - copy_recdes.area_size = sizeof (heap_hdr); - if (spage_get_record (thread_p, header_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, ©_recdes, COPY) != S_SUCCESS) + error = pgbuf_ordered_fix (thread_p, &header_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, &header_watcher); + if (error != NO_ERROR) { - assert_release (false); - vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, - "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, page_vpid.pageid); - goto error; + ASSERT_ERROR_AND_SET (error); + goto error_exit; } - memcpy (&heap_hdr, copy_recdes.data, sizeof (heap_hdr)); - for (i = 0; i < HEAP_NUM_BEST_SPACESTATS; i++) + /* serialize the snapshot with other heap header writers. */ + bestspace->to_entries (entries); + bestspace->get_estimates (num_pages, num_recs, recs_sumlen); + + heap_hdr = heap_get_header_stats_ptr (thread_p, header_watcher.pgptr); + if (!heap_hdr) { - if (VPID_EQ (&heap_hdr.estimates.best[i].vpid, &page_vpid)) - { - VPID_SET_NULL (&heap_hdr.estimates.best[i].vpid); - heap_hdr.estimates.best[i].freespace = 0; - heap_hdr.estimates.head = i; - heap_hdr.estimates.num_high_best--; - } - if (VPID_EQ (&heap_hdr.estimates.second_best[i], &page_vpid)) - { - VPID_SET_NULL (&heap_hdr.estimates.second_best[i]); - } + error = ER_FAILED; + goto error_exit; } - if (VPID_EQ (&heap_hdr.estimates.last_vpid, &page_vpid)) + + if (heap_hdr->bestspace.num_pages == 0 || heap_hdr->bestspace.num_pages > cubstorage::bestspace::MAX_SHARD_PAGE_COUNT) { - VPID_COPY (&heap_hdr.estimates.last_vpid, &prev_vpid); + assert_release (false); + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + error = ER_GENERIC_ERROR; + goto error_exit; } - if (VPID_EQ (&prev_vpid, &header_vpid)) + + num_vpids = heap_hdr->bestspace.num_pages; + std::memcpy (vpids, heap_hdr->bestspace.pages, num_vpids * sizeof (VPID)); + + /* keep the header fixed while updating shard pages to serialize with compact and heap reuse. */ + error = heap_update_bestspace_entries (thread_p, hfid, vpids, num_vpids, entries, num_entries); + if (error != NO_ERROR) { - /* Update next link. */ - VPID_COPY (&heap_hdr.next_vpid, &next_vpid); + goto error_exit; } - if (VPID_EQ (&heap_hdr.estimates.full_search_vpid, &page_vpid)) + + assert (!header_watcher.page_was_unfixed); + + heap_hdr = heap_get_header_stats_ptr (thread_p, header_watcher.pgptr); + if (!heap_hdr) { - VPID_SET_NULL (&heap_hdr.estimates.full_search_vpid); + error = ER_FAILED; + goto error_exit; } - /* Update header and log changes. */ - update_recdes.data = (char *) &heap_hdr; - update_recdes.length = sizeof (heap_hdr); - if (spage_update (thread_p, header_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &update_recdes) != SP_SUCCESS) + /* page allocation updates the header before publishing its in-memory delta. do not regress that update. */ + heap_hdr->num_pages = MAX (heap_hdr->num_pages, num_pages); + heap_hdr->num_recs = num_recs; + heap_hdr->recs_sumlen = recs_sumlen; + heap_hdr->bestspace.num_shards = num_shards; + + addr.vfid = &hfid->vfid; + addr.pgptr = header_watcher.pgptr; + addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; + log_append_redo_data (thread_p, RVHF_STATS, &addr, sizeof (*heap_hdr), heap_hdr); + pgbuf_ordered_set_dirty_and_free (thread_p, &header_watcher); + + free_and_init (entries); + return NO_ERROR; + +error_exit: + free_and_init (entries); + if (header_watcher.pgptr != NULL) { - assert_release (false); - vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, - "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, page_vpid.pageid); - goto error; + pgbuf_ordered_unfix (thread_p, &header_watcher); } - log_append_undoredo_data2 (thread_p, RVHF_STATS, &hfid->vfid, header_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, - sizeof (heap_hdr), sizeof (heap_hdr), copy_recdes.data, update_recdes.data); - pgbuf_set_dirty (thread_p, header_watcher.pgptr, DONT_FREE); + return error; +} - /* Update links in previous and next page. */ +/* + * heap_take_bestspace () - Take a bestspace from heap file + * return: + */ +static int +heap_take_bestspace (THREAD_ENTRY * thread_p, HFID * hfid, PGBUF_WATCHER * header_watcher, HEAP_HDR_STATS * header, + cubstorage::bestspace_entry * entries, cubstorage::bestspace_entry * candidates) +{ + LOG_DATA_ADDR addr = LOG_DATA_ADDR_INITIALIZER; + PGBUF_WATCHER page_watcher; + bool page_watcher_fixed = false; + int num_entries_in_page, num_entries_to_copy; + int remaining_entries; + int error_code = NO_ERROR; + int num_entries; + RECDES recdes; + int i; - if (prev_watcher.pgptr != NULL) + assert (thread_p); + assert (hfid); + assert (header); + assert (entries); + assert (candidates || !header->bestspace.num_candidates); + + /* copy the bestpages in shards from the heap shard pages */ + remaining_entries = header->bestspace.num_shards * cubstorage::bestspace::ENTRIES_PER_SHARD; + num_entries = 0; + for (i = 0; remaining_entries > 0 && i < (int) header->bestspace.num_pages; i++) { - /* Next link in previous page. */ - assert (!VPID_EQ (&header_vpid, &prev_vpid)); - copy_recdes.area_size = sizeof (chain); - if (spage_get_record (thread_p, prev_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, ©_recdes, COPY) != - S_SUCCESS) + if (VPID_ISNULL (&header->bestspace.pages[i])) { - assert_release (false); - vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, - "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, - page_vpid.pageid); - goto error; + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + error_code = ER_GENERIC_ERROR; + goto exit; } - memcpy (&chain, copy_recdes.data, copy_recdes.length); - VPID_COPY (&chain.next_vpid, &next_vpid); - update_recdes.data = (char *) &chain; - update_recdes.length = sizeof (chain); - if (spage_update (thread_p, prev_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &update_recdes) != SP_SUCCESS) + + error_code = heap_bestspace_fix_page (thread_p, hfid, &header->bestspace.pages[i], &page_watcher); + if (error_code != NO_ERROR) { - assert_release (false); - vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, - "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, - page_vpid.pageid); - goto error; + goto exit; } - log_append_undoredo_data2 (thread_p, RVHF_CHAIN, &hfid->vfid, prev_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, - sizeof (chain), sizeof (chain), copy_recdes.data, update_recdes.data); - pgbuf_set_dirty (thread_p, prev_watcher.pgptr, DONT_FREE); - } + page_watcher_fixed = true; - if (next_watcher.pgptr != NULL) - { - /* Previous link in next page. */ - copy_recdes.area_size = sizeof (chain); - if (spage_get_record (thread_p, next_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, ©_recdes, COPY) != - S_SUCCESS) + if (spage_get_record (thread_p, page_watcher.pgptr, HEAP_BESTSPACE_ENTRIES_SLOTID, &recdes, PEEK) != S_SUCCESS) { - assert_release (false); - vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, - "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, - page_vpid.pageid); - goto error; + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + error_code = ER_GENERIC_ERROR; + goto exit; } - memcpy (&chain, copy_recdes.data, sizeof (chain)); - VPID_COPY (&chain.prev_vpid, &prev_vpid); - update_recdes.data = (char *) &chain; - update_recdes.length = sizeof (chain); - if (spage_update (thread_p, next_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &update_recdes) != SP_SUCCESS) + if (recdes.length <= 0 || recdes.length % sizeof (cubstorage::bestspace_entry) != 0) { - assert_release (false); - vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, - "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, - page_vpid.pageid); - goto error; + /* Something was wrong. */ + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + error_code = ER_GENERIC_ERROR; + goto exit; } - log_append_undoredo_data2 (thread_p, RVHF_CHAIN, &hfid->vfid, next_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, - sizeof (chain), sizeof (chain), copy_recdes.data, update_recdes.data); - pgbuf_set_dirty (thread_p, next_watcher.pgptr, DONT_FREE); + + num_entries_in_page = recdes.length / sizeof (cubstorage::bestspace_entry); + num_entries_to_copy = MIN (num_entries_in_page, remaining_entries); + assert (num_entries_to_copy > 0); + memcpy (entries + num_entries, recdes.data, num_entries_to_copy * sizeof (cubstorage::bestspace_entry)); + + remaining_entries -= num_entries_to_copy; + num_entries += num_entries_to_copy; + + pgbuf_ordered_unfix (thread_p, &page_watcher); + page_watcher_fixed = false; } + assert (remaining_entries == 0); - /* Unfix current page. */ - pgbuf_ordered_unfix_and_init (thread_p, *page_ptr, &crt_watcher); - /* Deallocate current page. */ - if (file_dealloc (thread_p, &hfid->vfid, &page_vpid, FILE_HEAP) != NO_ERROR) + /* drain the candidates from the heap header */ + if (header->bestspace.num_candidates > 0) { - ASSERT_ERROR (); - vacuum_er_log_warning (VACUUM_ER_LOG_HEAP, - "Could not remove candidate empty heap page %d|%d.", page_vpid.volid, page_vpid.pageid); - goto error; - } + memcpy (candidates, header->bestspace.candidates, + header->bestspace.num_candidates * sizeof (cubstorage::bestspace_entry)); - /* Remove page from best space cached statistics. */ - (void) heap_stats_del_bestspace_by_vpid (thread_p, &page_vpid); + heap_bestspace_clear_candidates (header->bestspace.candidates, &header->bestspace.num_candidates, + cubstorage::bestspace::MAX_CANDIDATES_QUEUE_SIZE); - /* Finished. */ - log_sysop_commit (thread_p); - is_system_op_started = false; + addr.vfid = &hfid->vfid; + addr.pgptr = header_watcher->pgptr; + addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; + log_append_redo_data (thread_p, RVHF_STATS, &addr, sizeof (*header), header); + pgbuf_set_dirty (thread_p, header_watcher->pgptr, DONT_FREE); + } - /* Unfix all pages. */ - if (next_watcher.pgptr != NULL) +exit: + if (page_watcher_fixed) { - pgbuf_ordered_unfix (thread_p, &next_watcher); + pgbuf_ordered_unfix (thread_p, &page_watcher); } - if (prev_watcher.pgptr != NULL) + return error_code; +} + + +/* + * heap_build_bestspace () - Take and rebuild a bestspace from heap file + * return: + */ +// *INDENT-OFF* +static cubstorage::bestspace * +// *INDENT-ON* +heap_build_bestspace (THREAD_ENTRY * thread_p, OID * class_oid, HFID * hfid, PGBUF_WATCHER * header_watcher) +{ + // *INDENT-OFF* + cubstorage::bestspace *bestspace; + cubstorage::bestspace_entry *entries = NULL, *candidates = NULL; + // *INDENT-ON* + std::size_t num_entries, num_candidates, num_shards; + HEAP_HDR_STATS *header; + RECDES recdes; + int error; + + assert (thread_p); + assert (class_oid && hfid); + assert (header_watcher); + + /* get the header */ + if (spage_get_record (thread_p, header_watcher->pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) { - pgbuf_ordered_unfix (thread_p, &prev_watcher); + return NULL; } - pgbuf_ordered_unfix (thread_p, &header_watcher); + assert (recdes.length == sizeof (HEAP_HDR_STATS)); + header = ((HEAP_HDR_STATS *) recdes.data); - /* Page removed successfully. */ - vacuum_er_log (VACUUM_ER_LOG_HEAP, "Successfully remove heap page %d|%d.", page_vpid.volid, page_vpid.pageid); - return true; + /* get the max number of the entries */ + num_entries = header->bestspace.num_shards * cubstorage::bestspace::ENTRIES_PER_SHARD; + num_candidates = header->bestspace.num_candidates; -error: - if (is_system_op_started) + entries = (cubstorage::bestspace_entry *) malloc (num_entries * sizeof (cubstorage::bestspace_entry)); + if (!entries) { - log_sysop_abort (thread_p); + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, + num_entries * sizeof (cubstorage::bestspace_entry)); + return NULL; } - if (next_watcher.pgptr != NULL) + if (num_candidates > 0) { - pgbuf_ordered_unfix (thread_p, &next_watcher); + candidates = (cubstorage::bestspace_entry *) malloc (num_candidates * sizeof (cubstorage::bestspace_entry)); + if (!candidates) + { + free_and_init (entries); + + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, + num_candidates * sizeof (cubstorage::bestspace_entry)); + return NULL; + } } - if (prev_watcher.pgptr != NULL) + + /* there is no in-memory bestspace */ + error = heap_take_bestspace (thread_p, hfid, header_watcher, header, entries, candidates); + if (error != NO_ERROR) { - pgbuf_ordered_unfix (thread_p, &prev_watcher); + free_and_init (entries); + if (candidates) + { + free_and_init (candidates); + } + return NULL; } - if (header_watcher.pgptr != NULL) + + if (OID_IS_ROOTOID (class_oid)) { - pgbuf_ordered_unfix (thread_p, &header_watcher); + num_shards = 1; } - if (*page_ptr != NULL) + else { - if (*page_ptr != crt_watcher.pgptr) + num_shards = prm_get_integer_value (PRM_ID_BESTSPACE_SHARD_COUNT); + } + + cubstorage::bestspaces.create (hfid, num_shards, entries, + MIN (num_entries, num_shards * cubstorage::bestspace::ENTRIES_PER_SHARD), candidates, + num_candidates, header->num_pages, header->num_recs, header->recs_sumlen, + (std::uint16_t) header->unfill_space); + + bestspace = cubstorage::bestspaces.find (hfid); + if (!bestspace) + { + /* impossible */ + assert_release (false); + + free_and_init (entries); + if (candidates) { - /* jumped to here while fixing pages */ - assert (crt_watcher.page_was_unfixed); - *page_ptr = crt_watcher.pgptr; + free_and_init (candidates); } - assert (crt_watcher.pgptr == *page_ptr); - pgbuf_ordered_unfix_and_init (thread_p, *page_ptr, &crt_watcher); + + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + return NULL; } - else + + /* if the old shards is bigger than current option */ + if (num_entries > num_shards * cubstorage::bestspace::ENTRIES_PER_SHARD) { - assert (crt_watcher.pgptr == NULL); + bestspace->push_candidates (&entries[num_shards * 64], num_entries - num_shards * 64); } - /* Page was not removed. */ - return false; + + free_and_init (entries); + if (candidates) + { + free_and_init (candidates); + } + return bestspace; } /* - * heap_vpid_next () - Find next page of heap - * return: NO_ERROR - * hfid(in): Object heap file identifier - * pgptr(in): Current page pointer - * next_vpid(in/out): Next volume-page identifier - * - * Note: Find the next page of heap file. + * heap_find_bestspace () - Find or rebuild a bestspace from heap file + * return: */ -int -heap_vpid_next (THREAD_ENTRY * thread_p, const HFID * hfid, PAGE_PTR pgptr, VPID * next_vpid) +// *INDENT-OFF* +static cubstorage::bestspace * +// *INDENT-ON* +heap_find_bestspace (THREAD_ENTRY * thread_p, OID * class_oid, HFID * hfid, PGBUF_WATCHER * header_watcher) { - HEAP_CHAIN *chain; /* Chain to next and prev page */ - HEAP_HDR_STATS *heap_hdr; /* Header of heap file */ - RECDES recdes; /* Record descriptor to page header */ - int ret = NO_ERROR; + // *INDENT-OFF* + cubstorage::bestspace *bestspace; + // *INDENT-ON* + PGBUF_WATCHER watcher; + VPID vpid; + int error; -#if !defined (NDEBUG) - (void) pgbuf_check_page_ptype (thread_p, pgptr, PAGE_HEAP); -#endif /* !NDEBUG */ + /* find the bestspace */ + bestspace = cubstorage::bestspaces.find (hfid); + if (bestspace) + { + return bestspace; + } - /* Get either the heap header or chain record */ - if (spage_get_record (thread_p, pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) + /* NULL pointer requests lookup only. NULL OID value represents the root class during bootstrap. */ + if (!class_oid) { - /* Unable to get header/chain record for the given page */ - VPID_SET_NULL (next_vpid); - ret = ER_FAILED; + /* not an error */ + return NULL; } - else + + if (OID_ISNULL (class_oid)) { - pgbuf_get_vpid (pgptr, next_vpid); - /* Is this the header page ? */ - if (next_vpid->pageid == hfid->hpgid && next_vpid->volid == hfid->vfid.volid) - { - heap_hdr = (HEAP_HDR_STATS *) recdes.data; - *next_vpid = heap_hdr->next_vpid; - } - else + class_oid = oid_Root_class_oid; + } + + if (header_watcher != NULL) + { + /* build from the heap page */ + bestspace = heap_build_bestspace (thread_p, class_oid, hfid, header_watcher); + if (!bestspace) { - chain = (HEAP_CHAIN *) recdes.data; - *next_vpid = chain->next_vpid; + return NULL; } + return bestspace; } - return ret; -} - -/* - * heap_vpid_prev () - Find previous page of heap - * return: NO_ERROR - * hfid(in): Object heap file identifier - * pgptr(in): Current page pointer - * prev_vpid(in/out): Previous volume-page identifier - * - * Note: Find the previous page of heap file. - */ -int -heap_vpid_prev (THREAD_ENTRY * thread_p, const HFID * hfid, PAGE_PTR pgptr, VPID * prev_vpid) -{ - HEAP_CHAIN *chain; /* Chain to next and prev page */ - RECDES recdes; /* Record descriptor to page header */ - int ret = NO_ERROR; + /* fix the header page */ + vpid.volid = hfid->vfid.volid; + vpid.pageid = hfid->hpgid; -#if !defined (NDEBUG) - (void) pgbuf_check_page_ptype (thread_p, pgptr, PAGE_HEAP); -#endif /* !NDEBUG */ + PGBUF_INIT_WATCHER (&watcher, PGBUF_ORDERED_HEAP_HDR, hfid); + error = pgbuf_ordered_fix (thread_p, &vpid, OLD_PAGE, PGBUF_LATCH_WRITE, &watcher); + if (error != NO_ERROR) + { + ASSERT_ERROR_AND_SET (error); + return NULL; + } - /* Get either the header or chain record */ - if (spage_get_record (thread_p, pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &recdes, PEEK) != S_SUCCESS) + /* recheck bestspace after acquiring the latch. */ + /* another thread may have created it before us while we were waiting. */ + bestspace = cubstorage::bestspaces.find (hfid); + if (bestspace) { - /* Unable to get header/chain record for the given page */ - VPID_SET_NULL (prev_vpid); - ret = ER_FAILED; + /* other threads create the bestspace and found the space */ + pgbuf_ordered_unfix (thread_p, &watcher); + + /* found the bestspace */ + return bestspace; } - else + + /* build from the heap page */ + bestspace = heap_build_bestspace (thread_p, class_oid, hfid, &watcher); + if (!bestspace) { - pgbuf_get_vpid (pgptr, prev_vpid); - /* Is this the header page ? */ - if (prev_vpid->pageid == hfid->hpgid && prev_vpid->volid == hfid->vfid.volid) - { - VPID_SET_NULL (prev_vpid); - } - else - { - chain = (HEAP_CHAIN *) recdes.data; - *prev_vpid = chain->prev_vpid; - } + pgbuf_ordered_unfix (thread_p, &watcher); + return NULL; } - return ret; + pgbuf_ordered_unfix (thread_p, &watcher); + return bestspace; } /* - * heap_manager_initialize () - - * return: NO_ERROR - * - * Note: Initialization process of the heap file module. Find the - * maximum size of an object that can be inserted in the heap. - * Objects that overpass this size are stored in overflow. + * heap_find_bestpage () - Find the best page + * return: */ -int -heap_manager_initialize (void) +STATIC_INLINE int +heap_find_bestpage (THREAD_ENTRY * thread_p, OID * class_oid, HFID * hfid, std::uint16_t size, bool is_newrec, + PGBUF_WATCHER * page_watcher) { - int ret; - -#define HEAP_MAX_FIRSTSLOTID_LENGTH (sizeof (HEAP_HDR_STATS)) - - heap_Maxslotted_reclength = (spage_max_record_size () - HEAP_MAX_FIRSTSLOTID_LENGTH); - heap_Slotted_overhead = SPAGE_SLOT_SIZE; - - /* Initialize the class representation cache */ - ret = heap_chnguess_initialize (); - if (ret != NO_ERROR) - { - return ret; - } + // *INDENT-OFF* + cubstorage::bestspace *bestspace; + // *INDENT-ON* + int error; - ret = heap_classrepr_initialize_cache (); - if (ret != NO_ERROR) + bestspace = heap_find_bestspace (thread_p, class_oid, hfid, NULL); + if (!bestspace) { - return ret; + ASSERT_ERROR (); + error = er_errid (); + return error != NO_ERROR ? error : ER_FAILED; } - /* Initialize best space cache */ - ret = heap_stats_bestspace_initialize (); - if (ret != NO_ERROR) + /* update may unfix the fixed page (best page) so sync in-memory bestspace with disk first */ + if (bestspace->updatable ()) { - return ret; + error = heap_update_bestspace (thread_p, hfid, bestspace); + if (error != NO_ERROR) + { + return error; + } } - /* Initialize class OID->HFID cache */ - ret = heap_initialize_hfid_table (); - - return ret; + /* find */ + return bestspace->find (*thread_p, class_oid, hfid, size, is_newrec, *page_watcher); } /* - * heap_manager_finalize () - Terminate the heap manager - * return: NO_ERROR - * Note: Deallocate any cached structure. + * heap_add_bestpage () - Add the best page in candidates + * return: */ -int -heap_manager_finalize (void) +void +heap_add_bestpage (THREAD_ENTRY * thread_p, HFID * hfid, PAGE_PTR pgptr, std::uint16_t prev_freespace, + PGBUF_WATCHER * header_watcher) { - int ret; + // *INDENT-OFF* + cubstorage::bestspace *bestspace; + cubstorage::bestspace_entry candidate; + // *INDENT-ON* + int freespace; + VPID *vpid; - ret = heap_chnguess_finalize (); - if (ret != NO_ERROR) - { - return ret; - } + /* prev_freespace is not used but leave this for future feature */ + (void) prev_freespace; - ret = heap_classrepr_finalize_cache (); - if (ret != NO_ERROR) + bestspace = heap_find_bestspace (thread_p, NULL, hfid, header_watcher); + if (!bestspace) { - return ret; + return; } - ret = heap_stats_bestspace_finalize (); - if (ret != NO_ERROR) + freespace = spage_get_free_space_without_saving (thread_p, pgptr); + if (cubstorage::bestspace::size_to_tier (freespace) >= cubstorage::bestspace::tier::FS3) { - return ret; - } - - heap_finalize_hfid_table (); + vpid = pgbuf_get_vpid_ptr (pgptr); + assert_release (vpid != NULL); - return ret; + candidate.freespace = freespace; + candidate.volid = vpid->volid; + candidate.pageid = vpid->pageid; + bestspace->try_push_candidates (&candidate, 1); + } } /* @@ -5237,6 +4663,10 @@ heap_create_internal (THREAD_ENTRY * thread_p, HFID * hfid, const OID * class_oi PAGE_TYPE ptype = PAGE_HEAP; OID null_oid = OID_INITIALIZER; TDE_ALGORITHM tde_algo = TDE_ALGORITHM_NONE; + cubstorage::bestspace_entry header_page; + PGBUF_WATCHER header_page_watcher; + bool header_page_watcher_attached = false; + int header_freespace; int error_code = NO_ERROR; @@ -5267,6 +4697,8 @@ heap_create_internal (THREAD_ENTRY * thread_p, HFID * hfid, const OID * class_oi if (!HFID_IS_NULL (hfid)) { + cubstorage::bestspaces.destroy (hfid); + /* reuse heap file */ if (heap_reuse (thread_p, hfid, class_oid, reuse_oid) == NULL) { @@ -5329,55 +4761,47 @@ heap_create_internal (THREAD_ENTRY * thread_p, HFID * hfid, const OID * class_oi goto error; } - (void) heap_stats_del_bestspace_by_hfid (thread_p, hfid); - pgbuf_set_page_ptype (thread_p, addr_hdr.pgptr, PAGE_HEAP); /* Initialize header page */ spage_initialize (thread_p, addr_hdr.pgptr, heap_get_spage_type (), HEAP_MAX_ALIGN, SAFEGUARD_RVSPACE); + pgbuf_attach_watcher (thread_p, addr_hdr.pgptr, PGBUF_LATCH_WRITE, hfid, &header_page_watcher); + header_page_watcher_attached = true; /* Now insert header */ memset (&heap_hdr, 0, sizeof (heap_hdr)); heap_hdr.class_oid = *class_oid; + VFID_SET_NULL (&heap_hdr.ovf_vfid); + VPID_SET_NULL (&heap_hdr.next_vpid); + heap_hdr.last_vpid.volid = hfid->vfid.volid; + heap_hdr.last_vpid.pageid = hfid->hpgid; heap_hdr.unfill_space = (int) ((float) DB_PAGESIZE * prm_get_float_value (PRM_ID_HF_UNFILL_FACTOR)); - heap_hdr.estimates.num_pages = 1; - heap_hdr.estimates.num_recs = 0; - heap_hdr.estimates.recs_sumlen = 0.0; - - heap_hdr.estimates.best[0].vpid.volid = hfid->vfid.volid; - heap_hdr.estimates.best[0].vpid.pageid = hfid->hpgid; - heap_hdr.estimates.best[0].freespace = spage_max_space_for_new_record (thread_p, addr_hdr.pgptr); - - heap_hdr.estimates.head = 1; - for (i = heap_hdr.estimates.head; i < HEAP_NUM_BEST_SPACESTATS; i++) - { - VPID_SET_NULL (&heap_hdr.estimates.best[i].vpid); - heap_hdr.estimates.best[i].freespace = 0; - } - - heap_hdr.estimates.num_high_best = 1; - heap_hdr.estimates.num_other_high_best = 0; + heap_hdr.num_pages = 1; + heap_hdr.num_recs = 0; + heap_hdr.recs_sumlen = 0; - heap_hdr.estimates.num_second_best = 0; - heap_hdr.estimates.head_second_best = 0; - heap_hdr.estimates.tail_second_best = 0; - heap_hdr.estimates.num_substitutions = 0; + /* bestspace */ + header_freespace = + spage_max_space_for_new_record (thread_p, addr_hdr.pgptr) + - (int) DB_ALIGN (sizeof (HEAP_HDR_STATS), HEAP_MAX_ALIGN) - heap_Slotted_overhead; + header_page.freespace = (std::uint16_t) MAX (header_freespace, 0); + header_page.volid = hfid->vfid.volid; + header_page.pageid = hfid->hpgid; - for (i = 0; i < HEAP_NUM_BEST_SPACESTATS; i++) + error_code = heap_create_bestspace (thread_p, hfid, &heap_hdr, &header_page); + if (error_code != NO_ERROR) { - VPID_SET_NULL (&heap_hdr.estimates.second_best[i]); + ASSERT_ERROR (); + goto error; } + addr_hdr.pgptr = header_page_watcher.pgptr; + assert (addr_hdr.pgptr != NULL); - heap_hdr.estimates.last_vpid.volid = hfid->vfid.volid; - heap_hdr.estimates.last_vpid.pageid = hfid->hpgid; - - heap_hdr.estimates.full_search_vpid.volid = hfid->vfid.volid; - heap_hdr.estimates.full_search_vpid.pageid = hfid->hpgid; - + /* insert the header */ recdes.area_size = recdes.length = sizeof (HEAP_HDR_STATS); recdes.type = REC_HOME; recdes.data = (char *) &heap_hdr; @@ -5406,7 +4830,9 @@ heap_create_internal (THREAD_ENTRY * thread_p, HFID * hfid, const OID * class_oi addr_hdr.vfid = &hfid->vfid; addr_hdr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; log_append_redo_data (thread_p, RVHF_CREATE_HEADER, &addr_hdr, sizeof (heap_hdr), &heap_hdr); - pgbuf_set_dirty (thread_p, addr_hdr.pgptr, FREE); + pgbuf_set_dirty (thread_p, addr_hdr.pgptr, DONT_FREE); + pgbuf_ordered_unfix (thread_p, &header_page_watcher); + header_page_watcher_attached = false; addr_hdr.pgptr = NULL; } @@ -5435,7 +4861,15 @@ heap_create_internal (THREAD_ENTRY * thread_p, HFID * hfid, const OID * class_oi error: assert (error_code != NO_ERROR); - if (addr_hdr.pgptr != NULL) + if (header_page_watcher_attached) + { + if (header_page_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &header_page_watcher); + } + addr_hdr.pgptr = NULL; + } + else if (addr_hdr.pgptr != NULL) { pgbuf_unfix_and_init (thread_p, addr_hdr.pgptr); } @@ -5561,23 +4995,29 @@ heap_reinitialize_page (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, const bool is_h static const HFID * heap_reuse (THREAD_ENTRY * thread_p, const HFID * hfid, const OID * class_oid, const bool reuse_oid) { + cubstorage::bestspace_entry first_entry; + cubstorage::bestspace_entry candidate_entry; VPID vpid; /* Volume and page identifiers */ PAGE_PTR hdr_pgptr = NULL; /* Page pointer to header page */ PAGE_PTR pgptr = NULL; /* Page pointer */ LOG_DATA_ADDR addr; /* Address of logging data */ HEAP_HDR_STATS *heap_hdr = NULL; /* Header of heap structure */ HEAP_CHAIN *chain; /* Chain to next and prev page */ + PGBUF_WATCHER hdr_watcher; + bool hdr_watcher_attached = false; + HFID hfid_for_watcher; RECDES recdes; VPID last_vpid; + int freespace; int is_header_page; - int npages = 0; - int i; - bool need_update; + int npages; assert (class_oid != NULL); assert (!OID_ISNULL (class_oid)); + hfid_for_watcher = *hfid; VPID_SET_NULL (&last_vpid); + first_entry.set_null (); addr.vfid = &hfid->vfid; /* @@ -5611,6 +5051,7 @@ heap_reuse (THREAD_ENTRY * thread_p, const HFID * hfid, const OID * class_oid, c * future. */ + npages = 0; while (!(VPID_ISNULL (&vpid))) { /* @@ -5628,6 +5069,25 @@ heap_reuse (THREAD_ENTRY * thread_p, const HFID * hfid, const OID * class_oid, c is_header_page = (hdr_pgptr == pgptr) ? 1 : 0; + if (!is_header_page && heap_page_is_bestspace (thread_p, pgptr)) + { + if (heap_hdr == NULL) + { + er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0); + goto error; + } + + last_vpid = vpid; + + if (heap_vpid_next (thread_p, hfid, pgptr, &vpid) != NO_ERROR) + { + goto error; + } + pgbuf_unfix_and_init (thread_p, pgptr); + pgptr = NULL; + continue; + } + /* * Remove all the objects in this page */ @@ -5663,11 +5123,19 @@ heap_reuse (THREAD_ENTRY * thread_p, const HFID * hfid, const OID * class_oid, c goto error; } + freespace = spage_max_space_for_new_record (thread_p, pgptr); + /* save new class oid in the page. it dirties the page. */ if (is_header_page) { heap_hdr = (HEAP_HDR_STATS *) recdes.data; COPY_OID (&(heap_hdr->class_oid), class_oid); + + heap_bestspace_clear_candidates (heap_hdr->bestspace.candidates, &heap_hdr->bestspace.num_candidates, + cubstorage::bestspace::MAX_CANDIDATES_QUEUE_SIZE); + first_entry.freespace = freespace; + first_entry.volid = vpid.volid; + first_entry.pageid = vpid.pageid; } else { @@ -5676,19 +5144,12 @@ heap_reuse (THREAD_ENTRY * thread_p, const HFID * hfid, const OID * class_oid, c chain->max_mvccid = MVCCID_NULL; chain->flags = 0; HEAP_PAGE_SET_VACUUM_STATUS (chain, HEAP_PAGE_VACUUM_NONE); - } - - if (npages < HEAP_NUM_BEST_SPACESTATS) - { - heap_hdr->estimates.best[npages].vpid = vpid; - heap_hdr->estimates.best[npages].freespace = - spage_get_free_space_without_saving (thread_p, pgptr, &need_update); - } - - if (prm_get_integer_value (PRM_ID_HF_MAX_BESTSPACE_ENTRIES) > 0) - { - (void) heap_stats_add_bestspace (thread_p, hfid, &vpid, DB_PAGESIZE); + candidate_entry.freespace = freespace; + candidate_entry.volid = vpid.volid; + candidate_entry.pageid = vpid.pageid; + heap_bestspace_add_candidate (heap_hdr->bestspace.candidates, &heap_hdr->bestspace.num_candidates, + cubstorage::bestspace::MAX_CANDIDATES_QUEUE_SIZE, &candidate_entry); } npages++; @@ -5707,39 +5168,50 @@ heap_reuse (THREAD_ENTRY * thread_p, const HFID * hfid, const OID * class_oid, c } /* - * Reset the statistics. Set statistics for insertion back to first page - * and reset unfill space according to new parameters + * Reset the reused header and log the change (RVHF_STATS), which also covers + * the class_oid rewritten into heap_hdr above -- RVHF_REUSE_PAGE deliberately + * skips the header slot and relies on this redo record. */ VFID_SET_NULL (&heap_hdr->ovf_vfid); + heap_hdr->last_vpid = last_vpid; heap_hdr->unfill_space = (int) ((float) DB_PAGESIZE * prm_get_float_value (PRM_ID_HF_UNFILL_FACTOR)); - heap_hdr->estimates.num_pages = npages; - heap_hdr->estimates.num_recs = 0; - heap_hdr->estimates.recs_sumlen = 0.0; + heap_hdr->num_pages = npages; + heap_hdr->num_recs = 0; + heap_hdr->recs_sumlen = 0; + + heap_hdr->bestspace.num_shards = prm_get_integer_value (PRM_ID_BESTSPACE_SHARD_COUNT); - if (npages < HEAP_NUM_BEST_SPACESTATS) + assert (heap_hdr->bestspace.num_pages > 0); + pgbuf_attach_watcher (thread_p, hdr_pgptr, PGBUF_LATCH_WRITE, &hfid_for_watcher, &hdr_watcher); + hdr_watcher_attached = true; + + if (heap_update_bestspace_chain + (thread_p, &heap_hdr->class_oid, hfid, heap_hdr->bestspace.pages, heap_hdr->bestspace.num_pages) != NO_ERROR) { - heap_hdr->estimates.num_high_best = npages; - heap_hdr->estimates.num_other_high_best = 0; + goto error; } - else + if (heap_update_bestspace_entries + (thread_p, hfid, heap_hdr->bestspace.pages, heap_hdr->bestspace.num_pages, &first_entry, 1) != NO_ERROR) { - heap_hdr->estimates.num_high_best = HEAP_NUM_BEST_SPACESTATS; - heap_hdr->estimates.num_other_high_best = npages - HEAP_NUM_BEST_SPACESTATS; + goto error; } - heap_hdr->estimates.head = 0; - for (i = npages; i < HEAP_NUM_BEST_SPACESTATS; i++) + if (hdr_watcher.page_was_unfixed) { - VPID_SET_NULL (&heap_hdr->estimates.best[i].vpid); - heap_hdr->estimates.best[i].freespace = 0; + hdr_pgptr = hdr_watcher.pgptr; + heap_hdr = heap_get_header_stats_ptr (thread_p, hdr_pgptr); + if (heap_hdr == NULL) + { + goto error; + } } - heap_hdr->estimates.last_vpid = last_vpid; - - addr.pgptr = hdr_pgptr; + addr.pgptr = hdr_watcher.pgptr; addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; log_append_redo_data (thread_p, RVHF_STATS, &addr, sizeof (*heap_hdr), heap_hdr); - pgbuf_set_dirty (thread_p, hdr_pgptr, FREE); + pgbuf_set_dirty (thread_p, hdr_watcher.pgptr, DONT_FREE); + pgbuf_ordered_unfix (thread_p, &hdr_watcher); + hdr_watcher_attached = false; hdr_pgptr = NULL; return hfid; @@ -5749,7 +5221,15 @@ heap_reuse (THREAD_ENTRY * thread_p, const HFID * hfid, const OID * class_oid, c { pgbuf_unfix_and_init (thread_p, pgptr); } - if (hdr_pgptr != NULL) + if (hdr_watcher_attached) + { + if (hdr_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &hdr_watcher); + } + hdr_pgptr = NULL; + } + else if (hdr_pgptr != NULL) { pgbuf_unfix_and_init (thread_p, hdr_pgptr); } @@ -5854,7 +5334,7 @@ xheap_destroy (THREAD_ENTRY * thread_p, const HFID * hfid, const OID * class_oid file_postpone_destroy (thread_p, &hfid->vfid); - (void) heap_stats_del_bestspace_by_hfid (thread_p, hfid); + cubstorage::bestspaces.destroy (hfid); return NO_ERROR; } @@ -5898,7 +5378,7 @@ xheap_destroy_newly_created (THREAD_ENTRY * thread_p, const HFID * hfid, const O log_append_postpone (thread_p, RVHF_MARK_DELETED, &addr, sizeof (hfid->vfid), &hfid->vfid); - (void) heap_stats_del_bestspace_by_hfid (thread_p, hfid); + cubstorage::bestspaces.destroy (hfid); return ret; } @@ -6180,19 +5660,25 @@ heap_flush (THREAD_ENTRY * thread_p, const OID * oid) int xheap_reclaim_addresses (THREAD_ENTRY * thread_p, const HFID * hfid) { - VPID vpid; - VPID prv_vpid; - int best, i; + // *INDENT-OFF* + cubstorage::bestspace *bestspace; + cubstorage::bestspace_entry *entries = NULL; + cubstorage::bestspace_entry candidate; + // *INDENT-ON* + PGBUF_WATCHER hdr_page_watcher; + PGBUF_WATCHER curr_page_watcher; HEAP_HDR_STATS initial_heap_hdr; HEAP_HDR_STATS heap_hdr; RECDES hdr_recdes; LOG_DATA_ADDR addr; + VPID vpid; + VPID prv_vpid; int ret = NO_ERROR; - int free_space; + int freespace; int npages, nrecords, rec_length; - bool need_update; - PGBUF_WATCHER hdr_page_watcher; - PGBUF_WATCHER curr_page_watcher; + std::size_t max_candidates, max_bestpages; + std::size_t num_entries; + std::size_t i; PGBUF_INIT_WATCHER (&hdr_page_watcher, PGBUF_ORDERED_HEAP_HDR, hfid); PGBUF_INIT_WATCHER (&curr_page_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); @@ -6221,43 +5707,44 @@ xheap_reclaim_addresses (THREAD_ENTRY * thread_p, const HFID * hfid) { goto exit_on_error; } - prv_vpid = heap_hdr.estimates.last_vpid; + prv_vpid = heap_hdr.last_vpid; /* Copy the header to memory.. so we can log the changes */ memcpy (&initial_heap_hdr, hdr_recdes.data, sizeof (initial_heap_hdr)); /* - * Initialize best estimates + * Initialize base information */ - heap_hdr.estimates.num_pages = 0; - heap_hdr.estimates.num_recs = 0; - heap_hdr.estimates.recs_sumlen = 0.0; - heap_hdr.estimates.num_high_best = 0; - heap_hdr.estimates.num_other_high_best = 0; - heap_hdr.estimates.head = 0; + heap_hdr.num_pages = 0; + heap_hdr.num_recs = 0; + heap_hdr.recs_sumlen = 0; - for (i = 0; i < HEAP_NUM_BEST_SPACESTATS; i++) + if (OID_ISNULL (&heap_hdr.class_oid) || OID_IS_ROOTOID (&heap_hdr.class_oid)) { - VPID_SET_NULL (&heap_hdr.estimates.best[i].vpid); - heap_hdr.estimates.best[0].freespace = 0; + heap_hdr.bestspace.num_shards = 1; } - - /* Initialize second best estimates */ - heap_hdr.estimates.num_second_best = 0; - heap_hdr.estimates.head_second_best = 0; - heap_hdr.estimates.tail_second_best = 0; - heap_hdr.estimates.num_substitutions = 0; - - for (i = 0; i < HEAP_NUM_BEST_SPACESTATS; i++) + else { - VPID_SET_NULL (&heap_hdr.estimates.second_best[i]); + heap_hdr.bestspace.num_shards = prm_get_integer_value (PRM_ID_BESTSPACE_SHARD_COUNT); } - /* initialize full_search_vpid */ - heap_hdr.estimates.full_search_vpid.volid = hfid->vfid.volid; - heap_hdr.estimates.full_search_vpid.pageid = hfid->hpgid; + /* + * Initialize bestspace + */ + max_candidates = cubstorage::bestspace::MAX_CANDIDATES_QUEUE_SIZE; + max_bestpages = cubstorage::bestspace::ENTRIES_PER_SHARD * heap_hdr.bestspace.num_shards; + + heap_bestspace_clear_candidates (heap_hdr.bestspace.candidates, &heap_hdr.bestspace.num_candidates, max_candidates); - best = 0; + num_entries = 0; + entries = + (cubstorage::bestspace_entry *) malloc ((max_candidates + max_bestpages) * sizeof (cubstorage::bestspace_entry)); + if (!entries) + { + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, + (max_candidates + max_bestpages) * sizeof (cubstorage::bestspace_entry)); + goto exit_on_error; + } while (!(VPID_ISNULL (&prv_vpid))) { @@ -6280,6 +5767,12 @@ xheap_reclaim_addresses (THREAD_ENTRY * thread_p, const HFID * hfid) goto exit_on_error; } + if (heap_page_is_bestspace (thread_p, curr_page_watcher.pgptr)) + { + pgbuf_ordered_unfix (thread_p, &curr_page_watcher); + continue; + } + /* * Are there any objects in this page ? * Compare against > 1 since every heap page contains a header record @@ -6334,65 +5827,78 @@ xheap_reclaim_addresses (THREAD_ENTRY * thread_p, const HFID * hfid) { spage_collect_statistics (curr_page_watcher.pgptr, &npages, &nrecords, &rec_length); - heap_hdr.estimates.num_pages += npages; - heap_hdr.estimates.num_recs += nrecords; - heap_hdr.estimates.recs_sumlen += rec_length; - - free_space = spage_get_free_space_without_saving (thread_p, curr_page_watcher.pgptr, &need_update); + heap_hdr.num_pages += npages; + heap_hdr.num_recs += nrecords; + heap_hdr.recs_sumlen += rec_length; - if (free_space > HEAP_DROP_FREE_SPACE) - { - if (best < HEAP_NUM_BEST_SPACESTATS) - { - heap_hdr.estimates.best[best].vpid = vpid; - heap_hdr.estimates.best[best].freespace = free_space; - best++; - } - else - { - heap_hdr.estimates.num_other_high_best++; - heap_stats_put_second_best (&heap_hdr, &vpid); - } + freespace = spage_get_free_space_without_saving (thread_p, curr_page_watcher.pgptr); - if (prm_get_integer_value (PRM_ID_HF_MAX_BESTSPACE_ENTRIES) > 0) - { - (void) heap_stats_add_bestspace (thread_p, hfid, &vpid, free_space); - } - } + candidate.freespace = freespace; + candidate.volid = vpid.volid; + candidate.pageid = vpid.pageid; + heap_bestspace_add_candidate (entries, &num_entries, max_candidates + max_bestpages, &candidate); pgbuf_ordered_unfix (thread_p, &curr_page_watcher); } } - heap_hdr.estimates.num_high_best = best; - /* - * Set the rest of the statistics to NULL - */ - for (; best < HEAP_NUM_BEST_SPACESTATS; best++) + /* update shards */ + ret = heap_update_bestspace_entries (thread_p, hfid, heap_hdr.bestspace.pages, heap_hdr.bestspace.num_pages, + entries, MIN (num_entries, max_bestpages)); + if (ret != NO_ERROR) + { + goto exit_on_error; + } + + /* find in-memory bestspace */ + bestspace = heap_find_bestspace (thread_p, NULL, (HFID *) hfid, NULL); + if (bestspace) { - VPID_SET_NULL (&heap_hdr.estimates.best[best].vpid); - heap_hdr.estimates.best[best].freespace = 0; + bestspace->reset (entries, MIN (num_entries, max_bestpages)); + bestspace->set_estimates (heap_hdr.num_pages, heap_hdr.num_recs, heap_hdr.recs_sumlen); + if (num_entries > max_bestpages) + { + bestspace->push_candidates (&entries[max_bestpages], num_entries - max_bestpages); + } + } + else + { + /* update heap candidates */ + if (num_entries > max_bestpages) + { + heap_hdr.bestspace.num_candidates = num_entries - max_bestpages; + for (i = 0; i < heap_hdr.bestspace.num_candidates; i++) + { + heap_hdr.bestspace.candidates[i] = entries[i + max_bestpages]; + } + } } - /* Log the desired changes.. and then change the header We need to log the header changes in order to always benefit + /* log the desired changes.. and then change the header We need to log the header changes in order to always benefit * from the updated statistics and in order to avoid referencing deleted pages in the statistics. */ addr.pgptr = hdr_page_watcher.pgptr; addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; log_append_undoredo_data (thread_p, RVHF_STATS, &addr, sizeof (HEAP_HDR_STATS), sizeof (HEAP_HDR_STATS), &initial_heap_hdr, hdr_recdes.data); - /* Now update the statistics */ + /* now update the statistics */ if (spage_update (thread_p, hdr_page_watcher.pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &hdr_recdes) != SP_SUCCESS) { goto exit_on_error; } + free_and_init (entries); pgbuf_ordered_set_dirty_and_free (thread_p, &hdr_page_watcher); return ret; exit_on_error: + if (entries) + { + free_and_init (entries); + } + if (hdr_page_watcher.pgptr != NULL) { pgbuf_ordered_unfix (thread_p, &hdr_page_watcher); @@ -8013,6 +7519,46 @@ heap_next_internal (THREAD_ENTRY * thread_p, const HFID * hfid, OID * class_oid, } } + if (heap_page_is_bestspace (thread_p, scan_cache->page_watcher.pgptr)) + { + if (reversed_direction) + { + if (heap_vpid_prev (thread_p, hfid, scan_cache->page_watcher.pgptr, &vpid) != NO_ERROR) + { + if (old_page_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &old_page_watcher); + } + pgbuf_ordered_unfix (thread_p, &scan_cache->page_watcher); + return S_ERROR; + } + } + else if (heap_vpid_next (thread_p, hfid, scan_cache->page_watcher.pgptr, &vpid) != NO_ERROR) + { + if (old_page_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &old_page_watcher); + } + pgbuf_ordered_unfix (thread_p, &scan_cache->page_watcher); + return S_ERROR; + } + + pgbuf_replace_watcher (thread_p, &scan_cache->page_watcher, &old_page_watcher); + oid.volid = vpid.volid; + oid.pageid = vpid.pageid; + oid.slotid = -1; + if (oid.pageid == NULL_PAGEID) + { + OID_SET_NULL (next_oid); + if (old_page_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &old_page_watcher); + } + return S_END; + } + continue; + } + if (!get_rec_info && HEAP_SCAN_IS_LOCAL_CACHE_READ (scan_cache)) { /* New page (VPID_EQ failed above) -- copy the frame to the local cache. Keep the live page fixed @@ -8246,50 +7792,6 @@ heap_next_internal (THREAD_ENTRY * thread_p, const HFID * hfid, OID * class_oid, return scan; } - -/* - * heap_page_next_fix_old () - Fix next page in heap file - * return: SCAN_CODE - * (Either of S_SUCCESS, S_END, S_ERROR) - * thread_p(in): Thread entry - * hfid(in): Heap file identifier - * curr_vpid(in/out): Current page identifier - * scan_cache(in): Scan cache - * - * Note: Fix the next page in the heap file chain. If curr_vpid is NULL, - * fix the first page of heap file. The fixed page is kept in the - * scan cache page watcher. - */ -SCAN_CODE -heap_page_next_fix_old (THREAD_ENTRY * thread_p, HFID * hfid, VPID * curr_vpid, HEAP_SCANCACHE * scan_cache) -{ - SCAN_CODE scan_code = S_SUCCESS; - /* get next page */ - if (VPID_ISNULL (curr_vpid)) - { - /* set to first page */ - curr_vpid->pageid = hfid->hpgid; - curr_vpid->volid = hfid->vfid.volid; - } - else - { - scan_cache->page_watcher.pgptr = - heap_scan_pb_lock_and_fetch (thread_p, curr_vpid, OLD_PAGE_PREVENT_DEALLOC, S_LOCK, NULL, - &scan_cache->page_watcher); - if (scan_cache->page_watcher.pgptr == NULL) - { - return S_ERROR; - } - heap_vpid_next (thread_p, hfid, scan_cache->page_watcher.pgptr, curr_vpid); - if (OID_ISNULL (curr_vpid)) - { - /* no more pages to scan, but do not unfix last page. (unfix at heap_next_1page) */ - return S_END; - } - } - return scan_code; -} - /* * heap_next_1page () - Find next record in current page * return: SCAN_CODE @@ -8390,6 +7892,12 @@ heap_next_1page (THREAD_ENTRY * thread_p, const HFID * hfid, const VPID * vpid, } } + if (heap_page_is_bestspace (thread_p, scan_cache->page_watcher.pgptr)) + { + OID_SET_NULL (next_oid); + return S_END; + } + if (HEAP_SCAN_IS_LOCAL_CACHE_READ (scan_cache)) { /* New page (VPID_EQ failed above) -- copy the frame to the local cache. Keep the live page fixed @@ -9360,6 +8868,104 @@ heap_is_object_not_null (THREAD_ENTRY * thread_p, OID * class_oid, const OID * o return doesexist; } +/* + * heap_update_statistics () - Update heap object/page statistics from heap pages + * return: NO_ERROR if successful, error code otherwise + * hfid(in): Object heap file identifier + * heap_hdr(in/out): Heap header statistics + * header_watcher(in): Fixed heap header page watcher + * + * NOTE: heap_update_statistics should be called after the in-memory bestspace exist + */ +static int +heap_update_statistics (THREAD_ENTRY * thread_p, const HFID * hfid, HEAP_HDR_STATS * heap_hdr, + PGBUF_WATCHER * header_watcher) +{ + VPID vpid; + PGBUF_WATCHER pg_watcher; + PGBUF_WATCHER old_pg_watcher; + uint64_t num_recs, recs_sumlen; + int npages, nrecords, reclength; + int num_pages; + int error_code = NO_ERROR; + + assert (thread_p); + assert (hfid); + assert (heap_hdr); + assert (header_watcher && header_watcher->pgptr); + + PGBUF_INIT_WATCHER (&pg_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + PGBUF_INIT_WATCHER (&old_pg_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + + spage_collect_statistics (header_watcher->pgptr, &npages, &nrecords, &reclength); + num_pages = npages; + num_recs = nrecords; + recs_sumlen = reclength; + + vpid = heap_hdr->next_vpid; + while (!VPID_ISNULL (&vpid)) + { + error_code = pgbuf_ordered_fix (thread_p, &vpid, OLD_PAGE_PREVENT_DEALLOC, PGBUF_LATCH_READ, &pg_watcher); + if (error_code != NO_ERROR) + { + ASSERT_ERROR_AND_SET (error_code); + goto exit_on_error; + } + + if (old_pg_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &old_pg_watcher); + } + +#if !defined (NDEBUG) + (void) pgbuf_check_page_ptype (thread_p, pg_watcher.pgptr, PAGE_HEAP); +#endif /* !NDEBUG */ + + error_code = heap_vpid_next (thread_p, hfid, pg_watcher.pgptr, &vpid); + if (error_code != NO_ERROR) + { + ASSERT_ERROR_AND_SET (error_code); + goto exit_on_error; + } + + if (!heap_page_is_bestspace (thread_p, pg_watcher.pgptr)) + { + spage_collect_statistics (pg_watcher.pgptr, &npages, &nrecords, &reclength); + num_pages += npages; + num_recs += nrecords; + recs_sumlen += reclength; + + heap_add_bestpage (thread_p, (HFID *) hfid, pg_watcher.pgptr, 0, header_watcher); + } + + pgbuf_replace_watcher (thread_p, &pg_watcher, &old_pg_watcher); + } + + if (old_pg_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &old_pg_watcher); + } + assert (pg_watcher.pgptr == NULL); + + heap_hdr->num_pages = num_pages; + heap_hdr->num_recs = num_recs; + heap_hdr->recs_sumlen = recs_sumlen; + + return NO_ERROR; + +exit_on_error: + if (pg_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &pg_watcher); + } + if (old_pg_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &old_pg_watcher); + } + + return (error_code == NO_ERROR && (error_code = er_errid ()) == NO_ERROR) ? ER_FAILED : error_code; +} + /* * heap_get_num_objects () - Count the number of objects * return: number of records or -1 in case of an error @@ -9375,6 +8981,9 @@ heap_is_object_not_null (THREAD_ENTRY * thread_p, OID * class_oid, const OID * o int heap_get_num_objects (THREAD_ENTRY * thread_p, const HFID * hfid, int *npages, int *nobjs, int *avg_length) { + // *INDENT-OFF* + cubstorage::bestspace *bestspace; + // *INDENT-ON* VPID vpid; /* Page-volume identifier */ LOG_DATA_ADDR addr_hdr; /* Address of logging data */ RECDES hdr_recdes; /* Record descriptor to point to space statistics */ @@ -9382,9 +8991,8 @@ heap_get_num_objects (THREAD_ENTRY * thread_p, const HFID * hfid, int *npages, i PGBUF_WATCHER hdr_pg_watcher; /* - * Get the heap header in exclusive mode and call the synchronization to - * update the statistics of the heap. The number of record/objects is - * updated. + * Get the heap header in exclusive mode and synchronize heap statistics. + * The number of records/objects is updated by scanning all heap pages. */ PGBUF_INIT_WATCHER (&hdr_pg_watcher, PGBUF_ORDERED_HEAP_HDR, hfid); @@ -9411,16 +9019,30 @@ heap_get_num_objects (THREAD_ENTRY * thread_p, const HFID * hfid, int *npages, i } heap_hdr = (HEAP_HDR_STATS *) hdr_recdes.data; - if (heap_stats_sync_bestspace (thread_p, hfid, heap_hdr, pgbuf_get_vpid_ptr (hdr_pg_watcher.pgptr), true, true) < 0) + /* find or build the in-memory bestspace */ + bestspace = heap_find_bestspace (thread_p, &heap_hdr->class_oid, (HFID *) hfid, &hdr_pg_watcher); + if (!bestspace) + { + pgbuf_ordered_unfix (thread_p, &hdr_pg_watcher); + + assert_release (false); + return ER_FAILED; + } + + // update heap header + if (heap_update_statistics (thread_p, hfid, heap_hdr, &hdr_pg_watcher) != NO_ERROR) { pgbuf_ordered_unfix (thread_p, &hdr_pg_watcher); return ER_FAILED; } - *npages = heap_hdr->estimates.num_pages; - *nobjs = heap_hdr->estimates.num_recs; + // update in-memory + bestspace->set_estimates (heap_hdr->num_pages, heap_hdr->num_recs, heap_hdr->recs_sumlen); + + *npages = heap_hdr->num_pages; + *nobjs = heap_hdr->num_recs; if (*nobjs > 0) { - *avg_length = (int) ((heap_hdr->estimates.recs_sumlen / (float) *nobjs) + 0.9); + *avg_length = (int) ((heap_hdr->recs_sumlen / (float) *nobjs) + 0.9); } else { @@ -9447,50 +9069,61 @@ heap_get_num_objects (THREAD_ENTRY * thread_p, const HFID * hfid, int *npages, i int heap_estimate (THREAD_ENTRY * thread_p, const HFID * hfid, int *npages, int *nobjs, int *avg_length) { + // *INDENT-OFF* + cubstorage::bestspace *bestspace; + // *INDENT-ON* VPID vpid; /* Page-volume identifier */ PAGE_PTR hdr_pgptr = NULL; /* Page pointer */ RECDES hdr_recdes; /* Record descriptor to point to space statistics */ HEAP_HDR_STATS *heap_hdr; /* Heap header */ + std::uint64_t recs_num, recs_sumlen; + int num_pages; - /* - * Get the heap header in shared mode since it is an estimation of the - * number of objects. - */ - - vpid.volid = hfid->vfid.volid; - vpid.pageid = hfid->hpgid; - - hdr_pgptr = pgbuf_fix (thread_p, &vpid, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH); - if (hdr_pgptr == NULL) + bestspace = heap_find_bestspace (thread_p, NULL, (HFID *) hfid, NULL); + if (bestspace) { - /* something went wrong. Unable to fetch header page */ - return ER_FAILED; + bestspace->get_estimates (num_pages, recs_num, recs_sumlen); + *npages = num_pages; + *nobjs = (int) recs_num; } + else + { + vpid.volid = hfid->vfid.volid; + vpid.pageid = hfid->hpgid; + + hdr_pgptr = pgbuf_fix (thread_p, &vpid, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH); + if (hdr_pgptr == NULL) + { + /* something went wrong. Unable to fetch header page */ + return ER_FAILED; + } #if !defined (NDEBUG) - (void) pgbuf_check_page_ptype (thread_p, hdr_pgptr, PAGE_HEAP); + (void) pgbuf_check_page_ptype (thread_p, hdr_pgptr, PAGE_HEAP); #endif /* !NDEBUG */ - if (spage_get_record (thread_p, hdr_pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &hdr_recdes, PEEK) != S_SUCCESS) - { + if (spage_get_record (thread_p, hdr_pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &hdr_recdes, PEEK) != S_SUCCESS) + { + pgbuf_unfix_and_init (thread_p, hdr_pgptr); + return ER_FAILED; + } + + heap_hdr = (HEAP_HDR_STATS *) hdr_recdes.data; + *npages = heap_hdr->num_pages; + *nobjs = heap_hdr->num_recs; + recs_sumlen = heap_hdr->recs_sumlen; + pgbuf_unfix_and_init (thread_p, hdr_pgptr); - return ER_FAILED; } - heap_hdr = (HEAP_HDR_STATS *) hdr_recdes.data; - *npages = heap_hdr->estimates.num_pages; - *nobjs = heap_hdr->estimates.num_recs; if (*nobjs > 0) { - *avg_length = (int) ((heap_hdr->estimates.recs_sumlen / (float) *nobjs) + 0.9); + *avg_length = (int) ((recs_sumlen / (float) *nobjs) + 0.9); } else { *avg_length = 0; } - - pgbuf_unfix_and_init (thread_p, hdr_pgptr); - return *npages; } @@ -9610,6 +9243,13 @@ heap_get_capacity (THREAD_ENTRY * thread_p, const HFID * hfid, INT64 * num_recs, goto exit_on_error; } + if (heap_page_is_bestspace (thread_p, pg_watcher.pgptr)) + { + (void) heap_vpid_next (thread_p, hfid, pg_watcher.pgptr, &vpid); + pgbuf_replace_watcher (thread_p, &pg_watcher, &old_pg_watcher); + continue; + } + slotid = -1; j = spage_number_of_records (pg_watcher.pgptr); @@ -14633,83 +14273,6 @@ heap_check_all_pages (THREAD_ENTRY * thread_p, HFID * hfid) } } - if (valid_pg == DISK_VALID) - { - /* - * Check the statistics entries in the header - */ - - /* Fetch the header page of the heap file */ - vpid.volid = hfid->vfid.volid; - vpid.pageid = hfid->hpgid; - - pgptr = heap_scan_pb_lock_and_fetch (thread_p, &vpid, OLD_PAGE, S_LOCK, NULL, NULL); - if (pgptr == NULL) - { - return DISK_ERROR; - } - -#if !defined (NDEBUG) - (void) pgbuf_check_page_ptype (thread_p, pgptr, PAGE_HEAP); -#endif /* !NDEBUG */ - - if (spage_get_record (thread_p, pgptr, HEAP_HEADER_AND_CHAIN_SLOTID, &hdr_recdes, PEEK) != S_SUCCESS) - { - /* Unable to peek heap header record */ - pgbuf_unfix_and_init (thread_p, pgptr); - - return DISK_ERROR; - } - - heap_hdr = (HEAP_HDR_STATS *) hdr_recdes.data; - for (i = 0; i < HEAP_NUM_BEST_SPACESTATS && valid_pg != DISK_ERROR; i++) - { - if (!VPID_ISNULL (&heap_hdr->estimates.best[i].vpid)) - { - valid = file_check_vpid (thread_p, &hfid->vfid, &heap_hdr->estimates.best[i].vpid); - if (valid != DISK_VALID) - { - valid_pg = valid; - break; - } - } - } - -#if defined(SA_MODE) - if (prm_get_integer_value (PRM_ID_HF_MAX_BESTSPACE_ENTRIES) > 0) - { - HEAP_STATS_ENTRY *ent; - void *last; - int rc; - - rc = pthread_mutex_lock (&heap_Bestspace->bestspace_mutex); - - last = NULL; - while ((ent = (HEAP_STATS_ENTRY *) mht_get2 (heap_Bestspace->hfid_ht, hfid, &last)) != NULL) - { - assert_release (!VPID_ISNULL (&ent->best.vpid)); - if (!VPID_ISNULL (&ent->best.vpid)) - { - valid_pg = file_check_vpid (thread_p, &hfid->vfid, &ent->best.vpid); - if (valid_pg != DISK_VALID) - { - break; - } - } - assert_release (ent->best.freespace > 0); - } - - assert (mht_count (heap_Bestspace->vpid_ht) == mht_count (heap_Bestspace->hfid_ht)); - - pthread_mutex_unlock (&heap_Bestspace->bestspace_mutex); - } -#endif - - pgbuf_unfix_and_init (thread_p, pgptr); - - /* Need to check for the overflow pages.... */ - } - return valid_pg; } @@ -14825,53 +14388,19 @@ heap_check_all_heaps (THREAD_ENTRY * thread_p) static int heap_dump_hdr (FILE * fp, HEAP_HDR_STATS * heap_hdr) { - int i, j; int avg_length; int ret = NO_ERROR; - avg_length = ((heap_hdr->estimates.num_recs > 0) - ? (int) ((heap_hdr->estimates.recs_sumlen / (float) heap_hdr->estimates.num_recs) + 0.9) : 0); + avg_length = ((heap_hdr->num_recs > 0) ? (int) ((heap_hdr->recs_sumlen / (float) heap_hdr->num_recs) + 0.9) : 0); fprintf (fp, "CLASS_OID = %2d|%4d|%2d, ", heap_hdr->class_oid.volid, heap_hdr->class_oid.pageid, heap_hdr->class_oid.slotid); - fprintf (fp, "OVF_VFID = %4d|%4d, NEXT_VPID = %4d|%4d\n", heap_hdr->ovf_vfid.volid, heap_hdr->ovf_vfid.fileid, - heap_hdr->next_vpid.volid, heap_hdr->next_vpid.pageid); + fprintf (fp, "OVF_VFID = %4d|%4d, NEXT_VPID = %4d|%4d, Last vpid = %4d|%4d\n", heap_hdr->ovf_vfid.volid, + heap_hdr->ovf_vfid.fileid, heap_hdr->next_vpid.volid, heap_hdr->next_vpid.pageid, heap_hdr->last_vpid.volid, + heap_hdr->last_vpid.pageid); fprintf (fp, "unfill_space = %4d\n", heap_hdr->unfill_space); - fprintf (fp, "Estimated: num_pages = %d, num_recs = %d, avg reclength = %d\n", heap_hdr->estimates.num_pages, - heap_hdr->estimates.num_recs, avg_length); - fprintf (fp, "Estimated: num high best = %d, num others(not in array) high best = %d\n", - heap_hdr->estimates.num_high_best, heap_hdr->estimates.num_other_high_best); - fprintf (fp, "Hint of best set of vpids with head = %d\n", heap_hdr->estimates.head); - - for (j = 0, i = 0; i < HEAP_NUM_BEST_SPACESTATS; j++, i++) - { - if (j != 0 && j % 5 == 0) - { - fprintf (fp, "\n"); - } - fprintf (fp, "%4d|%4d %4d,", heap_hdr->estimates.best[i].vpid.volid, heap_hdr->estimates.best[i].vpid.pageid, - heap_hdr->estimates.best[i].freespace); - } - fprintf (fp, "\n"); - - fprintf (fp, - "Second best: num hints = %d, head of hints = %d, tail (next to insert) of hints = %d, num subs = %d\n", - heap_hdr->estimates.num_second_best, heap_hdr->estimates.head_second_best, - heap_hdr->estimates.tail_second_best, heap_hdr->estimates.num_substitutions); - for (j = 0, i = 0; i < HEAP_NUM_BEST_SPACESTATS; j++, i++) - { - if (j != 0 && j % 5 == 0) - { - fprintf (fp, "\n"); - } - fprintf (fp, "%4d|%4d,", heap_hdr->estimates.second_best[i].volid, heap_hdr->estimates.second_best[i].pageid); - } - fprintf (fp, "\n"); - - fprintf (fp, "Last vpid = %4d|%4d\n", heap_hdr->estimates.last_vpid.volid, heap_hdr->estimates.last_vpid.pageid); - - fprintf (fp, "Next full search vpid = %4d|%4d\n", heap_hdr->estimates.full_search_vpid.volid, - heap_hdr->estimates.full_search_vpid.pageid); + fprintf (fp, "Estimated: num_pages = %d, num_recs = %ld, avg reclength = %d\n", heap_hdr->num_pages, + heap_hdr->num_recs, avg_length); return ret; } @@ -15390,6 +14919,11 @@ heap_chkreloc_next (THREAD_ENTRY * thread_p, HEAP_CHKALL_RELOCOIDS * chk, PAGE_P return DISK_VALID; } + if (heap_page_is_bestspace (thread_p, pgptr)) + { + return DISK_VALID; + } + if (chk->verify_not_vacuumed && heap_get_class_oid_from_page (thread_p, pgptr, &class_oid) != NO_ERROR) { chk->not_vacuumed_res = DISK_ERROR; @@ -15850,108 +15384,6 @@ heap_chnguess_finalize (void) return ret; } -/* - * heap_stats_bestspace_initialize () - Initialize structure of best space - * return: NO_ERROR - */ -static int -heap_stats_bestspace_initialize (void) -{ - int ret = NO_ERROR; - - if (heap_Bestspace != NULL) - { - ret = heap_stats_bestspace_finalize (); - if (ret != NO_ERROR) - { - goto exit_on_error; - } - } - - heap_Bestspace = &heap_Bestspace_cache_area; - - pthread_mutex_init (&heap_Bestspace->bestspace_mutex, NULL); - - heap_Bestspace->num_stats_entries = 0; - - heap_Bestspace->hfid_ht = - mht_create ("Memory hash HFID to {bestspace}", HEAP_STATS_ENTRY_MHT_EST_SIZE, heap_hash_hfid, heap_compare_hfid); - if (heap_Bestspace->hfid_ht == NULL) - { - goto exit_on_error; - } - - heap_Bestspace->vpid_ht = - mht_create ("Memory hash VPID to {bestspace}", HEAP_STATS_ENTRY_MHT_EST_SIZE, heap_hash_vpid, heap_compare_vpid); - if (heap_Bestspace->vpid_ht == NULL) - { - goto exit_on_error; - } - - heap_Bestspace->free_list_count = 0; - heap_Bestspace->free_list = NULL; - - return ret; - -exit_on_error: - - return (ret == NO_ERROR) ? ER_FAILED : ret; -} - -/* - * heap_stats_bestspace_finalize () - Finish best space information - * return: NO_ERROR - * - * Note: Destroy hash table and memory for entries. - */ -static int -heap_stats_bestspace_finalize (void) -{ - HEAP_STATS_ENTRY *ent; - int ret = NO_ERROR; - - if (heap_Bestspace == NULL) - { - return NO_ERROR; - } - - if (heap_Bestspace->vpid_ht != NULL) - { - (void) mht_map_no_key (NULL, heap_Bestspace->vpid_ht, heap_stats_entry_free, NULL); - while (heap_Bestspace->free_list_count > 0) - { - ent = heap_Bestspace->free_list; - assert_release (ent != NULL); - - heap_Bestspace->free_list = ent->next; - ent->next = NULL; - - free (ent); - - heap_Bestspace->free_list_count--; - } - assert_release (heap_Bestspace->free_list == NULL); - } - - if (heap_Bestspace->vpid_ht != NULL) - { - mht_destroy (heap_Bestspace->vpid_ht); - heap_Bestspace->vpid_ht = NULL; - } - - if (heap_Bestspace->hfid_ht != NULL) - { - mht_destroy (heap_Bestspace->hfid_ht); - heap_Bestspace->hfid_ht = NULL; - } - - pthread_mutex_destroy (&heap_Bestspace->bestspace_mutex); - - heap_Bestspace = NULL; - - return ret; -} - /* * heap_chnguess_decache () - Decache a specific entry or all entries * return: NO_ERROR @@ -16605,11 +16037,11 @@ int heap_rv_undo_insert (THREAD_ENTRY * thread_p, LOG_RCV * rcv) { INT16 slotid; - int free_space = 0; + int freespace = 0; if (LOG_ISRESTARTED ()) { - free_space = spage_get_free_space_without_saving (thread_p, rcv->pgptr, NULL); + freespace = spage_get_free_space_without_saving (thread_p, rcv->pgptr); } slotid = rcv->offset; @@ -16637,7 +16069,7 @@ heap_rv_undo_insert (THREAD_ENTRY * thread_p, LOG_RCV * rcv) assert (heap_hfid_isvalid (&hfid) == DISK_VALID); #endif - heap_stats_update (thread_p, rcv->pgptr, &hfid, free_space); + heap_add_bestpage (thread_p, &hfid, rcv->pgptr, freespace); } end: @@ -17707,6 +17139,12 @@ heap_compact_pages (THREAD_ENTRY * thread_p, OID * class_oid) goto exit_on_error; } + if (heap_page_is_bestspace (thread_p, pg_watcher.pgptr)) + { + pgbuf_replace_watcher (thread_p, &pg_watcher, &old_pg_watcher); + continue; + } + if (spage_compact (thread_p, pg_watcher.pgptr) != NO_ERROR) { pgbuf_ordered_unfix (thread_p, &pg_watcher); @@ -18784,51 +18222,8 @@ heap_header_next_scan (THREAD_ENTRY * thread_p, int cursor, DB_VALUE ** out_valu goto cleanup; } - /* Unfill space */ - db_make_int (out_values[idx], heap_hdr->unfill_space); - idx++; - - /* Estimated */ - db_make_bigint (out_values[idx], heap_hdr->estimates.num_pages); - idx++; - - db_make_bigint (out_values[idx], heap_hdr->estimates.num_recs); - idx++; - - avg_length = ((heap_hdr->estimates.num_recs > 0) - ? (int) ((heap_hdr->estimates.recs_sumlen / (float) heap_hdr->estimates.num_recs) + 0.9) : 0); - db_make_int (out_values[idx], avg_length); - idx++; - - db_make_int (out_values[idx], heap_hdr->estimates.num_high_best); - idx++; - - db_make_int (out_values[idx], heap_hdr->estimates.num_other_high_best); - idx++; - - db_make_int (out_values[idx], heap_hdr->estimates.head); - idx++; - - /* Estimates_best_list */ - buf_p = buf; - end = buf + sizeof (buf); - for (i = 0; i < HEAP_NUM_BEST_SPACESTATS; i++) - { - if (i > 0) - { - if (fill_string_to_buffer (&buf_p, end, ", ") == -1) - { - break; - } - } - - heap_bestspace_to_string (temp, sizeof (temp), heap_hdr->estimates.best + i); - if (fill_string_to_buffer (&buf_p, end, temp) == -1) - { - break; - } - } - + /* Last vpid */ + vpid_to_string (buf, sizeof (buf), &heap_hdr->last_vpid); error = db_make_string_copy (out_values[idx], buf); idx++; if (error != NO_ERROR) @@ -18836,60 +18231,20 @@ heap_header_next_scan (THREAD_ENTRY * thread_p, int cursor, DB_VALUE ** out_valu goto cleanup; } - db_make_int (out_values[idx], heap_hdr->estimates.num_second_best); - idx++; - - db_make_int (out_values[idx], heap_hdr->estimates.head_second_best); - idx++; - - db_make_int (out_values[idx], heap_hdr->estimates.tail_second_best); - idx++; - - db_make_int (out_values[idx], heap_hdr->estimates.num_substitutions); + /* Unfill space */ + db_make_int (out_values[idx], heap_hdr->unfill_space); idx++; - /* Estimates_second_best */ - buf_p = buf; - end = buf + sizeof (buf); - for (i = 0; i < HEAP_NUM_BEST_SPACESTATS; i++) - { - if (i > 0) - { - if (fill_string_to_buffer (&buf_p, end, ", ") == -1) - { - break; - } - } - - vpid_to_string (temp, sizeof (temp), heap_hdr->estimates.second_best + i); - if (fill_string_to_buffer (&buf_p, end, temp) == -1) - { - break; - } - } - - error = db_make_string_copy (out_values[idx], buf); + /* Estimated */ + db_make_bigint (out_values[idx], heap_hdr->num_pages); idx++; - if (error != NO_ERROR) - { - goto cleanup; - } - vpid_to_string (buf, sizeof (buf), &heap_hdr->estimates.last_vpid); - error = db_make_string_copy (out_values[idx], buf); + db_make_bigint (out_values[idx], heap_hdr->num_recs); idx++; - if (error != NO_ERROR) - { - goto cleanup; - } - vpid_to_string (buf, sizeof (buf), &heap_hdr->estimates.full_search_vpid); - error = db_make_string_copy (out_values[idx], buf); + avg_length = ((heap_hdr->num_recs > 0) ? (int) ((heap_hdr->recs_sumlen / (float) heap_hdr->num_recs) + 0.9) : 0); + db_make_int (out_values[idx], avg_length); idx++; - if (error != NO_ERROR) - { - goto cleanup; - } assert (idx == out_cnt); @@ -19102,52 +18457,10 @@ heap_header_capacity_end_scan (THREAD_ENTRY * thread_p, void **ptr) db_private_free (thread_p, ctx->hfids); } - db_private_free (thread_p, ctx); - *ptr = NULL; - - return NO_ERROR; -} - -static char * -heap_bestspace_to_string (char *buf, int buf_size, const HEAP_BESTSPACE * hb) -{ - snprintf (buf, buf_size, "((%d|%d), %d)", hb->vpid.volid, hb->vpid.pageid, hb->freespace); - buf[buf_size - 1] = '\0'; - - return buf; -} - -/* - * fill_string_to_buffer () - fill string into buffer - * - * ----------------------------- - * | buffer | - * ----------------------------- - * ^ ^ - * | | - * start end - * - * return: the count of characters (not include '\0') which has been - * filled into buffer; -1 means error. - * start(in/out): After filling, start move to the '\0' position. - * end(in): The first unavailble position. - * str(in): - */ -static int -fill_string_to_buffer (char **start, char *end, const char *str) -{ - int len = (int) strlen (str); - - if (*start + len >= end) - { - return -1; - } - - memcpy (*start, str, len); - *start += len; - **start = '\0'; + db_private_free (thread_p, ctx); + *ptr = NULL; - return len; + return NO_ERROR; } /* @@ -19248,16 +18561,39 @@ heap_page_next (THREAD_ENTRY * thread_p, const OID * class_oid, const HFID * hfi pgbuf_replace_watcher (thread_p, &pg_watcher, &old_pg_watcher); } - /* get page pointer to next page */ - pg_watcher.pgptr = - heap_scan_pb_lock_and_fetch (thread_p, next_vpid, OLD_PAGE_PREVENT_DEALLOC, S_LOCK, NULL, &pg_watcher); - if (old_pg_watcher.pgptr != NULL) - { - pgbuf_ordered_unfix (thread_p, &old_pg_watcher); - } - if (pg_watcher.pgptr == NULL) + while (true) { - return S_ERROR; + /* get page pointer to next page */ + pg_watcher.pgptr = + heap_scan_pb_lock_and_fetch (thread_p, next_vpid, OLD_PAGE_PREVENT_DEALLOC, S_LOCK, NULL, &pg_watcher); + if (old_pg_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &old_pg_watcher); + } + if (pg_watcher.pgptr == NULL) + { + return S_ERROR; + } + + if (!heap_page_is_bestspace (thread_p, pg_watcher.pgptr)) + { + break; + } + + if (heap_vpid_next (thread_p, hfid, pg_watcher.pgptr, next_vpid) != NO_ERROR) + { + pgbuf_ordered_unfix (thread_p, &pg_watcher); + return S_ERROR; + } + + if (VPID_ISNULL (next_vpid)) + { + pgbuf_ordered_unfix (thread_p, &pg_watcher); + return S_END; + } + + /* keep the bestspace page fixed while following the link so vacuum cannot relink past it */ + pgbuf_replace_watcher (thread_p, &pg_watcher, &old_pg_watcher); } /* read page information and return scan code */ @@ -19323,15 +18659,38 @@ heap_page_prev (THREAD_ENTRY * thread_p, const OID * class_oid, const HFID * hfi pgbuf_replace_watcher (thread_p, &pg_watcher, &old_pg_watcher); } - pg_watcher.pgptr = - heap_scan_pb_lock_and_fetch (thread_p, prev_vpid, OLD_PAGE_PREVENT_DEALLOC, S_LOCK, NULL, &pg_watcher); - if (old_pg_watcher.pgptr != NULL) - { - pgbuf_ordered_unfix (thread_p, &old_pg_watcher); - } - if (pg_watcher.pgptr == NULL) + while (true) { - return S_ERROR; + pg_watcher.pgptr = + heap_scan_pb_lock_and_fetch (thread_p, prev_vpid, OLD_PAGE_PREVENT_DEALLOC, S_LOCK, NULL, &pg_watcher); + if (old_pg_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &old_pg_watcher); + } + if (pg_watcher.pgptr == NULL) + { + return S_ERROR; + } + + if (!heap_page_is_bestspace (thread_p, pg_watcher.pgptr)) + { + break; + } + + if (heap_vpid_prev (thread_p, hfid, pg_watcher.pgptr, prev_vpid) != NO_ERROR) + { + pgbuf_ordered_unfix (thread_p, &pg_watcher); + return S_ERROR; + } + + if (VPID_ISNULL (prev_vpid)) + { + pgbuf_ordered_unfix (thread_p, &pg_watcher); + return S_END; + } + + /* keep the bestspace page fixed while following the link so vacuum cannot relink past it */ + pgbuf_replace_watcher (thread_p, &pg_watcher, &old_pg_watcher); } /* read page information and return scan code */ @@ -20984,9 +20343,11 @@ heap_get_insert_location_with_lock (THREAD_ENTRY * thread_p, HEAP_OPERATION_CONT if (home_hint_p == NULL) { + assert (!HFID_IS_NULL (&context->hfid)); + /* find and fix page for insert */ - if (heap_stats_find_best_page (thread_p, &context->hfid, context->recdes_p->length, - true, context->scan_cache_p, context->home_page_watcher_p) == NULL) + if (heap_find_bestpage (thread_p, &context->class_oid, &context->hfid, context->recdes_p->length, true, + context->home_page_watcher_p) != NO_ERROR) { ASSERT_ERROR_AND_SET (error_code); return error_code; @@ -21127,8 +20488,10 @@ heap_find_location_and_insert_rec_newhome (THREAD_ENTRY * thread_p, HEAP_OPERATI } #endif - if (heap_stats_find_best_page (thread_p, &context->hfid, context->recdes_p->length, false, - context->scan_cache_p, context->home_page_watcher_p) == NULL) + assert (!HFID_IS_NULL (&context->hfid)); + + if (heap_find_bestpage (thread_p, &context->class_oid, &context->hfid, context->recdes_p->length, false, + context->home_page_watcher_p) != NO_ERROR) { ASSERT_ERROR_AND_SET (error_code); return error_code; @@ -22474,7 +21837,7 @@ heap_delete_home (THREAD_ENTRY * thread_p, HEAP_OPERATION_CONTEXT * context, boo static int heap_delete_physical (THREAD_ENTRY * thread_p, HFID * hfid_p, PAGE_PTR page_p, OID * oid_p) { - int free_space; + int freespace; /* check input */ assert (hfid_p != NULL); @@ -22483,7 +21846,7 @@ heap_delete_physical (THREAD_ENTRY * thread_p, HFID * hfid_p, PAGE_PTR page_p, O assert (oid_p->slotid != NULL_SLOTID); /* save old freespace */ - free_space = spage_get_free_space_without_saving (thread_p, page_p, NULL); + freespace = spage_get_free_space_without_saving (thread_p, page_p); /* physical deletion */ if (spage_delete (thread_p, page_p, oid_p->slotid) == NULL_SLOTID) @@ -22491,8 +21854,8 @@ heap_delete_physical (THREAD_ENTRY * thread_p, HFID * hfid_p, PAGE_PTR page_p, O return ER_FAILED; } - /* update statistics */ - heap_stats_update (thread_p, page_p, hfid_p, free_space); + /* insert into bestspace cadidates */ + heap_add_bestpage (thread_p, hfid_p, page_p, freespace); /* mark page as dirty */ pgbuf_set_dirty (thread_p, page_p, DONT_FREE); @@ -22895,18 +22258,18 @@ heap_update_relocation (THREAD_ENTRY * thread_p, HEAP_OPERATION_CONTEXT * contex context->recdes_p->type = REC_NEWHOME; rc = heap_insert_newhome (thread_p, context, context->recdes_p, &new_forward_oid, newhome_pg_watcher_p); - /* redo lsa for SUPPLEMENT_UPDATE log : relocation to relocation */ - if (context->do_supplemental_log) - { - LSA_COPY (&context->supp_redo_lsa, &tdes->tail_lsa); - } - if (rc != NO_ERROR) { ASSERT_ERROR (); goto exit; } + /* redo lsa for SUPPLEMENT_UPDATE log : relocation to relocation */ + if (context->do_supplemental_log) + { + LSA_COPY (&context->supp_redo_lsa, &tdes->tail_lsa); + } + /* new home record will be a REC_RELOCATION and will be placed in the original home page */ heap_build_forwarding_recdes (&new_home_recdes, REC_RELOCATION, &new_forward_oid); @@ -23220,18 +22583,18 @@ heap_update_home (THREAD_ENTRY * thread_p, HEAP_OPERATION_CONTEXT * context, boo context->recdes_p->type = REC_NEWHOME; error_code = heap_insert_newhome (thread_p, context, context->recdes_p, &forward_oid, newhome_pg_watcher_p); - /* redo lsa for SUPPLEMENT_UPDATE : REC_HOME to REC_RELOCATION */ - if (context->do_supplemental_log) - { - LSA_COPY (&context->supp_redo_lsa, &tdes->tail_lsa); - } - if (error_code != NO_ERROR) { ASSERT_ERROR (); goto exit; } + /* redo lsa for SUPPLEMENT_UPDATE : REC_HOME to REC_RELOCATION */ + if (context->do_supplemental_log) + { + LSA_COPY (&context->supp_redo_lsa, &tdes->tail_lsa); + } + /* forwarding record is REC_RELOCATION */ heap_build_forwarding_recdes (&forwarding_recdes, REC_RELOCATION, &forward_oid); @@ -23912,7 +23275,7 @@ heap_delete_logical (THREAD_ENTRY * thread_p, HEAP_OPERATION_CONTEXT * context) goto error; } - if (context->do_supplemental_log == true) + if (rc == NO_ERROR && context->do_supplemental_log == true) { (void) log_append_supplemental_lsa (thread_p, thread_p->trigger_involved ? LOG_SUPPLEMENT_TRIGGER_DELETE : @@ -23923,14 +23286,14 @@ heap_delete_logical (THREAD_ENTRY * thread_p, HEAP_OPERATION_CONTEXT * context) error: /* unfix or keep home page */ - if (context->scan_cache_p != NULL && context->home_page_watcher_p == &context->home_page_watcher - && context->scan_cache_p->cache_last_fix_page == true) + if (context->home_page_watcher_p->pgptr != NULL) { - pgbuf_replace_watcher (thread_p, context->home_page_watcher_p, &context->scan_cache_p->page_watcher); - } - else - { - if (context->home_page_watcher_p->pgptr != NULL) + if (context->scan_cache_p != NULL && context->home_page_watcher_p == &context->home_page_watcher + && context->scan_cache_p->cache_last_fix_page == true) + { + pgbuf_replace_watcher (thread_p, context->home_page_watcher_p, &context->scan_cache_p->page_watcher); + } + else { pgbuf_ordered_unfix (thread_p, context->home_page_watcher_p); } @@ -24554,6 +23917,12 @@ heap_vacuum_all_objects (THREAD_ENTRY * thread_p, HEAP_SCANCACHE * upd_scancache goto exit; } + if (heap_page_is_bestspace (thread_p, pg_watcher.pgptr)) + { + pgbuf_replace_watcher (thread_p, &pg_watcher, &old_pg_watcher); + continue; + } + temp_oid.volid = vpid.volid; temp_oid.pageid = vpid.pageid; worker.n_heap_objects = spage_number_of_slots (pg_watcher.pgptr) - 1; @@ -25176,26 +24545,6 @@ heap_rv_remove_flags_from_offset (INT16 offset) return offset & (~HEAP_RV_FLAG_VACUUM_STATUS_CHANGE); } -/* - * heap_should_try_update_stat () - checks if an heap update statistics is - * indicated - * - * - * return : NO_ERROR - * thread_p (in) : Thread entry. - * rcv (in) : Recovery data. - */ -bool -heap_should_try_update_stat (const int current_freespace, const int prev_freespace) -{ - if (current_freespace > prev_freespace && current_freespace > HEAP_DROP_FREE_SPACE - && prev_freespace < HEAP_DROP_FREE_SPACE) - { - return true; - } - return false; -} - /* * heap_scancache_add_partition_node () - add a new partition information to * to the scan_cache's partition list. @@ -26193,17 +25542,6 @@ heap_rv_undo_ovf_update (THREAD_ENTRY * thread_p, LOG_RCV * rcv) return error_code; } -/* - * heap_get_best_space_num_stats_entries - Returns the number of num_stats_entries - * return : the number of entries in the heap - * - */ -int -heap_get_best_space_num_stats_entries (void) -{ - return heap_Bestspace->num_stats_entries; -} - /* * heap_get_hfid_from_vfid () - Get hfid for file. Caller must be sure this file belong to a heap. * @@ -26401,6 +25739,187 @@ heap_alloc_new_page (THREAD_ENTRY * thread_p, HFID * hfid, OID class_oid, PGBUF_ return error_code; } +int +heap_alloc_new_pages (THREAD_ENTRY * thread_p, HFID * hfid, int npages, VPID * new_page_vpids, PGBUF_WATCHER * new_pg_watcher) +{ + PGBUF_WATCHER heap_hdr_watcher, last_pg_watcher; + PGBUF_WATCHER page_watcher; + HEAP_CHAIN new_page_chain, last_chain_prev; + HEAP_CHAIN *last_chain; + VPID *prev_vpid, *next_vpid; + VPID null_vpid, last_vpid; + VPID heap_hdr_vpid; + HEAP_HDR_STATS heap_hdr_prev; + HEAP_HDR_STATS *heap_hdr = NULL; + int error_code = NO_ERROR; + int i; + + LOG_DATA_ADDR addr = LOG_DATA_ADDR_INITIALIZER; + bool is_sysop_started = false; + + assert (thread_p != NULL); + assert (hfid != NULL); + assert (npages > 0); + assert (new_page_vpids != NULL); + assert (new_pg_watcher != NULL); + + PGBUF_INIT_WATCHER (new_pg_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + PGBUF_INIT_WATCHER (&heap_hdr_watcher, PGBUF_ORDERED_HEAP_HDR, hfid); + PGBUF_INIT_WATCHER (&last_pg_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + PGBUF_INIT_WATCHER (&page_watcher, PGBUF_ORDERED_HEAP_NORMAL, hfid); + + for (i = 0; i < npages; i++) + { + VPID_SET_NULL (&new_page_vpids[i]); + } + + heap_hdr_vpid.volid = hfid->vfid.volid; + heap_hdr_vpid.pageid = hfid->hpgid; + error_code = pgbuf_ordered_fix (thread_p, &heap_hdr_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, &heap_hdr_watcher); + if (error_code != NO_ERROR) + { + ASSERT_ERROR (); + goto error; + } + + heap_hdr = heap_get_header_stats_ptr (thread_p, heap_hdr_watcher.pgptr); + if (heap_hdr == NULL) + { + assert_release (false); + error_code = ER_FAILED; + goto error; + } + + assert (!VPID_ISNULL (&heap_hdr->last_vpid)); + heap_hdr_prev = *heap_hdr; + last_vpid = heap_hdr->last_vpid; + + new_page_chain.class_oid = heap_hdr->class_oid; + VPID_SET_NULL (&new_page_chain.prev_vpid); + VPID_SET_NULL (&new_page_chain.next_vpid); + new_page_chain.max_mvccid = MVCCID_NULL; + new_page_chain.flags = 0; + HEAP_PAGE_SET_VACUUM_STATUS (&new_page_chain, HEAP_PAGE_VACUUM_NONE); + + log_sysop_start (thread_p); + is_sysop_started = true; + + error_code = file_alloc_multiple (thread_p, &hfid->vfid, heap_vpid_init_new, &new_page_chain, npages, new_page_vpids); + if (error_code != NO_ERROR) + { + ASSERT_ERROR (); + goto error; + } + + VPID_SET_NULL (&null_vpid); + for (i = 0; i < npages; i++) + { + prev_vpid = (i == 0) ? &last_vpid : &new_page_vpids[i - 1]; + next_vpid = (i == npages - 1) ? &null_vpid : &new_page_vpids[i + 1]; + error_code = heap_add_chain_links (thread_p, hfid, &new_page_vpids[i], next_vpid, prev_vpid, &page_watcher, + false, false); + if (error_code != NO_ERROR) + { + ASSERT_ERROR (); + goto error; + } + } + + error_code = heap_get_last_page (thread_p, hfid, heap_hdr, NULL, &last_vpid, &last_pg_watcher); + if (error_code != NO_ERROR) + { + ASSERT_ERROR (); + goto error; + } + if (last_pg_watcher.pgptr == NULL) + { + assert_release (false); + error_code = ER_FAILED; + goto error; + } + + if (last_pg_watcher.pgptr == heap_hdr_watcher.pgptr) + { + heap_hdr->next_vpid = new_page_vpids[0]; + } + else + { + last_chain = heap_get_chain_ptr (thread_p, last_pg_watcher.pgptr); + if (last_chain == NULL) + { + assert_release (false); + error_code = ER_FAILED; + goto error; + } + + last_chain_prev = *last_chain; + last_chain->next_vpid = new_page_vpids[0]; + + addr.pgptr = last_pg_watcher.pgptr; + addr.vfid = &hfid->vfid; + addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; + log_append_undoredo_data (thread_p, RVHF_CHAIN, &addr, sizeof (HEAP_CHAIN), sizeof (HEAP_CHAIN), + &last_chain_prev, last_chain); + pgbuf_set_dirty (thread_p, addr.pgptr, DONT_FREE); + } + + pgbuf_ordered_unfix (thread_p, &last_pg_watcher); + last_pg_watcher.pgptr = NULL; + + heap_hdr->last_vpid = new_page_vpids[npages - 1]; + heap_hdr->num_pages += npages; + + addr.pgptr = heap_hdr_watcher.pgptr; + addr.vfid = &hfid->vfid; + addr.offset = HEAP_HEADER_AND_CHAIN_SLOTID; + log_append_undoredo_data (thread_p, RVHF_STATS, &addr, sizeof (HEAP_HDR_STATS), sizeof (HEAP_HDR_STATS), + &heap_hdr_prev, heap_hdr); + pgbuf_set_dirty (thread_p, addr.pgptr, DONT_FREE); + + log_sysop_commit (thread_p); + is_sysop_started = false; + + pgbuf_ordered_unfix (thread_p, &heap_hdr_watcher); + heap_hdr_watcher.pgptr = NULL; + + new_pg_watcher->pgptr = + heap_scan_pb_lock_and_fetch (thread_p, &new_page_vpids[0], OLD_PAGE, X_LOCK, NULL, new_pg_watcher); + if (new_pg_watcher->pgptr == NULL) + { + ASSERT_ERROR_AND_SET (error_code); + return error_code; + } + + assert (pgbuf_is_page_fixed_by_thread (thread_p, &new_page_vpids[0])); + return NO_ERROR; + +error: + if (page_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &page_watcher); + page_watcher.pgptr = NULL; + } + + if (last_pg_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &last_pg_watcher); + last_pg_watcher.pgptr = NULL; + } + + if (is_sysop_started) + { + log_sysop_abort (thread_p); + } + + if (heap_hdr_watcher.pgptr != NULL) + { + pgbuf_ordered_unfix (thread_p, &heap_hdr_watcher); + heap_hdr_watcher.pgptr = NULL; + } + + return error_code; +} + int heap_nonheader_page_capacity () { @@ -26791,8 +26310,8 @@ heap_update_and_log_header (THREAD_ENTRY * thread_p, const HFID * hfid, const PG heap_hdr_prev = *heap_hdr; // Now add the info to the header. - heap_hdr->estimates.last_vpid = new_last_vpid; - heap_hdr->estimates.num_pages += new_num_pages; + heap_hdr->last_vpid = new_last_vpid; + heap_hdr->num_pages += new_num_pages; heap_hdr->next_vpid = new_next_vpid; // Log this change. diff --git a/src/storage/heap_file.h b/src/storage/heap_file.h index a4c1e97d6ce..9fb275f85f7 100644 --- a/src/storage/heap_file.h +++ b/src/storage/heap_file.h @@ -99,9 +99,6 @@ class record_descriptor; } \ while (0) -/* A good space to accept insertions */ -#define HEAP_DROP_FREE_SPACE (int)(DB_PAGESIZE * 0.3) - /* * Heap scan structures */ @@ -116,13 +113,6 @@ struct heap_mvcc_delete_info MVCC_SATISFIES_DELETE_RESULT satisfies_delete_result; /* can delete row? */ }; -typedef struct heap_bestspace HEAP_BESTSPACE; -struct heap_bestspace -{ - VPID vpid; /* Vpid of one of the best pages */ - int freespace; /* Estimated free space in this page */ -}; - typedef struct heap_scancache_node HEAP_SCANCACHE_NODE; struct heap_scancache_node { @@ -430,10 +420,11 @@ extern SCAN_CODE heap_next_record_info (THREAD_ENTRY * thread_p, const HFID * hf DB_VALUE ** cache_recordinfo); extern SCAN_CODE heap_prev (THREAD_ENTRY * thread_p, const HFID * hfid, OID * class_oid, OID * prev_oid, RECDES * recdes, HEAP_SCANCACHE * scan_cache, int ispeeking); -extern SCAN_CODE heap_page_next_fix_old (THREAD_ENTRY * thread_p, HFID * hfid, VPID * curr_vpid, - HEAP_SCANCACHE * scan_cache); extern SCAN_CODE heap_next_1page (THREAD_ENTRY * thread_p, const HFID * hfid, const VPID * vpid, OID * class_oid, OID * next_oid, RECDES * recdes, HEAP_SCANCACHE * scan_cache, int ispeeking); + +extern bool heap_page_is_bestspace (THREAD_ENTRY * thread_p, PAGE_PTR page_heap); + extern SCAN_CODE heap_prev_record_info (THREAD_ENTRY * thread_p, const HFID * hfid, OID * class_oid, OID * next_oid, RECDES * recdes, HEAP_SCANCACHE * scan_cache, int ispeeking, DB_VALUE ** cache_recordinfo); @@ -679,8 +670,9 @@ extern int heap_rv_nop (THREAD_ENTRY * thread_p, LOG_RCV * rcv); extern int heap_rv_update_chain_after_mvcc_op (THREAD_ENTRY * thread_p, LOG_RCV * rcv); extern INT16 heap_rv_remove_flags_from_offset (INT16 offset); -extern void heap_stats_update (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, const HFID * hfid, int prev_freespace); -extern bool heap_should_try_update_stat (const int current_freespace, const int prev_freespace); +extern void heap_add_bestpage (THREAD_ENTRY * thread_p, HFID * hfid, PAGE_PTR pgptr, std::uint16_t prev_freespace = + 0, PGBUF_WATCHER * header_watcher = NULL); + extern int heap_rv_mvcc_redo_redistribute (THREAD_ENTRY * thread_p, LOG_RCV * rcv); extern int heap_vacuum_all_objects (THREAD_ENTRY * thread_p, HEAP_SCANCACHE * upd_scancache, MVCCID threshold_mvccid); extern SCAN_CODE heap_get_visible_version (THREAD_ENTRY * thread_p, const OID * oid, OID * class_oid, RECDES * recdes, @@ -703,7 +695,6 @@ extern SCAN_CODE heap_get_visible_version_internal (THREAD_ENTRY * thread_p, HEA extern SCAN_CODE heap_get_class_record (THREAD_ENTRY * thread_p, const OID * class_oid, RECDES * recdes_p, HEAP_SCANCACHE * scan_cache, int ispeeking); extern int heap_rv_undo_ovf_update (THREAD_ENTRY * thread_p, LOG_RCV * rcv); -extern int heap_get_best_space_num_stats_entries (void); extern int heap_get_hfid_from_vfid (THREAD_ENTRY * thread_p, const VFID * vfid, HFID * hfid); extern int heap_scan_cache_allocate_area (THREAD_ENTRY * thread_p, HEAP_SCANCACHE * scan_cache_p, int size); @@ -711,6 +702,8 @@ extern bool heap_is_page_header (THREAD_ENTRY * thread_p, PAGE_PTR page); extern int heap_alloc_new_page (THREAD_ENTRY * thread_p, HFID * hfid, OID class_oid, PGBUF_WATCHER * home_hint_p, VPID * new_page_vpid); +extern int heap_alloc_new_pages (THREAD_ENTRY * thread_p, HFID * hfid, int npages, VPID * new_page_vpids, + PGBUF_WATCHER * new_pg_watcher); extern int heap_nonheader_page_capacity (); diff --git a/src/storage/page_buffer.c b/src/storage/page_buffer.c index 899490a825e..88cd3335014 100644 --- a/src/storage/page_buffer.c +++ b/src/storage/page_buffer.c @@ -12332,7 +12332,7 @@ pgbuf_ordered_fix_release (THREAD_ENTRY * thread_p, const VPID * req_vpid, PAGE_ * cannot be fixed */ if (er_status == NO_ERROR) { - /* LK_FORCE_ZERO_WAIT is used in some page scan functions (e.g. heap_stats_find_page_in_bestspace) to + /* LK_FORCE_ZERO_WAIT is used in some page scan functions (e.g. heap_find_bestpage) to * skip busy pages; here we return an error code (which means the page was not fixed), however no error * is set : this allows scan of pages to continue */ assert (wait_msecs == LK_FORCE_ZERO_WAIT); @@ -12967,6 +12967,335 @@ pgbuf_ordered_fix_release (THREAD_ENTRY * thread_p, const VPID * req_vpid, PAGE_ return er_status; } +/* + * pgbuf_ordered_callback () - Temporarily unfix all ordered pages while executing a callback. + * return: error code + * thread_p (in): thread entry + * callback_func (in): callback executed without any fixed pages + * callback_args (in): callback arguments + * + * Note: All pages fixed by the current thread must be ordered pages and every fix must have a watcher. The callback + * must not leave any page fixed. Previously fixed pages are re-fixed in page order even when the callback + * returns an error. If re-fixing fails, some watchers may remain without a fixed page and callers must check + * watcher page pointers before using them. + */ +#if !defined(NDEBUG) +int +pgbuf_ordered_callback_debug (THREAD_ENTRY * thread_p, PGBUF_ORDERED_CALLBACK_FUNC callback_func, + void *callback_args, const char *caller_file, int caller_line, const char *caller_func) +#else /* NDEBUG */ +int +pgbuf_ordered_callback_release (THREAD_ENTRY * thread_p, PGBUF_ORDERED_CALLBACK_FUNC callback_func, void *callback_args) +#endif /* NDEBUG */ +{ + PGBUF_HOLDER_INFO ordered_holders_info[PGBUF_MAX_PAGE_FIXED_BY_TRAN]; + PGBUF_HOLDER_INFO *holder_info; + PGBUF_BUFFER_HASH *hash_anchor; + PGBUF_HOLDER *holder; + PGBUF_WATCHER *watcher; + PGBUF_BCB *bufptr; + PAGE_PTR pgptr; + PAGE_PTR refixed_pgptr; + PGBUF_ORDERED_GROUP group_id; + PGBUF_ORDERED_RANK rank; + int callback_status = NO_ERROR; + int er_status = NO_ERROR; + int previous_er_status; + int saved_pages_cnt = 0; + int holder_fix_cnt; + int fixed_count; + int thrd_idx; + int i, j; + + assert (thread_p != NULL); + assert (callback_func != NULL); + + if (!thread_p || !callback_func) + { + return ER_FAILED_ASSERTION; + } + + thrd_idx = thread_get_entry_index (thread_p); + + /* validate and save all holders before unfixing anything. */ + for (holder = pgbuf_Pool.thrd_holder_info[thrd_idx].thrd_hold_list; holder != NULL; holder = holder->thrd_link) + { + if (saved_pages_cnt >= PGBUF_MAX_PAGE_FIXED_BY_TRAN) + { + assert_release (false); + er_status = ER_FAILED_ASSERTION; + goto exit; + } + + if (holder->watch_count <= 0 || holder->fix_count != holder->watch_count + || holder->watch_count > PGBUF_MAX_PAGE_WATCHERS) + { + /* raw fix cannot be restored because there is no watcher whose page pointer can be updated. */ + assert_release (false); + er_status = ER_FAILED_ASSERTION; + goto exit; + } + + if (!PGBUF_IS_ORDERED_PAGETYPE (holder->bufptr->iopage_buffer->iopage.prv.ptype)) + { + assert_release (false); + er_status = ER_FAILED_ASSERTION; + goto exit; + } + + holder_info = &ordered_holders_info[saved_pages_cnt]; + holder_info->watch_count = 0; + holder_info->latch_mode = PGBUF_LATCH_READ; + holder_info->ptype = (PAGE_TYPE) holder->bufptr->iopage_buffer->iopage.prv.ptype; + holder_info->prevent_dealloc = false; + VPID_COPY (&holder_info->vpid, &holder->bufptr->vpid); + VPID_SET_NULL (&group_id); + rank = PGBUF_ORDERED_RANK_UNDEFINED; + + CAST_BFPTR_TO_PGPTR (pgptr, holder->bufptr); + for (watcher = holder->first_watcher, j = 0; watcher != NULL; watcher = watcher->next, j++) + { + if (j >= PGBUF_MAX_PAGE_WATCHERS) + { + assert_release (false); + er_status = ER_FAILED_ASSERTION; + goto exit; + } + +#if !defined(NDEBUG) + assert (watcher->magic == PGBUF_WATCHER_MAGIC_NUMBER); +#endif + + if (watcher->pgptr != pgptr || watcher->curr_rank >= PGBUF_ORDERED_RANK_UNDEFINED + || (watcher->latch_mode != PGBUF_LATCH_READ && watcher->latch_mode != PGBUF_LATCH_WRITE)) + { + assert_release (false); + er_status = ER_FAILED_ASSERTION; + goto exit; + } + + if (j == 0) + { + VPID_COPY (&group_id, &watcher->group_id); + rank = (PGBUF_ORDERED_RANK) watcher->curr_rank; + } + else if (!VPID_EQ (&group_id, &watcher->group_id) || rank != watcher->curr_rank) + { + assert_release (false); + er_status = ER_FAILED_ASSERTION; + goto exit; + } + + holder_info->watcher[j] = watcher; + if (watcher->latch_mode == PGBUF_LATCH_WRITE) + { + holder_info->latch_mode = PGBUF_LATCH_WRITE; + } + } + + if (j != holder->watch_count) + { + assert_release (false); + er_status = ER_FAILED_ASSERTION; + goto exit; + } + + holder_info->watch_count = j; + VPID_COPY (&holder_info->group_id, &group_id); + holder_info->rank = rank; + saved_pages_cnt++; + } + + if (saved_pages_cnt > 1) + { + qsort (ordered_holders_info, saved_pages_cnt, sizeof (ordered_holders_info[0]), pgbuf_compare_hold_vpid_for_sort); + } + + /* unfix all saved pages. deallocation is prevented until each page is restored. */ + for (i = 0; i < saved_pages_cnt; i++) + { + holder_info = &ordered_holders_info[i]; + for (holder = pgbuf_Pool.thrd_holder_info[thrd_idx].thrd_hold_list; holder != NULL; holder = holder->thrd_link) + { + if (VPID_EQ (&holder_info->vpid, &holder->bufptr->vpid)) + { + break; + } + } + + assert (holder != NULL); + assert (holder->fix_count == holder_info->watch_count); + assert (holder->watch_count == holder_info->watch_count); + + holder_fix_cnt = holder->fix_count; + holder->watch_count = 0; + holder->first_watcher = NULL; + holder->last_watcher = NULL; + + CAST_BFPTR_TO_PGPTR (pgptr, holder->bufptr); + /* this must be called */ + pgbuf_bcb_register_avoid_deallocation (holder->bufptr); + holder_info->prevent_dealloc = true; + + while (holder_fix_cnt-- > 0) + { +#if !defined(NDEBUG) + pgbuf_unfix_debug (thread_p, pgptr, caller_file, caller_line, caller_func); +#else + pgbuf_unfix (thread_p, pgptr); +#endif + } + + for (j = 0; j < holder_info->watch_count; j++) + { + watcher = holder_info->watcher[j]; + assert (watcher->pgptr == pgptr); + PGBUF_CLEAR_WATCHER (watcher); + watcher->page_was_unfixed = true; +#if !defined(NDEBUG) + pgbuf_watcher_init_debug (watcher, caller_file, caller_line, true); +#endif + } + } + + assert (pgbuf_Pool.thrd_holder_info[thrd_idx].thrd_hold_list == NULL); + + callback_status = callback_func (thread_p, callback_args); + + /* only callback functions that do not fix any pages are allowed. */ + /* it must not leave a page fixed. */ + assert (pgbuf_Pool.thrd_holder_info[thrd_idx].thrd_hold_list == NULL); + + /* restore every page in the same global order used by ordered fix. */ + for (i = 0; i < saved_pages_cnt; i++) + { + holder_info = &ordered_holders_info[i]; + pgptr = NULL; + fixed_count = 0; + + for (j = 0; j < holder_info->watch_count; j++) + { +#if !defined(NDEBUG) + refixed_pgptr = pgbuf_fix_debug (thread_p, &holder_info->vpid, OLD_PAGE, holder_info->latch_mode, + PGBUF_UNCONDITIONAL_LATCH, caller_file, caller_line, caller_func); +#else + refixed_pgptr = pgbuf_fix_release (thread_p, &holder_info->vpid, OLD_PAGE, holder_info->latch_mode, + PGBUF_UNCONDITIONAL_LATCH); +#endif + if (refixed_pgptr == NULL) + { + break; + } + if (pgptr == NULL) + { + pgptr = refixed_pgptr; + } + else + { + assert (pgptr == refixed_pgptr); + } + fixed_count++; + } + + if (fixed_count != holder_info->watch_count) + { + previous_er_status = er_errid (); + if (previous_er_status == NO_ERROR) + { + previous_er_status = ER_FAILED; + } + + while (fixed_count-- > 0) + { +#if !defined(NDEBUG) + pgbuf_unfix_debug (thread_p, pgptr, caller_file, caller_line, caller_func); +#else + pgbuf_unfix (thread_p, pgptr); +#endif + } + + if (previous_er_status == ER_INTERRUPTED) + { + er_status = ER_INTERRUPTED; + } + else + { + er_status = ER_PB_ORDERED_REFIX_FAILED; + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, er_status, 3, holder_info->vpid.volid, + holder_info->vpid.pageid, previous_er_status); + } + goto exit; + } + + holder = pgbuf_get_holder (thread_p, pgptr); + if (holder == NULL) + { + assert_release (false); + while (fixed_count-- > 0) + { +#if !defined(NDEBUG) + pgbuf_unfix_debug (thread_p, pgptr, caller_file, caller_line, caller_func); +#else + pgbuf_unfix (thread_p, pgptr); +#endif + } + er_status = ER_FAILED_ASSERTION; + goto exit; + } + + assert (holder_info->prevent_dealloc); + holder_info->prevent_dealloc = false; + CAST_PGPTR_TO_BFPTR (bufptr, pgptr); + pgbuf_bcb_unregister_avoid_deallocation (bufptr); + +#if !defined(NDEBUG) + (void) pgbuf_check_page_ptype (thread_p, pgptr, holder_info->ptype); +#endif + + for (j = 0; j < holder_info->watch_count; j++) + { +#if !defined(NDEBUG) + pgbuf_add_watch_instance_internal (holder, pgptr, holder_info->watcher[j], + (PGBUF_LATCH_MODE) holder_info->watcher[j]->latch_mode, false, + caller_file, caller_line); +#else + pgbuf_add_watch_instance_internal (holder, pgptr, holder_info->watcher[j], + (PGBUF_LATCH_MODE) holder_info->watcher[j]->latch_mode, false); +#endif + } + } + + er_status = callback_status; + +exit: + for (i = 0; i < saved_pages_cnt; i++) + { + if (ordered_holders_info[i].prevent_dealloc) + { + hash_anchor = &pgbuf_Pool.buf_hash_table[PGBUF_HASH_VALUE (&ordered_holders_info[i].vpid)]; + bufptr = pgbuf_search_hash_chain (thread_p, hash_anchor, &ordered_holders_info[i].vpid); + if (bufptr == NULL) + { + assert (false); + pthread_mutex_unlock (&hash_anchor->hash_mutex); + continue; + } + + if (!pgbuf_bcb_should_avoid_deallocation (bufptr)) + { + assert (false); + } + else + { + pgbuf_bcb_unregister_avoid_deallocation (bufptr); + } + PGBUF_BCB_UNLOCK (bufptr); + } + } + + return er_status; +} + /* * pgbuf_get_groupid_and_unfix () - retrieves group identifier of page and performs unlatch if requested. * return: error code diff --git a/src/storage/page_buffer.h b/src/storage/page_buffer.h index 1182ad85ecf..9246370f40a 100644 --- a/src/storage/page_buffer.h +++ b/src/storage/page_buffer.h @@ -248,6 +248,8 @@ struct pgbuf_watcher #endif }; +typedef int (*PGBUF_ORDERED_CALLBACK_FUNC) (THREAD_ENTRY * thread_p, void *args); + // *INDENT-OFF* using pgbuf_aligned_buffer = cubmem::stack_block<(size_t) IO_MAX_PAGE_SIZE>; using pgbuf_resizable_buffer = cubmem::extensible_stack_block<(size_t) IO_MAX_PAGE_SIZE>; @@ -285,6 +287,12 @@ extern int pgbuf_ordered_fix_debug (THREAD_ENTRY * thread_p, const VPID * req_vp const PGBUF_LATCH_MODE requestmode, PGBUF_WATCHER * req_watcher, const char *caller_file, int caller_line, const char *caller_func); +#define pgbuf_ordered_callback(thread_p, callback_func, callback_args) \ + pgbuf_ordered_callback_debug(thread_p, callback_func, callback_args, ARG_FILE_LINE_FUNC) +extern int pgbuf_ordered_callback_debug (THREAD_ENTRY * thread_p, PGBUF_ORDERED_CALLBACK_FUNC callback_func, + void *callback_args, const char *caller_file, int caller_line, + const char *caller_func); + #define pgbuf_promote_read_latch(thread_p, pgptr_p, condition) \ pgbuf_promote_read_latch_debug(thread_p, pgptr_p, condition, ARG_FILE_LINE_FUNC) extern int pgbuf_promote_read_latch_debug (THREAD_ENTRY * thread_p, PAGE_PTR * pgptr_p, @@ -329,6 +337,11 @@ extern PAGE_PTR pgbuf_fix_release (THREAD_ENTRY * thread_p, const VPID * vpid, P extern int pgbuf_ordered_fix_release (THREAD_ENTRY * thread_p, const VPID * req_vpid, PAGE_FETCH_MODE fetch_mode, const PGBUF_LATCH_MODE requestmode, PGBUF_WATCHER * watcher_object); +#define pgbuf_ordered_callback(thread_p, callback_func, callback_args) \ + pgbuf_ordered_callback_release(thread_p, callback_func, callback_args) +extern int pgbuf_ordered_callback_release (THREAD_ENTRY * thread_p, PGBUF_ORDERED_CALLBACK_FUNC callback_func, + void *callback_args); + #define pgbuf_promote_read_latch(thread_p, pgptr_p, condition) \ pgbuf_promote_read_latch_release(thread_p, pgptr_p, condition) extern int pgbuf_promote_read_latch_release (THREAD_ENTRY * thread_p, PAGE_PTR * pgptr_p, diff --git a/src/storage/slotted_page.c b/src/storage/slotted_page.c index b3118d12296..ddcc177f31e 100644 --- a/src/storage/slotted_page.c +++ b/src/storage/slotted_page.c @@ -912,10 +912,9 @@ spage_get_free_space (THREAD_ENTRY * thread_p, PAGE_PTR page_p) * return: Total free space * * page_p(in): Pointer to slotted page - * need_update(out): Value of need_update_best_hint */ int -spage_get_free_space_without_saving (THREAD_ENTRY * thread_p, PAGE_PTR page_p, bool * need_update) +spage_get_free_space_without_saving (THREAD_ENTRY * thread_p, PAGE_PTR page_p) { SPAGE_HEADER *page_header_p; int free_space; @@ -925,11 +924,6 @@ spage_get_free_space_without_saving (THREAD_ENTRY * thread_p, PAGE_PTR page_p, b page_header_p = (SPAGE_HEADER *) page_p; SPAGE_VERIFY_HEADER (page_header_p); - if (need_update != NULL) - { - *need_update = page_header_p->need_update_best_hint; - } - free_space = page_header_p->total_free; if (free_space < 0) { /* defense code */ @@ -941,29 +935,6 @@ spage_get_free_space_without_saving (THREAD_ENTRY * thread_p, PAGE_PTR page_p, b return free_space; } -/* - * spage_set_need_update_best_hint () - Set need_update_best_hint on slotted page header - * return: void - * - * page_p(in): Pointer to slotted page - * need_update(in): Value of need_update_best_hint - * - * NOTE: We will set this value as TRUE when we failed updating best page hints - * on heap header. See heap_stats_update function. - */ -void -spage_set_need_update_best_hint (THREAD_ENTRY * thread_p, PAGE_PTR page_p, bool need_update) -{ - SPAGE_HEADER *page_header_p; - - assert (page_p != NULL); - - page_header_p = (SPAGE_HEADER *) page_p; - SPAGE_VERIFY_HEADER (page_header_p); - - page_header_p->need_update_best_hint = need_update; -} - /* * spage_max_space_for_new_record () - Find the maximum free space for a new * insertion @@ -1106,7 +1077,6 @@ spage_initialize (THREAD_ENTRY * thread_p, PAGE_PTR page_p, INT16 slot_type, uns page_header_p->num_records = 0; page_header_p->is_saving = is_saving; page_header_p->flags = SPAGE_HEADER_FLAG_NONE; - page_header_p->need_update_best_hint = 0; page_header_p->reserved_bits = 0; page_header_p->reserved1 = 0; @@ -4216,8 +4186,8 @@ spage_dump_header_to_string (char *buffer, int size, const SPAGE_HEADER * page_h n += snprintf (buffer + n, size - n, "TOTAL FREE AREA = %d, CONTIGUOUS FREE AREA = %d," - " FREE SPACE OFFSET = %d, NEED UPDATE BEST HINT = %d\n", page_header_p->total_free, - page_header_p->cont_free, page_header_p->offset_to_free_area, page_header_p->need_update_best_hint); + " FREE SPACE OFFSET = %d\n", page_header_p->total_free, + page_header_p->cont_free, page_header_p->offset_to_free_area); n += snprintf (buffer + n, size - n, "IS_SAVING = %d\n", page_header_p->is_saving); } @@ -4770,7 +4740,6 @@ spage_get_page_header_info (PAGE_PTR page_p, DB_VALUE ** page_header_info) db_make_int (page_header_info[HEAP_PAGE_INFO_CONT_FREE], page_header_p->cont_free); db_make_int (page_header_info[HEAP_PAGE_INFO_OFFSET_TO_FREE_AREA], page_header_p->offset_to_free_area); db_make_int (page_header_info[HEAP_PAGE_INFO_IS_SAVING], page_header_p->is_saving); - db_make_int (page_header_info[HEAP_PAGE_INFO_UPDATE_BEST], page_header_p->need_update_best_hint); return S_SUCCESS; } @@ -5049,9 +5018,6 @@ spage_header_next_scan (THREAD_ENTRY * thread_p, int cursor, DB_VALUE ** out_val db_make_int (out_values[idx], header->offset_to_free_area); idx++; - db_make_int (out_values[idx], header->need_update_best_hint); - idx++; - db_make_int (out_values[idx], header->is_saving); idx++; diff --git a/src/storage/slotted_page.h b/src/storage/slotted_page.h index e8f181f6713..79aa4af7379 100644 --- a/src/storage/slotted_page.h +++ b/src/storage/slotted_page.h @@ -74,13 +74,11 @@ struct spage_header int reserved1; int flags; /* Page flags: Always SPAGE_HEADER_FLAG_NONE, not currently used */ unsigned int is_saving:1; /* True if saving is need for recovery (undo) */ - unsigned int need_update_best_hint:1; /* True if we should update best pages hint for this page. See - * heap_stats_update. */ /* The followings are reserved for future use. */ /* SPAGE_HEADER should be 8 bytes aligned. Packing of bit fields depends on compiler's behavior. It's better to use * 4-bytes type in order not to be affected by the compiler. */ - unsigned int reserved_bits:30; + unsigned int reserved_bits:31; }; /* 4-byte disk storage slot design */ @@ -97,8 +95,7 @@ extern void spage_finalize (THREAD_ENTRY * thread_p); extern void spage_free_saved_spaces (THREAD_ENTRY * thread_p, void *first_save_entry); extern int spage_get_free_space (THREAD_ENTRY * thread_p, PAGE_PTR pgptr); -extern int spage_get_free_space_without_saving (THREAD_ENTRY * thread_p, PAGE_PTR page_p, bool * need_update); -extern void spage_set_need_update_best_hint (THREAD_ENTRY * thread_p, PAGE_PTR page_p, bool need_update); +extern int spage_get_free_space_without_saving (THREAD_ENTRY * thread_p, PAGE_PTR page_p); extern PGNSLOTS spage_number_of_records (PAGE_PTR pgptr); extern PGNSLOTS spage_number_of_slots (PAGE_PTR pgptr); extern void spage_initialize (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, INT16 slots_type, unsigned short alignment, diff --git a/src/storage/storage_common.h b/src/storage/storage_common.h index 49e1f1a5000..b74debc2468 100644 --- a/src/storage/storage_common.h +++ b/src/storage/storage_common.h @@ -496,7 +496,6 @@ typedef enum HEAP_PAGE_INFO_CONT_FREE, HEAP_PAGE_INFO_OFFSET_TO_FREE_AREA, HEAP_PAGE_INFO_IS_SAVING, - HEAP_PAGE_INFO_UPDATE_BEST, /* leave this last */ HEAP_PAGE_INFO_COUNT, diff --git a/src/transaction/locator_sr.c b/src/transaction/locator_sr.c index e6232b46ee7..06dea29e473 100644 --- a/src/transaction/locator_sr.c +++ b/src/transaction/locator_sr.c @@ -12797,6 +12797,25 @@ redistribute_partition_data (THREAD_ENTRY * thread_p, OID * class_oid, int no_oi } } + if (heap_page_is_bestspace (thread_p, scan_cache.page_watcher.pgptr)) + { + error = heap_vpid_next (thread_p, &hfid, scan_cache.page_watcher.pgptr, &vpid); + if (error != NO_ERROR) + { + goto exit; + } + + /* keep latch on current page until the next page is fixed */ + pgbuf_replace_watcher (thread_p, &scan_cache.page_watcher, &old_page_watcher); + + if (VPID_ISNULL (&vpid)) + { + /* no more pages in the current heap file */ + is_scan_end = true; + } + continue; + } + slotid = 0; while (true) { diff --git a/src/transaction/recovery.c b/src/transaction/recovery.c index 27057cf4959..08f14eb265a 100644 --- a/src/transaction/recovery.c +++ b/src/transaction/recovery.c @@ -841,6 +841,12 @@ struct rvfun RV_fun[] = { heap_rv_lob_remove_dir, NULL, NULL}, + {RVHF_UPDATE_BESTSPACE_ENTRIES, + "RVHF_UPDATE_BESTSPACE_ENTRIES", + NULL, + heap_rv_redo_update, + NULL, + log_rv_dump_hexa}, }; /* diff --git a/src/transaction/recovery.h b/src/transaction/recovery.h index 42c2e330a44..5611768d3b0 100644 --- a/src/transaction/recovery.h +++ b/src/transaction/recovery.h @@ -184,8 +184,9 @@ typedef enum RVPGBUF_SET_TDE_ALGORITHM = 127, RVFL_FHEAD_SET_TDE_ALGORITHM = 128, RVHF_LOB_REMOVE_DIR = 129, + RVHF_UPDATE_BESTSPACE_ENTRIES = 130, - RV_LAST_LOGID = RVHF_LOB_REMOVE_DIR, + RV_LAST_LOGID = RVHF_UPDATE_BESTSPACE_ENTRIES, RV_NOT_DEFINED = 999 } LOG_RCVINDEX;